使用DEVICE_ATTR往sysfs中为device添加“属性文件”,并对该文件添加相关操作,从而实现运行过程中动态控制device。
1.2、关于DEVICE_ATTR的定义DEVICE_ATTR相关的宏定义,定义位置在kernel/include/linux/device.h中。
#define DEVICE_ATTR(_name, _mode, _show, _store)
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
两个关键struct device_attribute和__ATTR(_name, _mode, _show, _store)
struct device_attribute的定义,该结构体定义在kernel/include/linux/device.h中
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
__ATTR定义在kernel/include/linux/sysfs.h中。
#define __ATTR(_name, _mode, _show, _store) {
.attr = {.name = __stringify(_name),
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) },
.show = _show,
.store = _store,
}
实际上是通过 __ATTR(_name, _mode, _show, store)初始化了device_attribute 这个结构体。该结构体名称为 dev_attr##_name
1.3、其中DEVICE_ATTR中的四个参数含义为:_name:在sysfs中生产的文件名称 _mode: 该文件具有的权限,与普通文件相同,UGO格式,如:0444只读、0666可读写 _show: 当cat命令作用于该文件时调用该函数 _store: 当echo数据到该文件时调用该函数1.4、使用例子:
//创建status_r节点,只读
static DEVICE_ATTR(status_r,S_IRUGO, status_r_show, NULL);
//创建status_rw节点,可读写
static DEVICE_ATTR(status_rw,S_IRUGO | S_IWUSR, status_rw_show, status_rw_store);
//
------------------------
static struct attribute *Device_Attr[] = {
&dev_attr_status_r.attr,
&dev_attr_status_rw.attr,
NULL
};
------------------------
static const struct attribute_group DEVICE_ATTR_GROUP = {
.attrs = Device_Attr,
};
------------------------
int ret;
ret = sysfs_create_group(&pdev->dev.kobj, &DEVICE_ATTR_GROUP);
if (ret < 0) {
printk("Can't create sysfs entriesn");
goto sysfs_create_err;
}
二、热插拔上报uevent
Uevent是Kobject的一部分,用于在Kobject状态发生改变时,例如增加、移除等,通知用户空间程序。 用户空间程序收到这样的事件后,去做相应的处理。kobject_uevent_env用于Linux下热插拔事件产生时,通知到用户空间的一种方式,uevent是sysfs向用户空间发出的消息。
2.1、举例子—hdmi热插拔上报的处理方法c语言监听热插拔实际的应用:
源码根目录下system/extras/tests/uevents/uevents.c
#include#include #define UEVENT_MSG_LEN 1024 int main(int argc, char *argv[]) { int device_fd; char msg[UEVENT_MSG_LEN+2]; int n; int i; device_fd = uevent_open_socket(64*1024, true); if(device_fd < 0) return -1; while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) { msg[n] = ' '; msg[n+1] = ' '; for (i = 0; i < n; i++) if (msg[i] == ' ') msg[i] = ' '; printf("%sn", msg); } return 0; }
mmm编译之后push到板子/system/bin上执行,然后插拔hdmi监听事件
rk3399:/system/bin # ./uevents change@/devices/platform/display-subsystem/drm/card0 ACTION=change DEVPATH=/devices/platform/display-subsystem/drm/card0 SUBSYSTEM=drm HOTPLUG=1 MAJOR=226 MINOR=0 DEVNAME=dri/card0 DEVTYPE=drm_minor SEQNUM=2608 change@/devices/virtual/switch/hdmi ACTION=change DEVPATH=/devices/virtual/switch/hdmi SUBSYSTEM=switch SWITCH_NAME=hdmi SWITCH_STATE=1 SEQNUM=2609
通过打印grep -rnH "SWITCH_NAME="定位上报代码
kernel/drivers/switch/switch_class.c
-->void switch_set_state(struct switch_dev *sdev, int state)
-->kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp);
hdmi插拔上报通过调用switch_set_state函数,然后在调用kobject_uevent_env将事件上报上去。相关的还有耳机等调用到。
2.3、创建自己的上报函数//驱动上报自己的uevent事件给应用监听事件
static void uevent_trigger(struct device *dev,int uevent_status)
{
char *event = NULL;
char *envp[3];
const char *name;
name = uevent_status? "1" : "0";
event = kasprintf(GFP_KERNEL, "DEVSTATE=%s", name);
printk("%s,event--->%sn", __func__, event);
envp[0] = "DEVNAME=status_r";
envp[1] = event;
envp[2] = NULL;
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
}
三、驱动启动一个c语言用语(touch)
在内核态,则可以通过call_usermodehelpere()实现该功能。
//驱动通过call_usermodehelper启动应用
static int execute_user_exe(char *exe_path_buf,char *parameter_path_buf)
{
int result=-1;
char *cmd_argv[] = {exe_path_buf,parameter_path_buf, NULL};
char *cmd_envp[] = {NULL,NULL, NULL};
result = call_usermodehelper(exe_path_buf, cmd_argv, cmd_envp,UMH_WAIT_PROC);
printk("THe result of call_usermodehelper is %dn", result);
return result;
}
通过后面的安装驱动程序,再使用echo指令的查看status_rw时调用的函数,并执行touch这个应用在/data目录下生成touch.txt文件
static ssize_t status_rw_store(struct device *dev,struct device_attribute *attr,const char *buf,size_t count)
{
ssize_t r;
#if 1 //输入echo 指令是通过封装好的execute_user_exe函数启动应用
char *cmd_path = "/system/bin/touch";
char *parameter_path[2]="/data/touch.txt";
execute_user_exe(cmd_path,parameter_path);
#endif
DBG("%s, buf: %s,count = %ldn", __func__, buf, count);
r = sscanf(buf, "%d", &status);
DBG("%s, r = %ld, status = %dn", __func__, r, status);
return count;
}
代码总结
a、安装本驱动程序后生成”文件“DEVICE_ATTR:
rk3399:/sys/devices/platform/DEVICE_ATTRb、使用cat命令,然后调用status_r_show->uevent_trigger->kobject_uevent_env上报了自己定义的事件。 c、使用echo命令,然后调用status_rw_store->execute_user_exe->call_usermodehelper启动了一个c语言的应用,在data下生成了touch,txt文件。 d、完整代码:
#include暑期编程PK赛 得CSDN机械键盘等精美礼品!#include #include #include #include #include #include #include #include #include #include #include #include #include #if 1 #define DBG(x...) printk(x) #else #define DBG(x...) do { } while (0) #endif static int status=0; static void set_state (int value) { DBG("%s, value = %dn", __func__, value); //if(gpio_is_valid(ppdata->led_io)) // gpio_direction_output(ppdata->led_io, value); } //驱动上报自己的uevent事件给应用监听事件 static void uevent_trigger(struct device *dev,int uevent_status) { char *event = NULL; char *envp[3]; const char *name; name = uevent_status ? "1" : "0"; event = kasprintf(GFP_KERNEL, "DEVSTATE=%s", name); DBG("%s,event--->%sn", __func__, event); envp[0] = "DEVNAME=status_r"; envp[1] = event; envp[2] = NULL; kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); } //驱动通过call_usermodehelper启动应用 static int execute_user_exe(char *exe_path_buf,char *parameter_path_buf) { int result=-1; char *cmd_argv[] = {exe_path_buf,parameter_path_buf, NULL}; char *cmd_envp[] = {NULL,NULL, NULL}; result = call_usermodehelper(exe_path_buf, cmd_argv, cmd_envp,UMH_WAIT_PROC); printk("THe result of call_usermodehelper is %dn", result); return result; } //使用cat指令的查看status_r时调用的函数,上报uevent事件 static ssize_t status_r_show(struct device *dev,struct device_attribute *attr,char *buf) { uevent_trigger(dev,status); set_state(1); return 0;//scnprintf(buf, PAGE_SIZE, "%d", status); } //使用cat指令的查看status_rw时调用的函数 static ssize_t status_rw_show(struct device *dev,struct device_attribute *attr,char *buf) { return scnprintf(buf, PAGE_SIZE, "%d", status); } //使用echo指令的查看status_rw时调用的函数 static ssize_t status_rw_store(struct device *dev,struct device_attribute *attr,const char *buf,size_t count) { ssize_t r; #if 1 //输入echo 指令是通过封装好的execute_user_exe函数启动应用 char *cmd_path = "/system/bin/touch"; char *parameter_path[2]="/data/touch.txt"; execute_user_exe(cmd_path,parameter_path); #endif DBG("%s, buf: %s,count = %ldn", __func__, buf, count); r = sscanf(buf, "%d", &status); DBG("%s, r = %ld, status = %dn", __func__, r, status); return count; } //创建status_r节点,只读 static DEVICE_ATTR(status_r,S_IRUGO, status_r_show, NULL); //创建status_rw节点,可读写 static DEVICE_ATTR(status_rw,S_IRUGO | S_IWUSR, status_rw_show, status_rw_store); static struct attribute *Device_Attr[] = { &dev_attr_status_r.attr, &dev_attr_status_rw.attr, NULL, }; static const struct attribute_group DEVICE_ATTR_GROUP = { .attrs = Device_Attr, }; static int platform_probe(struct platform_device *pdev) { int ret; ret = sysfs_create_group(&pdev->dev.kobj, &DEVICE_ATTR_GROUP); if (ret < 0) { printk("Can't create sysfs entriesn"); goto sysfs_create_err; } return 0; sysfs_create_err: return ret; } static int platform_remove(struct platform_device *pdev) { return 0; } static int platform_suspend(struct platform_device *pdev, pm_message_t state) { return 0; } static int platform_resume(struct platform_device *pdev) { return 0; } #if 0 //设备树下使用 static const struct of_device_id platform_of_match[] = { { .compatible = "DEVICE_ATTR", }, {}, }; MODULE_DEVICE_TABLE(of, platform_of_match); #else //无设备树下使用 static struct platform_device My_platform_device = { .name = "DEVICE_ATTR", .id = -1, }; #endif static struct platform_driver My_platform_driver = { .probe = platform_probe, .remove = platform_remove, .resume = platform_resume, .suspend = platform_suspend, .driver = { .name = "DEVICE_ATTR", .owner = THIS_MODULE, #if 0 //设备树下使用 .of_match_table = of_match_ptr(platform_of_match), #endif }, }; static int __init My_platform_init(void) { platform_driver_register(&My_platform_driver); platform_device_register(&My_platform_device); return 0; } static void __exit My_platform_exit(void) { platform_driver_unregister(&My_platform_driver); platform_device_unregister(&My_platform_device); } //late_initcall(My_platform_init); module_init(My_platform_init); module_exit(My_platform_exit); MODULE_DESCRIPTION("My platform Driver"); MODULE_LICENSE("GPL");



