Linux设备驱动之字符设备

来源:互联网 发布:时时彩遗漏数据软件 编辑:程序博客网 时间:2024/06/05 09:50

写在前面

字符设备是Linux设备驱动中最简单的设备,也是入门级驱动。网上已经有很详细的讲解,这篇文章侧重代码实现,提供了一个完整驱动框架的代码实现。

理论知识

重要结构体

字符设备驱动结构:

struct cdev {    struct kobject kobj;    struct module *owner;                       //一般为THIS_MODULE    const struct file_operations *ops;          //file_operation指针    struct list_head list;    dev_t dev;    unsigned int count;                       //引用计数};

file_operations结构体

struct file_operations {    struct module *owner;    loff_t (*llseek) (struct file *, loff_t, int);    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);    ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);    ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);    int (*iterate) (struct file *, struct dir_context *);    unsigned int (*poll) (struct file *, struct poll_table_struct *);    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);    int (*mmap) (struct file *, struct vm_area_struct *);    int (*open) (struct inode *, struct file *);    int (*flush) (struct file *, fl_owner_t id);    int (*release) (struct inode *, struct file *);    int (*fsync) (struct file *, loff_t, loff_t, int datasync);    int (*aio_fsync) (struct kiocb *, int datasync);    int (*fasync) (int, struct file *, int);    int (*lock) (struct file *, int, struct file_lock *);    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);    int (*check_flags)(int);    int (*flock) (struct file *, int, struct file_lock *);    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);    int (*setlease)(struct file *, long, struct file_lock **, void **);    long (*fallocate)(struct file *file, int mode, loff_t offset,              loff_t len);    void (*show_fdinfo)(struct seq_file *m, struct file *f);#ifndef CONFIG_MMU    unsigned (*mmap_capabilities)(struct file *);#endif};

对于read

unsigned long copy_to_user(void __user * to, const void *from, unsigned long count);

对于write

unsigned long copy_from_user(void *to, const void __user *from, unsigned long count);

设备号

申请:
已知设备号:

int register_chrdev_region(dev_t from, unsigned count, const char *name);

不知道设备号,动态分配:

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name); //申请成功之后,会把申请到的设备号,放入到第一个参数 dev_t *dev中

释放:

void unregister_chrdev_region(dev_t from, unsigned count);

注册与注销字符设备:

注册: 设备号与cdev结构体进行关联

int cdev_add(struct cdev *p, dev_t dev, unsigned count); 

注销:

void cdev_del(struct cdev *);

cdev设备与file_operation关联:

void cdev_init(struct cdev *cdev, const struct file_operations *fops);

字符设备的使用:

1.cdev结构体与fops关联
2.申请设备号
3.注册cdev
4.释放设备号
5.注销cdev设备

创建设备节点

自动创建/dev设备节点
创建class

struct class *cdev_class = class_create(THIS_MODULE, name);

删除class

class_destroy(cdev_class);

创建dev设备节点

struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) //parent表示父亲,如果为NULL,表示class目录下例如:device_create(cdev_class , NULL, devt, NULL, "name")

删除dev设备节点

void device_destroy(struct class *class, dev_t devt);

主副设备号:

要弄清楚三个东西
1. dev_t

        typedef __u32 __kernel_dev_t;        主设备号12位,次设备号12
  1. int major
  2. int minor

知道major和minor,获得dev_t

MKDEV(int major,int minor)

知道dev_t,获得major和minor: alloc_chrdev_region(dev_t *devt)

MAJOR(dev_t)MINOR(dev_t)

一个完成的字符设备框架 char_example.c

