在上一篇中我们已经了解了字符设备驱动的原理,也了解了应用层调用内核函数的机制,但是我们每次操作设备,都必须首先通过mknod命令创建一个设备文件名,比如说我们要打开u盘,硬盘等这些设备,难道我们还要自己创建,就如同刘老师常说的一句话,这也太山寨了吧,所以我们今天我们来点比较专业的,让函数帮我们自动创建;
在Linux 下,设备和驱动通常都需要挂接在一种总线上,总线有PCI、USB、I2C、SPI 等等,总线是处理器和设备之间的通道,在设备模型中,所有的设备都通过总线相连,一总线来管理设备和驱动函数;
因此我们先了解一下sys下的目录
block:用于管理块设备,系统中的每一个块设备会在该目录下对应一个子目录;
bus:用于管理总线,没注册一条总线,在该目录下有一个对应的子目录,其中,每个总线子目录下会有两个子目录:devices和drivers。
devices包含里系统中所有属于该总线的的设备。
drivers包含里系统中所有属于该总线的的驱动。
class:将系统中的设备按功能分类。
devices:该目录提供了系统中设备拓扑结构图。
dev:该目录已注册的设备节点的视图。
kernel:内核中的相关参数。
module:内核中的模块信息。
fireware:内核中的固件信息。
Fs:描述内核中的文件系统。
下面的代码是我们在sys/class中创建一个名为dog的类,然后在创建一个设备(wangcai);
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h> MODULE_LICENSE("GPL");
MODULE_AUTHOR("bunfly"); struct class *dog;
struct device *wangcai; int bunfly_init()
{
dog = class_create(THIS_MODULE, "dog");//创建一个dog类
if(IS_ERR(dog)) {
PTR_ERR(dog);
return ;
} wangcai = device_create(dog, NULL, MKDEV(, ), NULL, "wangcai%d", );//创建一个名为旺财的设备
if(IS_ERR(wangcai)) {
PTR_ERR(wangcai);
return ;
} return ;
} int bunfly_exit()
{
printk("this is bunfly_exit\n"); return ;
} module_init(bunfly_init);
module_exit(bunfly_exit);
在实际的工作中我们一般都不需要创建类,设备等,linux系统都为常见的设备分好了类,而设备厂商都已经提供了,我们做的就是来驱动这些设备;在sys/class类中我们经常用的就是misc(杂项类)
杂项设备也是在嵌入式系统中用得比较多的一种设备驱动,其定义如下:
struct device; struct miscdevice {
int minor; //次设备号
const char *name; //设备名
const struct file_operations *fops;//文件操作
struct list_head list; //形成链表
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
}; extern int misc_register(struct miscdevice * misc); //混杂设备注册
extern int misc_deregister(struct miscdevice *misc); //混杂设备注销 #define MODULE_ALIAS_MISCDEV(minor) \
MODULE_ALIAS("char-major-" __stringify(MISC_MAJOR) \
"-" __stringify(minor))
#endif
下面代码是在misc下注册一个名为bunfly_led的设备;插入模块后,在/dev下生成一个名为bunfly_led的设备名
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/miscdevice.h> MODULE_LICENSE("GPL");
MODULE_AUTHOR("bunfly"); struct file_operations fops;//方法
struct miscdevice led; int bunfly_init()
{
led.name = "bunfly_led";//设备名
led.fops = &fops;//关联方法
misc_register(&led);//在杂项类中注册led return ;
} int bunfly_exit()
{
printk("this is bunfly_exit\n");
misc_deregister(&led);//注销 return ;
} module_init(bunfly_init);
module_exit(bunfly_exit);
下面代码的功能是用ioctl()函数控制led灯,格式:./ioctl /dev/bunfly_led 0 (灯亮) | 1(灯灭)
#include <stdio.h>
#include <string.h>
#include <fcntl.h> //输入 ./ioctl /dev/bunly_led 1(灯灭) : 0(灯亮)
int main(int argc, char *argv[])
{
if(argc != ) {
printf("using %s <devname> 1:0\n", argv[]);
return ;
} int fd = ;
fd = open(argv[], O_RDWR);
if(fd < ) {
perror("open");
return ;
} //argv【2】为字符,需要atoi转换为数字
ioctl(fd, atoi(argv[]));
close(fd);
return ;
}
内核中:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/gpio.h> MODULE_LICENSE("GPL");
MODULE_AUTHOR("bunfly"); int bunfly_open(struct inode *n, struct file *fp);
long bunfly_ioctl(struct file *fp, unsigned int num, unsigned long vlaue);
void led_on();
void led_off(); struct file_operations fops;//方法
struct miscdevice led; unsigned long gpio_virt;
unsigned long *gpm4con, *gpm4dat; int bunfly_init()
{
fops.open = bunfly_open;//调用系统函数
fops.unlocked_ioctl = bunfly_ioctl; gpio_virt = ioremap(0x11000000, SZ_4K);//led物理地址到虚拟地址的映射
gpm4con = gpio_virt + 0x02e0;
gpm4dat = gpio_virt + 0x02e4; led.name = "bunfly_led";
led.fops = &fops;
misc_register(&led);//注册杂项类设备led return ;
} int bunfly_exit()
{
printk("this is bunfly_exit\n");
misc_deregister(&led);//注销设备 return ;
} module_init(bunfly_init);
module_exit(bunfly_exit); int bunfly_open(struct inode *n, struct file *fp)
{
printk("this is bunfly_open\n");
return ;
} long bunfly_ioctl(struct file *fp, unsigned int num, unsigned long vlaue)
{
if(num == ) {
led_on();
}
else {
if(num == ) {
led_off();
}
else {
printk("unkonw command %d\n", num);
}
} return ;
} void led_on()
{
*gpm4con &= ~0xffff;
*gpm4con |= 0x1111;
*gpm4dat = 0x0;
} void led_off()
{
*gpm4con &= ~0xffff;
*gpm4con |= 0x1111;
*gpm4dat = 0xf; }