http://blog.csdn.net/zhangjie201412/article/details/7703855

首先,边学习边记录点自己的代码,希望看了我写的代码觉得不怎么样的,多多提出来,让我也学习学习,我一定会虚心接受大家的指导。

这里我们是来学习android 驱动和android系统框架的,这里我只针对于整个android设备驱动的一个流程,从上到下的调用,而且在这里我们去使用android源码环境,原因是我使用的电脑比较破,编译android会挂,而且android BSP太大了,git下来很麻烦,所以这里我只是选用了ubuntu的环境来学习android 底层和系统层的开发,那么有人会问,没有源代码,如何去学习呢,又如何来演示出来呢?不急,慢慢道来,首先还是来谈一下android的底层和系统层,下图供参考,基本每个设计android的工程师都知道这张图


android使用的是Linux 内核,虽然说稍微改了点东西,增加了些移动嵌入式设备的特性,比如说early suspend、进程间通信(bind)、特有的log机制(logcat)等,但是Linux主流的一些东西都没有改变,所以,这里我们还是使用Linux下得驱动作为底层来学习,具体的android的特性,之后的博客中会做深入。

Linux内核驱动之上应该是android的HAL层,也就是传说中得硬件抽象层,我把这玩意一直理解成是Linux 应用层,虽然,有的厂家的代码写的比较庞大,比较好得还是使用C++的类来封装,典型的设备是传感器模块,一般都会有一个HAL层来衔接,这里我们使用Linux 的应用层编程 c/c++来实现调用驱动来收发数据等操作。

在上面应该是JNI层啦,在我们的学习中,JNI的基础知识在我的另外一个博客专栏中有提到,在这里我们也是使用JNI来实现接口。

然后是framework,我们还是使用java代码封装JNI,然后写一些java的测试代码,当然了,没有android的UI,对于java的UI编程,我也是没玩过,对于java我只是一个初学者,写测试代码还凑合,一般都是一边google一边写的 ^0^

接下来就开始行动吧,首先是我们的驱动部分,这些代码都是我自己写的,所以难免会有很多写的不对的地方,希望大家觉得我哪边写的不好的多提些意见,毕竟我也只是一个小菜鸟。

这个驱动完成的主要任务是从内核空间向用户空间发送一个坐标信息(模拟鼠标的动作),当然了,我们的驱动是虚拟的,当然不会主动的向用户空间poll数据,所以,我们要先自己往文件系统中写数据,然后驱动会通过input 子系统向user space发送数据。

下面是我驱动的一个结构图,一般我写个驱动都会先考虑很多,然后再着手去做,三思而后行嘛,驱动的整体架构如果出了问题,到最后是很难再修正的,只有重新写。


可以看到,我们的思路很清晰,初始化的时候开一个线程,注意这个线程是一个死循环,但是在循环当中有一个试图获得信号量的动作,如果得不到,就会进入休眠,如下是thread的代码:

[cpp] view plain copy print ?
  1. <span style="font-size: 16px;">static int work_thread(void *data)
  2. {
  3. int x, y, ret;
  4. struct virtual_dev *pvdev = (struct virtual_dev *)data;
  5. struct semaphore sema = pvdev->sem;
  6. // poll the data into user space
  7. printk(KERN_INFO "work thread running!!");
  8. while(pvdev->running) {
  9. do{
  10. ret = down_interruptible(&sema);
  11. } while(ret == -EINTR);
  12. //printk("done!\n");
  13. //poll the x and y data into user space
  14. x = pvdev->x;
  15. y = pvdev->y;
  16. input_report_abs(pvdev->input, ABS_X, x);
  17. input_report_abs(pvdev->input, ABS_Y, y);
  18. input_sync(pvdev->input);
  19. printk("position: %d | %d\n", x, y);
  20. }
  21. return 0;
  22. }</span>

唤醒这个线程的地方,就是调用up的地方:

[cpp] view plain copy print ?
  1. <span style="font-size: 16px;">static ssize_t write_position(struct device *dev,
  2. struct device_attribute *attr, const char *buffer, ssize_t count)
  3. {
  4. int x,y;
  5. sscanf(buffer, "%d%d", &x, &y);
  6. vdev->x = x;
  7. vdev->y = y;
  8. //do something with x and y ===> poll the data;
  9. up(&vdev->sem);
  10. return count;
  11. }</span>

