1 自动创建设备文件
创建设备文件的方式:1 手动创建 sudo mknod /dev/haha0 c 250 0
2 自动创建--》使用内核函数
linux内核为我们提供了一组内核函数,用于在模块加载时自动在/dev目录下创建响应的设备文件,并在卸载时删除该设备文件
创建:1 class_create--》创建设备文件类
2 device_create--》创建设备文件
删除:3 class_destory--》删除设备文件类
4 device_destory
设备 主设备--》一类设备
次设备--》该类设备中的某一个设备
设备文件:一个设备文件---》一个次设备
1 class_create--》创建设备文件类
struct class *class_create(owner, name)
owner:表示模块本身THIS_MODULE
name:设备模块名
返回值:指向设备文件类的指针(struct class表示设备文件类)
2 device_create--》创建设备文件
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
*class:指向设备文件类的指针
*parent:指向符设备的指针,一般为NULL
devt:设备号 由主次设备号构成
*drvdata:指向设备私有数据,若无,给NULL
*fmt:设备文件名(haha0/haha1...)
...:可变参数
返回值:成功 struct device *;失败err,需要判断
3 class_destroy
void class_destroy(struct class *cls)
作用:删除设备文件类
*cls:指向设备文件类的指针
4 device_destroy
void device_destroy(struct class *class, dev_t devt)
作用:删除设备文件
*class:指向设备文件类的指针
devt: 设备号
测试步骤:
1 sudo insmod hello.ko
2 dmesg |tail
3 ls -l /dev/hah*--》查看是否生成了设备文件haha0/haha1?
4 sudo ./test
5 sudo ./test1
6 sudo rmmod hello.ko
7 ls -l /dev/hah*--->查看设备文件是否被删除
2 ioctl man ioctl
ioctl-->系统调用函数-->应用程序用它给设备&内核发送控制指令
Linux内核给用户提供了两类系统调用函数:
一类是数据操作函数,如read write...
另一类是非数据操作函数,如ioctl,内核将对设备的操作交给了ioctl接口
(换句话说,应用程序可以使用ioctl接口去控制底层设备)
fd=open(“/dev/haha0”)-->250 0--->字符设备
int ioctl(int d, int request, ...);
d:文件描述符
request:指令码
...:可变参数,若有,那么该参数是传给内核驱动的参数
返回值:0成功 非0 失败
命名码:1 可以自定义 1 0
2 使用标准命名码 int--》4部分
30-31:数据的控制方向(get/set)
16-29:数据大小(14bit)
8-15:设备类型(8bit)
0-7:cmd-->命令码(区分命令的顺序序号)
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
common.h
#define TEST_CMD 0x1
#define TEST_CMD1 _IO('h',1)
#define TEST_CMD2 _IOW('h',2,int)
应用:test.c
fd=open("/dev/haha0",);
ioctl(fd,TEST_CMD);
ioctl(fd,TEST_CMD1);
ioctl(fd,TEST_CMD2,12345);
驱动:HelloIoctl
long (*unlocked_ioctl) (struct file *, unsigned int cmd, unsigned long arg)
{
switch(cmd)
case TEST_CMD:{printk();break;}
case TEST_CMD1:{printk();break;}
case TEST_CMD2:{printk();break;}
}
测试步骤:
1 sudo insmod hello.ko
2 ls -l /dev/haha*
3 sudo ./test
4 dmesg |tail-->查看ioctl发的指令是否被解析?
5 sudo rmmod hello.ko
3 驱动的互斥
设备号:主设备+次设备
主设备:代表一类设备--》驱动
次设备:一类设备中的某一个设备--》使用驱动程序的终端个体
多个子设备共用一个驱动程序,若多个子设备同时访问一个驱动程序时,就产生了竞态
访问,那么如何解决驱动的竞态呢?linux内核提供了多种互斥方法:
有:互斥锁 信号量 原子变量 自旋锁。。
3.1 互斥锁
mutex_t mutex;-->应用层互斥锁
1 定义互斥锁--》全局
struct mutex g_Mutex;-->内核互斥锁
2 初始化互斥锁
mutex_init(&g_Mutex);-->HelloModule
3 加锁
mutex_lock(&g_Mutex);-->HelloOpen
4 解锁
mutex_unlock(&g_Mutex);-->HelloClose
3.2 信号量
1 定义信号量
struct semaphore sem;//内核信号量
2 初始化信号量
sema_init(&sem,1)-->HelloModule
3 获取信号量-->HelloOpen
down_interruptible(&sem)-->p操作 >0:sem--;函数返回 =0:阻塞进程
down(&sem)
4 释放信号量-->HelloClose
up(&sem)--> v操作 sem_post(&semb)-->给sem++
3.3 原子变量
1 定义原子变量并初始化
atomic_t a=ATOMIC_INIT(1);
2 使用原子变量
需要通过linux内核提供的函数来操作原子变量
atomic_dec_and_test(&a): 该函数给原子变量a减1,然后测试其值是否等于0?
如果为0,返回true;如果不为0,返回false。
atomic_inc(&a):该函数给原子变量加1
sudo ./test-->open("/dev/haha0")
sudo ./test1--->open("/dev/haha1")
helloOpen:
if(!atomic_dec_and_test(&a)) 1->0-->-1
{
atomic_inc(&a); 0
return -EBUSY;
//不能使用驱动
}
//可以使用驱动资源
return 0;
helloClose:
atomic_inc(&a)0-->1
3.4 自旋锁(忙等锁)
1 定义自旋锁
spinlock_t g;-->自旋锁
2 初始化自旋锁
spin_lock_init(&g)-->HelloModule
3 获取自旋锁
spin_lock(&g)-->HelloOpen
4 释放自旋锁
spin_unlock(&g)-->HelloClose
测试步骤:
1 sudo insmod hello.ko
2 dmesg |tail
3 ls -l /dev/hah*
4 sudo ./test-->write sleep
5 sudo ./test1--->read
6 sudo rmmod hello.ko