目录
- 一、操作系统的框架
- 二、设备的类型
- 三、什么是设备
- 四、杂项字符设备的 API
- 五、代码实现
- 1、底层实现(内核)
- 2、应用层代码
- 3、交叉编译环境
- 4、结果展示
一、操作系统的框架
二、设备的类型
硬件设备其实是分类型的:
字符设备:所谓的字符备就是你操作这个设备的时候是通过字节一位一位的去操作的,这个开发板其实除了块设备和网络设备以为的所有设备几乎都是字符设备,比如led
beepkey lediicspiuartlcd 等他们是属于字符设备。所以你今后写驱动代码几乎写都是字符设备驱动的代码。一般使用符号c
块设备:就是你开发板上的u盘硬盘就是运行内核和存储内核—符号是b
网络设备:网络设备他是专门为网络数据的发送和接收给设计一个设备
所以咱们的开发板上的硬件几乎都是字符设备。
三、什么是设备
设备号其实就是设备的一个标识符,就好比你们当时操作系统的时候的进程的PID,PID的作用,就是为了区分不同的进程而定义的一个PID,开发板上那么多的硬件设备,内核怎么去区分每一个不同的设备呢?这里就搞另一个设备号,设备号就是给硬件一个编号,就好比每一个人的身份证号,他也是唯一的,设备号他又有两部分组成:主设备号+次设备号
主设备号:他代表一类设备。
次设备号:他代表这一类设备当中的具体哪一个设备比如咱们开发板上有两个 LED 灯,他们是属于一类设备,但是他们有属于不同的个体,所以为了区分这种情况,就是使用次设备号做区分。
Linux 下一切皆文件,不管你操作什么其实都是在操作文件,包括你硬件设备,这里驱动把硬件设备给抽象成为了一个特殊的文件,设备节点文件,位置就是/dev/。有了设备节点你就可以操作对应的硬件设备了。
设备节点上有重要的信息:
crw-rw-rw- 1 root tty 5, 0 9 月 2 09:46 tty
c:就代表设备的类型
-代表普通文件
l 代表链接文件 ---- 快捷方式
c 代表字符设备
d 代表目录文件
b 代表块设备
p 代表管道
s 代表套接字
rw-rw-rw- — 代表文件的权限
四、杂项字符设备的 API
特性:主设备固定是 10,它可以可以在/dev/给你自动生成一个设备节点名 , 这个名是有你自己确定,下面我的这个名字均为myled。
杂项字符设备依赖两个函数和两个核心结构体
注册函数和注销函数
注册函数:就是往内核里去注册申请一些设备信息,资源。
注销函数:就是把你申请的设备信息,或者是资源还给内核。
注册函数应该写到加载函数里
注销函数应该写到卸载函数里
函数功能:注册杂项字符设备
函数原型:*int misc_register(struct miscdevice misc);
函数头文件:#include <linux/miscdevice.h>
函数参数:misc:核心结构体
这个核心结构体里存放的就是杂项字符设备的关键的信息
结构体的原型:
struct miscdevice {
int minor;//次设备号 — 一般写 255 代表自动给你分配一个可以使
用的
const char *name;//设备节点的名字,这个名字就是在/dev 下载自动
生成的,一般这个名字要见名知意 led beep key lcd 等
*const struct file_operations fops;//他是操作这个设备的方法的集合
以下的成员变量不需要你填写 内核会自动的给你填写
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
const char *nodename;
umode_t mode;
};
owner = THIS_MODULE 他是一个固定的值,就代表当前的模块
这个结构体里存放的都是函数指针,本质是一个指针,只不过这个指针指向了一个函数
常用的成员变量:
llseek — 对应应用层的 lseek
open ---- 对应应用层的 open
release ---- 对应应用层的 close
read ---- 对应应用层的 read
write ---- 对应应用层的 write
mmap ----- 对应应用层的 mmap
函数返回值:成功返回 0 失败负数
*函数功能:注销你申请的杂项字符设备资源
函数原型: void misc_deregister(struct miscdevice misc)
函数头文件: #include <linux/miscdevice.h>
函数参数:misc:核心结构体
函数返回值:无
函数原型:
五、代码实现
1、底层实现(内核)
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
int myled_open (struct inode *inode, struct file *fp)
{
printk(“myled open ok\n”);
printk(“打开成功\n”);
return 0;
}
int myled_close (struct inode *inode, struct file *fp)
{
printk(“myled close ok\n”);
printk(“关闭成功\n”);
return 0;
}
struct file_operations myfops={
.owner = THIS_MODULE,
.open=myled_open,
.release=myled_close,
};
struct miscdevice mymisc={
.minor=255,
.name=“myled”,
.fops=&myfops,
};
static int __init myled_init(void)
{
int misc = 0;
misc = misc_register(&mymisc);
if(misc < 0)
{
printk(“misc_register error\n”);
return -1;
}
printk(“杂项字符设备注册成功\n”);
return 0;
}
static void __exit myled_exit(void)
{
misc_deregister(&mymisc);
printk(“杂项字符设备注销成功\n”);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE(“GPL”);
2、应用层代码
3、交叉编译环境
4、结果展示
经过交叉编译之后会生成led.ko和app,将其推送到开发板上,先运行led.ko再运行app即可。
app的运行会自己调用底层代码。