Linux驱动开发-字符设备驱动

1.Linux字符设备驱动结构

1.1 cdev结构体

Linux内核中使用cdev结构体描述一格字符设备

struct cdev{
    struct kobject kobj;
    struct module *owner;    /*所属模块*/
    struct file_operations *ops;   /*文件操作结构体*/
    struct list_head list;
    dev_t dev;                /*设备号,前12位主设备号,后20位次设备号*/
    unsigned int count;
}

Linux内核list_head分析

Linux:主设备号和次设备号

struct module结构体 和 THIS_MODULE详解

1.2 cdev操作函数

初始化函数cdev_init() /建立file_operations和cdev之间的关系

void cdev_init(struct cdev *cdev,struct file_operations *fops)
{
    memset(cdev,0,sizeof *cdev);
    INIT_LIST_HEAD(&cdev->list);
    kobject_init(&cdev->kobj,&ktype_cdev_default);
    cdev->ops=fops
}

分配和释放设备号

调用cdev_add()向系统注册字符设备前,需要使用函数申请设备号,这两个函数分别为:

  • int register_chrdev_region(dev_t from,unsigned count,const  char *name);
    (用于一直起始设备的设备号的情况)
  • int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count);
    (用于设备号未知的情况, 调用成功后把得到的设备号放入第一个参数dev中)

1.3Linux字符驱动设备驱动的组成

字符设备驱动读,写,IO控制函数模板:xxx_read(),xxx_write(),xxx_ioctl()

/*xxx为字符设备名*/
/*filp文件结构体指针,
*buf:用户空间内的缓存地址,
count:要读的字节数,
*f_pos要读的位置相对于开头的偏移*/
ssize_t xxx_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos)
{
    ...
    copy_to_user(buf,...,...);//内核空间复制到用户缓存区
    ...
}

ssize_t xxx_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_pos)
{
    ...
    copy_from_user(...,buf,...);//用户空间缓存区复制到内核空间
    ...
}
/*ioctl函数*/
long xxx_ioctl(struct file *filep,unsigned int cmd,unsigned long arg)
{
    ...
    switch (cmd){
    case xxx_cmd1:
        ...
        berak;
    case xxx_cmd2:
        ...
        break;
    default://不能支持的命令
        return -ENOTTY;

    }
    return 0;
}

定义字符设备文件操作函数 xxx_fops

struct file_operations xxx_fops={
    .owner=THIS_MODULE,
    .read=xxx_read,
    .write=xxx_write,
    .unlocked_ioctl=xxx_ioctl,
    ...
}

驱动加载,卸载函数:xxx_init(),xxx_exit()

//设备结构体
struct xxx_dev_t{
    struct cdev cdev;
    ...
}xxx_dev;
//设备驱动加载函数
static int __init xxx_init(void)
{
    ...
    cdev_init(&xxx_dev.cdev,&xxx_fops);
    xxx_dev.cdev.owner=THIS_MODULE;
    //获取字符设备号
    if(xxx_major){
        register_chrdev_region(xxx_dev_n0,1,DEV_NAME);
    }else{
        alloc_chrdev_region(&xxx_dev_no,0,1,DEV_NAME);
    }
    ret=cdev_add(&xxx_dev.cdev,xxx_dev_no,1);//注册设备
    ...
}
//驱动卸载
static void __exit xxx_exit(void)
{
    unregister_chrdev_region(xxx_dev_no,1);
    cdev_del(&xxx_dev.cdev);
    ...
} 

猜你喜欢

转载自blog.csdn.net/weixin_37058227/article/details/81978006