字符设备驱动程序

 

哈尔滨理工大学软件工程专业08-7李万鹏原创作品,转载请标明出处

http://blog.csdn.net/woshixingaaa/archive/2010/11/13/6007342.aspx

LINUX设备驱动程序分为字符设备驱动(无缓冲且只能顺序存取),块设备驱动程序(有缓冲且可以随机存取)。每个字符设备和块设备都必须有主,次设备号,主设备号相同的设备是同类设备(使用同一驱动程序)。这些设备中,有些设备是对实际物理硬件的抽象,而有些设备则是则是内核自身提供的功能(不依赖于特定的物理硬件,又称为“虚拟设备”)。每个设备在/dev目录下都有一个对应的文件(节点),可以通过 cat /proc/devices命令查看当前已经加载的设备驱动程序的主设备号。

在内核中,dev_t类型用来保存设备编号(包括主设备号和次设备号):

  • 主设备号=MAJOR(dev_t dev)
  • 次设备号=MINOR(dev_t dev)
  • 设备编号=MKDEV(int major,int minor)

 dev_t是无符长整型

  • typedef u_long dev_t ;
  • typedef unsigned long u_long;

分配和释放设备号:

  • int register_chrdev_region(dev_t first, unsigned int count, char *name);
  • int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
  • void unregister_chrdev_region(dev_t first, unsigned int count);

字符设备的注册:

  • void cdev_init(struct cdev *cdev, struct file_operations *fops);
  • int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
  • void cdev_del(struct cdev *dev);

早期的办法:

  • int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
  • int unregister_chrdev(unsigned int major, const char *name);

在内核空间和用户空间之间拷贝数据使用:

  • unsigned long copy_to_user(void __user*to,const void *from,unsigned long count);
  • unsigned long copy_from_user(void *to, const void __user *from,unsigned long count);

scull设备的布局:

这个结构是一个二维数组,qset是第一个下限,quantum是第二个下限。

下面是我写的驱动程序,没有用书上的,数了一下,书上的例子快到两千行代码了,所以自己写个容易的,设备是内存,first.c 需要注意的地方是,下面的struct file_operations first_fops是用C99标准写的,是标记化结构体初始化语法。左边的open,release,read,write都是系统调用,也就是说调用read是去执行read_test,调用write时执行write_test。在用户空间和内核空间之间传输数据应该使用函数copy_to_user和copy_from_user.

  1. #include <linux/module.h>  
  2. #include <linux/kernel.h>  
  3. #include <linux/init.h>  
  4. #include <linux/miscdevice.h>  
  5. #include <linux/sched.h>  
  6. #include <linux/delay.h>  
  7. #include <linux/poll.h>  
  8. #include <linux/spinlock.h>  
  9. #include <linux/cdev.h>  
  10. #include <linux/kobject.h>  
  11. #include <linux/fs.h>  
  12. #include <linux/device.h>  
  13. #include <asm/uaccess.h>   
  14. struct cdev first;  
  15. unsigned int dev;  
  16. char * data;  
  17. static ssize_t read_test(struct file *file, char *buf, size_t count,loff_t*pos);  
  18. static ssize_t write_test(struct file *file, const char *buf,size_t count,loff_t*pos);  
  19. static ssize_t open_test(struct inode *inode, struct file *file);  
  20. static ssize_t release_test(struct inode *inode, struct file *file);  
  21. struct file_operations first_fops={  
  22.     .owner = THIS_MODULE,    
  23.     .read = read_test,  
  24.     .write = write_test,  
  25.     .open = open_test,  
  26.     .release = release_test,  
  27. };  
  28. static ssize_t read_test(struct file *file, char *buf, size_t count,loff_t*pos){  
  29.     int len;  
  30.     if(count < 0)  
  31.         return -EINVAL;  
  32.     len = strlen(data);  
  33.     if(len < count)  
  34.         count = len;  
  35.     copy_to_user(buf,data,count);  
  36.     printk("read buf %s/n", buf);  
  37.     printk("read data %s/n", data);  
  38.     return count;  
  39. }  
  40. static ssize_t write_test(struct file *file, const char *buf,size_t count,loff_t*pos){  
  41.     if(count < 0)  
  42.         return -EINVAL;  
  43.     kfree(data);  
  44.     data = (char*)kmalloc(sizeof(char)*(count),GFP_KERNEL);  
  45.     if(!data)  
  46.         return -ENOMEM;  
  47.     copy_from_user(data,buf,count);  
  48.     printk("write buf %s/n",buf);  
  49.     printk("write data %s/n",data);  
  50.     return count;  
  51. }  
  52. static ssize_t open_test(struct inode *inode, struct file *file){  
  53.     return 0;  
  54. }  
  55. static ssize_t release_test(struct inode *inode, struct file *file){  
  56.     return 0;  
  57. }  
  58. static int __init init_first(void){  
  59.     int ret;  
  60.     ret = alloc_chrdev_region(&dev,0,1,"first");  
  61.     if(ret < 0){  
  62.         printk(KERN_ALERT "first register fail");  
  63.     }  
  64.     cdev_init(&first,&first_fops);  
  65.     first.owner = THIS_MODULE;  
  66.     ret = cdev_add(&first,dev,1);  
  67.     if(ret < 0){  
  68.         printk(KERN_ALERT "first registrr fail");  
  69.     }  
  70.     return 0;  
  71. }  
  72. static void __exit exit_first(void){  
  73.     unregister_chrdev_region(dev,"first");  
  74.     cdev_del(&first);  
  75. }  
  76. module_init(init_first);  
  77. module_exit(exit_first);  
 

初始化模块时:

  • 获得设备号 alloc_chrdev_region,若已知则调用register_chrdev_region
  • 初始化字符设备 cdev_init
  • 将设备添加到系统中 cdev_add

注销模块:

  • 释放设备号  unregister_chrdev_region
  • 删除已经注册的char设备 cdev_del

Makefile

  1. ifneq ($(KERNELRELEASE),)  
  2.     obj-m:= first.o  
  3. else  
  4.     KERNELDIR ?= /lib/modules/$(shell uname -r)/build  
  5.     PWD := $(shell pwd)  
  6. modules:  
  7.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules  
  8. modules_install:  
  9.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install  
  10. clear:    
  11.     rm -rf *.o  
  12. .PHONY: modules modules_install clear  
  13. endif  
  14.        

首先make,进行编译,生成first.ko文件。执行:

  • sudo insmod first.ko
  • cat /proc/devices 查看主设备号
  • sudo mknod /dev/test c 252 0 (c是字符设备的意思,250是我查看得到的主设备号,0是次设备号)

test.c

  1. #include <stdio.h>  
  2. #include <fcntl.h>  
  3. #include <stdlib.h>  
  4. #include <string.h>  
  5. #include <sys/types.h>  
  6. #include <sys/stat.h>   
  7. int main(){  
  8.     int fd;  
  9.     char s2[10]="0";  
  10.     char *s1 = "hacker";  
  11.     fd = open("/dev/test",O_RDWR);  
  12.     if(fd == -1){  
  13.         printf("Cannot open first/n");  
  14.         exit(0);  
  15.     }  
  16.     write(fd,s1,strlen(s1));  
  17.     read(fd,s2,strlen(s1));  
  18.     printf("显示出字符串:/n");  
  19.     printf("%s/n",s2);  
  20.     close(fd);  
  21.     return 0;  
  22. }  
 

  • 然后编译用户空间的测试文件test.c 运行 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值