Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4235912
  • 博文数量: 1148
  • 博客积分: 25453
  • 博客等级: 上将
  • 技术积分: 11949
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-06 21:14
文章分类

全部博文(1148)

文章存档

2012年(15)

2011年(1078)

2010年(58)

分类: LINUX

2011-12-18 12:45:34

本文的copyright归yuweixian4230@163.com 所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:yuweixian4230@163.com
博客:
yuweixian4230.blog.chinaunix.net  

书籍:linux设备驱动开发详解 第6章

1 cdev结构体
在linux2.6中,使用cdev结构体描述了一个字符设备,定义如下地址:
  1. 12struct cdev {
  2.   13 struct kobject kobj;    内嵌的kobject对象
  3.   14 struct module *owner;   所属模块 
  4.   15 const struct file_operations *ops;
  5.   16 struct list_head list双链表,struct list_head{struct list_head *next,*prev}
  6.   17 dev_t dev;             设备号
  7.   18 unsigned int count;
  8.   19};
cdev结构体的dev_t成员定义了设备号,为32bit,12bit主设备号,20bit次设备号
从下面宏从dev_t获得主设备号和次设备号
查看linux内核中,已经使用的设备号 src/Documentation/devices.txt
  1. 定义在include/linux/kdev_t.h
  2. 通过dev_t获得 主次设备号
  3. 7 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
  4. 8 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
  5. 通过主次设备号,获取 dev_t
  6. 9 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
cdev结构体的另一个重要成员 file_operations定义了字符设备驱动提供给虚拟文件系统的接口函数

内核提供了一组函数用于操作cdev结构体src/linux/include/linux/cdev.h
  1. 21 void cdev_init(struct cdev *, const struct file_operations *);
  2. 23 struct cdev *cdev_alloc(void);
  3. 25 void cdev_put(struct cdev *p);
  4. 27 int cdev_add(struct cdev *, dev_t, unsigned);
  5. 29 void cdev_del(struct cdev *);
  6. 31 int cdev_index(struct inode *inode);
  7. 33 void cd_forget(struct inode *);
  8. 35 extern struct backing_dev_info directly_mappable_cdev_bdi;
 1.1 cdev_init
函数用于初始化cdev的成员,并建立生cdev和file_operations之间连接
  1. /fs/char_dev.c 2.6.35
  2.  550 void cdev_init(struct cdev *cdev, const struct file_operations *fops)
  3.  551 {
  4.  552 memset(cdev, 0, sizeof *cdev);
  5.  553 INIT_LIST_HEAD(&cdev->list); //初始化链表  与 cdev_alloc 一致
  6.  554 kobject_init(&cdev->kobj, &ktype_cdev_default);//初始化 kobject 对象
  7.  555 cdev->ops = fops; //将传入的fops 传递给 cdev结构体
  8.  556 }
1.2 cdev_alloc()
用于动态申请一个cdev内存
  1. /fs/char_dev.c 2.6.35
  2.  532 struct cdev *cdev_alloc(void)
  3.  533 {
  4.  534 struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
  5.  535 if (p) {
  6.  536 INIT_LIST_HEAD(&p->list);
  7.  537 kobject_init(&p->kobj, &ktype_cdev_dynamic);
  8.  538 }
  9.  539 return p;
  10.  540 }
对如何使用 cdev_init 和 cdev_alloc查看博文cdev_init和cdev_alloc使用

1.3cdev_add()和cdev_del()
向系统添加和删除一个cdev
  1. src/fs/char_dev.c

  2.  480 int cdev_add(struct cdev *p, dev_t dev, unsigned count)//count表示什么???
  3.  481 {
  4.  482 p->dev = dev;
  5.  483 p->count = count;
  6.  484 return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
  7.  485 }
dev_t_ num 是 12bit主设备号+20次设备号
count 表示设备的数量,通常情况下是1,我现在碰到的代码中都是 1
2012.1.3号更新注释说明count用法:
dev_t dev; 假设这个设备号=主设备250,第一个次设备号0,第二个次设备号为1 
cdev_add(&cdev,dev,1);//添加第一个 c 250 0 的设备
cdev_add(&cdev,dev+1,1);//添加第二个 c 250 1 的设备


  1.  499 void cdev_del(struct cdev *p)
  2.  500 {
  3.  501 cdev_unmap(p->dev, p->count);
  4.  502  kobject_put(&p->kobj);
  5.  503 }

