自从移植驱动以来一直对应用层调用底层驱动的过程很感兴趣,每次看到Android的系统架构图时总是会自然的觉得上层APP调用底层驱动的时候使一层一层往下调用,从APP到framwork再到HAL最后到驱动,然而我总觉得这样一层一层调用,每一次指令的跳转和压栈是不是都会影响到系统的效率呢?虽然高速CPU经常需要等待慢速IO操作能忽略掉一些调用的时间花费,但有没有一种方法能让JAVA直接调用到驱动呢?事实上是有的,比如说sysfs(系统文件系统,这样翻译是不是很别扭?)。
什么是sysfs?Linux内核中有文档 "sysfs is a ram-based filesystem initially based on ramfs. It provides a means to export kernel data structures, their attributes, and the linkages between them to userspace.” --- documentation/filesystems/sysfs.txt,大致的翻译一下就是sysfs 是在ramfs基础上的一个基于内存的初始化文件系统,它提供了一个能导出内核数据结构,内核的一些属性,和在内核和用户空间建立一种链接的方法。也就是说它应该和同样是为了给上层调用而存在,那么上层要怎么来访问它呢?下面以一个摄像头的驱动为例,分析一下如何利用sysfs实现上层对驱动的调用(为了方便起见已删除和修改部分无关代码)。
先看驱动层
static __init int init_sensor(void) { int result; pr_err("Camera:%s,%s\n",__DATE__,__TIME__); camera_wq = create_singlethread_workqueue("camera_wq"); if(!camera_wq) pr_err("create camera_wq failed!\n"); result = i2c_add_driver(&sensor_driver); result = driver_create_file(&sensor_driver.driver, &driver_attr_camera); if(result<0) { return result; } printk("%s successful.\n",__FUNCTION__); return result; } static __exit void exit_sensor(void) { driver_remove_file(&sensor_driver.driver, &driver_attr_camera); i2c_del_driver(&sensor_driver); if (camera_wq) destroy_workqueue(camera_wq); } module_init(init_sensor); module_exit(exit_sensor);
这段初始化和退出代码比较简单,主要功能是系统加载驱动ko文件(卸载ko文件)的时候向i2c总线注册(卸载)一个驱动,并创建(卸载)一个工作队列,并在系统里创建(删除)一个驱动文件,而我们说的sysfs文件系统就从这个创建驱动文件开始,事实上这个文件最终就是 /sys/bus/i2c/drivers/camera/camera
展开driver_creat_file函数
int driver_create_file(struct device_driver *drv, const struct driver_attribute *attr) { int error; if (drv) error = sysfs_create_file(&drv->p->kobj, &attr->attr); else error = -EINVAL; return error; }由于这个函数调用层次比较深,就不一一展开,但是我们知道上面传入的第一个参数是一个驱动driver,并且就是刚刚注册到i2c总线的驱动结构体,第二个参数是一个属性attr,也就是driver_attr_camera,但是它却在整个驱动代码文件中找不到,但是我们仍然能看到会有这样一句话
static DRIVER_ATTR(camera, 0777, get_camera, set_camera);DRIVER_ATTR很明显是一个宏原型是
#define DRIVER_ATTR(_name, _mode, _show, _store) \ struct driver_attribute driver_attr_##_name = __ATTR(_name, _mode, _show, _store)这样就很明显了,调用那个宏的时候相当于定义一个结构体,结构体中保存了名字,访问权限,显示和存储,别问我怎么知道是显示和存储(其实就是上层读接口和上层写接口),因为我有看到下面两个函数
static ssize_t set_camera(struct device_driver *driver,const char *buf,size_t count) { char tempx[9]={0}; if( count>8 ) memcpy(tempx,buf,8); else memcpy(tempx,buf,count); pr_err("set_camera %s\n",buf); int value=tempx[1]-'0'; if('W'==tempx[0] ) { sensor_s_wb(camera_work_para.sd,value); } else if('E'==tempx[0]) { sensor_s_exp_bias(camera_work_para.sd,value); } else if('C'==tempx[0]) { sensor_s_colorfx(camera_work_para.sd,value); } else if('H'==tempx[0] ) { int i=0; for(i=0;i<ARRAY_SIZE(sensor_720P_regs_30);i++) { if( 0x3022==sensor_720P_regs_30[i].addr) { pr_err("set hfilp %s\n",buf); sensor_720P_regs_30[i].data &=0xFD; sensor_720P_regs_30[i].data |=( ('1'==tempx[1]) ? 0x02:0x00 ); break; } } //...此处省略N行相同或者类似代码... } } static ssize_t get_camera(struct device_driver *driver,char *buf) { pr_err("get_camera\n"); int count=12; return count; }这样上层就可以通过对文件 /sys/bus/i2c/drivers/camera/camera写就可以对应的执行set_camera函数,例如
try { FileOutputStream fops = new FileOutputStream( "/sys/bus/i2c/drivers/camera/camera"); fops.write("W1".getBytes()); //向驱动写入W1 fops.write("H".getBytes()); //向驱动写入H fops.flush(); fops.close(); } catch (FileNotFoundException e) { Log.d("err", "found error"); } catch (IOException e) { Log.d("err", "IO error"); }同样读取 /sys/bus/i2c/drivers/camera/camera执行get_camera
try { FileInputStream fis = new FileInputStream( "/sys/bus/i2c/drivers/camera/camera"); InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr, 4096); int num = 0; while ((num = br.read()) != 0) { //do some thing; ; } br.close(); } catch (FileNotFoundException e) { Log.d("err", "found error"); } catch (IOException e) { Log.d("err", "IO error"); }
最后补充一下,除了有DRIVER_ATTR这个宏以外还有DEVICE_ATTR,不同的宏使用不同的配套函数,但基本用法是类似的,所以不进行过多赘述
参考文档:http://blog.csdn.net/skyflying2012/article/details/11783847