第一、二期衔接——3.1 字符驱动设备—LED设备驱动框架搭建

字符驱动设备之LED设备驱动框架搭建

  • 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3)
  • 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统
  • 参考资料:《嵌入式Linux应用开发手册》
  • 开发环境:Linux 2.6.22.6 内核、arm-linux-gcc-3.4.5-glibc-2.3.6工具链


一、软件系统的大致分层

在这里插入图片描述
我们从上往下分析:
①、在应用程序中:执行openread等函数时,实际上会触发swi异常

②、在库中:进行系统调用,执行swi指令(swi val,每个函数都有对应的val值)引起CPU的异常

③、在内核中:系统调用接口根据发生异常的原因,即val值执行虚拟文件系统中的sys_opensys_read等函数。

④、在驱动程序中:sys_opensys_read等函数 调用 对应驱动程序中的xxx_openxxx_read函数(这个类似于C++中的多态),执行实现不同的功能

二、Linux驱动程序的分类

1、字符设备(Character device)

  字符设备如其名所示,是以字节流的形式访问设备的,如按键,串口,声卡,触摸屏等,存取时没有缓存

2、块设备(Block device)

  块设备,即数据是以块的形式存放的,所以通常在数据存放时会按照一定的格式(通过文件系统的类型定义),需要一定的缓存来支持。

3、网络接口(Network interface)

  网络接口有上述两种驱动的特点,但是又有所不同。其访问必须通过套接字,结合TCP/IP协议栈来使用

三、初步的代码框架

我这个里的编程方式是参考linux-2.6.22.6\drivers\leds\leds-s3c24xx.c目录下的源码,进行编写的。

1、编写open、write函数

新建一个驱动程序文件first_drv.c,参考内核的源码编写first_drv_open()、first_drv_write()函数

static int first_drv_open(struct inode *inode, struct file *file)
{
	printf("first_drv_open\n");
	return 0;
}

static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	printf("first_drv_write\n");
	return 0;
}

2、如何告诉内核有这个驱动程序

2.1 构建file_operations()结构体

参考内核源码,定义结构体,根据需求添加函数指针。
在这里插入图片描述

static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   first_drv_open,     
	.write	=	first_drv_write,	   
};

2.2 定义驱动的入口函数first_drv_init()、注册驱动register_chrdev()

内核源码:
在这里插入图片描述

  • major:主设备号,先设置为111
  • name:设备名称,可自定义
  • fops:文件系统的接口指针,即上述定义的结构体名称
static int first_drv_init(void)
{
	register_chrdev(111, "first_drv", &first_drv_fops); // 注册, 告诉内核
	return 0;
}

2.3 通过module_init()修饰入口函数,让内核怎么知道哪个设备对应哪个的入口

module_init(first_drv_init);

2.4 添加出口函数first_drv_exit()module_exit()修饰入口函数,用于卸载驱动

static void first_drv_exit(void)
{
	unregister_chrdev(major, "first_drv"); 
}

module_exit(first_drv_exit);

3、Makefile文件编写

参考内核源码:

KERN_DIR = /work/system/linux-2.6.22.6

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= first_drv.o

4、完整文件

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

static int first_drv_open(struct inode *inode, struct file *file)
{
	printk("first_drv_open\n");
	return 0;
}

static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	printk("first_drv_write\n");
	return 0;
}

static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   first_drv_open,     
	.write	=	first_drv_write,   
};

/* 入口函数 */
static int first_drv_init(void)
{
	register_chrdev(252, "first_drv", &first_drv_fops); // 注册, 告诉内核
	return 0;
}

/* 出口函数 */
static void first_drv_exit(void)
{
	unregister_chrdev(252, "first_drv"); 
}

/* 修饰 */
module_init(first_drv_init);
module_exit(first_drv_exit);

四、编译文件

1、编译

执行make命令后就生成如下文件
在这里插入图片描述

2、传输

利用NFS网络文件系系统,把.ko文件传输到开发板上
在这里插入图片描述

3、加载驱动

在开发板上的根文件系统下执行insmod first_drv.ko,加载驱动
cat /proc/devices可以看到加载驱动前,根文件系统下的驱动有:
在这里插入图片描述
加载驱动后:
在这里插入图片描述

五、测试文件

1、新建一个测试文件firsttest.c

代码如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* firstdrvtest on
  * firstdrvtest off
  */
int main(int argc, char **argv)
{
	int fd;
	int val = 1;
	fd = open("/dev/xxx", O_RDWR);
	if (fd < 0)
	{
		printf("./firstdrvtest <on|off>");
	}
	return 0;
}

2、上传到开发板的根文件系统,新建文件

根据代码中的/dev/xxx执行新建命令mkdir /dev/xxx 111 0,执行应用程序./firstdrvtest,可以看到成功调用了驱动程序。
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章