LDD3 笔记: 第3章 字符设备的驱动

时间:2021-02-11 04:02:19

第3章 字符设备驱动

一些重要的数据结构以及引用关系:

LDD3 笔记: 第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成员获得设备结构体,很方便。而且真正这样做的原因在多个设备文件时的驱动时,才真正体现作用