Chinaunix首页 | 论坛 | 博客
  • 博客访问: 807797
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: 嵌入式

2015-06-26 15:56:26

进入字符设备驱动的学习了。

1. 主设备号和次设备号

对字符设备访问是通过文件系统内的设备名称进行的。
查看/dev目录,可以看到很多文件:
drwxr-xr-x  2 root root                    60 2015-06-25 12:33 net
drwxr-xr-x  2 root root                    60(表示文件的大小) 2015-06-25 12:32 pktcdvd   (目录文件)
crw-r-----  1 root kmem      1(主设备号),   4(次设备号) 2015-06-25 12:32 port
crw-------  1 root root    108,             0 2015-06-25 12:32 ppp
crw-rw----  1 root root     10,             1 2015-06-25 12:32 psaux
crw-rw-rw-  1 root tty       5,             2 2015-06-25 18:30 ptmx
lrwxrwxrwx  1 root root                     4 2015-06-25 12:32 rtc -> rtc0  (链接文件,创建了符号链接)
crw-rw----  1 root root    254,             0 2015-06-25 12:32 rtc0
lrwxrwxrwx  1 root root                     3 2015-06-25 12:32 scd0 -> sr0
brw-rw----  1 root disk      8,             0 2015-06-25 12:32 sda
brw-rw----  1 root disk      8,             1 2015-06-25 12:33 sda1

其中:
d 开头的是普通的目录文件(directory
c 开头的是字符设备文件
b 开头的块设备文件
l 开头的是链接文件
字符设备含有主设备号,次设备号;其中主设备号用来标识设备对应的驱动程序;次设备号由内核使用,用于正确确定设备文件所指的设备,内核本身不关心次设备号的任何其他信息。

1.1 设备编号的内部表达

dev_t 用来保存设备编号:包括主设备号和次设备号;dev_t类型在<linux/types.h>中定义;
dev_t是一个32位的数,其中12位用来表示主设备号,而其余20位用来表示次设备号。

获取主、次设备号:
MAJOR(dev_t dev);
MINOR(dev_t dev);
获取设备编号:
MKDEV(int major, int minor);
  1. #define MINORBITS    20
  2. #define MINORMASK    ((1U << MINORBITS) - 1)

  3. #define MAJOR(dev)    ((unsigned int) ((dev) >> MINORBITS))
  4. #define MINOR(dev)    ((unsigned int) ((dev) & MINORMASK))
  5. #define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))

1.2 分配和释放设备编号

分配设备编号:

1. 静态分配设备编号
函数:int register_chrdev_region(dev_t from, unsigned count, const char *name)
  1. /**
  2.  * register_chrdev_region() - register a range of device numbers              (注册某个范围的设备编号
  3.  * @from: the first in the desired range of device numbers; must include      (指定范围起始的编号值
  4.  * the major number.
  5.  * @count: the number of consecutive device numbers required                  (指定申请连续设备编号的个数大小
  6.  * @name: the name of the device or driver.                                   (设备或驱动的名字)
  7.  *
  8.  * Return value is zero on success, a negative error code on failure.         (注册成功,则返回0;失败,则返回负数
  9.  */
  10. int register_chrdev_region(dev_t from, unsigned count, const char *name)


2. 自动分配设备编号
函数:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
  1. /**
  2.  * alloc_chrdev_region() - register a range of char device numbers
  3.  * @dev: output parameter for first assigned number    --------------------->(输出设备编号,保存在这个变量里面
  4.  * @baseminor: first of the requested range of minor numbers  -------------->(请求使用的第一个次设备号
  5.  * @count: the number of minor numbers required
  6.  * @name: the name of the associated device or driver
  7.  *
  8.  * Allocates a range of char device numbers. The major number will be        (动态分配设备编号,保存在第一个参数里面
  9.  * chosen dynamically, and returned (along with the first minor number)      (分配成功,返回0;失败,返回负数)
  10.  * in @dev. Returns zero or a negative error code.
  11.  */
  12. int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
  13.             const char *name)


释放设备编号:

一般在模块的清除函数中,调用该函数来释放设备。
函数:void unregister_chrdev_region(dev_t from, unsigned count)

  1. /**
  2.  * unregister_chrdev_region() - return a range of device numbers
  3.  * @from: the first in the range of numbers to unregister          (该范围内起始的设备编号)
  4.  * @count: the number of device numbers to unregister              (设备的个数)
  5.  *
  6.  * This function will unregister a range of @count device numbers,
  7.  * starting with @from. The caller should normally be the one who
  8.  * allocated those numbers in the first place...
  9.  */
  10. void unregister_chrdev_region(dev_t from, unsigned count)