可以看到,这个write_position是被注册当position文件被执行写操作的时候执行的。

[cpp] view plain copy print ?
  1. <span style="font-size: 16px;">/* attach the sysfs */
  2. DEVICE_ATTR(position, 0666, read_position, write_position);
  3. DEVICE_ATTR(color, 0666, read_color, write_color);
  4. DEVICE_ATTR(bcolor, 0666, read_bcolor, write_bcolor);</span>
相信看了结构图就知道我们的代码是如何写的吧,这个驱动比较简单,看下完整的代码

[cpp] view plain copy print ?
  1. <span style="font-size: 16px;">/*
  2. * This driver is named virtual touch, which can send some message into user space from kernel space,
  3. * for this driver just for study Linux device driver...
  4. * Jay Zhang
  5. * mail: zhangjie201412@live.com
  6. */
  7. #include <linux/module.h>
  8. #include <linux/err.h>
  9. #include <linux/input.h>
  10. #include <linux/hwmon.h>
  11. #include <linux/kthread.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/semaphore.h>
  14. #include <linux/slab.h>
  15. struct virtual_dev {
  16. struct platform_device *vt_dev;
  17. struct task_struct *run_thread;
  18. struct input_dev *input;
  19. struct semaphore sem;
  20. int x,y; //point position
  21. int color; //line color
  22. int bcolor; //background color
  23. int size; //line size
  24. int running;
  25. };
  26. struct virtual_dev *vdev = NULL;
  27. /* position read/write function */
  28. static ssize_t read_position (struct device *dev,
  29. struct device_attribute *attr, char *buf)
  30. {
  31. return sprintf(buf, "(%d, %d)\n", vdev->x, vdev->y);
  32. }
  33. static ssize_t write_position(struct device *dev,
  34. struct device_attribute *attr, const char *buffer, ssize_t count)
  35. {
  36. int x,y;
  37. sscanf(buffer, "%d%d", &x, &y);
  38. vdev->x = x;
  39. vdev->y = y;
  40. //do something with x and y ===> poll the data;
  41. up(&vdev->sem);
  42. return count;
  43. }
  44. /* color read/write function */
  45. static ssize_t read_color(struct device *dev,
  46. struct device_attribute *attr, char *buf)
  47. {
  48. return sprintf(buf, "line color is %d\n", vdev->color);
  49. }
  50. static ssize_t write_color(struct device *dev,
  51. struct device_attribute *attr, const char *buffer, ssize_t count)
  52. {
  53. int color;
  54. sscanf(buffer, "%d", &color);
  55. vdev->color = color;
  56. return count;
  57. }
  58. /* bcolor read/write function */
  59. static ssize_t read_bcolor(struct device *dev,
  60. struct device_attribute *attr, char *buf)
  61. {
  62. return sprintf(buf, "background color is %d\n", vdev->bcolor);
  63. }
  64. static ssize_t write_bcolor(struct device *dev,
  65. struct device_attribute *attr, const char *buffer, ssize_t count)
  66. {
  67. int bcolor;
  68. sscanf(buffer, "%d", &bcolor);
  69. vdev->bcolor = bcolor;
  70. return count;
  71. }
  72. /* attach the sysfs */
  73. DEVICE_ATTR(position, 0666, read_position, write_position);
  74. DEVICE_ATTR(color, 0666, read_color, write_color);
  75. DEVICE_ATTR(bcolor, 0666, read_bcolor, write_bcolor);
  76. //DEVICE_ATTR(size, 0666, read_size, write_size);
  77. /* attribute description */
  78. static struct attribute *vdev_attrs[] = {
  79. &dev_attr_position.attr,
  80. &dev_attr_color.attr,
  81. &dev_attr_bcolor.attr,
  82. // &dev_attr,size,
  83. NULL
  84. };
  85. /* attribute group */
  86. static struct attribute_group vdev_attr_group = {
  87. .attrs = vdev_attrs,
  88. };
  89. static int work_thread(void *data)
  90. {
  91. int x, y, ret;
  92. struct virtual_dev *pvdev = (struct virtual_dev *)data;
  93. struct semaphore sema = pvdev->sem;
  94. // poll the data into user space
  95. printk(KERN_INFO "work thread running!!");
  96. while(pvdev->running) {
  97. do{
  98. ret = down_interruptible(&sema);
  99. } while(ret == -EINTR);
  100. //printk("done!\n");
  101. //poll the x and y data into user space
  102. x = pvdev->x;
  103. y = pvdev->y;
  104. input_report_abs(pvdev->input, ABS_X, x);
  105. input_report_abs(pvdev->input, ABS_Y, y);
  106. input_sync(pvdev->input);
  107. printk("position: %d | %d\n", x, y);
  108. }
  109. return 0;
  110. }
  111. /*
  112. static int virtual_probe(struct platform_device *pdev)
  113. {
  114. int ret;
  115. //malloc for vdev
  116. vdev = kzalloc(sizeof(struct virtual_dev), GFP_KERNEL);
  117. if(!vdev) {
  118. vdev = NULL;
  119. printk(KERN_INFO "kzalloc for vdev failed.\n");
  120. ret = -ENOMEM;
  121. goto kzalloc_failed;
  122. }
  123. //initialized for semaphore
  124. sema_init(&(vdev->sem), 0);
  125. //initialized for input subsystem
  126. vdev->input = input_allocate_device();
  127. if(!(vdev->input)) {
  128. vdev->input = NULL;
  129. printk(KERN_INFO "allocate input device failed.\n");
  130. ret = -ENOMEM;
  131. goto allocate_input_failed;
  132. }
  133. set_bit(EV_ABS, vdev->input->evbit);
  134. //for x
  135. input_set_abs_params(vdev->input, ABS_X, -1024, 1024, 0, 0);
  136. //for y
  137. input_set_abs_params(vdev->input, ABS_Y, -1024, 1024, 0, 0);
  138. //set name
  139. vdev->input->name = "virtual-touch";
  140. ret = input_register_device(vdev->input);
  141. if(ret) {
  142. printk(KERN_ERR "%s: Unable to register input device: %s\n",__func__, vdev->input->name);
  143. goto input_register_failed;
  144. //return ret;
  145. }
  146. //initialized for sysfs of our virtual driver
  147. vdev->vt_dev = pdev;
  148. sysfs_create_group(&vdev->vt_dev->dev.kobj, &vdev_attr_group);
  149. //run thread to poll data
  150. vdev->run_thread = kthread_run(work_thread, vdev, "vt_thread");
  151. vdev->running = 1;
  152. platform_set_drvdata(pdev, vdev);
  153. printk(KERN_INFO "virtual touch device probe successful.\n");
  154. return 0;
  155. input_register_failed:
  156. input_free_device(vdev->input);
  157. allocate_input_failed:
  158. kfree(vdev);
  159. kzalloc_failed:
  160. return ret;
  161. }
  162. static struct platform_driver virtual_driver = {
  163. .probe = virtual_probe,
  164. .driver = {
  165. .name = "virtual touch",
  166. },
  167. };
  168. */
  169. static int virtual_init(void)
  170. {
  171. int ret;
  172. //malloc for vdev
  173. vdev = kzalloc(sizeof(struct virtual_dev), GFP_KERNEL);
  174. if(!vdev) {
  175. vdev = NULL;
  176. printk(KERN_INFO "kzalloc for vdev failed.\n");
  177. ret = -ENOMEM;
  178. goto kzalloc_failed;
  179. }
  180. //register a platform device
  181. vdev->vt_dev = platform_device_register_simple("virtual-touch", -1, NULL, 0);
  182. if(IS_ERR(vdev->vt_dev)) {
  183. PTR_ERR(vdev->vt_dev);
  184. printk("register device failed.\n");
  185. }
  186. //initialized for semaphore
  187. sema_init(&(vdev->sem), 0);
  188. //initialized for input subsystem
  189. vdev->input = input_allocate_device();
  190. if(!(vdev->input)) {
  191. vdev->input = NULL;
  192. printk(KERN_INFO "allocate input device failed.\n");
  193. ret = -ENOMEM;
  194. goto allocate_input_failed;
  195. }
  196. set_bit(EV_ABS, vdev->input->evbit);
  197. //for x
  198. input_set_abs_params(vdev->input, ABS_X, -1024, 1024, 0, 0);
  199. //for y
  200. input_set_abs_params(vdev->input, ABS_Y, -1024, 1024, 0, 0);
  201. //set name
  202. vdev->input->name = "virtual-touch";
  203. ret = input_register_device(vdev->input);
  204. if(ret) {
  205. printk(KERN_ERR "%s: Unable to register input device: %s\n",__func__, vdev->input->name);
  206. goto input_register_failed;
  207. //return ret;
  208. }
  209. //initialized for sysfs of our virtual driver
  210. // vdev->vt_dev = pdev;
  211. sysfs_create_group(&vdev->vt_dev->dev.kobj, &vdev_attr_group);
  212. //run thread to poll data
  213. vdev->run_thread = kthread_run(work_thread, vdev, "vt_thread");
  214. vdev->running = 1;
  215. // platform_set_drvdata(pdev, vdev);
  216. printk(KERN_INFO "virtual touch device probe successful.\n");
  217. return 0;
  218. input_register_failed:
  219. input_free_device(vdev->input);
  220. allocate_input_failed:
  221. kfree(vdev);
  222. kzalloc_failed:
  223. return ret;
  224. }
  225. static void virtual_exit(void)
  226. {
  227. vdev->running = 0;
  228. sysfs_remove_group(&(vdev->vt_dev->dev.kobj), &vdev_attr_group);
  229. input_unregister_device(vdev->input);
  230. platform_device_unregister(vdev->vt_dev);
  231. }
  232. module_init(virtual_init);
  233. module_exit(virtual_exit);
  234. MODULE_LICENSE("GPL");</span>

