Linux内核绝大部分代码是由c语言实现的
但是Linux内核中大量运用了面向对象的编程思想
c语言实现面向对象主要依靠struct
在内核中实现一个字符设备(例如LED 键盘 鼠标 触摸屏…)的驱动程序
其本质就是实例化一个struct cdev类型的对象 并注册到内核中去即可
struct cdev { //字符设备驱动框架的核心数据结构
...
const struct file_operations *ops;
dev_t dev;
};
1.设备号
本质就是一个32bit的无符号数据
设备号=主设备号(高12bit)
主设备号 用于区分不同类型的设备
次设备号 用于区分同一类设备中的不同个体
ls /dev/fb0 -l //lcd屏
ls /dev/ttySAC* -l
//uart控制器
1.1 静态申请注册设备号
找一个未被使用的主设备号为我所用
找的方式:cat /proc/devices //查看主设备号
cat /proc/devices
Character devices:
主设备号 被哪类硬件模块占用
1 mem
5 /dev/tty
5 /dev/console
5 /dev/ptmx
10 misc
13 input
21 sg
29 fb
81 video4linux
86 ch
89 i2c
...
申请注册注销设备号内核函数
int register_chrdev_region(dev_t from,unsigned count,const char *name);//注册设备函数
作用:向内核注册一系列的设备号
from, 要注册的第一设备号
count,连续注册的个数
name, 名称
void unregister_chrdev_region(dev_t from,unsigned count); //注销设备函数
作用: 注销一系列的设备号
from, 要注销的起始设备好号
count,连续注销个数
cat /proc/devices
vi led_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
MODULE_LICENSE("GPL");
dev_t dev; //num
unsigned int major = 200; //main num
unsigned int minor = 0; //ci num
int __init led_drv_init(void){
//dev = major<<20|minor;
dev = MKDEV(major,minor);
register_chrdev_region(dev,1,"myleds");
return 0;
}
void __exit led_drv_exit(void){
unregister_chrdev_region(dev,1);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
vi Makefile
obj-m += led_drv.o
KERNEL = ../../../linux/kernel
all:
make -C $(KERNEL) M=$(PWD) modules
clean:
make -C $(KERNEL) M=$(PWD) modules
make 在开发板上执行
insmod led_drv.ko
cat /proc/devices 200 myleds …
rmmod led_drv
cat /proc/devices
3.1.2 动态申请注册设备号
由内核分配一个主设备号为我所用
动态分配设备号函数
int alloc_chrdev_region(dev_t *dev,unsigned minor,unsigned count,char *name);
dev 传出参数
××××××××××××××××××××××××××××××××××××
2.操作函数集合
Linux中实现一个字符设备驱动,实则就是实例化一个struct cdev对象并注册到内核中去
××××××××××××××××××××××
3.内核中提供的关于cdev操作的api
void cdev_init(struct cdev *cdev,const struct file_operations *fops)
{
//将实现的操作函数集合和设备关联起来
}
//向内核注册cdev
//p,指向要注册的cdev
dev,设备号
count,要连续注册的个数
int cdev_add()
{
//将注册得到的设备号与该设备(cdev)绑定
}
void cdev_del(struct cdev *p) //指向的cdev从内核中注销掉
实验步骤:
vi led_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
MODULE_LICENSE("GPL");
dev_t dev; //num
unsigned int major = 0; //main num
unsigned int minor = 5; //ci num
struct cdev led_cdev;
int led_open(struct inode *inode,struct file *filp){
printk("<2>" "OK %s\n",__func__);
return 0;
}
int led_release(struct inode *inode,struct file *filp){
printk("<2>" "OK %s\n",__func__);
return 0;
}
struct file_operations led_fops={
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
};
int __init led_drv_init(void){
if(major){
//dev = major<<20|minor;
dev = MKDEV(major,minor);
register_chrdev_region(dev,1,"myleds");
}
else{
alloc_chrdev_region(&dev,minor,1,"myleds");
printk("<2>" "major=%d minor=%d\n",MAJOR(dev),MINOR(dev));
}
cdev_init(&led_cdev,&led_fops);
cdev_add(&led_cdev,dev,1);
return 0;
}
void __exit led_drv_exit(void){
cdev_del(&led_cdev);
unregister_chrdev_region(dev,1);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
vi Makefile
obj-m += led_drv.o
KERNEL = ../../../linux/kernel
all:
make -C $(KERNEL) M=$(PWD) modules
clean:
make -C $(KERNEL) M=$(PWD) modules
make
vi test.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(void){
int fd = 0;
fd = open("/dev/myleds",O_RDWR);
if(fd<0){
perror("open failed");
return -1;
}
printf("open myleds successed!\n");
sleep(10);
close(fd);
return 0;
}
arm-cortex_a9-linux-gnueabi-gcc test.c -o test
cp test …/…/rootfs/
到开发板执行命令
insmod led_drv.ko
mknod /dev/myleds c 244 5
./test
结论:
没有增加新的系统调用号 就能调用内核中led_open led_release
通过操作设备文件/dev/myleds实现的
体现了:linux下一切皆文件
实现原理:
open("/dev/myleds", O_RDWR)
5----->R7
swi
sys_open(...){
/dev/myleds ----> c 244 5------> led_cdev
------->led_cdev.ops // &led_fops
------>led_cdev.ops->open(....) //led_fops.open(....) //led_open(....)
}