字符设备是Linux驱动中最基本的一类设备驱动,字符设备就是一个字节,按照字节进行读写操作设备,读写数据是分先后顺序的。比如我们常见的点灯、按键、IIC、SPI、LCD等都是字符设备,这些设备的驱动就叫做字符设备驱动。
在Linux中开发一般只能是用户态,也就是用户只能编写应用程序,但是要作用于内核,那么就需要了解Linux中应用程序是如何调用内核中的驱动程序的,Linux 应用程序对驱动程序的调用如下图所示:
在Linux 中一切皆为文件,驱动加载成功以后会在“/dev”目录下生成一个相应的文件,应用程序通过对这个名为“/dev/xxx” (xxx 是具体的驱动文件名字)的文件进行相应的操作即可实现对硬件的操作。
应用程序运行在用户空间,而 Linux 驱动属于内核的一部分,因此驱动运行于内核空间。当我们在用户空间想要实现对内核的操作,比如使用 open 函数打开/dev/led 这个驱动,因为用户空间不能直接对内核进行操作,因此必须使用一个叫做“系统调用”的方法来实现从用户空间陷入到内核空间,这样才能实现对底层驱动的操作。 open、 close、 write 和 read 等这些函数是有 C 库提供的,在 Linux 系统中,系统调用作为 C 库的一部分。当我们调用 open 函数的时候流程如图所示:
即大致这样:
设备号分为主设备号与次设备号。在Linux环境下打开/dev
主设备号用来区分不同种类的设备,次设备号用来区分同一类型的多个设备。
1.添加:编写完驱动程序后,加载到内核。
2.查找:调用驱动程序,实现用户空间open或者read。
当我们在用户态调用open函数的时候就会发生一个软中断从而进入内核态(sys_call),中断号为:0x80(表示产生了一个系统调用);
open-->sys_call-->sys_open-->drv_open;
我们可以手动创建设备名。
sudo mknod +设备名字 +设备类型(c表示字符设备驱动) +主设备号+次设备号
b : create a block (buffered) pecial file。
c, u: create a character (unbuffered) special file。
p: create a FIFO
删除手动创建的设备名直接rm就好。
比如:
一个简单的字符设备驱动代码:
#include
#include
#include
#include
#include
#include
#include
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";//模块名
//led open
static int pin4_open(struct inode *inode,struct file *file)
{
printk("pin4_open\n");//内核打印函数用printk;与printf类似
return 0;
}
//led_write
static ssize_t pin4_write(struct file *file1,const char __user *buf,ssize_t count,loff_t *ppos)
{
printk("pin4_write\n");
return 0;
}
//led_read
static int pin4_read(struct file * file,char __user *buf,size_t size,loff_t *ppos)
{
printk("pin4_read\n");
return 0;
}
static struct file_operations pin4_fops={
.owner=THIS_MODULE,
.open=pin4_open,
.write=pin4_write,
.read=pin4_read,
};
int __init pin4_drv_init(void)
{
int ret;
devno=MKDEV(major,minor); //创建设备号
ret=register_chrdev(major,module_name,&pin4_fops); //注册驱动,表示把告诉内核把这个驱动加载到
//内核的链表里面
pin4_class=class_create(THIS_MODULE,"myfirstdemo");//让代码在dev下自动生成设备--类
pin4_class_dev=device_create(pin4_class,NULL,devno,NULL,module_name);//创建设备文件
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");
这个字符驱动流程大概是这样的:
obj-m:表示以生成块的方式编译,最终生成.ok的文件
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules
编译成功!发现多了一些文件。
scp pin4driver.ko [email protected]:/home/pi
#include
#include
#include
#include
void main()
{
int fd,data;
fd = open("/dev/pin4",O_RDWR);
if(fd<0){
printf("open fail\n");
perror("reson:");
}
else{
printf("open successful\n");
}
fd=write(fd,'1',1);
}
拷贝成功:
sudo insmod pin4driver.ko
设备添加成功!
打开成功但是没有任何打印;因为打印在内核态,而我们运行的终端是用户态,那我们怎么看到内核态的打印呢?
用dmesg查看:(打印内核的printk,将其检索出来)
验证成功!
如果需要卸载内核驱动的话:
sudo rmmod xxx(不需要加ko)