(1)Driver Attributes与driver_create_file
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *driver, char *buf);
ssize_t (*store)(struct device_driver *, const char * buf, size_t count);
};
Device drivers can export attributes via their sysfs directories.Drivers can declare attributes using a DRIVER_ATTR macro that works identically to the DEVICE_ATTR macro.在实际调试时,用cat xxx即相当于执行show;用echo 1/0 > xxx相当于执行store了。需要注意的是,不管是show还是store,buf都可返回字符串,在store时返回的字符串可标示store操作的结果好坏。
实例:在GSENSOR 8452驱动中申明的属性,可以在ADB中查看到属性。
static DRIVER_ATTR(chipinfo, S_IRUGO, show_chipinfo_value, NULL);
static struct driver_attribute *mma8452q_attr_list[] = {
&driver_attr_chipinfo, /*chip information*/
........................................
};
static int mma8452q_create_attr(struct device_driver *driver)
{
for(idx = 0; idx < num; idx++)
{
if(err = driver_create_file(driver, mma8452q_attr_list[idx]))
{
GSE_ERR("driver_create_file (%s) = %d\n", mma8452q_attr_list[idx]->attr.name, err);
break;
}
}
return err;
}
之后,在终端路径/sys/bus/platform/drivers/gsensor,就可以看到属性值
(2)Device Attributes配合device_create_file
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);
};
Attributes of devices can be exported via drivers using a simple procfs-like interface,Attributes are declared using a macro called DEVICE_ATTR.实例,在电池驱动中添加的属性,也可以在ADB中查出。
static ssize_t show_Power_On_Voltage(struct device *dev,struct device_attribute *attr, char *buf)
{
int ret_value=1;
ret_value = Batt_VoltToPercent_Table[0].BattVolt;
printk("[EM] Power_On_Voltage : %d\n", ret_value);
return sprintf(buf, "%u\n", ret_value);
}
static DEVICE_ATTR(Power_On_Voltage, 0777, show_Power_On_Voltage, store_Power_On_Voltage);
static int mt6573_battery_probe(struct platform_device *dev)
{
...............................
device_create_file(&(dev->dev), &dev_attr_Power_On_Voltage);
..............................
}
注意电池设备在probe中已经用platform_device_register(&battery_device),这里的battery_device就是device_create_file的第一个dev。之后,在路径/sys/devices/platform/mt6573-battery中可以看到属性
需要注意的是,DEVICE_ATTR的第二个权限参数比较关键,改成0777或者S_IRWXUGO | S_IRWXUGO,否则上层系统或者apk不能正常读写属性。
(3)Device Attributes配合sysfs_create_file/sysfs_create_group
同样是DEVICE_ATTR,创建sysfs的函数不同,得到的结果也不同。device_create_file的形参是struct device *和struct device_attribute *,而sysfs_create_file的形参是struct kobject *和struct attribute *。所以两者的使用方法有区别:前者用在模块XXX_probe(struct platform_device *dev)中,因为需要&(dev->dev)得到要求的第一个形参;后者用在类似某I2C_probe(struct i2c_client *client,xxx)中,因为需要&client->dev.kobj得到要求的第一个形参;两者的第二个参数差别不大,struct device_attribute的一个成员就是struct attribute。下面举一个使用sysfs_create_file的实例,实现使用ADB的cat 和echo命令来显示固件版本号和命令升级,其中实例中具体调用到的函数请见http://blog.csdn.net/zhandoushi1982/article/details/7704416。在TP驱动文件中中添加DEVICE_ATTR属性
static ssize_t melfas_version_show(struct device *dev,struct device_attribute *attr, char *buf) //显示固件版本号
{
ssize_t num_read_chars = 0;
u8 fwver = 0;
if(mfs_i2c_read_single_reg(0x21,&fwver) ==false)
num_read_chars = snprintf(buf, PAGE_SIZE, "get tp fw version fail!\n"); //提示读固件号错误
else
num_read_chars = snprintf(buf, PAGE_SIZE, "%02X\n", fwver);
return num_read_chars; //返回固件版本号值
}
static DEVICE_ATTR(melfasver, S_IRUGO|S_IWUSR, melfas_version_show, melfas_version_store);
static ssize_t melfas_fwupdate_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t count)
{ //函数的形参并没有用到
int UpResult;
mt65xx_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
UpResult = ms6000_firmware_upgrade(); //可以在串口信息看到升级过程
if(UpResult== MS6000_RET_SUCCESS){
printk("MFS6000 DOWNLOAD SUCCESS \r\n");
msleep(100);
}
else
mfs6000_print_fail_result(UpResult);
i2c_client->addr = MS6000_8BIT_I2CADDR;
mt_set_gpio_mode(GPIO_I2C0_SCA_PIN, GPIO_I2C0_SCA_PIN_M_SCL);
mt_set_gpio_mode(GPIO_I2C0_SDA_PIN, GPIO_I2C0_SDA_PIN_M_SDA);
mt65xx_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
return count;
}
static DEVICE_ATTR(melfasupdate, S_IRUGO|S_IWUSR, melfas_fwupdate_show, melfas_fwupdate_store);
在static int __devinit tpd_probe(struct i2c_client *client, const struct i2c_device_id *id)中添加:
int err = 0;
err = sysfs_create_file(&client->dev.kobj, &dev_attr_melfasver.attr);
if (0 != err) {
printk("sysfs_create_file dev_attr_melfasver failed \r\n");
sysfs_remove_file(&client->dev.kobj, &dev_attr_melfasver.attr);
} else {
printk("melfas:dev_attr_melfasver- sysfs_create_file() succeeded.\n");
}
err = sysfs_create_file(&client->dev.kobj, &dev_attr_melfasupdate.attr);
if (0 != err) {
printk("sysfs_create_file dev_attr_melfasupdate failed \r\n");
sysfs_remove_file(&client->dev.kobj, &dev_attr_melfasupdate.attr);
} else {
printk("melfas:dev_attr_melfasupdate - sysfs_create_file() succeeded.\n");
}
编译烧录,最后查看属性的路径是:
用cat melfasver即可回显固件版本号,用echo 1 > melfasupdate即可实现发命令升级。需要注意的是,要留意store的操作参数,上文仅仅是提供一个操作接口并不带参数;如果需要用到*buf这个参数,注意它是字符,比如*buf为'0'或者'1'。
类似地,用sysfs_create_group创建的属性组,最终属性在下面目录
(4)上面的sysfs_create_file/sysfs_create_group建立时都是跟client关联的,如何创建一个sysfs属性,使它不跟驱动中的client关联?答案是kobject_create_and_add。
sysfs 文件系统,是用户空间与内核空间进行交互的一个媒介,内核空间与用户空间的映射关系如下表所示:
内核空间(internel) ——->用户空间(externel)
内核对象(kernel objects) ——->目录(directories)
对象属性(object attributes) ——->普通文件(regular files)
可以先使用kobject_create_and_add() 創建目錄(该函数不需形参,可见不与任何client关联),再透過sysfs_create_group(example_kobj, &attr_group) 創建目录下的属性文件组。最终创建的属性文件直接在/sys下面找,更直接简单。一个实例,假设用到appStatuswitch这个标志位来控制底层逻辑:
u8 appStatuswitch = 0;
static ssize_t appStatus_switch_cmd_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return snprintf(buf, 50 - 1,
"%d\n", appStatuswitch);
}
static ssize_t appStatus_switch_cmd_store(struct device *dev, struct device_attribute *attr, const char *ubuf, size_t n)
{
printk("appStatus_switch_cmd_store is %c \r\n",*ubuf);
if('1' == *ubuf)
appStatuswitch = 1;
else
appStatuswitch = 0;
return n;
}
static DEVICE_ATTR(appStatus, 0777, appStatus_switch_cmd_show, appStatus_switch_cmd_store);
static struct kobject *appStatus_kobj;
static struct attribute *appStatus_attributes[] = {
&dev_attr_appStatus.attr,
NULL
};
static const struct attribute_group appStatus_attr_group = {
.attrs = appStatus_attributes,
};
xxxx_main_init(void)
{
int status;
appStatus_kobj = kobject_create_and_add("appStatus", NULL);
if (!appStatus_kobj)
printk(KERN_ERR "%s: get appStatus obj fail\n",__func__);
status = sysfs_create_group(appStatus_kobj, &appStatus_attr_group);
if (status)
printk(KERN_ERR "%s: create appStatus_attr_group fail\n",__func__);
}
结果
(5)安卓JAVA层基本的读写属性的语句,假设值1或者0
假设sysfs设备属性值是"/sys/devices/platform/mt-i2c.1/i2c-1/1-0038/ftstpwakeupswitch",基本的读写函数
//write data
public void writeFile(String filePath,int i) throws IOException{
FileOutputStream out = null;
try{
File f = new File(filePath);
out = new FileOutputStream(f);
out.write(i);
}catch(IOException e){
e.printStackTrace();
}finally{
if (out != null) {
out.close();
}
}
}
//read data
public String readFile(String filePath) throws IOException{
String res="";
FileInputStream input = null;
File f = new File(filePath);
try {
input = new FileInputStream(f);
byte[] buffer = new byte[1];
input.read(buffer, 0, 1);
res = new String(buffer);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (input != null){
try {
input.close();
} catch (IOException e) {
}
}
}
return res;
}
实际使用时,readFile("/sys/devices/platform/mt-i2c.1/i2c-1/1-0038/ftstpwakeupswitch");或者writeFile("/sys/devices/platform/mt-i2c.1/i2c-1/1-0038/ftstpwakeupswitch", 49);即可(49是1的ASCII码,48是0的ASDII码。写“1”或者“0”必须写49或者48,不然下层判断1或者0是失败的。因为FileOutputStream.write写字符串转成的byte[])。
(6)利用store属性来实现带参数的操作
以下是ES8323为例,RN6752驱动是一样的方法。通常我们使用store时,直接用echo 1 > ttt访问对应的store属性。其实我们可以把1换成复杂的字串,通过解析内容来实现不同的判断,实现不同的功能。最简单的就是读取某个寄存器值,或者往某个寄存器写值。
static ssize_t es8323_store(struct device *dev,struct device_attribute *attr,const char *_buf, size_t _count)
{
const char * p=_buf;
u32 reg, val;
printk("es8323_store buf is %s \r\n",p);
if(!strncmp(_buf, "get", strlen("get")))
{
p+=strlen("get");
cur_reg=(u32)strtol(p, 16);
val=snd_soc_read(es8323_codec, cur_reg);
printk("%s(): get 0x%04x=0x%04x\n", __FUNCTION__, cur_reg, val);
}
else if(!strncmp(_buf, "put", strlen("put")))
{
p+=strlen("put");
reg=strtol(p, 16);
p=strchr(_buf, '=');
if(p)
{
++ p;
val=strtol(p, 16);
snd_soc_write(es8323_codec, reg, val);
printk("%s(): set 0x%04x=0x%04x\n", __FUNCTION__, reg, val);
}
else
printk("%s(): Bad string format input!\n", __FUNCTION__);
}
else
printk("%s(): Bad string format input!\n", __FUNCTION__);
return _count;
}
这样的话,我们通过echo get03 > /sys/class/es8323/dev/es8323来读取0x03寄存器的值,通过echo put00=03 > /sys/class/es8323/dev/es8323来往00寄存器写03。
通过UART LOG查看执行结果
[ 6774.150654] es8323_store buf is put00=03
[ 6774.150663]
[ 6774.156598] es8323_store(): set 0x0000=0x0003
[ 6780.062097] es8323_store buf is get00
[ 6780.062105]
[ 6780.062143] es8323_store(): get 0x0000=0x0003
查看寄存器的值如果觉得看LOG不直观,直接通过sprintf把字串返回到屏幕上。