主编号标识设备相连的驱动,次编号被内核用来决定引用哪个设备。
在内核中,
dev_t 类型(在
<linux/types.h>中定义)用来持有设备编号。对于
2.6.0 内核,
dev_t 是 32 位的量,
12 位用作主编号, 20
位用作次编号.
应当利用在
<linux/kdev_t.h>中的一套宏定义.
为获得一个 dev_t
的主或者次编号, 使用:
(dev_t)-->主设备号、次设备号 | MAJOR(dev_t |
主设备号、次设备号-->(dev_t) | MKDEV(int major,int |
在建立一个字符驱动时你的驱动需要做的第一件事是获取一个或多个设备编号来使用。并且应当在不再使用它们时释放它。
|
安排主编号最好的方式,
我们认为, 是缺省使用动态分配,
而留给自己在加载时或者甚至在编译时指定主编号的选项权.
以下是在scull.c中用来获取主设备好的代码:
|
动态分配的缺点是你无法提前创建设备节点,
因为分配给你的模块的主编号会变化.
对于驱动的正常使用,
这不是问题, 因为一旦编号分配了,
你可从 /proc/devices
中读取它.
大部分的基础性的驱动操作包括
3 个重要的内核数据结构,file_operations,
file 和 inode.
file_operations结构定义在
<linux/fs.h>
|
struct
file,定义于 <linux/fs.h>与用户空间程序的
FILE 指针没有任何关系。文件结构代表一个打开的文件.它由内核在
open 时创建,
并传递给在文件上操作的任何函数,
直到最后的关闭.
在文件的所有实例都关闭后,
内核释放这个数据结构.
inode
结构,是在内核内部用来表示文件的。因此,
它和代表打开文件描述符的文件结构是不同的.
可能有代表单个文件的多个打开描述符的许多文件结构,
但是它们都指向一个单个 inode
结构.
struct
cdev *my_cdev = cdev_alloc();
void
cdev_init(struct cdev *cdev, const struct file_operations *fops)
cdev.owner
= THIS_MODULE;
int
cdev_add(struct cdev *p, dev_t dev, unsigned count)
从系统中移除一个字符设备:void
cdev_del(struct cdev *p)
以下是scull中的初始化代码(之前已经为struct
scull_dev
分配了空间):
|
open
方法提供给驱动来做任何的初始化来准备后续的操作.
在大部分驱动中, open
应当进行下面的工作:
●检查设备特定的错误(例如设备没准备好,
或者类似的硬件错误)
●如果它第一次打开,
初始化设备
●如果需要,
更新 f_op 指针.
●分配并填充要放进
filp->private_data 的任何数据结构
但是,
事情的第一步常常是确定打开哪个设备.
记住 open 方法的原型是:
|
inode
参数有我们需要的信息,以它的
i_cdev 成员的形式,
里面包含我们之前建立的 cdev
结构.
|
这个宏使用一个指向
container_field
类型的成员的指针,
它在一个
container_type
类型的结构中,
并且返回一个指针指向包含结构.
在
scull_open,
这个宏用来找到适当的设备结构:
|
识别打开的设备的另外的方法是查看存储在
inode
结构的次编号.
如果你使用
register_chrdev
注册你的设备,
你必须使用这个技术.
确认使用
iminor
从
inode
结构中获取次编号,
并且确定它对应一个你的驱动真正准备好处理的设备.
|
●释放 open
分配在 filp->private_data
中的任何东西
●在最后的
close 关闭设备
scull
的基本形式没有硬件去关闭,
因此需要的代码是最少的:
|
以下是scull模型的结构体:
|
scull驱动程序引入了两个Linux内核中用于内存管理的核心函数,它们的定义都在<linux/slab.h>:
|
以下是scull模块中的一个释放整个数据区的函数(类似清零),将在scull以写方式打开和scull_cleanup_module中被调用:
|
以下是scull模块中的一个沿链表前行得到正确scull_set指针的函数,将在read和write方法中被调用:
|
其实这个函数的实质是:如果已经存在这个scull_set,就返回这个scull_set的指针。如果不存在这个scull_set,一边沿链表为scull_set分配空间一边沿链表前行,直到所需要的scull_set被分配到空间并初始化为止,就返回这个scull_set的指针。
|
read
和 write 方法的
buff 参数是用户空间指针.
因此, 它不能被内核代码直接解引用.
scull 中的读写代码需要拷贝一整段数据到或者从用户地址空间.
这个能力由下列内核函数提供,
它们拷贝一个任意的字节数组:
|
你可以调用 __copy_to_user
和 __copy_from_user 来代替.
这是有用处的, 例如,
如果你知道你已经检查了这些参数.
但是, 要小心;
事实上,
如果你不检查你传递给这些函数的用户空间指针,
那么你可能造成内核崩溃和/或安全漏洞.
而值得一提的是上两个函数与
|
之间的关系,前者调用后者,但前者在调用前对用户空间指针进行了检查。
|
/* copy */ #include //#include #include #include /* * * */ int { struct int int for if for kfree(dptr->data); kfree(dptr->data); dptr->data } next kfree(dptr); } dev->size dev->quantum dev->qset dev->data return } /* * */ int { struct dev filp->private_data /* if if return scull_trim(dev); up(&dev->sem); } return } int { return } struct .owner .read .write .open .release }; /* * */ /* * * * */ void { int dev_t /* if for scull_trim(scull_devices cdev_del(&scull_devices.cdev); } kfree(scull_devices); } /* unregister_chrdev_region(devno, } /* * */ static { int cdev_init(&dev->cdev, dev->cdev.owner // dev->cdev.ops err /* if printk(KERN_NOTICE } int { int dev_t /* * * */ if dev result } result "scull"); scull_major } if printk(KERN_WARNING return } /* scull_devices if result goto } memset(scull_devices, /* for scull_devices.quantum scull_devices.qset init_MUTEX(&scull_devices.sem); scull_setup_cdev(&scull_devices, } return fail: scull_cleanup_module(); return } module_init(scull_init_module); module_exit(scull_cleanup_module); MODULE_AUTHOR("Duck"); MODULE_LICENSE("Dual |
/* copy */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * */ int int int int int module_param(scull_major, module_param(scull_minor, module_param(scull_nr_devs, module_param(scull_quantum, module_param(scull_qset, struct /* * * */ int { struct int int for if for kfree(dptr->data); kfree(dptr->data); dptr->data } next kfree(dptr); } dev->size dev->quantum dev->qset dev->data return } /* * */ int { struct dev filp->private_data /* if if return scull_trim(dev); up(&dev->sem); } return } int { return } /* * */ struct { struct /* if qs if return memset(qs, } /* while if qs->next if return memset(qs->next, } qs continue; } return } /* * */ ssize_t loff_t { struct struct int int int ssize_t if return if goto if count /* item rest s_pos /* dptr if goto /* if count if retval goto } *f_pos retval out: up(&dev->sem); return } ssize_t loff_t { struct struct int int int ssize_t if return /* item rest s_pos /* dptr if goto if dptr->data if goto memset(dptr->data, } if dptr->data[s_pos] if goto } /* if count if retval goto } *f_pos retval /* if dev->size out: up(&dev->sem); return } struct .owner .read .write .open .release }; /* * */ /* * * * */ void { int dev_t /* if for scull_trim(scull_devices cdev_del(&scull_devices.cdev); } kfree(scull_devices); } /* unregister_chrdev_region(devno, } /* * */ static { int cdev_init(&dev->cdev, dev->cdev.owner // dev->cdev.ops err /* if printk(KERN_NOTICE } int { int dev_t /* * * */ if dev result } result "scull"); scull_major } if printk(KERN_WARNING return } /* scull_devices if result goto } memset(scull_devices, /* for scull_devices.quantum scull_devices.qset init_MUTEX(&scull_devices.sem); scull_setup_cdev(&scull_devices, } return fail: scull_cleanup_module(); return } module_init(scull_init_module); module_exit(scull_cleanup_module); MODULE_AUTHOR("Duck"); MODULE_LICENSE("Dual |
yannzi@pc:~/linuxdriver2.6/字符驱动03$
make
make
-C /lib/modules/2.6.28-12-generic/build
M=/home/yannzi/linuxdriver2.6/字符驱动03
modules
make[1]:
正在进入目录 `/usr/src/linux-headers-2.6.28-12-generic'
CC
[M] /home/yannzi/linuxdriver2.6/字符驱动03/scull.o
Building
modules, stage 2.
MODPOST
1 modules
CC
/home/yannzi/linuxdriver2.6/字符驱动03/scull.mod.o
LD
[M] /home/yannzi/linuxdriver2.6/字符驱动03/scull.ko
make[1]:正在离开目录
`/usr/src/linux-headers-2.6.28-12-generic'
yannzi@pc:~/linuxdriver2.6/字符驱动03$
sudo insmod scull.ko
[sudo]
password for yannzi:
yannzi@pc:~/linuxdriver2.6/字符驱动03$
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
250
scull
251
hidraw
252
usb_endpoint
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
yannzi@pc:~/linuxdriver2.6/字符驱动03$
sudo mknod -m 666 scull0 c 250 0
yannzi@pc:~/linuxdriver2.6/字符驱动03$
sudo mknod -m 666 scull1 c 250 1
yannzi@pc:~/linuxdriver2.6/字符驱动03$
sudo mknod -m 666 scull2 c 250 2
yannzi@pc:~/linuxdriver2.6/字符驱动03$
sudo mknod -m 666 scull3 c 250 3
yannzi@pc:~/linuxdriver2.6/字符驱动03$
ls -l > /dev/scull0
yannzi@pc:~/linuxdriver2.6/字符驱动03$
ls -l /dev/scull0
crwxrwxrwx
1 root staff 250, 0 2009-05-31 22:13 /dev/scull0
yannzi@pc:~/linuxdriver2.6/字符驱动03$
cat /dev/scull0
总用量 596
-rw-r--r--
1 yannzi yannzi 487 2009-05-31 21:53 Makefile
-rw-r--r--
1 yannzi yannzi 487 2009-05-31 21:53 Makefile~
-rw-r--r--
1 yannzi yannzi 0 2009-05-31 21:53 Module.markers
-rw-r--r--
1 yannzi yannzi 59 2009-05-31 21:53 modules.order
-rw-r--r--
1 yannzi yannzi 0 2009-05-31 21:53 Module.symvers
-rw-r--r--
1 yannzi yannzi 1599 2009-05-31 22:15 path
drwxr-xr-x
2 yannzi yannzi 4096 2009-05-31 21:49 scull
-rw-r--r--
1 yannzi yannzi 7984 2009-05-31 21:43 scull.c
-rw-r--r--
1 yannzi yannzi 5415 2009-05-31 21:40 scull.c~
-rw-r--r--
1 yannzi yannzi 5153 2005-02-01 04:31 scull.h
-rw-r--r--
1 yannzi yannzi 16185 2009-05-31 21:53 scull.ko
-rwxr-xr-x
1 yannzi yannzi 896 2009-05-31 22:12 scull_load.sh
-rw-r--r--
1 yannzi yannzi 1708 2009-05-31 22:12 scull_load.sh~
-rw-r--r--
1 yannzi yannzi 1203 2009-05-31 21:53 scull.mod.c
-rw-r--r--
1 yannzi yannzi 11068 2009-05-31 21:53 scull.mod.o
-rw-r--r--
1 yannzi yannzi 5740 2009-05-31 21:53 scull.o
drwxr-xr-x
2 yannzi yannzi 4096 2007-10-25 09:53 scull-test
-rw-r--r--
1 yannzi yannzi 692 2009-05-31 22:14 test_load
-rw-r--r--
1 yannzi yannzi 0 2009-05-31 22:14 test_load~
-rw-r--r--
1 yannzi yannzi 500952 2009-05-31 21:59 字符驱动.eio
用户201423 2009-6-4 20:39