Linux驱动

【一】驱动的概念:通过软件驱动硬件,使硬件处于某种工作模式,提供控制硬件的方法。

【二】Linux架构体系(精简):


【三】Linux驱动的开发步骤:1、驱动编写;2、驱动编译;3、驱动使用、测试

[1] Linux驱动模块的编写:

遵循内核提供的方法,Linux驱动的开发是调用的内核提供的函数宏(宏函数);

1、入口,加载。将驱动加载到Linux内核中时,从入口函数执行。

入口函数调用 内核提供的 module_init(入口函数名); 宏函数进行注册。

入口函数的返回值为 int ,形参为 void :

int __init xxx_func(void){

}

ps: __init 用来将驱动的入口函数的地址统一放在一个 __init段中进行统一加载。

2、出口,卸载。将驱动从Linux内核中卸载、删除时,从出口函数执行

出口函数通过 module_exit(出口函数名); 宏函数 进行注册

出口函数的返回值为 void ,形参为 void:

void __exit xxx_func(void){

}

ps: __exit 用来将驱动的出口函数的地址统一放在一个 __exit段中进行统一加载。

3、GPL协议申明,表明该驱动的代码遵循GPL开源协议

申明通过 MODULE_LICENSE("GPL"); 宏函数进行申明,表明遵循GPL协议。


内核文件中的 /include/linux/init.h 中可以看到声明的 module_init();module_exit(); 宏函数,/include/linux/module.h 中可以看到声明的 MODULE_LICENSE宏函数。

所以在模块编写是要包含相应的头文件

#include <linux/init.h>

#include <linux/module.h>


示例的驱动代码:

demo.c:

#include <linux/init.h>

#include <linux/module.h>

int __init demo_init(void){

//在这里完成驱动的工作内容

printk(KERN_INFO "-----%s------%s------%d-----%s",__FILE__,__func__,__LINE__," driver is running.."); //驱动用的格式化输出的函数,和printf很像,可以设置打印输出的级别。

return 0;

}

void __exit demo_exit(void){

//在这里编写驱动卸载、退出的逻辑

printk(KERN_INFO "-----%s------%s------%d-----%s",__FILE__,__func__,__LINE__," driver is stop..");

}

module_init(demo_init);

module_exit(demo_exit);

MOUDLE_LICENSE("GPL");

[2] 内核编译模块:

编译器:gcc  如果是用于其他平台,则需要交叉编译工具

如何去编译?

在编写应用层代码时:gcc make 预处理

在编译驱动时,需要用内核提供的相关的函数,需要内核构建的方法。

1、编译内核模块的Makefile :

编译方法:

外部编译:将内核模块的源文件放在内核源码外部进行编译

内部编译:将内核模块的源文件放在内核源码中进行编译,需要Kconfig,Makefile,make memuconfig

静态编译、将内核模块编译进uImage中

动态编译:编译生成一个动态模块 xxxx.ko

开发时使用外部动态编译,方便测试模块时的加载和卸载。

内核源码里的/kernelxxx/Documentation/kbuild 文件包含了编译内核模块的编译详细说明


内核模块编译的MakeFile:

KERNDIR:= /lib/moudle/3.2.0-29-generic-pae/build

PWD:= $(shell pwd) # 执行pwd命令,并把结果赋给PWD变量

obj-m:= demo.o  #指定要编译生成的.ko模块依赖于哪个.o文件

all:

    make -C $(KERNDIR) M=$(PWD) modules

clean:

    make -C $(KERNDIR) M=$(PWD) clean


[3] 使用内核模块

1、查看内核模块信息的命令

              modinfo xxx.ko

可以查看内核模块的信息

查看当前内核中已经插入的动态模块:

          lsmod


查看内核的日志信息

          dmesg

              选项:

                -c      清除当前内核日志信息

2、将内核模块加载到内核中,和内核形成一个整体来运行。需要管理员权限。

          insmod xxx.ko

在加载模块时,加载函数会被调用,加载只会执行一次

3、将内核中的内核模块从内核中卸载出来

            remod xxxx.ko

在卸载模块时,卸载函数会被调用,卸载函数只会执行一次

【四】尝试编写字符设备驱动程序框架

[1]  内核模块是Linux内核进行组件管理的一种方式,设备驱动都是基于内核模块进行注册和注销的。不单单是字符设备,块设备和网络设备都是基于模块进行加载和删除。

