一、实验思路
四部分:
内核模块 + led驱动 + kobject + kobj_attribute
1、内核模块
动态加载功能
2、led驱动
控制硬件led
3、kobject
在/sys创建目录项,并将 kobject 和 kernfs_node 关联起来
4、kobj_attribute
为kobject对象的属性文件提供独有的读写接口。
结构体 struct kobj_attribute
在 struct attribute
的基础上封装了两个操作接口。
这两个操作接口需要我们去实现。
struct attribute {
const char *name;
umode_t mode;
...
};
struct kobj_attribute {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
char *buf);
ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count);
};
__ATTR
定义如下
#define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}
二、驱动源码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#define DEV_MAJOR 0 /* 动态申请主设备号 */
#define DEV_NAME "red_led" /*led设备名字 */
/* GPIO虚拟地址指针
* __iomem 表示:该指针是指向一个I/O的内存空间
*/
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
static int foo;
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
/* buf 将会被自动拷贝到用户空间。
* 详见教程102。
*/
return sprintf(buf, "%d\n", foo);
}
static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
int ret;
/* buf内容来自用户空间,由内核自动完成了。
* kstrtoint 是将子串buf以十进制的格式输出到foo。
*/
ret = kstrtoint(buf, 10, &foo);
if (ret < 0)
return ret;
return count;
}
/* __ATTR 定义在 include/linux/sysfs.h。
* show成员 和 store成员 最终分别会被 kobject->ktype 下的 kobj_sys_ops 下的。
* kobj_attr_show 和 kobj_attr_store 调用。
* foo 对应属性文件名。
*/
static struct kobj_attribute foo_attribute =
__ATTR(foo, 0664, foo_show, foo_store);
/* __ATTR 定义详见下 */
static ssize_t led_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
int var;
if (strcmp(attr->attr.name, "led") == 0)
var =123;
return sprintf(buf, "%d\n", var);
}
static ssize_t led_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
if (strcmp(attr->attr.name, "led") == 0){
if(!memcmp(buf,"on",2)) {
iowrite32(0 << 3, GPIO1_DR);
} else if(!memcmp(buf,"off",3)) {
iowrite32(1 << 3, GPIO1_DR);
}
}
return count;
}
/* led 对应属性名 */
static struct kobj_attribute led_attribute =
__ATTR(led, 0664, led_show, led_store);
static struct attribute *attrs[] = {
&foo_attribute.attr,
&led_attribute.attr,
NULL, /* need to NULL terminate the list of attributes */
};
static struct attribute_group attr_group = {
.attrs = attrs,
};
static struct kobject *led_kobj;
static int __init led_init(void)
{
int retval;
/* GPIO相关寄存器映射 */
IMX6U_CCM_CCGR1 = ioremap(0x20c406c, 4);
SW_MUX_GPIO1_IO03 = ioremap(0x20e006c, 4);
SW_PAD_GPIO1_IO03 = ioremap(0x20e02f8, 4);
GPIO1_GDIR = ioremap(0x0209c004, 4);
GPIO1_DR = ioremap(0x0209c000, 4);
/* 使能GPIO1时钟 */
iowrite32(0xffffffff, IMX6U_CCM_CCGR1);
/* 设置GPIO1_IO03复用为普通GPIO*/
iowrite32(5, SW_MUX_GPIO1_IO03);
/*设置GPIO属性*/
iowrite32(0x10B0, SW_PAD_GPIO1_IO03);
/* 设置GPIO1_IO03为输出功能 */
iowrite32(1 << 3, GPIO1_GDIR);
/* LED输出高电平 */
iowrite32(1<< 3, GPIO1_DR);
/* 创建一个kobject对象,上一层节点设置为 NULL,此kobject对象在 sysfs 下的根目录。
* 此函数执行完会在 /sys 目录下生成一个名为"led_kobject"的目录。
* 此函数详见教程 101。
*/
led_kobj = kobject_create_and_add("led_kobject", NULL);
if (!led_kobj)
return -ENOMEM;
/* 为kobject设置属性文件,并且将属性文件和操作接口绑定起来。
* 此函数详见教程 102
*/
retval = sysfs_create_group(led_kobj, &attr_group);
if (retval)
kobject_put(led_kobj);
return retval;
return 0;
}
static void __exit led_exit(void)
{
/* 取消映射 */
iounmap(IMX6U_CCM_CCGR1);
iounmap(SW_MUX_GPIO1_IO03);
iounmap(SW_PAD_GPIO1_IO03);
iounmap(GPIO1_DR);
iounmap(GPIO1_GDIR);
/* 注销字符设备驱动 */
kobject_put(led_kobj);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("embedfire ");
MODULE_DESCRIPTION("led_module");
MODULE_ALIAS("led_module");
三、测试
/sys 目录下产生对应的目录,目录下产生对应的属性文件。
/lib/modules/4.1.15 # cd /sys/
/sys #
/sys #
/sys # ls
block class devices fs led_object power
bus dev firmware kernel module
/sys #
/sys # ls led_object/
foo led
/sys #
/sys #
/sys # cd led_object/
/sys/led_object #
/sys/led_object #
/sys/led_object #
/sys/led_object # ls
foo led
/sys/led_object #
/sys/led_object #
/sys/led_object # cat foo
0
/sys/led_object # echo 66 > foo
/sys/led_object # cat foo
66
/sys/led_object # echo 666 > foo
/sys/led_object # cat foo
666
/sys/led_object # ls
foo led
/sys/led_object # cat led
123
/sys/led_object # echo on >led
/sys/led_object # echo off >led
/sys/led_object #