uevent机制
kobject对象可以通过uevent机制往用户空间发送信息
-
kobject_uevent:内核消息发送接口
- 广播方式发送
-
NETLINK:特殊的网络通信,本地主机使用。mdev / udev 负责监控此连接,有消息就获取。
- 传统做法是内核执行
hotplug 程序
进行消息通知(效率低、不优雅)
- 传统做法是内核执行
-
udev/mdev:用户空间守护进程,监听广播信息
- 默认开机启动,systemd
- udevadm monitor:打印uevent事件。
shell下输入此指令,udev会将其收到的广播包打印出来。
内核消息发送
消息类型
enum kobject_action {
/* 添加一个 kobject 对象 */
KOBJ_ADD,
/* 删除一个 kobject 对象 */
KOBJ_REMOVE,
/* 改变一个 kobject 对象 */
KOBJ_CHANGE,
/* 移动一个 kobject 对象 */
KOBJ_MOVE,
KOBJ_ONLINE,
KOBJ_OFFLINE,
KOBJ_BIND,
KOBJ_UNBIND,
KOBJ_MAX
};
kobject_uevent()函数
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
return kobject_uevent_env(kobj, action, NULL);
}
kobject_uevent_env()函数
int kobject_uevent_env(struct kobject *kobj,
enum kobject_action action,
char *envp_ext[])
{
struct kobj_uevent_env *env;
...
top_kobj = kobj;
/* while 循坏查找 kobj 所隶属的最顶层 kobject 或者 kset 指针不为空的 kobj */
while (!top_kobj->kset && top_kobj->parent)
top_kobj = top_kobj->parent;
/* 判断kobj的kset指针是否为空 */
if (!top_kobj->kset) {
pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
"without kset!\n", kobject_name(kobj), kobj,
__func__);
return -EINVAL;
}
/* 得到 kobj 指向的 kset 对象 */
kset = top_kobj->kset;
/* 获取 kset 的 uevent_ops */
uevent_ops = kset->uevent_ops;
/* 若kobject->uevent_suppress为1,表示kobj不适用uevent。
* 即不允许这个 kobject 发送 uevent 事件。
*/
if (kobj->uevent_suppress) {
pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
"caused the event to drop!\n",
kobject_name(kobj), kobj, __func__);
return 0;
}
/* 过滤event事件 */
if (uevent_ops && uevent_ops->filter)
if (!uevent_ops->filter(kset, kobj)) {
pr_debug("kobject: '%s' (%p): %s: filter function "
"caused the event to drop!\n",
kobject_name(kobj), kobj, __func__);
return 0;
}
...
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
/* 获取kobj在sysfs中的路径 */
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
retval = -ENOENT;
goto exit;
}
/* 经过上面的一系列判断,下面开始正式发送消息到用户空间 */
/* 向 env 结构体里面填充消息内容 */
/* 1、填充消息具体类型 */
retval = add_uevent_var(env, "ACTION=%s", action_string);
if (retval)
goto exit;
/* kobject 在 sysfs 中的路径 */
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
if (retval)
goto exit;
/* 当前 kobject 的上一层节点 */
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
if (retval)
goto exit;
...
if (uevent_ops && uevent_ops->uevent) {
retval = uevent_ops->uevent(kset, kobj, env);
if (retval) {
pr_debug("kobject: '%s' (%p): %s: uevent() returned "
"%d\n", kobject_name(kobj), kobj,
__func__, retval);
goto exit;
}
}
...
/* 本地socket通信,发送广播信息 */
retval = kobject_uevent_net_broadcast(kobj, env, action_string,
devpath);
...
}
驱动源码
#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>
/* GPIO虚拟地址指针 */
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 struct kset *example_kset;
static struct kobject *led_kobj;
static int foo;
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
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;
ret = kstrtoint(buf, 10, &foo);
if (ret < 0)
return ret;
return count;
}
static struct kobj_attribute foo_attribute =
__ATTR(foo, 0664, foo_show, foo_store);
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 << 4, GPIO1_DR);
} else if(!memcmp(buf,"off",3)) {
iowrite32(1 << 4, GPIO1_DR);
}
}
return count;
}
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 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);
/*创建一个kset对象*/
example_kset = kset_create_and_add("kset_example", NULL, NULL);
if (!example_kset)
return -ENOMEM;
/* 创建一个kobject对象*/
led_kobj = kobject_create_and_add("led_kobject", &example_kset->kobj);
led_kobj->kset = example_kset;
if (!led_kobj)
return -ENOMEM;
/* 为kobject设置属性文件 */
retval = sysfs_create_group(led_kobj, &attr_group);
if (retval)
kobject_put(led_kobj);
/* 让此 kobject 发送一个 KOBJ_ADD 事件。
* 注意一定要设置此 kobject 的 kset,不能让其 kset 为空。
* 否则 kobject_uevent 提前判断异常退出。
*/
kobject_uevent(led_kobj, KOBJ_ADD);
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);
/*取消字符设备的注册*/
kset_unregister(example_kset);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("embedfire ");
MODULE_DESCRIPTION("led_module");
MODULE_ALIAS("led_module");