动态分配主设备号

对于一个新的驱动程序,不要随便选择一个当前未使用的设备号作为主设备号,而应该使用动态分配机制获取主设备号。
动态分配的缺点:由于不能保证动态分配的主设备号始终一致,所以无法预先创建设备节点。但是可以可以从 /proc/devices 中读取得到。

在scull文件中有main.c文件,里面有获取主设备号的代码:
  1. /*
  2.  * Get a range of minor numbers to work with, asking for a dynamic
  3.  * major unless directed otherwise at load time.
  4.  */
  5.     if (scull_major) {                                                   // 如果major已经指定,则静态分配设备编号
  6.         dev = MKDEV(scull_major, scull_minor);
  7.         result = register_chrdev_region(dev, scull_nr_devs, "scull");
  8.     } else {                                                             // 否则动态分配设备编号
  9.         result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
  10.                 "scull");
  11.         scull_major = MAJOR(dev);
  12.     }

  13.     if (result < 0) {                                                    // 判断返回值,看是否分配成功:为0,表示成功;负值,表示失败。
  14.         printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
  15.         return result;
  16.     }


scull代码,简单测试步骤:
1. 编译,生成模块文件scull.ko
book@book-desktop:/work/ldd/examples/scull$ make
make -C /lib/modules/2.6.31-14-generic/build M=/work/ldd/examples/scull LDDINC=/work/ldd/examples/scull/../include modules
make[1]: Entering directory `/usr/src/linux-headers-2.6.31-14-generic'
  CC [M]  /work/ldd/examples/scull/main.o
  CC [M]  /work/ldd/examples/scull/pipe.o
  CC [M]  /work/ldd/examples/scull/access.o
  LD [M]  /work/ldd/examples/scull/scull.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /work/ldd/examples/scull/scull.mod.o
  LD [M]  /work/ldd/examples/scull/scull.ko
make[1]: Leaving directory `/usr/src/linux-headers-2.6.31-14-generic'

2. 运行脚本文件,加载模块,生成字符节点文件,可以在/dev下面查看
(注意:运行scull_load可能需要权限,用chmod改变权限先。)
book@book-desktop:/work/ldd/examples/scull$ sudo ./scull_load
book@book-desktop:/work/ldd/examples/scull$ ls -l /dev/scull*
lrwxrwxrwx 1 root root        6 2015-06-25 22:28 /dev/scull -> scull0
crw-rw-r-- 1 root staff 251,  0 2015-06-25 22:28 /dev/scull0
crw-rw-r-- 1 root staff 251,  1 2015-06-25 22:28 /dev/scull1
crw-rw-r-- 1 root staff 251,  2 2015-06-25 22:28 /dev/scull2
crw-rw-r-- 1 root staff 251,  3 2015-06-25 22:28 /dev/scull3
lrwxrwxrwx 1 root root       10 2015-06-25 22:28 /dev/scullpipe -> scullpipe0
crw-rw-r-- 1 root staff 251,  4 2015-06-25 22:28 /dev/scullpipe0
crw-rw-r-- 1 root staff 251,  5 2015-06-25 22:28 /dev/scullpipe1
crw-rw-r-- 1 root staff 251,  6 2015-06-25 22:28 /dev/scullpipe2
crw-rw-r-- 1 root staff 251,  7 2015-06-25 22:28 /dev/scullpipe3
crw-rw-r-- 1 root staff 251, 11 2015-06-25 22:28 /dev/scullpriv
crw-rw-r-- 1 root staff 251,  8 2015-06-25 22:28 /dev/scullsingle
crw-rw-r-- 1 root staff 251,  9 2015-06-25 22:28 /dev/sculluid
crw-rw-r-- 1 root staff 251, 10 2015-06-25 22:28 /dev/scullwuid

3. 查看/proc/devices文件下面是否注册成功
book@book-desktop:/work/ldd/examples/scull$ cat /proc/devices
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  6 lp
  7 vcs
 10 misc
 13 input
 14 sound
 21 sg
 29 fb
 99 ppdev
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
216 rfcomm
251 scull
251 scullp
251 sculla
252 hidraw
253 usbmon
254 rtc

Block devices:
  1 ramdisk
  2 fd