下面是Makefile

[cpp] view plain copy print ?
  1. <span style="font-size: 16px;">obj-m := virtualtouch.o
  2. KERNEL_DIR := /lib/modules/$(shell uname -r)/build
  3. PWD := $(shell pwd)
  4. all:
  5. make -C $(KERNEL_DIR) SUBDIRS=$(PWD) modules
  6. clean:
  7. rm *.o *.ko *.mod.c *.symvers modules.order</span>

然后我们来编译模块再加载

# make

# insmod virtualtouch.ko

然后看下我们生成的文件系统

root@jay-Vbox:/sys/devices/platform/virtual-touch# pwd
/sys/devices/platform/virtual-touch
root@jay-Vbox:/sys/devices/platform/virtual-touch# tree
.
├── bcolor
├── color
├── modalias
├── position
├── power
│ ├── async
│ ├── autosuspend_delay_ms
│ ├── control
│ ├── runtime_active_kids
│ ├── runtime_active_time
│ ├── runtime_enabled
│ ├── runtime_status
│ ├── runtime_suspended_time
│ └── runtime_usage
├── subsystem -> ../../../bus/platform
└── uevent


2 directories, 14 files

还有我们的input subsystem的文件系统,使用dmesg查看

[ 413.650710] input: virtual-touch as /devices/virtual/input/input6
[ 413.662695] virtual touch device probe successful.
[ 413.663616] work thread running!!

