栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Linux 设备驱动开发(一)

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux 设备驱动开发(一)

前言

设备有设备名字、设备号(包括主、次设备号)、设备驱动函数三个属性。在/dev目录中包含了所有Linux系统中使用的外部设备。但是这里并不是放的外部设备的驱动程序,这一点和windows,dos操作系统不一样。它实际上是一个访问这些外部设备的端口。我们可以非常方便地去访问这些外部设备,和访问一个文件,一个目录没有任何区别。

1、什么是主设备号和次设备号?
主设备号用来表示一个特定的驱动程序。次设备号用来表示使用该驱动程序的各设备。
通过执行ls -l 命令,可以看到在修改日期之前,有两个用逗号分隔的数字,分别为主设备号和次设备号。

2、从上层应用到底层驱动的执行过程
以“open("/dev/pin4", O_RDWR)”函数的执行过程为例来说明。
(1)应用程序使用open函数打开代表pin4的设备文件。
(2)open函数触发异常进入内核态中,并调用系统调用中的sys_cal函数。
(3)sys_call函数调用sys_open函数,并根据文件名找到相关的设备号。
(4)根据设备号从驱动链表里找出相应驱动,并执行驱动程序。
(5)返回一个文件句柄给应用程序。

一、内核驱动基本框架

1、驱动代码编写相关API

static int __init xxxx_init(void) //加载模块时的初始化函数,也就是驱动模块的入口函数
static void __exit xxxx_exit(void) //卸载模块时的函数,也就是卸载某个驱动时要执行的函数
module_init(xxxx_init);//表明驱动模块的入口函数
module_exit(xxxx_exit);//表明驱动模块的出口函数
MODULE_LICENSE(“GPL v2”);
Linux是开源的系统,那就要我们遵守一定的规范,我们一般用GPL v2规范,所以 在驱动编写时都要声明一下

获取设备号
MKDEV(MAJOR, MINOR)
MAJOR :主设备号
MINOR :次设备号

向内核注册了一个字符设备,并把驱动程序的基本入口点指针存放在内核的字符设备地址表中,在用户进程对该设备执行系统调用时提供入口地址
实际上就是将file_operations结构体指针fops绑定为设备驱动函数地址,并插入到内核中的驱动链表中
int __register_chrdev(unsigned int major,const char *name,const struct file_operations *fops)
参数
major:主设备号
name:设备名称
fops:文件系统的接口指针
返回:成功返回主设备号,失败返回小于0的整数
注销字符设备,与__register_chrdev配对使用
int unregister_chrdev(unsigned int major, const char *module_name)
参数
major:主设备号
module_name:设备名称

创建class并注册到内核中
struct class *class_create(THIS_MODULE,char *module_name);
参数
module_name:设备名称
返回:class结构体指针
注销class,与class_create配对使用
class_destroy(struct class * cls);

创建一个设备节点
struct device *device_create(struct class *class,NULL,dev_t devt,NULL, char module_name);
参数
class:所要创建的设备所从属的类
devt:设备号
module_name:设备名称
返回:成功返回 0,失败小于0的整数。
注销设备节点,与device_create配对使用
device_destroy(struct class
cls,dev_t devt)

1.1 驱动入口函数步骤
(1)注册字符设备,给以主设备号和设备名对应的设备驱动绑定驱动函数,并插入到内核中的驱动链表中
(2)注册类
(3)在类中注册设备,在/dev中自动生成设备文件
1.2驱动出口函数步骤
(1)注销设备
(2)注销类
(3)注销字符设备,并将相应驱动从内核中的驱动链表移出
2、内核驱动编译步骤
(1)把驱动代码拷贝到driver/char
(2)修改Makefile,告诉编译器,要编译该驱动文件
(3)通过 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules 编译生成模块 .ko文件

3、驱动测试

(有些命令需要用sudo指令使用管理者权限)
加载驱动模块

sudo insmod xxx.ko

卸载驱动模块

sudo rmmod xxx //不需要写.ko

查看内核中的模块信息

lsmod