#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/delay.h>#include <linux/time.h>#include <linux/fs.h>#include <linux/uaccess.h>#include <linux/slab.h>#include <linux/gpio.h>int char_example_major = 0;   //主设备号dev_t char_devno;   //设备号//私有结构体struct char_example_dev{    struct cdev cdev;};struct char_example_dev *char_example_devp;    //实例化struct class *char_example_class;   //实例化class类//open函数static int char_example_open (struct inode *inode, struct file *filp){    printk(KERN_INFO "%s\n", __func__);    return 0;}//read函数static ssize_t char_example_read (struct file *filp, char __user *buf, size_t count, loff_t *ppos){    char *data = "data_read\n";    printk(KERN_INFO "%s\n", __func__);    copy_to_user(buf,data,strlen(data));    return strlen(data);}//write函数static ssize_t char_example_write (struct file *filp, const char __user *buf, size_t count, loff_t *ppos){    char data[30];    printk(KERN_INFO "%s\n", __func__);    memset(data, 0, 30);    copy_from_user(data, buf, count);    printk(KERN_INFO "%s : data is %s\n", __func__, data);    return strlen(data);}//file_operations 定义static struct file_operations char_example_fops = {    .owner = THIS_MODULE,    .open  = char_example_open,    .read  = char_example_read,    .write = char_example_write,};//模块初始化static int char_example_init(void){    int err, ret = -ENODEV;    struct device *dev_temp;    printk(KERN_INFO "%s\n", __func__);    //动态分配设备号    ret = alloc_chrdev_region(&char_devno, 0, 1, "char_example");    char_example_major = MAJOR(char_devno);  //获得主设备号    if(ret){        printk(KERN_ERR "%s : chrdev_region fail\n", __func__);        goto chrdev_region_fail;    }    //分配dev结构体内存    char_example_devp = kmalloc(sizeof(struct char_example_dev), GFP_KERNEL);    if(char_example_devp == NULL){        printk(KERN_ERR "%s : kmalloc is fail\n", __func__);        goto kmalloc_fail;    }    //初始化内存空间    memset(char_example_devp, 0, sizeof(struct char_example_dev));    //将cdev与file_opertaions关联    cdev_init(&char_example_devp->cdev, &char_example_fops);    //填充cdev结构体的成员变量的内容    char_example_devp->cdev.owner = THIS_MODULE;    char_example_devp->cdev.ops = &char_example_fops;    //注册cdev字符设备    err = cdev_add(&char_example_devp->cdev, char_devno, 1);    if(err){        printk(KERN_ERR "%s : cdev_add fail\n", __func__);        goto cdev_add_fail;    }    //创建class设备    char_example_class = class_create(THIS_MODULE, "char_example");    if(IS_ERR(char_example_class)){        printk(KERN_ERR "%s : class_create fail\n", __func__);        goto class_create_fail;    }    //创建class节点   也就是/dev/char_example    dev_temp = device_create(char_example_class, NULL, char_devno, NULL, "char_example");    if(IS_ERR(dev_temp)){        printk(KERN_ERR "%s : device_create fail\n", __func__);        goto device_create_fail;    }    printk(KERN_INFO "%s : init end\n", __func__);    return 0;//处理一些错误device_create_fail:    class_destroy(char_example_class);class_create_fail:    cdev_del(&char_example_devp->cdev);cdev_add_fail:    kfree(char_example_devp);kmalloc_fail:chrdev_region_fail:    unregister_chrdev_region(char_devno,1);    return -1;}//模块卸载static void char_example_exit(void){    printk(KERN_INFO "%s\n", __func__);    device_destroy(char_example_class, char_devno);    class_destroy(char_example_class);    cdev_del(&char_example_devp->cdev);    kfree(char_example_devp);    unregister_chrdev_region(char_devno,1);}module_init(char_example_init);module_exit(char_example_exit);//一些声明MODULE_LICENSE("GPL");MODULE_DESCRIPTION("linux char driver base");MODULE_AUTHOR("xiaolei");

对应测试程序 char_example_test.c

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>int main(void){    int fd, ret;    char data[20];    fd = open("/dev/char_example", O_RDWR);    if (fd < 0)    {        printf("can't open!\n");    }    write(fd, "xiaolei_write", strlen("xiaolei_write"));    ret = read(fd, data, 1);    printf("ret = %d\n", ret);    printf("read data is %s", data);    close(fd);    return 0;}

测试结果如下

这里写图片描述

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 火山下雪 火山的分类 火山岛自然生态风景区旅游 火山极速版邀请码 火山极速邀请码 日本火山喷发 西西里岛火山喷发 火山岛自然生态风景区 火山爆发图片 火山石的作用 火山极速版多少金币一元 在火山口附近 基拉韦厄火山 火山岛在哪里 黄石公园火山 在火山口附近寻找宝藏 日本火山爆发 火山的英语单词 黄椅山火山森林公园 火山泥一洗白有用吗 火山是怎么爆发的 火山地质公园 悦诗风吟火山泥水 黑色火山攻略 火山冷矿泉水 火山为什么会爆发 火山是怎样形成的 火山为什么会喷发 印尼火山喷发 洪田火山公园 火山泥一洗白 为什么火山会爆发 尤耶亚科火山 火山喷发的原因 世界著名火山 埃特纳火山爆发 勒沃托比火山 黄石公园超级火山 美食大战老鼠火山遗迹 父女岛七天生火顺序 父女岛7天怎么生火