按照log可以知道,我们生成的event6

root@jay-Vbox:/dev/input# pwd
/dev/input
root@jay-Vbox:/dev/input# ls -l event6
crw-r----- 1 root root 13, 70 Jul 2 22:07 event6

OK,了解了我们生成的文件系统之后,我们写一个测试程序来测试下,嘿嘿 ^0^

贴代码如下:

[cpp] view plain copy print ?
  1. <span style="font-size: 16px;">include <stdlib.h>
  2. #include <fcntl.h>
  3. #include <linux/input.h>
  4. int main(void)
  5. {
  6. struct input_event ev_temp;
  7. int fd = open("/dev/input/event6", O_RDWR);
  8. if(fd < 0) {
  9. printf("open device failed.\n");
  10. return 0;
  11. }
  12. printf("open done!\n");
  13. //return 0;
  14. int count, x, y;
  15. while(1) {
  16. printf("read!\n");
  17. count = read(fd, &ev_temp, sizeof(struct input_event)) ;
  18. if(EV_ABS == ev_temp.type) {
  19. if(ev_temp.code == ABS_X)
  20. x = ev_temp.value;
  21. else if(ev_temp.code == ABS_Y)
  22. y = ev_temp.value;
  23. printf("position : (%d, %d)\n", x, y);
  24. } else if(EV_SYN == ev_temp.type) {
  25. printf("sync!\n");
  26. }
  27. }
  28. return 0;
  29. }</span>

