1、class结构体
(1)内核通过class_create(…)函数,创建一个类,这个类存放于/sys中
(2)device_create(…)函数来在/dev目录下创建相应的设备节点。
(3)在加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sys下寻找对应的类从而创建设备节点。
struct class {
const char *name; // 类名称
struct module *owner; // 类所属的模块,比如 usb模块、led模块等
struct class_attribute *class_attrs; // 类所添加的属性
const struct attribute_group **dev_groups; // 类所包含的设备所添加的属性
struct kobject *dev_kobj; // 用于标识 类所包含的设备属于块设备还是字符设备
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); // 用于在设备发出 uevent 消息时添加环境变量
char *(*devnode)(struct device *dev, umode_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_private 结构的指针
};
2、设备文件创建的函数
/***********************************************************************
* @description : 创建一个class
* @param - owner : THIS_MODULE
* @param - name : 类名字
* @return : 成功,返回struct class的指针;其他 失败
***********************************************************************/
struct class *class_create(struct module *owner, const char *name);
/*******************************************************************************
* @description : 生成设备文件
* @param - class : 指向该设备应注册到的struct class的指针(上一个函数)
* @param - parent : 指向此新设备的父struct设备的指针(如果有),没有填NULL
* @param - devt : 要添加的char设备的dev_t
* @param - drvdata : 要添加到设备中以进行回调的数据(如果有),没有填NULL
* @param - fmt : 设备名称的字符串
* @return : 成功,返回struct device指针;其他 失败
*********************************************************************************/
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
;
3、设备文件销毁的函数
/* 删除设备类 */
void class_destroy(struct class *cls);
/* 创建设备文件 */
void device_destroy(struct class *class, dev_t devt);
4、示例
dev_t s_devNo; /* 设备号 */
static struct cdev *pcdev;
static struct class *module_class;
#define GPJ0CON 0xE0200240 //GPJ0CON物理地址
#define GPJ0DAT 0xE0200244 //GPJ0DAT物理地址
unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;
static int LED_test_open(struct inode *inode, struct file *file)
{
//***************** 动态映射操作寄存器 *************/
//动态映射GPJ0CON
if (!request_mem_region(GPJ0CON, 4, "GPJ0CON"))
return -EINVAL;
pGPJ0CON = ioremap(GPJ0CON, 4);
//动态映射GPJ0CON
if (!request_mem_region(GPJ0DAT, 4, "GPJ0DAT"))
return -EINVAL;
pGPJ0DAT = ioremap(GPJ0DAT, 4);
return 0;
}
static int LED_test_release(struct inode *inode, struct file *file)
{
// 解除映射
iounmap(pGPJ0CON);
release_mem_region(GPJ0CON, 4);
iounmap(pGPJ0DAT);
release_mem_region(GPJ0DAT, 4);
return 0;
}
//应用层和驱动之间的数据交换,copy_from_user:从用户空间复制到内核空间,copy_to_user:从内核空间复制到用户空间
static ssize_t LED_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
char kBuf[1024] = "module_test_read";
int ret = copy_to_user(ubuf, kBuf, count);
if (ret == 0)
printk(KERN_DEBUG, "copy_to_user success");
return count;
}
static ssize_t LED_test_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
{
char kBuf[1024];
int ret = copy_from_user(kBuf, ubuf, count);
if (ret == 0)
printk(KERN_DEBUG, "copy_from_user success");
*pGPJ0CON = 0x11111111;
if (kBuf[0] = '1')
*pGPJ0DAT = (0 << 3);
else if (kBuf[0] = '0')
*pGPJ0DAT = (1 << 3);
return count;
}
struct file_operations LED_test_fops =
{
.owner = THIS_MODULE,
.read = LED_test_read,
.write = LED_test_write,
.open = LED_test_open,
.release = LED_test_realse,
};
static int __init chrdev_LED_init(void)
{
//printk是内核源码中用来打印信息的函数,KERN_DEBUG是打印级别
printk(KERN_DEBUG "chrdev_LED_init");
/*************** 动态申请设备号 *****************/
int ret = alloc_chrdev_region(&s_devNo, 0, 1, "LED_MODULE_TEST");
if (ret < 0)
{
printk(KERN_ERR "alloc_chrdev_region failed");
goto chrdev_error;
}
/*************** 注册字符设备驱动 **************/
//建立cdev和fops连接
pcdev = cdev_alloc();
pcdev->owner = THIS_MODULE;
cdev_init(pcdev, &LED_test_fops);
//向内核注册字符设备驱动
ret = cdev_add(pcdev, s_devNo, 1);
if (ret < 0)
{
printk(KERN_ERR "cdev_add failed");
unregister_chrdev_region(s_devNo, 1);
goto cdev_error;
}
/************* 创建设备类和设备文件 *************/
module_class = class_create(THIS_MODULE, "module_class");
if (IS_ERR(module_class))
return -EINVAL;
// 设备文件名是/dev/module_test
device_create(module_class, NULL, s_devNo, NULL, "module_test");
chrdev_error:
return -EINVAL;
cdev_error:
cdev_del(pcdev);
unregister_chrdev_region(s_devNo, 1);
class_error:
cdev_del(pcdev);
unregister_chrdev_region(s_devNo, 1);
return 0;
}
static void __exit chrdev_LED_exit(void)
{
printk(KERN_DEBUG "chrdev_LED_exit");
device_destroy(module_class, s_devNo);
class_destroy(module_class);
//注销字符设备驱动
cdev_del(pcdev);
//释放设备号
unregister_chrdev_region(s_devNo, 1);
}
module_init(chrdev_LED_init);
module_exit(chrdev_LED_exit);
//宏定义
MODULE_LICENSE("GPL") //模块的许可证
MODULE_AUTHOR("xy_L") //模块的作者
MODULE_DESCIPTION("chrdev LED module") //模块的描述
MODULE_ALIAS("LED_test") //模块的别名