2.分配和释放设备号

在调用cdev_add()函数向系统注册字符设备之前,应首先调用register_chrdev_region()alloc_chrdev_region()函数向系统申请设备号,
register_chrdev_region()函数用于已知起始设备的设备号的情况,而alloc_chrdev_region()用于设备号未知,向系统动态申请未被占用的设备号的情况。
unregister_chrdev_region(dev_t from,unsigned count);注销字符设备
函数原型为:
  1. src/fs/char_dev.c
  2. dev_t from:   设备号 ,必须包含主设备号  
  3. unsigned count:  申请的 设备数量
  4. const char *name:
  5. 193  int register_chrdev_region(dev_t from, unsigned count, const char *name)
  6.  194 {
  7.  195 struct char_device_struct *cd;
  8.  196 dev_t to = from + count;
  9.  197 dev_t n, next;
  10.  198
  11.  199 for (n = from; n < to; n = next) {
  12.  200 next = MKDEV(MAJOR(n)+1, 0);
  13.  201 if (next > to)
  14.  202 next = to;
  15.  203 cd = __register_chrdev_region(MAJOR(n), MINOR(n),
  16.  204 next - n, name);
  17.  205 if (IS_ERR(cd))
  18.  206 goto fail;
  19.  207 }
  20.  208 return 0;
  21.  209 fail:
  22.  210 to = n;
  23.  211 for (n = from; n < to; n = next) {
  24.  212 next = MKDEV(MAJOR(n)+1, 0);
  25.  213 kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
  26.  214 }
  27.  215 return PTR_ERR(cd);
  28.  216 }
  1. dev_t *dev:生成的设备号,放入*dev指针中
  2. unsigned baseminor:次设备号开始数值,一般为0,现在我碰到的代码中 都为 0
  3. ussigned count:设备数量
  4. dev_t devno;//主次设备号
  5. alloc_chrdev_region(&devno,0,2,"ywxdevice");
假如动态分配的主设备号为250:那么我将注册 c 250 0
                                     c  250 1 两个设备节点
  1. 229   int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
  2.  231  {
  3.  232 struct char_device_struct *cd;
  4.  233   cd = __register_chrdev_region(0, baseminor, count, name);
  5.  234   if (IS_ERR(cd))
  6.  235   return PTR_ERR(cd);
  7.  236   *dev = MKDEV(cd->major, cd->baseminor);
  8.  237   return 0;
  9.  238  }
  1. 304  void unregister_chrdev_region(dev_t from, unsigned count)
  2.  305 {
  3.  306 dev_t to = from + count;
  4.  307 dev_t n, next;
  5.  308
  6.  309 for (n = from; n < to; n = next) {
  7.  310 next = MKDEV(MAJOR(n)+1, 0);
  8.  311 if (next > to)
  9.  312 next = to;
  10.  313 kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
  11.  314 }
  12.  315 }

3.file_operations结构体
  1. src/include/linux/fs.h

  2. 1485 struct file_operations {
  3. 1486 struct module *owner;
  4. 1487 loff_t (*llseek) (struct file *, loff_t, int);
  5. 1488 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
  6. 1489 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
  7. 1490 ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
  8. 1491 ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
  9. 1492 int (*readdir) (struct file *, void *, filldir_t);
  10. 1493 unsigned int (*poll) (struct file *, struct poll_table_struct *);
  11. 1494 int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
  12. 1495 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
  13. 1496 long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
  14. 1497 int (*mmap) (struct file *, struct vm_area_struct *);
  15. 1498 int (*open) (struct inode *, struct file *);
  16. 1499 int (*flush) (struct file *, fl_owner_t id);
  17. 1500 int (*release) (struct inode *, struct file *);
  18. 1501 int (*fsync) (struct file *, int datasync);
  19. 1502 int (*aio_fsync) (struct kiocb *, int datasync);
  20. 1503 int (*fasync) (int, struct file *, int);
  21. 1504 int (*lock) (struct file *, int, struct file_lock *);
  22. 1505 ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
  23. 1506 unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
  24. 1507 int (*check_flags)(int);
  25. 1508 int (*flock) (struct file *, int, struct file_lock *);
  26. 1509 ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
  27. 1510 ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
  28. 1511 int (*setlease)(struct file *, long, struct file_lock **);
  29. 1512};
