栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

linux驱动DEVICE

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

linux驱动DEVICE

一、DEVICE_ATTR使用 1.1、作用

使用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_ATTR
b、使用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 
#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");
暑期编程PK赛 得CSDN机械键盘等精美礼品!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1015899.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号