这个。。。这个小测试程序就不多说了,open ----> read ----> show

测试程序的编写一般是参考驱动中report出去的到底是什么类型的数据。

# gcc main -o virtual-touch.c

root@jay-Vbox:/home/jay/workspace/virtual/main# ./main
open done!

read!


这个时候被阻塞在read函数这边,因为没有数据被读出,所以会阻塞在那边得不到返回,当然了,我们也可以使用非阻塞的去读(open的时候标志设置成 | NOBLOCK),接着接着,咱们来出发input 设备

root@jay-Vbox:/home/jay/workspace/virtual/main# echo 1 2 > /sys/devices/platform/virtual-touch/position
root@jay-Vbox:/home/jay/workspace/virtual/main# position : (1, 32767)
read!
position : (1, 2)
read!
sync!
read!

看到没,看到没,数据已经打印出来了,具体的流程大家可以自己分析驱动代码,最主要的还是那张驱动架构图。

好了,现在,我们来写一个jni来封装下read函数

。。。等等,咱们还是先看我们的java代码,一般jni是提供接口的,但是接口到底是怎么样的还是取决于java中的需要。

这里我写了一个class来封装open,read等函数。

GetPosition.java

[cpp] view plain copy print ?
  1. <span style="font-size: 16px;">class GetPosition
  2. {
  3. private int x;
  4. private int y;
  5. private native int readEvent();
  6. private native void openEvent(String path);
  7. private native void closeEvent();
  8. GetPosition(String path) {
  9. x = 0;
  10. y = 0;
  11. openEvent(path);
  12. }
  13. public void close()
  14. {
  15. closeEvent();
  16. }
  17. public void setXY(int x, int y) {
  18. this.x = x;
  19. this.y = y;
  20. }
  21. public int getX()
  22. {
  23. return this.x;
  24. }
  25. public int getY()
  26. {
  27. return this.y;
  28. }
  29. public void read()
  30. {
  31. int retVal = readEvent();
  32. setXY(retVal/256, retVal%256);
  33. }
  34. static {
  35. System.loadLibrary("virtouch");
  36. }
  37. }</span>

我承认,我写的java代码很烂,好吧,咱不多看了,只要看这里的native函数,这里封装了open、read、close函数,

好,然后咱们使用javah来生成jni的头文件。

# javah -jni GetPosition

[cpp] view plain copy print ?
  1. <span style="font-size: 16px;">/* DO NOT EDIT THIS FILE - it is machine generated */
  2. #include <jni.h>
  3. /* Header for class GetPosition */
  4. #ifndef _Included_GetPosition
  5. #define _Included_GetPosition
  6. #ifdef __cplusplus
  7. extern "C" {
  8. #endif
  9. /*
  10. * Class: GetPosition
  11. * Method: readEvent
  12. * Signature: ()I
  13. */
  14. JNIEXPORT jint JNICALL Java_GetPosition_readEvent
  15. (JNIEnv *, jobject);
  16. /*
  17. * Class: GetPosition
  18. * Method: openEvent
  19. * Signature: (Ljava/lang/String;)V
  20. */
  21. JNIEXPORT void JNICALL Java_GetPosition_openEvent
  22. (JNIEnv *, jobject, jstring);
  23. /*
  24. * Class: GetPosition
  25. * Method: closeEvent
  26. * Signature: ()V
  27. */
  28. JNIEXPORT void JNICALL Java_GetPosition_closeEvent
  29. (JNIEnv *, jobject);
  30. #ifdef __cplusplus
  31. }
  32. #endif</span>

然后我们按照这个头文件来完成我们的jni

virtual-touch.c

