您的位置:首页 > 运维架构 > Linux

linux设备驱动day3-驱动介绍,注册和加载实现

2016-08-24 23:24 267 查看
linux设备的分类:

字符设备:

串口、终端、触摸屏、键盘

查看方式:ls -l /dev 以c开头的文件都是字符设备文件。

块设备:

Flash、ramdisk(内存磁盘)、harddisk(硬盘)

查看方式:ls -l /dev 以b开头的文件

网络设备:

ifconfig -a

Linux抽象了对硬件的处理,所有的硬件设备都可以看作普通文件一样看待,可以使用和操作文件相同的,标准的系统调用接口来完成对设备的打开,关闭,读写和IO控制操作

字符设备和块设备是通过文件节点访问的,在Linux文件系统中,可以找到或者mknod创建设备对应的文件名,称为设备文件。

块设备和字符设备有两个数字分别是主设备号和次设备号,普通文件只有文件大小。

同一个驱动程序可以管理多个设备,依靠次设备号来区别。

主设备号标识该设备的种类,也标识了该设备所使用的驱动程序。

次设备号:标识使用同一设备驱动程序的不同硬件设备。次设备号只能在驱动程序内部使用,系统内核直接把次设备号传递给驱动程序,由驱动程序去管理。

设备驱动程序有三个接口:

(加载接口)系统引导接口:系统各设备初始化, 激活,运行驱动程序

(控制硬件接口)交互接口:驱动程序与具体设备间进行交互

(系统调用接口)实现接口:操作系统内核 通过数据结构file_operations实现具体的函数功能

软中断切换用户态进入内核态,地址空间不一样,不能直接对地址操作

Linux内核加载驱动程序:

1、系统启动时,通过代码自身加载模块,称为静态编译入内核,驱动程序开发完毕口一般这么使用

2、Linux系统启动后,通过insmod命令加载模块,称为动态加载。

驱动标识:文件节点,设备号

接口的实现:

编写驱动要遵守一定的协议:

MODULE_AUTHOR(” “);

MODULE_LICENSE(“GPL”); //这是必须的

MODULE_DESCRIPTION(” “);

MODULE_ALIAS(” “);

加载接口:int __init hello_init(void) (加载)、void __exit hello_exit(void)(卸载)(表示运行一次)

注册接口:module_init(hello_init);//模块加载函数

module_exit(hello_exit);//模块卸载函数

在内核里打印用printk,不能用printf函数

编译驱动程序要用Makefile来编译

生成的xxxx.ko文件放到板子上进行加载

加载命令:insmod xxxxx.ko 把内核模块加入到系统中, 会调用之前定义的init函数

查看动态加载的内核模块:lsmod 可以看到刚刚加载的内核模块

驱动程序打印的信息会打印到系统日志中 /var/log/syslog

卸载模块:rmmod xxxx, 会调用定义的exit函数

查看某个模块的信息:modinfo xxxxx.ko

申请设备号(在init时申请):register_chrdev(arg1, arg2, arg3);//注册字符设备,返回设备号

arg1 = 0 表示由系统分配设备号,arg1 > 0 表示自己规定一个,但不能冲突

arg2 是一个字符串,表示设备的名字

arg3 是一个结构体指针,(struct file_operations)结构体成员是一系列函数指针, 要给成员函数赋值,定义出相关函数(系统调用相关接口)

定义出一系列的函数实际就是在实现系统调用接口(open, release, read, write)

释放设备号(在exit时释放):unregister_chrdev(arg1, arg2);

arg1是要释放的主设备号

arg2 是出册时的设备名字

结构体的定义包含在头文件

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/slab.h>

MODULE_AUTHOR("CYW");
MODULE_LICENSE("GPL"); //必须要写的遵守协议
MODULE_DESCRIPTION("A simple test for kernel module");
MODULE_ALIAS("a simple module");

struct file_operations hello_fops = { //定义一个结构体
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write,
}; //部分成员初始化

#define DEVICE_NAME   "Hello_Driver" //注册成功会出现在/proc/devices中
int major;

static int __init hello_init(void) //加载驱动
{
printk(DEVICE_NAME":Hello world in kernel module\n"); //不能用printf

major = register_chrdev(0, DEVICE_NAME, &hello_fops);//向内核申请主设备号,需要结构体地址参数

if(major < 0){ //申请失败
printk(DEVICE_NAME":register %s fail\n", DEVICE_NAME);
return major;
}
printk(DEVICE_NAME":got major number: %d\n", major);
return 0;
}

static void __exit hello_exit(void) //卸载驱动
{
printk(DEVICE_NAME":Hello world in exit\n"); //不能用printf
unregister_chrdev(major, DEVICE_NAME); //释放资源,根据主设备号和名字
}

module_init(hello_init);//模块加载所用函数
module_exit(hello_exit);//模块卸载所用函数

int hello_open(struct inode *pinode, struct file *pfile) //打开驱动
{
printk(DEVICE_NAME":hello_open\n");
wr_pos = 0;
rd_pos = 0;
pbuff =(unsigned char *)kmalloc(BUFF_SIZE, 0);
if(pbuff == NULL){
printk(DEVICE_NAME":kmalloc fail\n");
return 1;
}
return 0;
}
int hello_release(struct inode *pinode, struct file *pfile) //释放驱动
{
printk(DEVICE_NAME":hello_release\n");
kfree(pbuff);
return 0;
}
/*
*__user *buff, 用户空间内存地址
*
*/

ssize_t hello_read(struct file *pfile, char __user *buff, size_t bufsize, loff_t *poffset) //读驱动
{
int i = 0;
for(i = 0; i < bufs
4000
ize && wr_pos - rd_pos > 0; i++){ //循环拷贝一个字节到用户空间
put_user(pbuff[rd_pos % BUFF_SIZE] , buff+i);
rd_pos++;
}
if(i >= 0)
return i;
#if 0
int len = wr_pos - rd_pos;
printk(DEVICE_NAME":hello_read\n");
if( len > 0){
int err = copy_to_user(buff, pbuff+(rd_pos % BUFF_SIZE), len > bufsize ? bufsize : len); //把内核空间的数据读到用户空间
rd_pos += len > bufsize ? bufsize : len;
if(!err)
return len > bufsize ? bufsize : len;
}
#endif
return -1;
}

ssize_t hello_write(struct file *pfile, const char __user *buff, size_t bufsize, loff_t *poffset) //写驱动
{
int i = 0;
for(i = 0; i < bufsize && free_size() > 0; i++){ //拷贝一个字节到内核空间
get_user(pbuff[wr_pos % BUFF_SIZE] , buff+i);
wr_pos++;
}
if(i >= bufsize)
return bufsize;

#if 0
int free_len = BUFF_SIZE - (wr_pos - rd_pos);
printk(DEVICE_NAME":hello_write\n");
if( free_len > bufsize){
int err = copy_from_user(pbuff+wr_pos % BUFF_SIZE, buff, bufsize);//把用户空间的数据写道内核空间
wr_pos += bufsize;
if(!err)
return bufsize;
}
#endif
return -1;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 内核驱动