open使用查看“1.2 file_operations open 使用


globalmem程序代码: globalmem.rar   将rar修改为tar.bz2
  1. #include <linux/module.h>//MODULE_LICENSE() module_init
  2. #include <linux/init.h>//__init __exit
  3. #include <linux/fs.h>//register_chrdev_region
  4. #include <linux/kernel.h>//contain_of kmalloc kfree printk
  5. #include <linux/cdev.h>//struct cdev cdev;
  6. #include <linux/errno.h>
  7. //#include <linux/mm.h>
  8. //#include <linux/sched.h>
  9. #include <linux/types.h>// MAJOR(dev_t dev) MKDEV
  10. //#include <asm/io.h>
  11. //#include <asm/system.h>
  12. #include <asm/uaccess.h> //copy_to_user copy_from_user

  13. #include <linux/stat.h>//00666
  14. #include <linux/slab.h>//kmalloc kfree
  15. #include <linux/moduleparam.h>//module_param()

  16. #define GLOBALMEM_SIZE 0x1000
  17. #define MEM_CLEAR 0x1
  18. #define GLOBALMEM_MAJOR 240 //240-250 LOCAL/EXPERIMENTAL USE

  19. static int globalmem_major = GLOBALMEM_MAJOR;
  20. module_param(globalmem_major,int,S_IRUGO);

  21. struct globalmem_dev{
  22.     struct cdev cdev;
  23.     unsigned char mem[GLOBALMEM_SIZE];
  24. };
  25. struct globalmem_dev *globalmem_devp;

  26. int globalmem_open(struct inode *inode,struct file *filp)
  27. {
  28.     struct globalmem_dev *dev;
  29.     //将设备结构体指针赋值给文件私有数据指针
  30.     dev = container_of(inode->i_cdev,struct globalmem_dev,cdev);
  31.     filp->private_data = dev;
  32.     return 0;
  33. }

  34. int globalmem_release(struct inode *inode,struct file *filp)
  35. {
  36.     return 0;
  37. }
  38. static int globalmem_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
  39. {
  40.     struct globalmem_dev *dev = filp->private_data;
  41.     switch(cmd)
  42.     {
  43.         case MEM_CLEAR:
  44.             memset(dev->mem,0,GLOBALMEM_SIZE);
  45.             printk(KERN_INFO"globalmem is set to zero\n");
  46.             break;
  47.         default:
  48.             return -EINVAL;//22 invaild arguments
  49.     }
  50.     return 0;
  51. }
// read write 看博文“1.3 file_operations read write 使用
  1. //size 要复制的数据字节数
  2. //loff_t *ppos :在文件中的位置
  3. static ssize_t globalmem_read(struct file *filp,char __user *buf,size_t size,loff_t *ppos)
  4. {
  5.     unsigned long p = *ppos;//p在文件中的位置,范围[0,GLOBALMEM_SIZE-1]
  6.     unsigned int count = size; //读取的 字节数 
  7.     int ret = 0;
  8.     struct globalmem_dev *dev = filp->private_data;//获得设备结构体指针
  9.     if(p >= GLOBALMEM_SIZE)// 超出 GLOBALMEM_SIZE大小,
  10.         return 0;
  11.     if(count > GLOBALMEM_SIZE - p)//读取的数据 超出范围
  12.         count = GLOBALMEM_SIZE - p;
  13.     if(copy_to_user(buf,(void *)(dev->mem+p),count))
  14.     {
  15.         ret = -EFAULT;//bad address
  16.     }
  17.     else
  18.     {
  19.         *ppos += count;//ppos在文件中的位置,重新设置
  20.         ret = count;
  21.         printk(KERN_INFO"read %u byte(s) form %lu\n",count,p);
  22.     }
  23.     return ret;
  24. }

  25. static ssize_t globalmem_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos)
  26. {
  27.     unsigned long p = *ppos;//在文件中位置,范围[0,GLOBALMEM_SIZE-1]
  28.     unsigned int count = size;//读数据的 字节数 
  29.     int ret = 0;
  30.     struct globalmem_dev *dev = filp->private_data;

  31.     if(p >= GLOBALMEM_SIZE)
  32.         return 0;
  33.     if(count > GLOBALMEM_SIZE- p)
  34.         count = GLOBALMEM_SIZE - p;
  35.     if(copy_from_user(dev->mem + p,buf,count))
  36.         ret = -EFAULT;//bad address
  37.     else
  38.     {
  39.         *ppos += count;
  40.         ret = count;
  41.         printk(KERN_INFO"written %u byte(s) from %lu\n",count,p);
  42.     }
  43.     return ret;
  44. }
