实现字符设备启动程序的基本框架
- 申请设备号,使用alloc_chrdev_region动态分配一个或多个设备号;
- 创建设备类,使用class_create函数创建自己的设备类,在 sys/class 可见;
- 注册设备,通过cdev_init和cdev_add注册设备,并实现file_operations,与之建立关联;
- 创建设备,使用device_create将注册的设备建立关联,设备会在 /dev 目录创建。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/err.h>
static dev_t led_devt;
struct cdev *led_cdev;
struct class *led_cls;
struct device *led_dev;
int led_dev_open(struct inode *inode, struct file *file) {
printk(KERN_ALERT "%s\n", __FUNCTION__);
return 0;
}
int led_dev_release(struct inode *inode, struct file *file) {
printk(KERN_ALERT "%s\n", __FUNCTION__);
return 0;
}
long led_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
printk(KERN_ALERT "%s\n", __FUNCTION__);
return 0;
}
/* 创建 file_operations */
static const struct file_operations led_dev_fops = {
.open = led_dev_open,
.release = led_dev_release,
.unlocked_ioctl = led_dev_ioctl,
};
static int __init drv_led_init(void) {
int err;
printk(KERN_ALERT "%s\n", __FUNCTION__);
/* 申请设备号 */
err = alloc_chrdev_region(&led_devt, 0, 1, "dev_led");
if(err < 0) {
pr_err("alloc_chrdev_region error!\n");
goto err_alloc_chrdev;
}
/* 创建设备类,可在系统中 /sys/class 中可见 */
led_cls = class_create(THIS_MODULE, "drv_leds");
if(IS_ERR(led_cls)) {
pr_err("class_create error!\n");
err = PTR_ERR(led_cls);
goto err_class_create;
}
/* 创建一个cdev对象 */
led_cdev = cdev_alloc();
if(IS_ERR(led_cdev)) {
pr_err("cdev_alloc error!\n");
err = PTR_ERR(led_cdev);
goto err_cdev_alloc;
}
/* 初始化cdev对象,将cdev对象与对印的file_operations建立关联 */
cdev_init(led_cdev, &led_dev_fops);
/* 注册设备,让用户访问设备 */
err = cdev_add(led_cdev, led_devt, 1);
if(err < 0) {
pr_err("cdev_add error!\n");
goto err_cdev_add;
}
/* 创建设备 */
led_dev = device_create(led_cls, NULL, led_devt, NULL, "led0");
if(IS_ERR(led_dev)) {
pr_err("device_create error!\n");
err = PTR_ERR(led_dev);
goto err_device_create;
}
return 0;
err_device_create:
err_cdev_add:
cdev_del(led_cdev);
err_cdev_alloc:
class_destroy(led_cls);
err_class_create:
unregister_chrdev_region(led_devt, 1);
err_alloc_chrdev:
return err;
}
static void __exit drv_led_exit(void) {
printk(KERN_ALERT "%s\n", __FUNCTION__);
device_destroy(led_cls, led_devt);
class_destroy(led_cls);
cdev_del(led_cdev);
unregister_chrdev_region(led_devt, 1);
}
module_init(drv_led_init);
module_exit(drv_led_exit);
MODULE_LICENSE("GPL");
MODULE_VERSION("V0.0.1");
MODULE_AUTHOR("dengcaixiang");