在加载驱动之前,先来看一下/proc/devices,里面是现在内核所支持的设备。
第一列是主设备号,对应的是内核数组chrdevs的下标,第二列是它的名字。
加载驱动的命令:insmod first_drv.ko
cat /proc/devices
写个测试驱动程序来测试它。(见代码二)
mknod /dev/xxx c 主设备号 次设备号
register_chrdev(111,"first_drv",&first_drv_fops);
怎么确定这设备号111?
1、先cat /proc/devices,有哪一个空缺项,然后选取那个空缺项。(1~255)。
2、也可以写为0,系统会自动给我们分配一个主设备号,它在数组chrdevs找一个空缺项,给你返回主设备号。
major=register_chrdev(0,...,...);
1、驱动的主设备号:自动分配主设备号;手工指定主设备号。
2、应用:open("/dev/xxx");
xxx文件怎么来的?
a.mknod /dev/xxx c 主设备号 次设备号(手工建立,需要先知道主设备号)
b.自动创建:对于应用程序,udev机制;对于busybox来说,mdev程序
cd /sys(系统目录下的信息)。注册驱动时,先会在sys目录下生成设备信息
mdev:会根据系统的信息,创建设备节点。(驱动程序里,如果能提供系统的信息,mdev就能自动帮我们创建设备节点)
★★★
为什么sys目录下的信息一更改,mdev就会自动运行、生成呢?
vi /etc/init.d/rcS:
在创建根文件系统时:
内核里一旦有设备加载进去或者卸载之后,就会根据调用/proc/sys/kernel/hotplug所指示的应用程序。
程序一:first_dev.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
static struct class *firstdrv_class; //一个类
static struct class_device *firstdrv_class_dev; //一个类里面再建立一个设备
static int first_drv_open(struct inode *inode, struct file *file)
{
printk("first_drv_open\n");
return 0;
}
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
printk("first_drv_write\n");
return 0;
}
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = first_drv_open,
.write = first_drv_write,
};
int major;
int first_drv_init(void)
{
//0:让系统给我们自动分配一个主设备号,在数组中找个空缺,返回主设备号
major = register_chrdev(0, "first_drv", &first_drv_fops); //注册驱动程序,告诉内核,再内核数组里,把fops结构填充进去
/* 下面这两条代码就会在sys目录下,class目录下,建立firstdrv这个类,再firstdrv这个类下,会创建xyz这个设备,再xyz目录下有个dev文件,里面有主设备号:次设备号 */
/* 然后mdev根据上面的系统信息,就会创建/dev/xyz这个设备节点 */
/* ★★下面两条代码会根据自动分配的主设备号,生成设备文件,并且生成系统信息 */
//先建立一个类
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
//在这个类下面建立一个设备
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz");
return 0;
}
void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv"); //卸载驱动
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL"); //license,可以解决class_device_unregister和class_destory不能识别的问题
程序二:测试程序firstdrvtest.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fd;
int val = 1;
fd = open("/dev/xyz", O_RDWR); //无论主设备号怎么变,/dev/xyz都是系统为我们创建的
if (fd < 0)
{
printf("can't open!\n");
}
write(fd, &val, 4);
return 0;
}
编译程序:arm-linux-gcc -o fristdrvtest fristdrvtest.c