//loff_t offset:在文件中的位置 [0,GLOBALMEM_SIZE]
//int orig:命令
  1. static loff_t globalmem_llseek(struct file *filp,loff_t offset,int orig)
  2. {
  3.     loff_t ret = 0;
  4.     switch(orig)
  5.     {
  6.         case 0:
  7.             if(offset < 0)
  8.             {
  9.                 ret = -EINVAL;//
  10.                 break;
  11.             }
  12.             if((unsigned int)offset > GLOBALMEM_SIZE)
  13.             {
  14.                 ret = -EINVAL;
  15.                 break;
  16.             }
  17.             filp->f_pos = (unsigned int)offset;
  18.             ret = filp->f_pos;
  19.             break;
  20.         case 1:
  21.             if((filp->f_pos + offset) > GLOBALMEM_SIZE)
  22.             {
  23.                 ret = -EINVAL;    
  24.                 break;
  25.             }
  26.             if((filp->f_pos + offset) < 0)
  27.             {
  28.                 ret = -EINVAL;
  29.                 break;
  30.             }
  31.             filp->f_pos +=offset;
  32.             ret = filp->f_pos;
  33.             break;
  34.         default:
  35.             ret = -EINVAL;
  36.             break;
  37.     }
  38.     return ret;
  39. }

  40. static const struct file_operations globalmem_fops={
  41.     .owner = THIS_MODULE,
  42.     .llseek = globalmem_llseek,
  43.     .read = globalmem_read,
  44.     .write = globalmem_write,
  45.     .ioctl = globalmem_ioctl,
  46.     .open = globalmem_open,
  47.     .release = globalmem_release,
  48. };

  49. static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
  50. {
  51.     int err,devno = MKDEV(globalmem_major,index);
  52.     cdev_init(&dev->cdev,&globalmem_fops);
  53.     dev->cdev.owner = THIS_MODULE;
  54.     err = cdev_add(&dev->cdev,devno,1);
  55.     if(err)
  56.         printk(KERN_INFO"error %d ading globalmem %d",err,index);
  57. }

  58. static int __init globalmem_init(void)
  59. {
  60.     int result;
  61.     dev_t devno = MKDEV(globalmem_major,0);
  62.     if(globalmem_major)
  63.     {
  64.         result = register_chrdev_region(devno,1,"globalmem");
  65.         printk("register_chrdev_region\n");
  66.     }
  67.     else
  68.     {
  69.         result = alloc_chrdev_region(&devno,0,1,"globalmem");
  70.         globalmem_major = MAJOR(devno);
  71.         printk("alloc_chrdev_region\n");
  72.     }
  73.     if(result < 0)
  74.         return result;
  75.      //可以使用 zmalloc申请内核空间,这样就不需要memset重新清空内存了,zmalloc已经完成清空内存操作
  76.     globalmem_devp = kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
  77.     if(!globalmem_devp)    
  78.     {
  79.         result = -ENOMEM;//no memory
  80.         goto fail_malloc;
  81.     }
  82.     memset(globalmem_devp,0,sizeof(struct globalmem_dev));
  83.     globalmem_setup_cdev(globalmem_devp,0);

  84.     printk("init finished..\n");
  85.     return 0;
  86. fail_malloc:
  87.     unregister_chrdev_region(devno,1);
  88.     return result;
  89. }

  90. static void __exit globalmem_exit(void)
  91. {
  92.     cdev_del(&globalmem_devp->cdev);
  93.     kfree(globalmem_devp);
  94.     unregister_chrdev_region(MKDEV(globalmem_major,0),1);
  95.     printk("exit..\n");
  96. }
  97. MODULE_AUTHOR("Song");
  98. MODULE_LICENSE("GPL");

  99. module_init(globalmem_init);
  100. module_exit(globalmem_exit);