259 blkext
  7 loop
  8 sd
  9 md
 11 sr
 65 sd
 66 sd
 67 sd
 68 sd
 69 sd
 70 sd
 71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
252 device-mapper
253 pktcdvd
254 mdp

4. 运行scull_unload文件,卸载模块,并查看:找不到设备节点和设备文件了。
book@book-desktop:/work/ldd/examples/scull$ sudo ./scull_unload
book@book-desktop:/work/ldd/examples/scull$ ls -l /dev/scull*
ls: cannot access /dev/scull*: No such file or directory
book@book-desktop:/work/ldd/examples/scull$ cat /proc/devices
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  6 lp
  7 vcs
 10 misc
 13 input
 14 sound
 21 sg
 29 fb
 99 ppdev
108 ppp
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
216 rfcomm
252 hidraw
253 usbmon
254 rtc

Block devices:
  1 ramdisk
  2 fd
259 blkext
  7 loop
  8 sd
  9 md
 11 sr
 65 sd
 66 sd
 67 sd
 68 sd
 69 sd
 70 sd
 71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
252 device-mapper
253 pktcdvd
254 mdp


2. 重要的数据结构


最最重要的概念来了。

2.1 重要的数据结构

三个重要的内核数据结构,定义在<linux/fs.h>
struct file_operations 
struct file        
struct inode

这里只贴出来定义:
struct file_operations
  1. /*
  2.  * NOTE:
  3.  * read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl
  4.  * can be called without the big kernel lock held in all filesystems.
  5.  */
  6. struct file_operations {
  7.     struct module *owner;
  8.     loff_t (*llseek) (struct file *, loff_t, int);
  9.     ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
  10.     ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
  11.     ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
  12.     ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
  13.     int (*readdir) (struct file *, void *, filldir_t);
  14.     unsigned int (*poll) (struct file *, struct poll_table_struct *);
  15.     int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
  16.     long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
  17.     long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
  18.     int (*mmap) (struct file *, struct vm_area_struct *);
  19.     int (*open) (struct inode *, struct file *);
  20.     int (*flush) (struct file *, fl_owner_t id);
  21.     int (*release) (struct inode *, struct file *);
  22.     int (*fsync) (struct file *, struct dentry *, int datasync);
  23.     int (*aio_fsync) (struct kiocb *, int datasync);
  24.     int (*fasync) (int, struct file *, int);
  25.     int (*lock) (struct file *, int, struct file_lock *);
  26.     ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
  27.     ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
  28.     unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
  29.     int (*check_flags)(int);
  30.     int (*dir_notify)(struct file *filp, unsigned long arg);
  31.     int (*flock) (struct file *, int, struct file_lock *);
  32.     ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
  33.     ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
  34. };

struct file
  1. struct file {
  2.     /*
  3.      * fu_list becomes invalid after file_free is called and queued via
  4.      * fu_rcuhead for RCU freeing
  5.      */
  6.     union {
  7.         struct list_head    fu_list;
  8.         struct rcu_head     fu_rcuhead;
  9.     } f_u;
  10.     struct path        f_path;
  11. #define f_dentry    f_path.dentry
  12. #define f_vfsmnt    f_path.mnt
  13.     const struct file_operations    *f_op;
  14.     atomic_t        f_count;
  15.     unsigned int         f_flags;
  16.     mode_t            f_mode;
  17.     loff_t            f_pos;
  18.     struct fown_struct    f_owner;
  19.     unsigned int        f_uid, f_gid;
  20.     struct file_ra_state    f_ra;

  21.     unsigned long        f_version;
  22. #ifdef CONFIG_SECURITY
  23.     void            *f_security;
  24. #endif
  25.     /* needed for tty driver, and maybe others */
  26.     void            *private_data;

  27. #ifdef CONFIG_EPOLL
  28.     /* Used by fs/eventpoll.c to link all the hooks to this file */
  29.     struct list_head    f_ep_links;
  30.     spinlock_t        f_ep_lock;
  31. #endif /* #ifdef CONFIG_EPOLL */
  32.     struct address_space    *f_mapping;
  33. };


