Linux设备驱动之字符设备

字符设备是3大类设备(字符设备、块设备和网络设备)中较简单的一类设备,其驱动程序中完成的主要工作是初始化、添加和删除cdev结构体,申请和释放设备号,以及填充 file_operations结构体中的操作函数,实现file_operations结构体中的read()、write()和ioctl()等函数是驱动设计的主体工作。

参考例程 源码 /* * 虚拟字符设备globalmem实例: * 在globalmem字符设备驱动中会分配一片大小为 GLOBALMEM_SIZE(4KB) * 的内存空间,并在驱动中提供针对该片内存的读写、控制和定位函数,以供用户空间的进程能通过 * Linux系统调用访问这片内存。 */ #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #define DEV_NAME "globalmem" /* /dev中显示的设备名 */ #define DEV_MAJOR 0 /* 指定主设备号,为0则动态获取 */ /* ioctl用的控制字 */ #define GLOBALMEM_MAGIC 'M' #define MEM_CLEAR _IO(GLOBALMEM_MAGIC, 0) /*--------------------------------------------------------------------- local vars */ /*globalmem设备结构体*/ typedef struct { struct cdev cdev; /* 字符设备cdev结构体*/ #define MEM_SIZE 0x1000 /*全局内存最大4K字节*/ unsigned char mem[MEM_SIZE]; /*全局内存*/ struct semaphore sem; /*并发控制用的信号量*/ } globalmem_dev_t; static int globalmem_major = DEV_MAJOR; static globalmem_dev_t *globalmem_devp; /*设备结构体指针*/ /*--------------------------------------------------------------------- file operations */ /*文件打开函数*/ static int globalmem_open(struct inode *inodep, struct file *filep) { /* 获取dev指针 */ globalmem_dev_t *dev = container_of(inodep->i_cdev, globalmem_dev_t, cdev); filep->private_data = dev; return 0; } /*文件释放函数*/ static int globalmem_release(struct inode *inodep, struct file *filep) { return 0; } /*读函数*/ static ssize_t globalmem_read(struct file *filep, char __user *buf, size_t len, loff_t *ppos) { globalmem_dev_t *dev = filep->private_data; unsigned long p = *ppos; int ret = 0; /*分析和获取有效的长度*/ if (p > MEM_SIZE) { printk(KERN_EMERG "%s: overflow!\n", __func__); return - ENOMEM; } if (len > MEM_SIZE - p) { len = MEM_SIZE - p; } if (down_interruptible(&dev->sem)) /* 获得信号量*/ return - ERESTARTSYS; /*内核空间->用户空间*/ if (copy_to_user(buf, (void*)(dev->mem + p), len)) { ret = - EFAULT; }else{ *ppos += len; printk(KERN_INFO "%s: read %d bytes from %d\n", DEV_NAME, (int)len, (int)p); ret = len; } up(&dev->sem); /* 释放信号量*/ return ret; } /*写函数*/ static ssize_t globalmem_write(struct file *filep, const char __user *buf, size_t len, loff_t *ppos) { globalmem_dev_t *dev = filep->private_data; int ret = 0; unsigned long p = *ppos; if (p > MEM_SIZE) { printk(KERN_EMERG "%s: overflow!\n", __func__); return - ENOMEM; } if (len > MEM_SIZE - p) { len = MEM_SIZE - p; } if (down_interruptible(&dev->sem)) /* 获得信号量*/ return - ERESTARTSYS; /*用户空间->内核空间*/ if (copy_from_user(dev->mem + p, buf, len)) { ret = - EFAULT; }else{ *ppos += len; printk(KERN_INFO "%s: written %d bytes from %d\n", DEV_NAME, (int)len, (int)p); ret = len; } up(&dev->sem); /* 释放信号量*/ return ret; } /* seek文件定位函数 */ static loff_t globalmem_llseek(struct file *filep, loff_t offset, int start) { globalmem_dev_t *dev = filep->private_data; int ret = 0; if (down_interruptible(&dev->sem)) /* 获得信号量*/ return - ERESTARTSYS; switch (start) { case SEEK_SET: if (offset < 0 || offset > MEM_SIZE) { printk(KERN_EMERG "%s: overflow!\n", __func__); return - ENOMEM; } ret = filep->f_pos = offset; break; case SEEK_CUR: if ((filep->f_pos + offset) < 0 || (filep->f_pos + offset) > MEM_SIZE) { printk(KERN_EMERG "%s: overflow!\n", __func__); return - ENOMEM; } ret = filep->f_pos += offset; break; default: return - EINVAL; break; } up(&dev->sem); /* 释放信号量*/ printk(KERN_INFO "%s: set cur to %d.\n", DEV_NAME, ret); return ret; } /* ioctl设备控制函数 */ static long globalmem_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { globalmem_dev_t *dev = filep->private_data; switch (cmd) { case MEM_CLEAR: if (down_interruptible(&dev->sem)) /* 获得信号量*/ return - ERESTARTSYS; memset(dev->mem, 0, MEM_SIZE); up(&dev->sem); /* 释放信号量*/ printk(KERN_INFO "%s: clear.\n", DEV_NAME); break; default: return - EINVAL; } return 0; } /*文件操作结构体*/ static const struct file_operations globalmem_fops = { .owner = THIS_MODULE, .open = globalmem_open, .release = globalmem_release, .read = globalmem_read, .write = globalmem_write, .llseek = globalmem_llseek, .compat_ioctl = globalmem_ioctl }; /*---------------------------------------------------------------------*/ /*初始化并注册cdev*/ static int globalmem_setup(globalmem_dev_t *dev, int minor) { int ret = 0; dev_t devno = MKDEV(globalmem_major, minor); cdev_init(&dev->cdev, &globalmem_fops); dev->cdev.owner = THIS_MODULE; ret = cdev_add(&dev->cdev, devno, 1); if (ret) { printk(KERN_NOTICE "%s: Error %d dev %d.\n", DEV_NAME, ret, minor); } return ret; } /*设备驱动模块加载函数*/ static int __init globalmem_init(void) { int ret = 0; dev_t devno; /* 申请设备号*/ if(globalmem_major){ devno = MKDEV(globalmem_major, 0); ret = register_chrdev_region(devno, 2, DEV_NAME); }else{ /* 动态申请设备号 */ ret = alloc_chrdev_region(&devno, 0, 2, DEV_NAME); globalmem_major = MAJOR(devno); } if (ret < 0) { return ret; } /* 动态申请设备结构体的内存,创建两个设备 */ globalmem_devp = kmalloc(2*sizeof(globalmem_dev_t), GFP_KERNEL); if (!globalmem_devp) { unregister_chrdev_region(devno, 2); return - ENOMEM; } ret |= globalmem_setup(&globalmem_devp[0], 0); /* globalmem0 */ ret |= globalmem_setup(&globalmem_devp[1], 1); /* globalmem1 */ if(ret) return ret; init_MUTEX(&globalmem_devp[0].sem); /*初始化信号量*/ init_MUTEX(&globalmem_devp[1].sem); printk(KERN_INFO "globalmem: up %d,%d.\n", MAJOR(devno), MINOR(devno)); return 0; } /*模块卸载函数*/ static void __exit globalmem_exit(void) { cdev_del(&globalmem_devp[0].cdev); cdev_del(&globalmem_devp[1].cdev); kfree(globalmem_devp); unregister_chrdev_region(MKDEV(globalmem_major, 0), 2); printk(KERN_INFO "globalmem: down.\n"); } /* 定义参数 */ module_param(globalmem_major, int, S_IRUGO); module_init(globalmem_init); module_exit(globalmem_exit); /* 模块描述及声明 */ MODULE_AUTHOR("Archie Xie <archixie@cnblogs.com>"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("A char device module just for demo."); MODULE_ALIAS("cdev gmem"); MODULE_VERSION("1.0"); 用户空间验证

切换到root用户

插入模块

insmod globalmem.ko

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/69cd9c9626f18becda60646ba2c5496f.html