驱动开发--初识(字符设备scull)
由于工作需要,学习驱动开发。学习的主要参考资料是LDD3。操作系统为Ubuntu164.
scull( Simple Character Utility for Loading Localities)是一个字符驱动,scull只是操作从内核分配的内存,不依赖硬件,操作一块内存区域就好像这块内存是一个设备(也是Linux操作系统的精妙所在)。所以以它为开始学习的起点便于我们学习。
如果在 /dev 目录下输入 ls -l 会显示如下:
total 0
crw------- 1 root root 10, 235 11月 7 08:44 autofs
drwxr-xr-x 2 root root 360 11月 7 2018 block
drwxr-xr-x 2 root root 60 11月 7 2018 bsg
crw------- 1 root root 10, 234 11月 7 08:44 btrfs-control
drwxr-xr-x 3 root root 60 11月 7 2018 bus
drwxr-xr-x 2 root root 3780 11月 7 08:44 char
crw------- 1 root root 5, 1 11月 7 08:44 console
lrwxrwxrwx 1 root root 11 11月 7 2018 core -> /proc/kcore
drwxr-xr-x 2 root root 60 11月 7 2018 cpu
crw------- 1 root root 10, 59 11月 7 08:44 cpu_dma_latency
crw------- 1 root root 10, 203 11月 7 08:44 cuse
drwxr-xr-x 5 root root 100 11月 7 2018 disk
drwxr-xr-x 2 root root 100 11月 7 2018 dri
crw------- 1 root root 10, 61 11月 7 08:44 ecryptfs
crw-rw---- 1 root video 29, 0 11月 7 08:44 fb0
lrwxrwxrwx 1 root root 13 11月 7 2018 fd -> /proc/self/fd
brw-rw---- 1 root disk 2, 0 11月 7 08:44 fd0
crw-rw-rw- 1 root root 1, 7 11月 7 08:44 full
crw-rw-rw- 1 root root 10, 229 11月 7 08:44 fuse
其中最后修改日期前面的数表示主设备号和次设备号,如最后一行的10,229;
解释一下 /dev 目录:这里并不是放的外部设备的驱动程序。这一点和我们常用的windows操作系统不一样。它实际上是一个访问这些外部设备的端口。我们可以非常方便地去访问这些外部设备,和访问一个文件,一个目录没有任何区别。
MAJOR(dev_t dev); //获得主设备号
MINOR(dev_t dev); //获得次设备号
如果有设备号需要将其转换为一个dev_t,使用:
MKDEV(int major,int minor);
在内核中, dev_t 类型(在 <linux/types.h>中定义)用来持有设备编号 -- 主次部分都包括. 对于 2.6.0 内核, dev_t 是 32 位的量, 12 位用作主编号, 20 位用作次编号.其定义如下:
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
int register_chrdev_region(dev_t first, unsigned int count, char *name);//自己分配设备号
这里, first 是你要分配的起始设备编号. first 的次编号部分常常是 0, 但是没有要求是那个效果. count
是你请求的连续设备编号的总数. 注意, 如果 count 太大, 你要求的范围可能溢出到下一个次编号;
但是只要你要求的编号范围可用, 一切都仍然会正确工作.
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);//动 态分配设备号
使用这个函数, dev 是一个只输出的参数, 它在函数成功完成时持有你的分配范围的第一个数.
fisetminor 应当是请求的第一个
void unregister_chrdev_region(dev_t first, unsigned int count);//释放设备号
调用 unregister_chrdev_region 的地方常常是你的模块的 cleanup 函数.