1.最近研究了下字符驱动,现在将过程记录下来。
2.首先应该搞明白你要再那个内核下编写驱动,2.4x or 2.6x ???我现在是2.4编写驱动,然后了解结构file_operations,这个结构在linux/fs.h中定义。
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); };上面的结构来自于内核2.4.2,这个结构的作用是把你的设备执行相应操作的函数指针对应起来,比如你要执行read操作,你就应该有一个read函数;
后面会看到例子里面;
然后是给file_operations结构赋值,这里有两种不同的写法分别在2.4与2.6内核里;
2.4里面比较老的方法如下:
struct file_operations fops = { read: device_read, write: device_write, open: device_open, release: device_release };2.6里面的赋值如下:
struct file_operations fops = { .read = device_read, .write = device_write, .open = device_open, .release = device_release };如果你没有初始化结构成员的值,那么它会被gcc设置为NULL。
3.接下来我们需要注册一个设备;
字符设备是被当作为一个设备文件来操作的,这个设备文件在/dev目录下;主设备号告诉驱动应该和那个设备文件关联,副设备号是为了告诉驱动应该去操作
哪个具体的设备文件,当这个驱动与多个设备文件关联的时候;
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);使用上面的函数注册一个设备,注册一个设备到系统,我们不需要传递副设备号给系统;
major主设备号是你请求的设备号,name 是你的设备名称,这个设备名称会出现在/proc/devices里面, fops为相应的file_operations结构指针;
如果major为0,系统会动态分配一个主设备号给我们,如果动态生成,我们还需要将设备文件与主设备号关联;
这里提供三种方法进行配对:
a.动态分配后,由驱动打印出主设备号,然后我们通过mknod手动创建设备文件;
b.动态分配后,通过自动脚本读取/proc/devices里面的设备号建立设备文件;
c.动态分配后,直接在驱动里面调用mknod命令创建设备文件;
4.卸载设备;
当你已经完成了字符设备的所有操作函数与注册设备后,你需要知道如何卸载一个设备;
在卸载设备前我们来认识下几个宏:这些宏定义在linux/modules.h里面
MOD_INC_USE_COUNT: 递增使用计数器
MOD_DEC_USE_COUNT: 递减使用计数器
MOD_IN_USE: 显示使用计数器
unregister_chrdev(Major, DEVICE_NAME);此函数用来卸载一个设备;
5.下面我们来看下例子代码
#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 BUFFERSIZE 200 #define DEVICE_MAJOR 250 #define DEVICE_NAME "mycdev" #define SUCCESS 0 static int device_major = DEVICE_MAJOR; static char msg[BUFFERSIZE]; static char *msg_Ptr; static int Device_Open = 0; static int init_module(void); static void cleanup_module(void); static int my_cdev_open(struct inode *, struct file *); static int my_cdev_release(struct inode *, struct file *); static size_t my_cdev_read(struct file *, char *, size_t, loff_t *); static size_t my_cdev_write(struct file *, const char *, size_t, loff_t *); static loff_t my_cdev_llseek(struct file *filp, loff_t offset, int orig); static const struct file_operations my_cdev_fops= { .owner = THIS_MODULE, .open = my_cdev_open, .release = my_cdev_release, .read = my_cdev_read, .write = my_cdev_write, .llseek = my_cdev_llseek, }; static int my_cdev_open(struct inode *node, struct file *filp) { static int counter = 0; if(Device_Open) return -EBUSY; Device_Open++; sprintf(msg, "I already told you %d times Hello world!\n", counter++); msg_Ptr = msg; MOD_INC_USE_COUNT; return 0; } static int my_cdev_release(struct inode* node, struct file* filp) { Device_Open--; MOD_DEC_USE_COUNT; return 0; } static size_t my_cdev_read(struct file *filp, char *buf, size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; if(*msg_Ptr == 0) return 0; if(p >= BUFFERSIZE) return count ? -ENXIO : 0; if(copy_to_user(buf, (void*)(msg_Ptr+p), count)) { ret = -EFAULT; }else{ *ppos += count; ret = count; } return ret; } static size_t my_cdev_write(struct file *filp, const char *buf, size_t size, loff_t *ppos) { unsigned long p = *ppos; unsigned int count = size; int ret = 0; if(p >= BUFFERSIZE) return count ? -ENXIO : 0; if(count > BUFFERSIZE - p) count = BUFFERSIZE -p; if(copy_from_user(msg_Ptr, buf, count)) { ret = -EFAULT; }else{ *ppos += count; ret = count; } return ret; } static loff_t my_cdev_llseek(struct file *filp, loff_t offset, int orig) { loff_t ret = 0; switch(orig) { case 0: if(offset < 0) { ret = -EINVAL; break; } if(offset > BUFFERSIZE) { ret = -EINVAL; break; } filp->f_pos = (unsigned int)offset; ret = filp->f_pos; break; default: ret = -EINVAL; break; } return ret; } /* static void my_cdev_setup(struct my_cdev *dev, int index) { int err; dev_t devno = MKDEV(DEVICE_MAJOR, index); cdev_init(&dev->cdev, &my_cdev_fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &my_cdev_fops; err = cdev_add(&dev->cdev, devno, 1); if(err) printk(KERN_NOTICE "Error %d adding LED%d", err, index); } */ static int init_module(void) { device_major = register_chrdev(0, DEVICE_NAME, &my_cdev_fops); if(device_major < 0) { printk("Register the character device failed with %d\n", device_major); return device_major; } printk("<1>I was assigned major number %d. To talk to\n", device_major); printk("<1>the driver, create a dev file with\n"); printk("'mknod /dev/mycdev c %d 0'.\n", device_major); printk("<1>Try various minor numbers. Try to cat and echo to\n"); printk("the device file.\n"); printk("<1>Remove the device file and module when done.\n"); return 0; } static void cleanup_module(void) { int ret = unregister_chrdev(device_major, DEVICE_NAME); if(ret < 0) printk("Error in unregister_chrdev: %d\n", ret); } MODULE_AUTHOR("Robert Luo"); MODULE_LICENSE("Dual BSD/GPL");
然后根据提示建立一个设备文件mknod /dev/mycdev c 254 0
最后我们写一段测试代码来看看驱动是否可以用了;
#include <stdio.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #define BUFFERSIZE 200 int main(void) { int fp =0; char str[BUFFERSIZE]; fp = open("/dev/mycdev", O_RDWR); if(!fp) { printf("Open device failed.\n"); return -1; } write(fp, "Hello, my devices", strlen("Hello, my devices")); lseek(fp, 0, 0 ); read(fp, str, BUFFERSIZE); printf("Read content: %s\n", str); close(fp); }
Read content: Hello, my devicesu 0 times Hello world!
Read content: Hello, my devicesu 1 times Hello world!
Read content: Hello, my devicesu 2 times Hello world!