上一篇博客描述了一个简单的串口应用程序和驱动程序,了解了应用程序访问串口的基本操作,如
打开串口,设置串口,写串口,读串口,关闭串口等。和Linux串口驱动的基本框架。这里将了解Android
下的串口系统框架,仍然使用上一篇博客中的tiny_serial.c作为本例的驱动,本例实现的功能不变,无
论应用程序写任何数据到串口,都能从该串口中读回。
关于Android系统服务基本框架,可以参考http://www.cnblogs.com/hackfun/p/7612617.html博客,
Android串口(serial port)服务框架与该例子十分类似,这里作简单描述。当然也有些不同的地方,后面会分析。
(A) 串口服务的基本框架
1. 注册驱动
2. 注册hal
3. 注册JNI
4. 注册和添加SerialService
5. 串口管理SerialManager
(B) 打开串口
(C) 设置权限
(D) 测试串口
(A) 串口服务的基本框架
1. 注册驱动
通过加载kernel/driver/tty/serial/tiny_serial.c驱动,生成/dev/ttytiny0节点,应用通过访问
该节点,实现的对串口的open、read、write、close等操作。
2. 注册hal
在Android串口服务中,省略了hal层,即通过JNI直接访问驱动。
3. 注册JNI
通过frameworks/base/services/core/jni/com_android_server_SerialService.cpp和
frameworks/base/core/jni/android_hardware_SerialPort.cpp两个JNI文件对驱动访问,
为JAVA提供底层驱动访问的接口,如:
privatenative ParcelFileDescriptor native_open(String path);private native void native_open(FileDescriptor pfd, intspeed) throws IOException;private native voidnative_close();private native int native_read_array(byte[] buffer, intlength) throws IOException;private native int native_read_direct(ByteBuffer buffer, intlength) throws IOException;private native void native_write_array(byte[] buffer, intlength) throws IOException;private native void native_write_direct(ByteBuffer buffer, intlength) throws IOException;private native void native_send_break();
4. 注册和添加SerialService
frameworks/base/services/core/java/com/android/server/SerialService.java的SerialService类
中提供了服务端通过JNI放问驱动的接口,如:
publicString[] getSerialPorts()public ParcelFileDescriptor openSerialPort(String path)
通过向frameworks/base/services//java/com/android/server/SystemServer.java的服务管理器ServiceManager
添加服务:
serial = newSerialService(context);
ServiceManager.addService(Context.SERIAL_SERVICE, serial);
并且,通过frameworks/base/core/java/android/app/SystemServiceRegistry.java
1 registerService(Context.SERIAL_SERVICE, SerialManager.class,2 new CachedServiceFetcher() {3 @Override4 publicSerialManager createService(ContextImpl ctx) {5 IBinder b =ServiceManager.getService(Context.SERIAL_SERVICE);6 return newSerialManager(ctx, ISerialManager.Stub.asInterface(b));7 }});
可以访问串口服务SerialService,客户端通过获得SerialService,就能远程调用getSerialPorts()和
openSerialPort(String path)接口
5. 串口管理SerialManager
frameworks/base/core/java/android/hardware/SerialManager.java中SerialManager对串口操作进一步管理,
客户端只要实例化一个SerialManager对象,使用该对象的方法访问串口。不过,该对象只对
openSerialPort(String name, int speed)管理。
(B) 打开串口
通过以上简单分析Android串口服务框架之后,这里进一步分析串口的打开open操作流程。
在http://www.cnblogs.com/hackfun/p/7612617.html
博客中,只有服务端调用JNI访问底层驱动,而在串口服务中,客户端也调用JNI直接访问底层驱动。服务端
只负责对串口的open操作,在frameworks/base/services/core/jni/com_android_server_SerialService.cpp中
1 static jobject android_server_SerialService_open(JNIEnv *env, jobject /*thiz*/, jstring path)2 {3 const char *pathStr = env->GetStringUTFChars(path, NULL);4 //打开/dev/tty*节点
5 int fd = open(pathStr, O_RDWR |O_NOCTTY);6 if (fd < 0) {7 ALOGE("could not open %s", pathStr);8 env->ReleaseStringUTFChars(path, pathStr);9 returnNULL;10 }11 env->ReleaseStringUTFChars(path, pathStr);12
13 jobject fileDescriptor =jniCreateFileDescriptor(env, fd);14 if (fileDescriptor ==NULL) {15 returnNULL;16 }17 //返回文件描述符,用于跨进程访问文件
18 return env->NewObject(gParcelFileDescriptorOffsets.mClass,19 gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);20 }
而read、write、close等相关操作在frameworks/base/core/jni/android_hardware_SerialPort.cpp中,
其中android_hardware_SerialPort_open只对串口进行相关设置,如波特,数据位等。由此看出
frameworks/base/services/core/jni/com_android_server_SerialService.cpp和
frameworks/base/core/jni/android_hardware_SerialPort.cpp可能处于两个不同的线程中,这两个不
同的线程对同一个文件进行访问,需要对文件描述符进行转换。
在frameworks/base/core/jni/android_hardware_SerialPort.cpp中
1 static void android_hardware_SerialPort_open(JNIEnv *env, jobject thiz, jobject fileDescriptor, jint speed)2 {3 ......4 int fd =jniGetFDFromFileDescriptor(env, fileDescriptor);5 //duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy
6 fd =dup(fd);7 if (fd < 0) {8 jniThrowException(env, "java/io/IOException", "Could not open serial port");9 return;10 }11 ......12 }
(C) 设置权限
device/sprd/scx35l/common/rootdir/root/ueventd.sc8830.rc
1 ......2 /dev/ttytiny0 0660system system3 ......
device/sprd/scx35l/common/sepolicy/file_contexts
1 ......2 /dev/ttytiny0 u:object_r:serial_device:s03 ......
device/sprd/scx35l/common/sepolicy/system_app.te
......
allow system_app serial_device:chr_file { open read write ioctl};
......
(D) 测试串口
这里引用Android6.0源码目录下的一个串口测试app,还要添加一些权限等设置才能正常使用。
串口测试APP源码:frameworks/base/tests/SerialChat
1. 设置权限:
frameworks/base/tests/SerialChat/Android.mk
1 ......2 LOCAL_CERTIFICATE :=platform3 ......
frameworks/base/tests/SerialChat/AndroidManifest.xml
1 ......2
3 package="com.android.serialchat"
4 android:sharedUserId="android.uid.system">
5 ......
2. 添加串口节点名称
应用程序根据这个名称来打开对应的串口。
frameworks/base/core/res/res/values/config.xml
1 ......2
3 "/dev/ttytiny0"
4
5 ......
用mmm frameworks/base/tests/SerialChat -B 编译出的SerialChat.apk push到机器之后,就可以
进行测试了。
a.点击打开SerialChat.apk:
b.输入要发送的内容
c.点击确定(打钩的位置)发送,并且接收显示在上方。