103 设备驱动模型试验 kobject 点灯

一、实验思路

四部分:
内核模块 + led驱动 + kobject + kobj_attribute

1、内核模块

动态加载功能

2、led驱动

控制硬件led

3、kobject

在/sys创建目录项,并将 kobject 和 kernfs_node 关联起来

4、kobj_attribute

为kobject对象的属性文件提供独有的读写接口。

结构体 struct kobj_attributestruct 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 # 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值