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 "android_runtime/AndroidRuntime.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
31using namespace android;
32
33static jfieldID field_context;
34
35static void
36android_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
160static void
161android_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
168static jint
169android_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
190static jint
191android_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
207static void
208android_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
224static void
225android_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
239static void
240android_hardware_SerialPort_send_break(JNIEnv *env, jobject thiz)
241{
242    int fd = env->GetIntField(thiz, field_context);
243    tcsendbreak(fd, 0);
244}
245
246static 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
261int register_android_hardware_SerialPort(JNIEnv *env)
262{
263    jclass clazz = env->FindClass("android/hardware/SerialPort");
264    if (clazz == NULL) {
265        ALOGE("Can't find android/hardware/SerialPort");
266        return -1;
267    }
268    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
269    if (field_context == NULL) {
270        ALOGE("Can't find SerialPort.mNativeContext");
271        return -1;
272    }
273
274    return AndroidRuntime::registerNativeMethods(env, "android/hardware/SerialPort",
275            method_table, NELEM(method_table));
276}
277