字符设备:I/O传输过程中以字符为单位进行传输。用户在对字符设备进行读写请求时,实际硬件的读写操作一般紧接着发生。(除了块设备和网络设备,其他的设备都是字符设备,LCD、键盘、触摸屏、GPIO口)

字符设备是最基本的最常用的设备,。它将千差万别的各种硬件设备采用一个统一的接口封装起来,屏蔽硬件差异,简化了应用层操作。

块设备:块设备与字符相反,它的数据传输模块以块(内存缓冲)为单位传输,用户对块读写时,硬件上的读写操作不会紧接着发生,即用户请求和硬件操作是异步的。(磁盘、闪存类等存储设备)

网络设备:网络设备是一类特殊的设备它不能像字符设备和块设备那样通过对应的设备文件访问,也不能直接通过read和weite进行数据请求,而是通过socket接口函数进行访问。(网卡)

-

设备文件:字符设备和块设备有设备文件,网络设备没有设备文件。

[2]  字符设备驱动必须提供一个通用字符设备的结构体

描述所字符设备驱动的结构体: cdev结构体


cdev结构体

struct module *owner; //赋值THIS_MODULE 表明该结构体指的是当前的驱动。

dev_t  dev; //设备号

设备号是用来唯一标识设备的,就像身份证号码一样,内核就是用设备号来管理设备。

数据类型是32位的无符号整型(32-bit unsigned integer number),由两部分组成,主设备号+次设备号,来表示同种设备中的若干个不同设备。主设备号是高12bit,次设备号是低20bit 。

调用内核中的宏函数来操作设备号:

MAJOR(dev_t dev)  //从设备号中提取主设备号

MINOR(dev_t dev) //从设备号中提取次设备号

MKDEV(int ma,int mi) //根据主次设备号生成设备号

关于上述三个函数,内核的源码中是这样写的:

unsigned int count; //设备个数,表明该驱动同时驱动了多少个设备

struct list_head list; //cdev列表

const struct file_operations *ops; //文件结构体指针变量

file_operayions 是操作方法集合,包含了很多函数指针。应用程序通过系统调用这些函数指针调用驱动的方法来操作设备。

应用程序 》系统调用 》内核函数指针 》操作设备

file_operations 结构体

这是常用的file_operatrions常用的函数接口

file_operations 是提供给应用层的操作方法集


[3]  编写字符设备驱动:

0、分配设备号,注册设备号:

方法1:自动分配设备号,下面是内核提供的分配设备号的函数

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, count char *name)

参数:

    dev   :定义一个dev_t类型定义的变量,取地址传入,通过参数的方式给写入设备号到变量中

    baseminor    :次设备号起始,规定次设备号从哪个数值开始,一般设定从0开始

    count   :指设备的个数

    name     :给设备起的名字

返回值 :成功则返回0,失败则返回一个负数的错误代码

方法2:指定设备号注册

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

参数:

    from   :填写需要注册的设备号(MKDEV(major, minor))

    count   :指设备的个数

    name     :给设备起的名字

返回值 :成功则返回0,失败则返回一个负数的错误代码


既然有注册设备号,那就会有注销设备号。内核提供了注销设备号的方法,无论是自动分配的还是指定注册的,都使用这个函数来注销设备号:

void unregister_chrdev_region(dev_t from, unsigned count)

参数:

    from   :填写需要注销的设备号

    count   :指需要注销的设备的个数

1、 为cdev结构体分配内存空间:内核提供了分配内存空间的函数:

                struct cdev *cdev_alloc(void)

cdev_alloc 为cdev结构体分配空间,无需传参,成功返回一个cdev分配到的结构体地址,失败则返回NULL

2、初始化cdev结构体:内核提供cdev初始化函数:

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

参数:

    cdev  :cdev结构体指针

    fops   :操作方法集结构体

3、添加(注册)字符设备到内核中,由内核统一管理,内核提供添加(注册)字符设备到内核中的函数:

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

参数 :

     p  :cdev结构体指针

     dev   :设备号

      count  :设备个数

返回值 :成功则返回0,失败则返回一个负数的错误代码

4、删除(注销)字符设备:

void cdev_del(struct cdev *p)

删除字符设备的函数应该卸载内核模块的卸载函数中。

【五】开始编写字符设备驱动代码:

复习:内核模块三要素:入口函数(module_init)、出口函数(module_exit)、协议申明(MODULE_LICENSE)

注册字符设备,申请cdev结构体,初始化,添加到内核,从内核中删除字符设备中要释放cdev结构体内存


【六】开始编译写好的驱动代码










文章未完成,还在整理中,2020/02/28

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容