嵌入式Linux字符设备驱动开发流程——以LED为例
前言
留空
头文件
#include
查看系统设备类
ls /sys/class
设备类结构体
文件(路径):include/linux/device.h
/**
* struct class - device classes
* @name: Name of the class.
* @owner:The module owner.
* @class_attrs: Default attributes of this class.
* @dev_attrs:Default attributes of the devices belong to the class.
* @dev_bin_attrs: Default binary attributes of the devices belong to the class.
* @dev_kobj: The kobject that represents this class and links it into the hierarchy.
* @dev_uevent: Called when a device is added, removed from this class, or a
*few other things that generate uevents to add the environment
*variables.
* @devnode:Callback to provide the devtmpfs.
* @class_release: Called to release this class.
* @dev_release: Called to release the device.
* @suspend:Used to put the device to sleep mode, usually to a low power
*state.
* @resume: Used to bring the device from the sleep mode.
* @ns_type:Callbacks so sysfs can detemine namespaces.
* @namespace:Namespace of the device belongs to this class.
* @pm: The default device power management operations of this class.
* @p:The private data of the driver core, no one other than the
*driver core can touch this.
*
* A class is a higher-level view of a device that abstracts out low-level
* implementation details. Drivers may see a SCSI disk or an ATA disk, but,
* at the class level, they are all simply disks. Classes allow user space
* to work with devices based on what they do, rather than how they are
* connected or how they work.
*/
struct class {
const char*name;
struct module *owner;
struct class_attribute*class_attrs;
struct device_attribute *dev_attrs;
struct bin_attribute*dev_bin_attrs;
struct kobject*dev_kobj;
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(struct device *dev, mode_t *mode);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct kobj_ns_type_operations *ns_type;
const void *(*namespace)(struct device *dev);
const struct dev_pm_ops *pm;
struct subsys_private *p;
};
创建设备类函数
所属文件名:class.c
/**
* class_create - create a struct class structure
* @owner: pointer to the module that is to "own" this struct class
* @name: pointer to a string for the name of this class.
* @key: the lock_class_key for this class; used by mutex lock debugging
*
* This is used to create a struct class pointer that can then be used
* in calls to device_create().
*
* Returns &struct class pointer on success, or ERR_PTR() on error.
*
* Note, the pointer created here is to be destroyed when finished by
* making a call to class_destroy().
*/
struct class *__class_create(struct module *owner, const char *name,
struct lock_class_key *key)
{
struct class *cls;
int retval;
cls = kzalloc(sizeof(*cls), GFP_KERNEL);
if (!cls) {
retval = -ENOMEM;
goto error;
}
cls->name = name;
cls->owner = owner;
cls->class_release = class_create_release;
retval = __class_register(cls, key);
if (retval)
goto error;
return cls;
error:
kfree(cls);
return ERR_PTR(retval);
}
释放设备类
所属文件名:class.c
/**
* class_destroy - destroys a struct class structure
* @cls: pointer to the struct class that is to be destroyed
*
* Note, the pointer to be destroyed must have been created with a call
* to class_create().
*/
void class_destroy(struct class *cls)
{
if ((cls == NULL) || (IS_ERR(cls)))
return;
class_unregister(cls);
}
创建设备节点函数
所属文件名:core.c
/**
* 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
* @fmt: string for the device's name
*
* This function can be used by char device classes.A struct device
* will be created in sysfs, registered to the specified class.
*
* A "dev" file will be created, showing the dev_t for the device, if
* the dev_t is not 0,0.
* If a pointer to a parent struct device is passed in, the newly created
* struct device will be a child of that device in sysfs.
* The pointer to the struct device will be returned from the call.
* Any further sysfs files that might be required can be created using this
* pointer.
*
* Returns &struct device pointer on success, or ERR_PTR() on error.
*
* Note: the struct class passed to this function must have previously
* been created with a call to class_create().
*/
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
}
释放设备节点
所属文件名:core.c
/**
* device_destroy - removes a device that was created with device_create()
* @class: pointer to the struct class that this device was registered with
* @devt: the dev_t of the device that was previously registered
*
* This call unregisters and cleans up a device that was created with a
* call to device_create().
*/
void device_destroy(struct class *class, dev_t devt)
{
struct device *dev;
dev = class_find_device(class, NULL, &devt, __match_devt);
if (dev) {
put_device(dev);
device_unregister(dev);
}
}
程序
#include //模块头文件
#include //内核头文件
#include //内核初始化
#include //字符设备函数
#include //字符设备描述
#include //系列设备号处理宏
#include //内存分配头文件
#include //设备类节点头文件
#define DEVICE_NAME "leds"//字符设备名称
#define NODE_NAME "led"
#define DEVICE_MINOR_NUM2 //字符设备数量
#define DEV_MAJOR 0 //主设备号
#define DEV_MINOR 0 //次设备号,0为自动分配
#define REGDEV_SIZE 3000
static int leds_major = DEV_MAJOR;//主设备号变量
static int leds_minor = DEV_MINOR;//次设备号变量
static dev_t leds_dev;//设备号
struct cdev *leds_cdev; //字符设备结构体变量
static struct class *leds_class;//类结构体变量
struct file_operations leds_fops = {
.owner = THIS_MODULE,
};
static __init int leds_init(void)
{
int ret = 0;
int i;
ret = alloc_chrdev_region(&leds_dev, leds_minor, DEVICE_MINOR_NUM, DEVICE_NAME);
if(ret < 0){
printk(KERN_EMERG "register_chrdev_region req %d is failed!\n", DEV_MAJOR);
return ret;
}
leds_major = MAJOR(leds_dev); //主设备号
leds_minor = MINOR(leds_dev); //次设备号
printk(KERN_EMERG "leds chrdev major=%d, minor=%d\n", leds_major, leds_minor);
leds_class = class_create(THIS_MODULE,DEVICE_NAME);
leds_cdev = kmalloc(DEVICE_MINOR_NUM * sizeof(struct cdev), GFP_KERNEL);
if(leds_cdev == NULL)
{
printk(KERN_EMERG "kmalloc failed");
unregister_chrdev_region(leds_dev, DEVICE_MINOR_NUM);
return -ENOMEM;
}
//memset(leds_cdev, 0, DEVICE_MINOR_NUM * sizeof(struct dev_cdev));
for(i=0; i
cdev_init(&leds_cdev[i], &leds_fops);
leds_cdev[i].owner = THIS_MODULE;
leds_cdev[i].ops = &leds_fops;
ret = cdev_add(&leds_cdev[i], MKDEV(leds_major, leds_minor+i), 1);//注册到设备
device_create(leds_class, NULL, MKDEV(leds_major, leds_minor+i), NULL, NODE_NAME"%d", i);
if(ret < 0){
printk(KERN_EMERG "cdev_add %d failed!\n", i);
}
else{
printk(KERN_EMERG "cdev_add %d success!\n", i);
}
}
return ret;
}
static __exit void leds_exit(void)
{
int i;
for(i=0; i
cdev_del(&leds_cdev[i]);//注销设备
device_destroy(leds_class, MKDEV(leds_major, leds_minor+i));
}
class_destroy(leds_class);
kfree(leds_cdev); //释放内存
unregister_chrdev_region(leds_dev, DEVICE_MINOR_NUM); //注销设备号
printk(KERN_EMERG "leds chrdev exit \n");
}
module_init(leds_init);
module_exit(leds_exit);
MODULE_LICENSE("GPL");
编译
make
加载编译后的模块
insmod leds.ko
查看字符设备节点
ls /dev/l*
红框内为系统动态分配的设备号
结束语
以上则为嵌入式Linux设备节点生成的相关内容。
~谢谢支持~
如果文章对您有帮助,欢迎移至上方按钮打赏博主;