/** * @file demo_misc.c * @author Rick Chan (cy187lion@sina.com) * @brief Linux misc driver demo. 在 Linux 驱动中,会把一些无法归类的设备定义为混杂设备,它们是拥 有着共同的特性的简单字符设备。它们的特点是共享统一的主设备号 10, 但每个设备可以选择一个单独的次设备号。 通常情况下,一个字符设备都不得不在初始化的过程中进行下面的步骤: 1.通过alloc_chrdev_region()分配主/次设备号 2.使用cdev_init()和cdev_add()来以一个字符设备注册自己 而一个misc驱动,则可以只用一个调用misc_register()来完成这所有 的步骤。 所有的 miscdevice 设备形成一个链表,对设备访问时,内核根据次设 备号查找对应的 miscdevice 设备,然后调用其 file_operations 中注册的文件操作方法进行操作。 * @version 0.1.1 * @date 2020-04-27 * * @copyright Copyright (c) 2020 * */ #include #include #include #include #include MODULE_AUTHOR("Rick Chan"); MODULE_LICENSE("GPL"); #define DEMO_MAGIC 'R' #define DEMO_IOC_SIZE 8 #define DEMO_CTL_IOC _IOC(_IOC_READ|_IOC_WRITE, DEMO_MAGIC, 0, DEMO_IOC_SIZE) #define DEMO_CTL_IO _IO(DEMO_MAGIC, 1) #define DEMO_CTL_IOR _IOR(DEMO_MAGIC, 2, uint16_t) #define DEMO_CTL_IOW _IOW(DEMO_MAGIC, 3, int32_t) #define DEMO_CTL_IOWR _IOWR(DEMO_MAGIC, 4, uint32_t) #define DEMO_CTL_MAX 5 static int demo_open(struct inode *inode, struct file* filp) { return 0; } static int demo_release(struct inode *inode, struct file* filp) { return 0; } static loff_t demo_llseek(struct file *filp, loff_t offset, int origin) { return filp->f_pos; } static ssize_t demo_read(struct file *filp, char __user *buffer, size_t count, loff_t *offset) { const char __user *p = buffer; return p-buffer; } static ssize_t demo_write(struct file *filp, const char __user *buffer, size_t count, loff_t *offset) { const char __user *p = buffer; return p-buffer; } static long demo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { static int err = 0; // 检测 cmd 合法性. if(DEMO_MAGIC!=_IOC_TYPE(cmd)) return -EINVAL; if(DEMO_CTL_MAX<_IOC_NR(cmd)) return -EINVAL; // 根据命令类型, 检测参数空间是否可以访问. if(_IOC_DIR(cmd)&_IOC_READ) err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); else if(_IOC_DIR(cmd)&_IOC_WRITE) err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); if (err) return -EFAULT; // 根据命令, 来选择执行哪个分支. switch(cmd) { case DEMO_CTL_IOC: { uint8_t tmp[DEMO_IOC_SIZE]; copy_from_user(&tmp, (void*)arg, _IOC_SIZE(cmd)); copy_to_user((void*)arg, &tmp, _IOC_SIZE(cmd)); } break; case DEMO_CTL_IO: break; case DEMO_CTL_IOR: { uint16_t tmp = 0x55AA; copy_to_user((void*)arg, &tmp, _IOC_SIZE(cmd)); } break; case DEMO_CTL_IOW: { int32_t tmp = 0; copy_from_user(&tmp, (void*)arg, _IOC_SIZE(cmd)); } break; case DEMO_CTL_IOWR: { uint32_t tmp = 0; copy_from_user(&tmp, (void*)arg, _IOC_SIZE(cmd)); copy_to_user((void*)arg, &tmp, _IOC_SIZE(cmd)); } break; default: return -ENOIOCTLCMD; } return 0; } static int demo_mmap(struct file* filp, struct vm_area_struct* vma) { return 0; } static struct file_operations demo_fops = { .owner = THIS_MODULE, .open = demo_open, .release = demo_release, .llseek = demo_llseek, .read = demo_read, .write = demo_write, .unlocked_ioctl = demo_ioctl, .mmap = demo_mmap }; static struct miscdevice demo_dev = { .minor = MISC_DYNAMIC_MINOR, // Dynamically allocate minor. .name = "demo_misc", .fops = &demo_fops, }; static int __init demo_init(void) { misc_register(&demo_dev); // 注册混杂设备驱动. return 0; } static void __exit demo_exit(void) { misc_deregister(&demo_dev); // 撤销混杂设备驱动. } module_init(demo_init); module_exit(demo_exit);