开始学习驱动的时候,是将驱动程序编译成模块然后用mknod命令手动建立设备节点以提供给应用程序调用。这对于刚开始调试驱动程序的时候常用的一种方法。但是,当有种需要必须在系统启动的时候就将驱动程序就绪,来供应用层程序调用。这时就不能再手动的建立设备节点了,而必须自动的创建设备节点(不需要人为的操作)。
★注册类
注册类的目的是为了使mdev可以在/dev/目录下建立设备节点。
首先要定义一个类,利用struct class结构体。这个结构体定义在头文件include/linux/device.h中
struct class {
const char * name;
struct module * owner;
struct subsystem subsys;
struct list_head children;
struct list_head devices;
struct list_head interfaces;
struct semaphore sem; /* locks both the children and interfaces lists */
struct kobject *virtual_dir;
struct class_attribute * class_attrs;
struct class_device_attribute * class_dev_attrs;
struct device_attribute * dev_attrs;
int (*uevent)(struct class_device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
int (*dev_uevent)(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size);
void (*release)(struct class_device *dev);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *, pm_message_t state);
int (*resume)(struct device *);
}
然后使用
完成对类的注册。其中第一个参数一般为:THIS_MODULE。第二个参数为:设备节点的名称
举个例子:
★创建设备节点
创建设备节点的函数:
<pre name="code" class="cpp">struct device *device_create(struct class *class, struct device *parent,dev_t devt, const char *fmt, ...)
{
va_list args;
struct device *dev = NULL;
int retval = -ENODEV;
if (class == NULL || IS_ERR(class))
goto error;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
retval = -ENOMEM;
goto error;
}
dev->devt = devt;
dev->class = class;
dev->parent = parent;
dev->release = device_create_release;
va_start(args, fmt);
vsnprintf(dev->bus_id, BUS_ID_SIZE, fmt, args);
va_end(args);
retval = device_register(dev);
if (retval)
goto error;
return dev;
error:
kfree(dev);
return ERR_PTR(retval);
}
该函数的四个参数从左到右以此为:创建设备节点所属的类、该设备的父节点(若果没有就指定为NULL)、设备号、设备名称、次设备号。
★销毁类和设备节点
注意不要忘记了还要销毁类和销毁设备节点。
销毁类:参数为用struct class结构体定义的变量
void class_destroy(struct class *cls)
{
if ((cls == NULL) || (IS_ERR(cls)))
return;
class_unregister(cls);
}
销毁设备节点:
void device_destroy(struct class *class, dev_t devt)
{
struct device *dev = NULL;
struct device *dev_tmp;
down(&class->sem);
list_for_each_entry(dev_tmp, &class->devices, node) {
if (dev_tmp->devt == devt) {
dev = dev_tmp;
break;
}
}
up(&class->sem);
if (dev)
device_unregister(dev);
}
★例子(自己写的延时驱动)
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/device.h>
#define NAME "ralink_drive_delay"
#define RALINK_GPIO_DEVNAME "my_delay"
#define delay_us 0 //most least is 10 US
#define delay_ms 1 //Ms
int delay_MAJOR = 109;
MODULE_LICENSE("Dual BSD/GPL");
static long Ralink_delay_ioctl(struct inode * inode, struct file * file, unsigned int cmd,unsigned long arg)
{
switch(cmd)
{
case delay_us:
udelay(10 * arg);
return 0;
case delay_ms:
udelay(1000);
return 0;
default:
return -1;
}
}
static struct file_operations My_delay_fops =
{
.owner = THIS_MODULE,
.ioctl = Ralink_delay_ioctl,
};
static struct class *delay_class;
static int __init my_delay_init(void)
{
int ret = 0;
ret = register_chrdev(delay_MAJOR, RALINK_GPIO_DEVNAME,&My_delay_fops);
if(ret < 0)
{
printk("unable to register character device\n");
return ret;
}
if (delay_MAJOR == 0)
{
delay_MAJOR = ret;
printk(KERN_DEBUG NAME ": got dynamic major %d\n", ret);
}
//注册一个类,使mdev可以在"/dev/目录下建立设备节点"
delay_class = class_create(THIS_MODULE, RALINK_GPIO_DEVNAME);
if(IS_ERR(delay_class))
{
printk("failed in My_led class.\n");
return -1;
}
device_create(delay_class, NULL, MKDEV(delay_MAJOR, 0),RALINK_GPIO_DEVNAME 0);//
//第一个参数是所要创建的设备所从属的类
//第二个参数是这个设备的父节点,没有指定就是NULL
//第三个参数是设备号
//第四个参数是设备名称
//第五个参数是从设备号
补充(2016.6.2)
最近在编写另一个驱动用到自动创建设备节点的时候,就是参考上面device_create函数,结果在insmod驱动模块的时候出现Oops的错误。查找了一下错误原因发现是因为device_create这个函数里面的参数搞错了,现在贴出Linux2.6.30.4中关于这个函数参数的介绍:
device_create - creates a device and registers it with sysfs
@class: pointer to the struct class that this device should be registered to
@parent: pointer to the parent struct device of this new device, if any
@devt: the dev_t for the char device to be added
@drvdata: the data to be added to the device for callbacks//(如果没有设置为NULL)
@fmt: string for the device's name//第五个参数是设备名称
printk("my_delay driver initialized\n");
return 0;
}
void __exit my_delay_exit(void)
{
unregister_chrdev(delay_MAJOR,RALINK_GPIO_DEVNAME);
device_destroy(delay_class,MKDEV(delay_MAJOR,0));//注销设备节点
class_destroy(delay_class);//销毁类
printk("my_delay driver exited\n");
}
module_init(my_delay_init);
module_exit(my_delay_exit);