字符设备驱动
编写Linux字符设备驱动程序的流程基本步骤如下:
1、查看原理图、数据手册,了解设备的操作方法;
2、在内核中找到相近的驱动程序,以它作为模板进行开发,有时候需要从零开始;
3、注册驱动程序,给用户空间提供访问的接口,如在/dev/目录下生成设备文件;
4、设计所要实现的操作,如open(),close(),read(),write()、ioctl()等函数;
5、如果有需要实现中断服务;
6、编译该驱动程序到内核中,编写应用程序测试驱动程序;
在Linux内核中使用cdev结构体来描述字符设备,通过其成员dev_t来定义设备号(分为主、次设备号)以确定字符设备的唯一性。通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open()、read()、write()等。
在Linux字符设备驱动中,模块加载函数通过register_chrdev_region( ) 或alloc_chrdev_region( )来静态或者动态获取设备号,通过cdev_init( )建立cdev与file_operations之间的连接,通过cdev_add( )向系统添加一个cdev以完成注册。模块卸载函数通过cdev_del( )来注销cdev,通过unregister_chrdev_region( )来释放设备号。
用户空间访问该设备的程序通过Linux系统调用,如open( )、read( )、write( ),来“调用”file_operations来定义字符设备驱动提供给VFS的接口函数。
详尽实现代码如下:
/* * Copyright (C) Continential- weizhen.diao@conti.engineering.com * *The purpose of the following program is to create char devive drivern for realizing * the read,write,control and orientation funtion by using Linux system call */ #include <linux/module.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/uaccess.h> #define GLOBALMEM_SIZE 0x1000 #define MEM_CLEAR 0x1 #define CONTI_globalmem_major 230 static int CONTI_globalmem_major = CONTI_globalmem_major; /*module_param is used to define the parameters for the specified module, three * parameters are name, data type and permission respectively*/ module_param(CONTI_globalmem_major, int, S_IRUGO); /*For the purpose of packaging, cdev and global memory for the specified module is *integreted into the CONTI_globalmem_dev object*/ struct CONTI_globalmem_dev { struct cdev cdev; unsigned char mem[GLOBALMEM_SIZE]; }; struct CONTI_globalmem_dev *CONTI_globalmem_devp; static int CONTI_globalmem_open(struct inode *inode, struct file *filp) { filp->private_data = CONTI_globalmem_devp; return 0; } static int CONTI_globalmem_release(struct inode *inode, struct file *filp) { return 0; } static long CONTI_globalmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct CONTI_globalmem_dev *dev = filp->private_data; switch (cmd) { case MEM_CLEAR: memset(dev->mem, 0, GLOBALMEM_SIZE); printk(KERN_INFO "globalmem is set to zero\n"); break; default: return -EINVAL; } return 0; } static ssize_t CONTI_globalmem_read(struct file *filp, char __user * buf, size_t size, loff_t * ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct CONTI_globalmem_dev *dev = filp->private_data; if (p >= GLOBALMEM_SIZE) return 0; if (count > GLOBALMEM_SIZE - p) count = GLOBALMEM_SIZE - p; if (copy_to_user(buf, dev->mem + p, count)) { ret = -EFAULT; } else { *ppos += count; ret = count; printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p); } return ret; } static ssize_t CONTI_globalmem_write(struct file *filp, const char __user * buf, size_t size, loff_t * ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; struct CONTI_globalmem_dev *dev = filp->private_data; if (p >= GLOBALMEM_SIZE) return 0; if (count > GLOBALMEM_SIZE - p) count = GLOBALMEM_SIZE - p; if (copy_from_user(dev->mem + p, buf, count)) ret = -EFAULT; else { *ppos += count; ret = count; printk(KERN_INFO "written %u bytes(s) from %lu\n", count, p); } return ret; } static loff_t CONTI_globalmem_llseek(struct file *filp, loff_t offset, int orig) { loff_t ret = 0; switch (orig) { case 0: if (offset < 0) { ret = -EINVAL; break; } if ((unsigned int)offset > GLOBALMEM_SIZE) { ret = -EINVAL; break; } filp->f_pos = (unsigned int)offset; ret = filp->f_pos; break; case 1: if ((filp->f_pos + offset) > GLOBALMEM_SIZE) { ret = -EINVAL; break; } if ((filp->f_pos + offset) < 0) { ret = -EINVAL; break; } filp->f_pos += offset; ret = filp->f_pos; break; default: ret = -EINVAL; break; } return ret; } static const struct file_operations CONTI_globalmem_fops = { .owner = THIS_MODULE, .llseek = CONTI_globalmem_llseek, .read = CONTI_globalmem_read, .write = CONTI_globalmem_write, .unlocked_ioctl = CONTI_globalmem_ioctl, .open = CONTI_globalmem_open, .release = CONTI_globalmem_release, }; static void globalmem_setup_cdev(struct CONTI_globalmem_dev *dev, int index) { int err, devno = MKDEV(CONTI_globalmem_major, index); cdev_init(&dev->cdev, &CONTI_globalmem_fops); dev->cdev.owner = THIS_MODULE; err = cdev_add(&dev->cdev, devno, 1); if (err) printk(KERN_NOTICE "Error %d adding globalmem%d", err, index); } static int __init globalmem_init(void) { int ret; dev_t devno = MKDEV(CONTI_globalmem_major, 0); if (CONTI_globalmem_major) ret = register_chrdev_region(devno, 1, "globalmem"); else { ret = alloc_chrdev_region(&devno, 0, 1, "globalmem"); CONTI_globalmem_major = MAJOR(devno); } if (ret < 0) return ret; CONTI_globalmem_devp = kzalloc(sizeof(struct CONTI_globalmem_dev), GFP_KERNEL); if (!CONTI_globalmem_devp) { ret = -ENOMEM; goto fail_malloc; } globalmem_setup_cdev(CONTI_globalmem_devp, 0); return 0; fail_malloc: unregister_chrdev_region(devno, 1); return ret; } module_init(globalmem_init); static void __exit globalmem_exit(void) { cdev_del(&CONTI_globalmem_devp->cdev); kfree(CONTI_globalmem_devp); unregister_chrdev_region(MKDEV(CONTI_globalmem_major, 0), 1); } module_exit(globalmem_exit); MODULE_AUTHOR("Hemingway <weizhen.diao@conti.engineering.com>"); MODULE_LICENSE("GPL v2");
![](https://images2015.cnblogs.com/blog/867028/201610/867028-20161006201117395-1350899748.png)