查看模块打印信息

dmesg

手动生成设备,设备名为test,主设备号为8,次设备号为1

sudo mknod test 8 1

查看权限

ls -l /dev/pin4

验证模块加载成功步骤:
(1)装载驱动:sudo insmod xxx.ko
(2)驱动装载后生成设备,比如/dev/pin4,通过sudo chmod 666 /dev/pin4
添加访问权限(所有用户可读可写)
(3)运行测试程序编译的可执行文件pin4test调用驱动
(4)内核的printk相当于内核层的pritnf,通过dmesg查看打印信息

1、进入/home/sh/Desktop/learnPi/linux-rpi-4.14.y/drivers/char文件夹
cd /home/sh/Desktop/learnPi/linux-rpi-4.14.y/drivers/char
2、在当前文件夹的Makefile文件添加下面的命令
vi Makefile 
obj-m                           += pin4driver.o
3、将pin4driver.c放到/home/sh/Desktop/learnPi/linux-rpi-4.14.y/drivers/char字符设备驱动文件夹
cp ~/Desktop/pin4driver.c .
4、进入/home/sh/Desktop/learnPi/linux-rpi-4.14.y文件夹
cd /home/sh/Desktop/learnPi/linux-rpi-4.14.y
5、编译驱动模块
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules
6、将编译完成的.ko文件通过scp发送到树莓派
scp drivers/char/pin4driver.ko pi@192.168.31.106:/home/pi/Desktop
7、加载驱动模块
sudo insmod pin4driver.ko
8、查看内核中的模块信息是否有pin4driver
lsmod 
9、给/dev/pin4添加权限
sudo chmod 666 /dev/pin4
10、编译pin4test.c输出为pin4test可执行文件
gcc pin4test.c -o pin4test
11、运行pin4test
./pin4test
12、查看打印信息
dmesg 
//文件pin4driver.c
#include //file_operation声明
#include //module_init module_exit声明
#include //_init _exit声明
#include //class device 声明
#include //copy_from_user的头文件
#include //设备号 dev_t类型声明
#include //ioremap iounmap的头文件

static struct class *pin4_class;
static struct device *pin4_class_dev;
static dev_t devno;//设备号
static int major=231;//主设备号
static int minor=0;//次设备号
static char *module_name="pin4";//模块名

static int pin4_open(struct inode *inode, struct file *file)
{
    printk("pin4_openn");//内核的打印函数,和printf函数
    return 0;
}

static ssize_t pin4_write(struct file *file, const char __user * buf, size_t count, loff_t *ppos)
{
    printk("pin4_writen");
    return 0;
}
static struct file_operations pin4_flops = 
{
    .owner  =   THIS_MODULE,
    .open   =   pin4_open,
    .write  =   pin4_write,
};
int __init pin4_drv_init(void)//真实驱动入口
{
	int ret;
	devno=MKDEV(major,minor);//创建设备号
	register_chrdev(major, module_name, &pin4_flops);//注册字符设备,并告诉内核把驱动加入到驱动链表中
	pin4_class=class_create(THIS_MODULE,"myfirstdemo");//创建类
	pin4_class_dev=device_create(pin4_class,NULL,devno,NULL,module_name);//创建设备文件,让代码在dev下自动生成设备
    return 0;
}

void __exit pin4_drv_exit(void)
{
    device_destroy(pin4_class,devno);//注销设备
	class_destroy(pin4_class);//注销类
	unregister_chrdev(major, module_name);//注销字符设备
}

module_init(pin4_drv_init);//入口 内核加载该驱动的时候,这个宏被调用
module_exit(pin4_drv_exit);//出口 内核卸载该驱动的时候,这个宏被调用
MODULE_LICENSE("GPL v2");//GPL v2规范
//文件pin4test.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
        int fd;
        fd=open("/dev/pin4",O_RDWR);
        if(fd<0)
        {
                printf("load fail!n");
                perror("why");
        }
        else
                printf("load success!n");
        write(fd,"1", 1);
        close(fd);
        return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/289210.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号