操作过程:
1. 插入模块文件,在程序代码中,主设备号设置为 240
  1. ywx@ywx:~/Desktop/module/baohua/globalmem$ sudo insmod ./globalmem.ko
  2. ywx@ywx:~/Desktop/module/baohua/globalmem$ cat /proc/devices
  3. Character devices:
  4.   1 mem
  5.   4 /dev/vc/0
  6. 136 pts
  7. 180 usb
  8. 189 usb_device
  9. 240 globalmem

2.新建设备文件,并测试
  1. root@ywx:/home/ywx/desktop# mknod /dev/globalmem c 240 0
  2. root@ywx:/home/ywx/desktop# echo "i love linux" > /dev/globalmem
  3. root@ywx:/home/ywx/desktop# cat /dev/globalmem
  4. i love linux
当使用 mknod /dev/globalmem c 240 1的时候,会出错。因为在程序代码中只申请了一个设备,次设备号为0,如果需要多个次设备,需要在程序代码中修改并增加设备数量及次设备号。


3. 参看/proc/modules 和 /sys/modules/globalmem 
 系统中多了 globalmem 这个设备
  1. ywx@ywx:~/desktop$ cat /proc/modules | grep glo
  2. globalmem 1816 0 - Live 0xe0a27000

  3. ywx@ywx:~/desktop$ ls /sys/module/globalmem/
  4. holders initstate notes parameters refcnt sections srcversion

4. dmesg 测试程序代码 printk
  1. root@ywx:/home/ywx/desktop# dmesg | tail -4
  2. [ 6439.327891] register_chrdev_region
  3. [ 6439.327958] init finished..
  4. [ 6555.013116] written 13 byte(s) from 0
  5. [ 6558.571373] read 4096 byte(s) form 0

5. 测试 动态设备节点
 当major主设备号为0时,系统会自动设置并分配空闲的主设备号,注意,主设备号240-250是用于 实验 使用的。所以当我们自动分配主设备号时,经常是分配250这个主设备号
  1. ywx@ywx:~/desktop/module/baohua/globalmem$ sudo insmod ./globalmem.ko globalmem_major=0
  2. ywx@ywx:~/desktop/module/baohua/globalmem$ cat /proc/devices
  3. Character devices:
  4.   1 mem
  5.   4 /dev/vc/0
  6. 128 ptm
  7. 136 pts
  8. 180 usb
  9. 189 usb_device
  10. 250 globalmem
6. 使用程序代码测试 将rar修改为tar.bz2
  1. #include <stdio.h>//printf memset
  2. #include <unistd.h>
  3. #include <fcntl.h>//
  4. #include <linux/stat.h>//S_IRUSR=00600
  5. #include <stdlib.h>//exit
  6. #include <string.h>//strcpy

  7. int main(int argc, char **argv)
  8. {
  9.     int fd = 0;
  10.     int ret = 0;
  11.     char buf[10];
  12.     
  13.     //clear memory
  14.     memset(buf,'a',10);
  15.     printf("%s\n",buf);
  16.     
  17.     //open
  18.     fd = open("/dev/globalmem",O_RDWR,S_IRUSR|S_IWUSR);

  19.     //write
  20.     ret = write(fd,"yuweixian",strlen("yuweixian"));//attentions: "\n"

  21.     //lseek set to file offset
  22.     ret = lseek(fd,0,SEEK_SET);

  23.     //read
  24.     ret = read(fd,buf,strlen(buf));
  25.     printf("%s\n",buf);

  26.     close(fd);
  27.     exit(EXIT_SUCCESS);
  28. }
  1. root@ywx:/home/ywx/desktop/module/baohua/globalmem# ./app
  2. aaaaaaaaaa
  3. yuweixian

当使用 cat /dev/globalmem,显示如下:
在显示 “yuweixian” 打印信息后,/dev/globalmem内存中还有很多其他混乱的数据,这是因为我们没有输入结束符






















阅读(1999) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~