第3章 字符设备驱动
一些重要的数据结构以及引用关系:
第一步:MAJOR设备编号的动态分配
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);
获取设备编号的代码:
if (scull_major) {
devno = MKDEV(scull_major, scull_minor);// dev_t devno
result = register_chrdev_region(devno, scull_nr_devs, "scull");
} else {
result = alloc_chrdev_region(&devno, scull_minor, scull_nr_devs, "scull");
scull_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
return result;
}
一般,提供缺省的MAJOR, 用户指定的MAJOR则通过选项参数传入;如果冲突则动态分配设备编号
加载模块并创建设备文件的脚本:
#!/bin/sh
module="scull"
device="scull"
mode="664"
# invoke insmod with all arguments we got
# and use a pathname, as newer modutils don't look in . by default
/sbin/insmod ./$module.ko $* || exit 1
# remove stale nodes
rm -f /dev/${device}[0-3]
major=$(awk "\\$2==\"$module\" {print \\$1}" /proc/devices)
mknod /dev/${device}0 c $major 0
mknod /dev/${device}1 c $major 1
mknod /dev/${device}2 c $major 2
mknod /dev/${device}3 c $major 3
# give appropriate group/permissions, and change the group.
# Not all distributions have staff, some have "wheel" instead.
group="staff"
grep -q '^staff:' /etc/group || group="wheel"
chgrp $group /dev/${device}[0-3]
chmod $mode /dev/${device}[0-3]
第二步:建立具体的设备结构
一般的,此设备中嵌入cdev结构(见上图最左边方框)
struct scull_dev {
struct scull_qset *data; /* Pointer to first quantum set */
int quantum; /* the current quantum size */
int qset; /* the current array size */
unsigned long size; /* amount of data stored here */
unsigned int access_key; /* used by sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
第三步:向内核注册字符设备
方法一:动态分配cdev
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &my_fops;
方法二:已经嵌入到我们的结构体,即已存在了
void cdev_init(struct cdev *cdev, struct file_operations *fops);//先初始化
int cdev_add(struct cdev *dev, dev_t devno, unsigned int count); //注册到内核
void cdev_del(struct cdev *dev); //卸载cdev
因为cdev_ini中已经初始化了ops成员,所以再使用dev->ops = &scull_fops;就是赘余了,如下:
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
int err, devno = MKDEV(scull_major, scull_minor + index);
cdev_init(&dev->cdev, &scull_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scull_fops;
err = cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err)
printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}
第四步:写file_operation结构体中的操作函数指针成员的实现
省略
一个值得一提的技巧:通常将struct file的成员private_data指针指向我们的设备结构体(这里是struct scull_dev). 做这个赋值,是在open函数中做的。其他操作函数直接可以通过file的private成员获得设备结构体,很方便。而且真正这样做的原因在多个设备文件时的驱动时,才真正体现作用