[cpp] view plain copy print ?
  1. <span style="font-size: 16px;">#include <stdio.h>
  2. #include "GetPosition.h"
  3. #include <stdlib.h>
  4. #include <linux/input.h>
  5. #include <fcntl.h>
  6. int fd;
  7. JNIEXPORT jint JNICALL
  8. Java_GetPosition_readEvent(JNIEnv *env, jobject obj)
  9. {
  10. static int x, y;
  11. int count;
  12. struct input_event ev_temp;
  13. count = read(fd, &ev_temp, sizeof(struct input_event));
  14. //AGAIN:
  15. if(EV_ABS == ev_temp.type) {
  16. if(ev_temp.code == ABS_X) {
  17. x = ev_temp.value;
  18. } else if(ev_temp.code == ABS_Y) {
  19. y = ev_temp.value;
  20. }
  21. printf("x: %d, y: %d\n", x, y);
  22. }
  23. //else if(EV_SYN == ev_temp.type) {
  24. return (x*256 + y);
  25. //}
  26. //goto AGAIN;
  27. }
  28. JNIEXPORT void JNICALL
  29. Java_GetPosition_openEvent(JNIEnv *env, jobject obj, jstring prompt)
  30. {
  31. char path[64];
  32. const jbyte *str;
  33. str = (*env)->GetStringUTFChars(env, prompt, NULL);
  34. if(str == NULL) {
  35. printf("error: str is NULL!\n");
  36. return ;
  37. }
  38. sprintf(path, "%s", str);
  39. fd = open(path, O_RDWR);
  40. if(fd < 0) {
  41. printf("open %s failed...\n", path);
  42. }
  43. (*env)->ReleaseStringUTFChars(env, prompt, str);
  44. }
  45. JNIEXPORT void JNICALL
  46. Java_GetPosition_closeEvent(JNIEnv *env, jobject obj)
  47. {
  48. close(fd);
  49. }</span>

jni的基础知识,大家可以参考我的一个博客专栏,嘻嘻~~~打广告了

http://blog.csdn.net/column/details/jnijni.html

用命令来生成动态库

这里给大家提供一个shell脚本来使用

[cpp] view plain copy print ?
  1. <span style="font-size: 16px;">#/bin/bash
  2. if [ $# != 2 ]
  3. then
  4. echo "input argument error!"
  5. else
  6. cc -I /usr/lib/jvm/java-6-sun/include/linux -I /usr/lib/jvm/java-6-sun/include/ -fPIC -shared -o $1lib$2.so $2.c
  7. fi
  8. </span>

好了,最后是我们的java测试程序

[cpp] view plain copy print ?
  1. <span style="font-size: 16px;">class Test {
  2. // private native int openEvent(String path);
  3. // private native int closeEvent();
  4. // private int getPosition();
  5. public static void main(String[] args)
  6. {
  7. GetPosition getPosition = new GetPosition("/dev/input/event5");
  8. //openEvent("/dev/input/event5");
  9. while(true) {
  10. getPosition.read();
  11. System.out.println("[ " + getPosition.getX() + " , " + getPosition.getY() + "]");
  12. }
  13. //getPosition.close();
  14. //System.out.println()
  15. }
  16. /*
  17. static {
  18. System.loadLibrary("virtouch");
  19. }
  20. */
  21. }</span>

继续测试下

可以得到跟之前一样的结果。

OK,现在我们可以再Linux下学习 Linux 驱动还有jni了。




参考:

在Linux下如何使用GCC编译程序、简单生成静态库及动态库。

http://blog.csdn.net/clozxy/article/details/5716227

Linux下JNI实现

http://www.cnblogs.com/xiaoxiaoboke/archive/2012/02/13/2349775.html

更多相关文章

  1. Android性能监测小工具——安测试
  2. Android中一张图片加载后所占用内存大小的获取与测试
  3. android studio配置系列 - 收藏集 - 掘金
  4. 移动端开发新趋势Flutter
  5. android eclipse xml不自动代码提示
  6. ANDROID自动化测试工具:ROBOTIUM
  7. android音频编辑之音频合成
  8. android 自定义RadioButton样式
  9. Android基础开源应用项目

随机推荐

  1. Android(安卓)studio 遇到Android(安卓)S
  2. inputtype
  3. Android(安卓)单选按钮Radio的使用
  4. Android中Dialog对话框
  5. scrollView嵌套recyclerView 显示不全
  6. Android总结篇系列:Android(安卓)权限
  7. Android(安卓)WebView
  8. EditText设置键盘操作
  9. android MotionEvent中getX()和getRawX()
  10. android ImageView scaleType属性