1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "SerialPortJNI"
18 
19 #include "utils/Log.h"
20 
21 #include "jni.h"
22 #include "JNIHelp.h"
23 #include "core_jni_helpers.h"
24 
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <termios.h>
30 
31 using namespace android;
32 
33 static jfieldID field_context;
34 
35 static void
android_hardware_SerialPort_open(JNIEnv * env,jobject thiz,jobject fileDescriptor,jint speed)36 android_hardware_SerialPort_open(JNIEnv *env, jobject thiz, jobject fileDescriptor, jint speed)
37 {
38     switch (speed) {
39         case 50:
40             speed = B50;
41             break;
42         case 75:
43             speed = B75;
44             break;
45         case 110:
46             speed = B110;
47             break;
48         case 134:
49             speed = B134;
50             break;
51         case 150:
52             speed = B150;
53             break;
54         case 200:
55             speed = B200;
56             break;
57         case 300:
58             speed = B300;
59             break;
60         case 600:
61             speed = B600;
62             break;
63         case 1200:
64             speed = B1200;
65             break;
66         case 1800:
67             speed = B1800;
68             break;
69         case 2400:
70             speed = B2400;
71             break;
72         case 4800:
73             speed = B4800;
74             break;
75         case 9600:
76             speed = B9600;
77             break;
78         case 19200:
79             speed = B19200;
80             break;
81         case 38400:
82             speed = B38400;
83             break;
84         case 57600:
85             speed = B57600;
86             break;
87         case 115200:
88             speed = B115200;
89             break;
90         case 230400:
91             speed = B230400;
92             break;
93         case 460800:
94             speed = B460800;
95             break;
96         case 500000:
97             speed = B500000;
98             break;
99         case 576000:
100             speed = B576000;
101             break;
102         case 921600:
103             speed = B921600;
104             break;
105         case 1000000:
106             speed = B1000000;
107             break;
108         case 1152000:
109             speed = B1152000;
110             break;
111         case 1500000:
112             speed = B1500000;
113             break;
114         case 2000000:
115             speed = B2000000;
116             break;
117         case 2500000:
118             speed = B2500000;
119             break;
120         case 3000000:
121             speed = B3000000;
122             break;
123         case 3500000:
124             speed = B3500000;
125             break;
126         case 4000000:
127             speed = B4000000;
128             break;
129         default:
130             jniThrowException(env, "java/lang/IllegalArgumentException",
131                               "Unsupported serial port speed");
132             return;
133     }
134 
135     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
136     // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy
137     fd = dup(fd);
138     if (fd < 0) {
139         jniThrowException(env, "java/io/IOException", "Could not open serial port");
140         return;
141     }
142     env->SetIntField(thiz, field_context, fd);
143 
144     struct termios tio;
145     if (tcgetattr(fd, &tio))
146         memset(&tio, 0, sizeof(tio));
147 
148     tio.c_cflag =  speed | CS8 | CLOCAL | CREAD;
149     // Disable output processing, including messing with end-of-line characters.
150     tio.c_oflag &= ~OPOST;
151     tio.c_iflag = IGNPAR;
152     tio.c_lflag = 0; /* turn of CANON, ECHO*, etc */
153     /* no timeout but request at least one character per read */
154     tio.c_cc[VTIME] = 0;
155     tio.c_cc[VMIN] = 1;
156     tcsetattr(fd, TCSANOW, &tio);
157     tcflush(fd, TCIFLUSH);
158 }
159 
160 static void
android_hardware_SerialPort_close(JNIEnv * env,jobject thiz)161 android_hardware_SerialPort_close(JNIEnv *env, jobject thiz)
162 {
163     int fd = env->GetIntField(thiz, field_context);
164     close(fd);
165     env->SetIntField(thiz, field_context, -1);
166 }
167 
168 static jint
android_hardware_SerialPort_read_array(JNIEnv * env,jobject thiz,jbyteArray buffer,jint length)169 android_hardware_SerialPort_read_array(JNIEnv *env, jobject thiz, jbyteArray buffer, jint length)
170 {
171     int fd = env->GetIntField(thiz, field_context);
172     jbyte* buf = (jbyte *)malloc(length);
173     if (!buf) {
174         jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
175         return -1;
176     }
177 
178     int ret = read(fd, buf, length);
179     if (ret > 0) {
180         // copy data from native buffer to Java buffer
181         env->SetByteArrayRegion(buffer, 0, ret, buf);
182     }
183 
184     free(buf);
185     if (ret < 0)
186         jniThrowException(env, "java/io/IOException", NULL);
187     return ret;
188 }
189 
190 static jint
android_hardware_SerialPort_read_direct(JNIEnv * env,jobject thiz,jobject buffer,jint length)191 android_hardware_SerialPort_read_direct(JNIEnv *env, jobject thiz, jobject buffer, jint length)
192 {
193     int fd = env->GetIntField(thiz, field_context);
194 
195     jbyte* buf = (jbyte *)env->GetDirectBufferAddress(buffer);
196     if (!buf) {
197         jniThrowException(env, "java/lang/IllegalArgumentException", "ByteBuffer not direct");
198         return -1;
199     }
200 
201     int ret = read(fd, buf, length);
202     if (ret < 0)
203         jniThrowException(env, "java/io/IOException", NULL);
204     return ret;
205 }
206 
207 static void
android_hardware_SerialPort_write_array(JNIEnv * env,jobject thiz,jbyteArray buffer,jint length)208 android_hardware_SerialPort_write_array(JNIEnv *env, jobject thiz, jbyteArray buffer, jint length)
209 {
210     int fd = env->GetIntField(thiz, field_context);
211     jbyte* buf = (jbyte *)malloc(length);
212     if (!buf) {
213         jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
214         return;
215     }
216     env->GetByteArrayRegion(buffer, 0, length, buf);
217 
218     jint ret = write(fd, buf, length);
219     free(buf);
220     if (ret < 0)
221         jniThrowException(env, "java/io/IOException", NULL);
222 }
223 
224 static void
android_hardware_SerialPort_write_direct(JNIEnv * env,jobject thiz,jobject buffer,jint length)225 android_hardware_SerialPort_write_direct(JNIEnv *env, jobject thiz, jobject buffer, jint length)
226 {
227     int fd = env->GetIntField(thiz, field_context);
228 
229     jbyte* buf = (jbyte *)env->GetDirectBufferAddress(buffer);
230     if (!buf) {
231         jniThrowException(env, "java/lang/IllegalArgumentException", "ByteBuffer not direct");
232         return;
233     }
234     int ret = write(fd, buf, length);
235     if (ret < 0)
236         jniThrowException(env, "java/io/IOException", NULL);
237 }
238 
239 static void
android_hardware_SerialPort_send_break(JNIEnv * env,jobject thiz)240 android_hardware_SerialPort_send_break(JNIEnv *env, jobject thiz)
241 {
242     int fd = env->GetIntField(thiz, field_context);
243     tcsendbreak(fd, 0);
244 }
245 
246 static JNINativeMethod method_table[] = {
247     {"native_open",             "(Ljava/io/FileDescriptor;I)V",
248                                         (void *)android_hardware_SerialPort_open},
249     {"native_close",            "()V",  (void *)android_hardware_SerialPort_close},
250     {"native_read_array",       "([BI)I",
251                                         (void *)android_hardware_SerialPort_read_array},
252     {"native_read_direct",      "(Ljava/nio/ByteBuffer;I)I",
253                                         (void *)android_hardware_SerialPort_read_direct},
254     {"native_write_array",      "([BI)V",
255                                         (void *)android_hardware_SerialPort_write_array},
256     {"native_write_direct",     "(Ljava/nio/ByteBuffer;I)V",
257                                         (void *)android_hardware_SerialPort_write_direct},
258     {"native_send_break",       "()V",  (void *)android_hardware_SerialPort_send_break},
259 };
260 
register_android_hardware_SerialPort(JNIEnv * env)261 int register_android_hardware_SerialPort(JNIEnv *env)
262 {
263     jclass clazz = FindClassOrDie(env, "android/hardware/SerialPort");
264     field_context = GetFieldIDOrDie(env, clazz, "mNativeContext", "I");
265 
266     return RegisterMethodsOrDie(env, "android/hardware/SerialPort",
267             method_table, NELEM(method_table));
268 }
269