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 < bufsize && 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;
}