当前位置:首页 > 公众号精选 > IOT物联网小镇
[导读]作 者:道哥,10年嵌入式开发老兵,专注于:C/C、嵌入式、Linux。关注下方公众号,回复【书籍】,获取Linux、嵌入式领域经典书籍;回复【PDF】,获取所有原创文章(PDF格式)。目录混乱的API函数旧的API函数新的API函数代码实操创建驱动程序源文件创建Makefile...

作  者:道哥,10 年嵌入式开发老兵,专注于:C/C 、嵌入式、Linux


关注下方公众号,回复【书籍】,获取 Linux、嵌入式领域经典书籍;回复【PDF】,获取所有原创文章( PDF 格式)。


目录


  • 混乱的 API 函数


  • 旧的 API 函数


  • 新的 API 函数


  • 代码实操


    • 创建驱动程序源文件


    • 创建 Makefile 文件


    • 编译、加载驱动模块


  • 应用程序


    • 打开、读取、写入设备


    • 卸载驱动模块


    • 小结


  • 自动在 /dev 目录下创建设备节点


  • 代码下载


别人的经验,我们的阶梯!


大家好,我是道哥,今天我为大伙儿解说的技术知识点是:【字符设备的驱动程序】


在上一篇文章中,讨论的是Linux系统中,驱动模块的两种编译方式。


我们就继续以此为基础,用保姆级的粒度一步一步操作,来讨论一下字符设备驱动程序的编写方法。


  1. 这篇文章的实际操作部分,使用的是的 API 函数;


  2. 下一篇文章,再来演示新的 API 函数;


混乱的 API 函数

我在刚开始接触Linux驱动的时候,非常的困扰:注册一个字符设备,怎么有这么多的 API 函数啊?


参考的每一篇文章中,使用的函数都不一样,但是执行结果都是符合预期的!


比如下面这几个:


  1. register_chrdev(...);


  2. register_chrdev_regin(...);


  3. cdev_add(...);


它们的功能都是向系统注册字符设备,但是只从函数名上看,初学者谁能分得清它们的区别?!


这也难怪,Linux系统经过这么多年的发展,代码更新是很正常的事情。


但是,我们参考的文章就没法做到:很详细的把文章中所描述内容的背景介绍清楚,往往都是文章作者在自己的实际工作环境中,测试某种方法解决了自己的问题,于是就记录成文。


不同的文章、不同的工作上下文、不同的API函数调用,这往往就苦了我们初学者,特别是我这种有选择障碍症的人!


其实,上面这个几个函数都是正确的,它们的功能都是类似的,它们是Linux系统中不同阶段的产物。


旧的 API 函数

在Linux内核代码2.4版本和早期的2.6版本中,注册、卸载字符设备驱动程序的经典方式是:


注册设备:


int register_chrdev(unsigned int major,const char *name,struct file_operations *fops);
参数1 major:如果为0 - 由操作系统动态分配一个主设备号给这个设备;如果非0 - 驱动程序向系统申请,使用这个主设备号;


参数2 name:设备名称;


参数3 fops:file_operations 类型的指针变量,用于操作设备;


如果是动态分配,那么这个函数的返回值就是:操作系统动态分配给这个设备的主设备号。


这个动态分配的设备号,我们要把它记住,因为在其他的API函数中需要使用它。


卸载设备:


int unregister_chrdev(unsigned int major,const char *name)
参数1 major:设备的主设备号,也就是 register_chrdev() 函数的返回值(动态),或者驱动程序指定的设备号(静态方式);


参数2 name:设备名称;


新的 API 函数

注册设备:


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);
上面这2个注册设备的函数,其实对应着旧的 API 函数 register_chrdev:把参数 1 表示的动态分配、静态分配,拆分成2个函数而已。


也就是说:


register_chrdev_region(): 静态注册设备;


alloc_chrdev_region(): 动态注册设备;


这两个函数的参数含义是:


register_chrdev_region参数:


参数1 from: 注册指定的设备号,这是静态指定的,例如:MKDEV(200, 0) 表示起始主设备号 200, 起始次设备号为 0;


参数2 count: 驱动程序指定连续注册的次设备号的个数,例如:起始次设备号是 0,count 为 10,表示驱动程序将会使用 0 ~ 9 这 10 个次设备号;


参数3 name:设备名称;


alloc_chrdev_region参数:


参数1 dev: 动态注册就是系统来分配设备号,那么驱动程序就要提供一个指针变量来接收系统分配的结果(设备号);


参数2 baseminor: 驱动程序指定此设备号的起始值;


参数3 count: 驱动程序指定连续注册的次设备号的个数,例如:起始次设备号是 0,count 为 10,表示驱动程序将会使用 0 ~ 9 这 10 个次设备号;


参数4 name:设备名称;


补充一下关于设备号的内容:


这里的结构体dev_t,用来保存设备号,包括设备号和设备号。


它本质上是一个32位的数,其中的12位用来表示设备号,而其余20位用来表示设备号。


系统中定义了3宏,来实现dev_t变量、主设备号、次设备号之间的转换:


MAJOR(dev_t dev): 从  dev_t 类型中获取主设备号;


MINOR(dev_t dev):  从 dev_t 类型中获取次设备号;


MKDEV(int major,int minor): 把主设备号和次设备号转换为 dev_t 类型;


卸载设备:


void unregister_chrdev_region(dev_t from, unsigned count);
参数1 from: 注销的设备号;


参数2 count: 注销的连续次设备号的个数;


代码实操

下面,我们就用旧的API函数,一步一步的描述字符设备驱动程序的:编写、加载和卸载过程


如何使用新的API函数来编写字符设备驱动程序,下一篇文章再详细讨论。


以下所有操作的工作目录,都是与上一篇文章相同的,即:~/tmp/linux-4.15/drivers/。


创建驱动目录和驱动程序

$ cd linux-4.15/drivers/
$ mkdir my_driver1
$ cd my_driver1
$ touch driver1.c
driver1.c文件的内容如下(不需要手敲,文末有代码下载链接):


#include

  • #include

  • #include

  • #include

  • #include

  • #include

  • #include

  • #include

  • #include

  • #include


  • static unsigned int major;

    int driver1_open(struct inode *inode, struct file *file)
    {
    printk("driver1_open is called. \n");
    return 0;
    }

    ssize_t driver1_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
    {
    printk("driver1_read is called. \n");
    return 0;
    }

    ssize_t driver1_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos)
    {
    printk("driver1_write is called. \n");
    return 0;
    }

    static const struct file_operations driver1_ops={
    .owner = THIS_MODULE,
    .open = driver1_open,
    .read = driver1_read,
    .write = driver1_write,
    };

    static int __init driver1_init(void)
    {
    printk("driver1_init is called. \n");

    major = register_chrdev(0, "driver1",
  • 本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
    换一批
    延伸阅读

    9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

    关键字: 阿维塔 塞力斯 华为

    加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

    关键字: AWS AN BSP 数字化

    伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

    关键字: 汽车 人工智能 智能驱动 BSP

    北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

    关键字: 亚马逊 解密 控制平面 BSP

    8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

    关键字: 腾讯 编码器 CPU

    8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

    关键字: 华为 12nm EDA 半导体

    8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

    关键字: 华为 12nm 手机 卫星通信

    要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

    关键字: 通信 BSP 电信运营商 数字经济

    北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

    关键字: VI 传输协议 音频 BSP

    北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

    关键字: BSP 信息技术
    关闭
    关闭