1—学习笔记.驱动开篇

2020.11.25
韦东山视频学习笔记

字符设备驱动程序框架

总体来说,分为++APP应用层、驱动层、硬件层++这三个层面

应该和STM32的开发方式可能比较接近吧。硬件小哥画原理图画PCB焊板子(硬件层),企业帮我们做好了函数的封装,让我们刻意调用标准库或者HAL库来开发,而不需要直接操作寄存器(驱动层),我们通过调用需要的函数来初始化等等(应用层)

暂时这样理解吧…

  • APP层面的应用开发,有各种的函数调用,这些函数会在驱动程序中编写完善。所以APP不直接和硬件打交道,没有办法做一个光荣的 “点灯工程师”
  • Driver:驱动层,对具体的函数来进行编写,通过和硬件打交道,控制寄存器等等来达到我们想要的效果
  • 硬件层:老本行就不记笔记了

驱动程序的编写流程

  1. 确定主设备号,也可以让内核来分配

  2. 定义一个file_operations结构体,这个结构体相当于把我们这个驱动要完成的事件给抽象出来

  3. 实现对应的drv_open/drv_read/drv_write函数,这些函数和应用层的函数都有所对应。把这些函数填入file_operations结构体里。

  4. 用register_chrdev来把file_operations结构体告诉内核,把他注册进内核

  5. 入口函数:在安装驱动时会调用这个函数,由他来注册驱动函数

  6. 出口函数:unregister_chrdev,出口函数会调用这个函数

  7. 其他的完善:提供设备信息,自动创建设备节点:class_create,device_create

小细节:

  1. 驱动程序根据主设备号来知道我们要驱动什么类型的设备:LED、OLED

  2. 驱动程序根据次设备号来知道我们要驱动哪一个设备:LED_1 or LED_2

驱动程序的分层:

个人拙见:还是和STM32的开发做对比,分层就相当于同一个模块的代码,可以给不同型号的芯片使用,只需要修改他的引脚或者状态就可以完美移植,这就是分层。XX小车之家的MPU6050的代码封装就很好,只需要做设定上的修改,就可以实现我想要的功能,嘿嘿嘿

这里我的理解是,将驱动程序进一步分层,暂时分为上下两层:

  1. 上层是让硬件实现具体效果的代码,它的核心对象是file_operations结构体
    static struct file_operations led_drv = {
    .owner = THIS_MODULE,
    .open = led_drv_open,
    .read = led_drv_read,
    .write = led_drv_write,
    .release = led_drv_close,
    };

2.下层是具体到哪一个硬件,作出什么动作的指令式的代码,它的核心对象是led_operations

struct led_operations 
{
    int (*init) (int which); /* 初始化 LED, which-哪个 LED */
    int (*ctl) (int which, char status); /* 控制 LED, which-哪个 LED, status:1-亮,0-灭*/
 };
struct led_operations *get_board_led_opr(void);
/*************分割线***************************/
static struct led_operations board_demo_led_opr = {
	.init = board_demo_led_init,
	.ctl  = board_demo_led_ctl,
};
//这里对这个结构体做定义

下面是韦老师的驱动程序代码:

/* 1. 确定主设备号                                                                 */
static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;


#define MIN(a, b) (a < b ? a : b)
/* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{......}

/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{......}

static int led_drv_open (struct inode *node, struct file *file)
{......}

static int led_drv_close (struct inode *node, struct file *file)
{......}

/*2. 定义自己的file_operations结构体将我们需要的函数定义成我们想要的*/
static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.read    = led_drv_read,
	.write   = led_drv_write,
	.release = led_drv_close,
};


/* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init led_init(void)
{......}

/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit led_exit(void)
{......}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");

心得:大概的了解了驱动程序和应用程序之间的关系,以及驱动程序的大概框架。

零碎的知识点:

1、C语言的函数指针

2、C语言的这种面向对象的特性和C++的面向对象特性有何不同,有待总结。

3、还有一点是 MODULE_LICENSE(“GPL”); 这一句代码,这个是模块的许可声明,没加会报错:

从2.4.10版本内核开始,模块必须通过MODULE_LICENSE宏声明此模块的许可

证,否则在加载此模块时,会收到内核被污染 “kernel tainted”

的警告。从linux/module.h文件中可以看到,被内核接受的有意义的许可证有

“GPL”,“GPL v2”,“GPL and additional rights”,“Dual BSD/GPL”,“Dual

MPL/GPL”,“Proprietary”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值