struct inode:
  1. struct inode {
  2.     struct hlist_node    i_hash;
  3.     struct list_head    i_list;
  4.     struct list_head    i_sb_list;
  5.     struct list_head    i_dentry;
  6.     unsigned long        i_ino;
  7.     atomic_t        i_count;
  8.     unsigned int        i_nlink;
  9.     uid_t            i_uid;
  10.     gid_t            i_gid;
  11.     dev_t            i_rdev;
  12.     unsigned long        i_version;
  13.     loff_t            i_size;
  14. #ifdef __NEED_I_SIZE_ORDERED
  15.     seqcount_t        i_size_seqcount;
  16. #endif
  17.     struct timespec        i_atime;
  18.     struct timespec        i_mtime;
  19.     struct timespec        i_ctime;
  20.     unsigned int        i_blkbits;
  21.     blkcnt_t        i_blocks;
  22.     unsigned short i_bytes;
  23.     umode_t            i_mode;
  24.     spinlock_t        i_lock;    /* i_blocks, i_bytes, maybe i_size */
  25.     struct mutex        i_mutex;
  26.     struct rw_semaphore    i_alloc_sem;
  27.     const struct inode_operations    *i_op;
  28.     const struct file_operations    *i_fop;    /* former ->i_op->default_file_ops */
  29.     struct super_block    *i_sb;
  30.     struct file_lock    *i_flock;
  31.     struct address_space    *i_mapping;
  32.     struct address_space    i_data;
  33. #ifdef CONFIG_QUOTA
  34.     struct dquot        *i_dquot[MAXQUOTAS];
  35. #endif
  36.     struct list_head    i_devices;
  37.     union {
  38.         struct pipe_inode_info    *i_pipe;
  39.         struct block_device    *i_bdev;
  40.         struct cdev        *i_cdev;
  41.     };
  42.     int            i_cindex;

  43.     __u32            i_generation;

  44. #ifdef CONFIG_DNOTIFY
  45.     unsigned long        i_dnotify_mask; /* Directory notify events */
  46.     struct dnotify_struct    *i_dnotify; /* for directory notifications */
  47. #endif

  48. #ifdef CONFIG_INOTIFY
  49.     struct list_head    inotify_watches; /* watches on this inode */
  50.     struct mutex        inotify_mutex;    /* protects the watches list */
  51. #endif

  52.     unsigned long        i_state;
  53.     unsigned long        dirtied_when;    /* jiffies of first dirtying */

  54.     unsigned int        i_flags;

  55.     atomic_t        i_writecount;
  56. #ifdef CONFIG_SECURITY
  57.     void            *i_security;
  58. #endif
  59.     void            *i_private; /* fs or device private pointer */
  60. };

2.2 字符设备的注册和移除

内核用struct cdev结构来表示字符设备,在内核调用设备的操作之前,必须分配并注册一个或者多个cdev结构。

  1. static struct char_device_struct {
  2.     struct char_device_struct *next;  // 链表指针
  3.     unsigned int major;
  4.     unsigned int baseminor;
  5.     int minorct;
  6.     char name[64];
  7.     struct file_operations *fops;                  // file_operations结构体,指定操作函数
  8.     struct cdev *cdev;        /* will die */
  9. } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
步骤:
1. 先分配并注册设备结构体struct cdev
scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);   // scull文件中的main.c
2. 初始化这个结构体
void cdev_init(struct cdev *, const struct file_operations *); 

3. 将这个结构体告诉内核
int cdev_add(struct cdev *, dev_t, unsigned);

例子:scull目录中的main.c

  1. /*
  2.  * Set up the char_dev structure for this device.
  3.  */
  4. static void scull_setup_cdev(struct scull_dev *dev, int index)
  5. {
  6.     int err, devno = MKDEV(scull_major, scull_minor + index);
  7.     
  8.     cdev_init(&dev->cdev, &scull_fops);    // 初始化设备结构体struct scull_dev,与scull_fops进行联系
  9.     dev->cdev.owner = THIS_MODULE;
  10.     dev->cdev.ops = &scull_fops;           // 多余的,在cdev_init(&dev->cdev, &scull_fops)函数中已经做了这一步
  11.     err = cdev_add (&dev->cdev, devno, 1); // 注册结构体到内核

  12.     /* Fail gracefully if need be */
  13.     if (err)
  14.         printk(KERN_NOTICE "Error %d adding scull%d", err, index);
  15. }


字符设备的移除,可以调用函数:

  1. /**
  2.  * cdev_del() - remove a cdev from the system
  3.  * @p: the cdev structure to be removed
  4.  *
  5.  * cdev_del() removes @p from the system, possibly freeing the structure
  6.  * itself.
  7.  */
  8. void cdev_del(struct cdev *p)
  9. {
  10.     cdev_unmap(p->dev, p->count);
  11.     kobject_put(&p->kobj);
  12. }


       

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