From f5c330abb65b94a5fbdcd1e09977b9269402801c Mon Sep 17 00:00:00 2001 From: guowenxue <guowenxue@gmail.com> Date: Thu, 04 Apr 2024 02:47:30 +0800 Subject: [PATCH] Driver:IGKBoard-All: Add x86_64 linux driver --- kernel/drivers/x86_64/Makefile | 25 + kernel/drivers/x86_64/testapp/makefile | 32 ++ kernel/drivers/x86_64/ldd1_hello.c | 20 + kernel/drivers/x86_64/testapp/app2_chrdev.c | 74 +++++ kernel/drivers/x86_64/testapp/app3_ioctl.c | 68 +++++ kernel/drivers/x86_64/ldd2_chrdev.c | 230 +++++++++++++++++ kernel/drivers/x86_64/ldd3_ioctl.c | 295 ++++++++++++++++++++++ 7 files changed, 744 insertions(+), 0 deletions(-) diff --git a/kernel/drivers/x86_64/Makefile b/kernel/drivers/x86_64/Makefile new file mode 100644 index 0000000..a14dce4 --- /dev/null +++ b/kernel/drivers/x86_64/Makefile @@ -0,0 +1,25 @@ +# Linux driver build kernel source code path on X86 server +KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build + +ARCH=${shell uname -p} + +# Fix bug: module verification failed: signature and/or required key missing - tainting kernel +CONFIG_MODULE_SIG=n + +# Linux kernel modules +obj-m += ldd1_hello.o +obj-m += ldd2_chrdev.o +obj-m += ldd3_ioctl.o + +modules: + $(MAKE) -C $(KERNAL_DIR) M=$(shell pwd) modules + @make clear + +clear: + @rm -f *.o .*.cmd *.cmd *.mod *.mod.c + @rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f + @rm -f .*ko.cmd .*.o.cmd .*.o.d + @rm -f *.unsigned + +clean: + @rm -f *.ko diff --git a/kernel/drivers/x86_64/ldd1_hello.c b/kernel/drivers/x86_64/ldd1_hello.c new file mode 100644 index 0000000..93a0e12 --- /dev/null +++ b/kernel/drivers/x86_64/ldd1_hello.c @@ -0,0 +1,20 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> + +static __init int hello_init(void) +{ + printk(KERN_ALERT "hello module installed.\n"); + return 0; +} + +static __exit void hello_exit(void) +{ + printk(KERN_ALERT "hello module removed.\n"); +} + +module_init(hello_init); +module_exit(hello_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("GuoWenxue <guowenxue@gmail.com>"); diff --git a/kernel/drivers/x86_64/ldd2_chrdev.c b/kernel/drivers/x86_64/ldd2_chrdev.c new file mode 100644 index 0000000..11492f5 --- /dev/null +++ b/kernel/drivers/x86_64/ldd2_chrdev.c @@ -0,0 +1,230 @@ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> /* printk() */ +#include <linux/slab.h> /* kmalloc() */ +#include <linux/fs.h> /* everything... */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/cdev.h> /* cdev */ +#include <linux/fcntl.h> /* O_ACCMODE */ +#include <linux/uaccess.h> /* copy_*_user */ +#include <linux/version.h> /* kernel version code */ +#include <linux/moduleparam.h> + +/* device name and major number */ +#define DEV_NAME "chrdev" +#define DEV_SIZE 1024 +#define CONFIG_AUTODEV 1 /* Auto create device node in driver or not */ + +//#define DEV_MAJOR 79 +#ifndef DEV_MAJOR +#define DEV_MAJOR 0 +#endif + +int dev_major = DEV_MAJOR; +module_param(dev_major, int, S_IRUGO); + +typedef struct chrdev_s +{ + struct cdev cdev; +#ifdef CONFIG_AUTODEV + struct class *class; + struct device *device; +#endif + char *data; /* data buffer */ + uint32_t size; /* data buffer size */ + uint32_t bytes; /* data bytes in the buffer */ +} chrdev_t; + +static struct chrdev_s dev; + +static ssize_t chrdev_read (struct file *file, char __user *buf, size_t count, loff_t *f_pos) +{ + struct chrdev_s *dev = file->private_data; + ssize_t nbytes; + ssize_t rv = 0; + + /* no data in buffer */ + if( !dev->bytes ) + return 0; + + /* copy data to user space */ + nbytes = count>dev->bytes ? dev->bytes : count; + if( copy_to_user(buf, dev->data, nbytes) ) + { + rv = -EFAULT; + goto out; + } + + /* update return value and data bytes in buffer */ + rv = nbytes; + dev->bytes -= nbytes; + +out: + return rv; +} + +static ssize_t chrdev_write (struct file *file, const char __user *buf, size_t count, loff_t *f_pos) +{ + struct chrdev_s *dev = file->private_data; + ssize_t nbytes; + ssize_t rv = 0; + + /* no space left */ + if( dev->bytes >= dev->size ) + return -ENOSPC; + + /* check copy data bytes */ + if( dev->size - dev->bytes < count) + nbytes = dev->size - dev->bytes; + else + nbytes = count; + + /* copy data from user space */ + if( copy_from_user(&dev->data[dev->bytes], buf, nbytes) ) + { + rv = -EFAULT; + goto out; + } + + /* update return value and data bytes in buffer */ + rv = nbytes; + dev->bytes += nbytes; + +out: + return rv; +} + +static int chrdev_open (struct inode *inode, struct file *file) +{ + struct chrdev_s *dev; /* device struct address */ + + /* get the device struct address by container_of() */ + dev = container_of(inode->i_cdev, struct chrdev_s, cdev); + + /* save the device struct address for other methods */ + file->private_data = dev; + + return 0; +} + +static int chrdev_close (struct inode *node, struct file *file) +{ + return 0; +} + +static struct file_operations chrdev_fops = { + .owner = THIS_MODULE, + .open = chrdev_open, /* open() implementation */ + .read = chrdev_read, /* read() implementation */ + .write = chrdev_write, /* write() implementation */ + .release = chrdev_close, /* close() implementation */ +}; + +static int __init chrdev_init(void) +{ + dev_t devno; + int rv; + + /* malloc and initial device read/write buffer */ + dev.data = kmalloc(DEV_SIZE, GFP_KERNEL); + if( !dev.data ) + { + printk(KERN_ERR " %s driver kmalloc() failed\n", DEV_NAME); + return -ENOMEM; + } + dev.size = DEV_SIZE; + dev.bytes = 0; + memset(dev.data, 0, dev.size); + + /* dynamic alloc device node major number if not set */ + if(0 != dev_major) + { + devno = MKDEV(dev_major, 0); + rv = register_chrdev_region(devno, 1, DEV_NAME); + } + else + { + rv = alloc_chrdev_region(&devno, 0, 1, DEV_NAME); + dev_major = MAJOR(devno); + } + + if(rv < 0) + { + printk(KERN_ERR "%s driver can't use major %d\n", DEV_NAME, dev_major); + return -ENODEV; + } + + /* setup and register cdev into kernel */ + cdev_init(&dev.cdev, &chrdev_fops); + dev.cdev.owner = THIS_MODULE; + rv = cdev_add(&dev.cdev, devno, 1); + if( rv ) + { + rv = -ENODEV; + printk(KERN_ERR "%s driver regist failed, rv=%d\n", DEV_NAME, rv); + goto failed1; + } + +#ifdef CONFIG_AUTODEV + /* create device node in user space */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0) + dev.class = class_create(DEV_NAME); +#else + dev.class = class_create(THIS_MODULE, DEV_NAME); +#endif + if (IS_ERR(dev.class)) { + rv = PTR_ERR(dev.class); + goto failed2; + } + + dev.device = device_create(dev.class, NULL, MKDEV(dev_major, 0), NULL, DEV_NAME); + if( !dev.device ) + { + rv = -ENODEV; + printk(KERN_ERR "%s driver create device failed\n", DEV_NAME); + goto failed3; + } +#endif + + printk(KERN_INFO "%s driver on major[%d] installed.\n", DEV_NAME, dev_major); + return 0; + +#ifdef CONFIG_AUTODEV +failed3: + class_destroy(dev.class); + +failed2: + cdev_del(&dev.cdev); +#endif + +failed1: + unregister_chrdev_region(devno, 1); + kfree(dev.data); + + printk(KERN_ERR "%s driver installed failed.\n", DEV_NAME); + return rv; +} + +static void __exit chrdev_exit(void) +{ +#ifdef CONFIG_AUTODEV + device_del(dev.device); + class_destroy(dev.class); +#endif + + cdev_del(&dev.cdev); + unregister_chrdev_region(MKDEV(dev_major,0), 1); + + kfree(dev.data); + + printk(KERN_INFO "%s driver removed!\n", DEV_NAME); + return; +} + +module_init(chrdev_init); +module_exit(chrdev_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("GuoWenxue <guowenxue@gmail.com>"); diff --git a/kernel/drivers/x86_64/ldd3_ioctl.c b/kernel/drivers/x86_64/ldd3_ioctl.c new file mode 100644 index 0000000..295808d --- /dev/null +++ b/kernel/drivers/x86_64/ldd3_ioctl.c @@ -0,0 +1,295 @@ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> /* printk() */ +#include <linux/slab.h> /* kmalloc() */ +#include <linux/fs.h> /* everything... */ +#include <linux/errno.h> /* error codes */ +#include <linux/types.h> /* size_t */ +#include <linux/cdev.h> /* cdev */ +#include <linux/fcntl.h> /* O_ACCMODE */ +#include <linux/uaccess.h> /* copy_*_user */ +#include <linux/version.h> /* kernel version code */ +#include <linux/moduleparam.h> + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0) +#define access_ok_wrapper(type,arg,cmd) access_ok(type, arg, cmd) +#else +#define access_ok_wrapper(type,arg,cmd) access_ok(arg, cmd) +#endif + +/* ioctl definitions, use 'z' as magic number */ +#define CHRDEV_IOC_MAGIC 'z' +#define CHRDEV_IOCRESET _IO(CHRDEV_IOC_MAGIC, 0) +#define CHRDEV_IOCSET _IOW(CHRDEV_IOC_MAGIC, 1, int) +#define CHRDEV_IOCGET _IOR(CHRDEV_IOC_MAGIC, 2, int) +#define CHRDEV_IOC_MAXNR 3 + +/* device name and major number */ +#define DEV_NAME "chrdev" +#define DEV_SIZE 1024 +#define CONFIG_AUTODEV 1 /* Auto create device node in driver or not */ + +//#define DEV_MAJOR 79 +#ifndef DEV_MAJOR +#define DEV_MAJOR 0 +#endif + +int dev_major = DEV_MAJOR; +module_param(dev_major, int, S_IRUGO); + +typedef struct chrdev_s +{ + struct cdev cdev; +#ifdef CONFIG_AUTODEV + struct class *class; + struct device *device; +#endif + char *data; /* data buffer */ + uint32_t size; /* data buffer size */ + uint32_t bytes; /* data bytes in the buffer */ +} chrdev_t; + +static struct chrdev_s dev; + +static ssize_t chrdev_read (struct file *file, char __user *buf, size_t count, loff_t *f_pos) +{ + struct chrdev_s *dev = file->private_data; + ssize_t nbytes; + ssize_t rv = 0; + + /* no data in buffer */ + if( !dev->bytes ) + return 0; + + /* copy data to user space */ + nbytes = count>dev->bytes ? dev->bytes : count; + if( copy_to_user(buf, dev->data, nbytes) ) + { + rv = -EFAULT; + goto out; + } + + /* update return value and data bytes in buffer */ + rv = nbytes; + dev->bytes -= nbytes; + +out: + return rv; +} + +static ssize_t chrdev_write (struct file *file, const char __user *buf, size_t count, loff_t *f_pos) +{ + struct chrdev_s *dev = file->private_data; + ssize_t nbytes; + ssize_t rv = 0; + + /* no space left */ + if( dev->bytes >= dev->size ) + return -ENOSPC; + + /* check copy data bytes */ + if( dev->size - dev->bytes < count) + nbytes = dev->size - dev->bytes; + else + nbytes = count; + + /* copy data from user space */ + if( copy_from_user(&dev->data[dev->bytes], buf, nbytes) ) + { + rv = -EFAULT; + goto out; + } + + /* update return value and data bytes in buffer */ + rv = nbytes; + dev->bytes += nbytes; + +out: + return rv; +} + +/* The ioctl() implementation */ +long chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct chrdev_s *dev = file->private_data; + int rv, data; + size_t bytes = sizeof(data); + + /* + * extract the type and number bitfields, and don't decode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if (_IOC_TYPE(cmd) != CHRDEV_IOC_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > CHRDEV_IOC_MAXNR) return -ENOTTY; + + /* + * the direction is a bitmask, and VERIFY_WRITE catches R/W transfers. + * `Type' is user-oriented, while access_ok is kernel-oriented, + * so the concept of "read" and "write" is reversed + */ + if (_IOC_DIR(cmd) & _IOC_READ) + rv = !access_ok_wrapper(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + rv = !access_ok_wrapper(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (rv) return -EFAULT; + + switch(cmd) { + case CHRDEV_IOCRESET: + dev->bytes = 0; + memset(dev->data, 0, dev->size); + rv = 0; + break; + + /* Last 4 bytes in the buffer used to save ioctl() data*/ + case CHRDEV_IOCSET: + rv = __get_user(data, (int __user *)arg); + if( !rv ) + memcpy(&dev->data[dev->size-bytes], &data, bytes); + break; + + case CHRDEV_IOCGET: + memcpy(&data, &dev->data[dev->size-bytes], bytes); + rv = __put_user(data, (int __user *)arg); + break; + + default: + return -ENOTTY; + } + + return rv; +} + +static int chrdev_open (struct inode *inode, struct file *file) +{ + struct chrdev_s *dev; /* device struct address */ + + /* get the device struct address by container_of() */ + dev = container_of(inode->i_cdev, struct chrdev_s, cdev); + + /* save the device struct address for other methods */ + file->private_data = dev; + + return 0; +} + +static int chrdev_close (struct inode *node, struct file *file) +{ + return 0; +} + +static struct file_operations chrdev_fops = { + .owner = THIS_MODULE, + .open = chrdev_open, /* open() implementation */ + .read = chrdev_read, /* read() implementation */ + .write = chrdev_write, /* write() implementation */ + .release = chrdev_close, /* close() implementation */ + .unlocked_ioctl = chrdev_ioctl, /* ioctl() implementation */ +}; + +static int __init chrdev_init(void) +{ + dev_t devno; + int rv; + + /* malloc and initial device read/write buffer */ + dev.data = kmalloc(DEV_SIZE, GFP_KERNEL); + if( !dev.data ) + { + printk(KERN_ERR " %s driver kmalloc() failed\n", DEV_NAME); + return -ENOMEM; + } + dev.size = DEV_SIZE; + dev.bytes = 0; + memset(dev.data, 0, dev.size); + + /* dynamic alloc device node major number if not set */ + if(0 != dev_major) + { + devno = MKDEV(dev_major, 0); + rv = register_chrdev_region(devno, 1, DEV_NAME); + } + else + { + rv = alloc_chrdev_region(&devno, 0, 1, DEV_NAME); + dev_major = MAJOR(devno); + } + + if(rv < 0) + { + printk(KERN_ERR "%s driver can't use major %d\n", DEV_NAME, dev_major); + return -ENODEV; + } + + /* setup and register cdev into kernel */ + cdev_init(&dev.cdev, &chrdev_fops); + dev.cdev.owner = THIS_MODULE; + rv = cdev_add(&dev.cdev, devno, 1); + if( rv ) + { + rv = -ENODEV; + printk(KERN_ERR "%s driver regist failed, rv=%d\n", DEV_NAME, rv); + goto failed1; + } + +#ifdef CONFIG_AUTODEV + /* create device node in user space */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0) + dev.class = class_create(DEV_NAME); +#else + dev.class = class_create(THIS_MODULE, DEV_NAME); +#endif + if (IS_ERR(dev.class)) { + rv = PTR_ERR(dev.class); + goto failed2; + } + + dev.device = device_create(dev.class, NULL, MKDEV(dev_major, 0), NULL, DEV_NAME); + if( !dev.device ) + { + rv = -ENODEV; + printk(KERN_ERR "%s driver create device failed\n", DEV_NAME); + goto failed3; + } +#endif + + printk(KERN_INFO "%s driver on major[%d] installed.\n", DEV_NAME, dev_major); + return 0; + +#ifdef CONFIG_AUTODEV +failed3: + class_destroy(dev.class); + +failed2: + cdev_del(&dev.cdev); +#endif + +failed1: + unregister_chrdev_region(devno, 1); + kfree(dev.data); + + printk(KERN_ERR "%s driver installed failed.\n", DEV_NAME); + return rv; +} + +static void __exit chrdev_exit(void) +{ +#ifdef CONFIG_AUTODEV + device_del(dev.device); + class_destroy(dev.class); +#endif + + cdev_del(&dev.cdev); + unregister_chrdev_region(MKDEV(dev_major,0), 1); + + kfree(dev.data); + + printk(KERN_INFO "%s driver removed!\n", DEV_NAME); + return; +} + +module_init(chrdev_init); +module_exit(chrdev_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("GuoWenxue <guowenxue@gmail.com>"); diff --git a/kernel/drivers/x86_64/testapp/app2_chrdev.c b/kernel/drivers/x86_64/testapp/app2_chrdev.c new file mode 100644 index 0000000..8378243 --- /dev/null +++ b/kernel/drivers/x86_64/testapp/app2_chrdev.c @@ -0,0 +1,74 @@ +/********************************************************************************* + * Copyright: (C) 2023 Avnet + * All rights reserved. + * + * Filename: file.c + * Description: This file + * + * Version: 1.0.0(2023年11月02日) + * Author: Guo Wenxue <guowenxue@avnet.com> + * ChangeLog: 1, Release initial version on "2023年11月02日 14时47分52秒" + * + ********************************************************************************/ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +/* ioctl definitions, use 'z' as magic number */ +#define CHRDEV_IOC_MAGIC 'z' +#define CHRDEV_IOCRESET _IO(CHRDEV_IOC_MAGIC, 0) +#define CHRDEV_IOCSET _IOW(CHRDEV_IOC_MAGIC, 1, int) +#define CHRDEV_IOCGET _IOR(CHRDEV_IOC_MAGIC, 2, int) + +int main (int argc, char **argv) +{ + char *devname = "/dev/chrdev"; + char buf[1024]; + int rv, fd; + int value = 0x12345678; + + fd = open(devname, O_RDWR|O_TRUNC); + if( fd < 0 ) + { + printf("Open device %s failed: %s\n", devname, strerror(errno)); + return 1; + } + + rv = write(fd, "Hello", 5); + if( rv< 0) + { + printf("Write data into device failed, rv=%d: %s\n", rv, strerror(errno)); + return 2; + } + printf("Write %d bytes data okay\n", rv); + +#if 0 + lseek(fd, 3, SEEK_SET); +#endif + + memset(buf, 0, sizeof(buf)); + rv = read(fd, buf, sizeof(buf)); + if( rv< 0) + { + printf("Read data from device failed, rv=%d: %s\n", rv, strerror(errno)); + return 3; + } + printf("Read %d bytes data: %s\n", rv, buf); + +#if 0 + ioctl(fd, CHRDEV_IOCSET, &value); + value = 0; + ioctl(fd, CHRDEV_IOCGET, &value); + printf("ioctl() read value=0x%0x\n", value); +#endif + + close(fd); + return 0; +} + diff --git a/kernel/drivers/x86_64/testapp/app3_ioctl.c b/kernel/drivers/x86_64/testapp/app3_ioctl.c new file mode 100644 index 0000000..da89b72 --- /dev/null +++ b/kernel/drivers/x86_64/testapp/app3_ioctl.c @@ -0,0 +1,68 @@ +/********************************************************************************* + * Copyright: (C) 2023 Avnet + * All rights reserved. + * + * Filename: file.c + * Description: This file + * + * Version: 1.0.0(2023年11月02日) + * Author: Guo Wenxue <guowenxue@avnet.com> + * ChangeLog: 1, Release initial version on "2023年11月02日 14时47分52秒" + * + ********************************************************************************/ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +/* ioctl definitions, use 'z' as magic number */ +#define CHRDEV_IOC_MAGIC 'z' +#define CHRDEV_IOCRESET _IO(CHRDEV_IOC_MAGIC, 0) +#define CHRDEV_IOCSET _IOW(CHRDEV_IOC_MAGIC, 1, int) +#define CHRDEV_IOCGET _IOR(CHRDEV_IOC_MAGIC, 2, int) + +int main (int argc, char **argv) +{ + char *devname = "/dev/chrdev"; + char buf[1024]; + int rv, fd; + int value = 0x12345678; + + fd = open(devname, O_RDWR|O_TRUNC); + if( fd < 0 ) + { + printf("Open device %s failed: %s\n", devname, strerror(errno)); + return 1; + } + + rv = write(fd, "Hello", 5); + if( rv< 0) + { + printf("Write data into device failed, rv=%d: %s\n", rv, strerror(errno)); + return 2; + } + printf("Write %d bytes data okay\n", rv); + + memset(buf, 0, sizeof(buf)); + rv = read(fd, buf, sizeof(buf)); + if( rv< 0) + { + printf("Read data from device failed, rv=%d: %s\n", rv, strerror(errno)); + return 3; + } + printf("Read %d bytes data: %s\n", rv, buf); + + ioctl(fd, CHRDEV_IOCSET, &value); + value = 0; + ioctl(fd, CHRDEV_IOCGET, &value); + printf("ioctl() read value=0x%0x\n", value); + + close(fd); + return 0; +} + diff --git a/kernel/drivers/x86_64/testapp/makefile b/kernel/drivers/x86_64/testapp/makefile new file mode 100644 index 0000000..f5bb853 --- /dev/null +++ b/kernel/drivers/x86_64/testapp/makefile @@ -0,0 +1,32 @@ +#******************************************************************************** +# Copyright: (C) 2023 LingYun IoT System Studio +# All rights reserved. +# +# Filename: Makefile +# Description: This file used to compile all the C file to respective binary, +# and it will auto detect cross compile or local compile. +# +# Version: 1.0.0(11/08/23) +# Author: Guo Wenxue <guowenxue@gmail.com> +# ChangeLog: 1, Release initial version on "11/08/23 16:18:43" +# +#******************************************************************************* + + +BUILD_ARCH=$(shell uname -m) +ifneq ($(findstring "x86_64" "i386", $(BUILD_ARCH)),) + CROSS_COMPILE?=arm-linux-gnueabihf- +endif + +CC=${CROSS_COMPILE}gcc + +SRCFILES = $(wildcard *.c) +BINARIES=$(SRCFILES:%.c=%) + +all: ${BINARIES} + +%: %.c + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) + +clean: + rm -f ${BINARIES} -- Gitblit v1.9.1