1 /*
2 * Copyright (C) 2010 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 "UsbRequestJNI"
18
19 #include "utils/Log.h"
20
21 #include "jni.h"
22 #include "JNIHelp.h"
23 #include "android_runtime/AndroidRuntime.h"
24
25 #include <usbhost/usbhost.h>
26
27 #include <stdio.h>
28
29 using namespace android;
30
31 static jfieldID field_context;
32
get_request_from_object(JNIEnv * env,jobject java_request)33 struct usb_request* get_request_from_object(JNIEnv* env, jobject java_request)
34 {
35 return (struct usb_request*)env->GetIntField(java_request, field_context);
36 }
37
38 // in android_hardware_UsbDeviceConnection.cpp
39 extern struct usb_device* get_device_from_object(JNIEnv* env, jobject connection);
40
41 static jboolean
android_hardware_UsbRequest_init(JNIEnv * env,jobject thiz,jobject java_device,jint ep_address,jint ep_attributes,jint ep_max_packet_size,jint ep_interval)42 android_hardware_UsbRequest_init(JNIEnv *env, jobject thiz, jobject java_device,
43 jint ep_address, jint ep_attributes, jint ep_max_packet_size, jint ep_interval)
44 {
45 ALOGD("init\n");
46
47 struct usb_device* device = get_device_from_object(env, java_device);
48 if (!device) {
49 ALOGE("device null in native_init");
50 return false;
51 }
52
53 // construct an endpoint descriptor from the Java object fields
54 struct usb_endpoint_descriptor desc;
55 desc.bLength = USB_DT_ENDPOINT_SIZE;
56 desc.bDescriptorType = USB_DT_ENDPOINT;
57 desc.bEndpointAddress = ep_address;
58 desc.bmAttributes = ep_attributes;
59 desc.wMaxPacketSize = ep_max_packet_size;
60 desc.bInterval = ep_interval;
61
62 struct usb_request* request = usb_request_new(device, &desc);
63 if (request)
64 env->SetIntField(thiz, field_context, (int)request);
65 return (request != NULL);
66 }
67
68 static void
android_hardware_UsbRequest_close(JNIEnv * env,jobject thiz)69 android_hardware_UsbRequest_close(JNIEnv *env, jobject thiz)
70 {
71 ALOGD("close\n");
72 struct usb_request* request = get_request_from_object(env, thiz);
73 if (request) {
74 usb_request_free(request);
75 env->SetIntField(thiz, field_context, 0);
76 }
77 }
78
79 static jboolean
android_hardware_UsbRequest_queue_array(JNIEnv * env,jobject thiz,jbyteArray buffer,jint length,jboolean out)80 android_hardware_UsbRequest_queue_array(JNIEnv *env, jobject thiz,
81 jbyteArray buffer, jint length, jboolean out)
82 {
83 struct usb_request* request = get_request_from_object(env, thiz);
84 if (!request) {
85 ALOGE("request is closed in native_queue");
86 return false;
87 }
88
89 if (buffer && length) {
90 request->buffer = malloc(length);
91 if (!request->buffer)
92 return false;
93 memset(request->buffer, 0, length);
94 if (out) {
95 // copy data from Java buffer to native buffer
96 env->GetByteArrayRegion(buffer, 0, length, (jbyte *)request->buffer);
97 }
98 } else {
99 request->buffer = NULL;
100 }
101 request->buffer_length = length;
102
103 if (usb_request_queue(request)) {
104 if (request->buffer) {
105 // free our buffer if usb_request_queue fails
106 free(request->buffer);
107 request->buffer = NULL;
108 }
109 return false;
110 } else {
111 // save a reference to ourselves so UsbDeviceConnection.waitRequest() can find us
112 request->client_data = (void *)env->NewGlobalRef(thiz);
113 return true;
114 }
115 }
116
117 static int
android_hardware_UsbRequest_dequeue_array(JNIEnv * env,jobject thiz,jbyteArray buffer,jint length,jboolean out)118 android_hardware_UsbRequest_dequeue_array(JNIEnv *env, jobject thiz,
119 jbyteArray buffer, jint length, jboolean out)
120 {
121 struct usb_request* request = get_request_from_object(env, thiz);
122 if (!request) {
123 ALOGE("request is closed in native_dequeue");
124 return -1;
125 }
126
127 if (buffer && length && request->buffer && !out) {
128 // copy data from native buffer to Java buffer
129 env->SetByteArrayRegion(buffer, 0, length, (jbyte *)request->buffer);
130 }
131 free(request->buffer);
132 env->DeleteGlobalRef((jobject)request->client_data);
133 return request->actual_length;
134 }
135
136 static jboolean
android_hardware_UsbRequest_queue_direct(JNIEnv * env,jobject thiz,jobject buffer,jint length,jboolean out)137 android_hardware_UsbRequest_queue_direct(JNIEnv *env, jobject thiz,
138 jobject buffer, jint length, jboolean out)
139 {
140 struct usb_request* request = get_request_from_object(env, thiz);
141 if (!request) {
142 ALOGE("request is closed in native_queue");
143 return false;
144 }
145
146 if (buffer && length) {
147 request->buffer = env->GetDirectBufferAddress(buffer);
148 if (!request->buffer)
149 return false;
150 } else {
151 request->buffer = NULL;
152 }
153 request->buffer_length = length;
154
155 if (usb_request_queue(request)) {
156 request->buffer = NULL;
157 return false;
158 } else {
159 // save a reference to ourselves so UsbDeviceConnection.waitRequest() can find us
160 // we also need this to make sure our native buffer is not deallocated
161 // while IO is active
162 request->client_data = (void *)env->NewGlobalRef(thiz);
163 return true;
164 }
165 }
166
167 static int
android_hardware_UsbRequest_dequeue_direct(JNIEnv * env,jobject thiz)168 android_hardware_UsbRequest_dequeue_direct(JNIEnv *env, jobject thiz)
169 {
170 struct usb_request* request = get_request_from_object(env, thiz);
171 if (!request) {
172 ALOGE("request is closed in native_dequeue");
173 return -1;
174 }
175 // all we need to do is delete our global ref
176 env->DeleteGlobalRef((jobject)request->client_data);
177 return request->actual_length;
178 }
179
180 static jboolean
android_hardware_UsbRequest_cancel(JNIEnv * env,jobject thiz)181 android_hardware_UsbRequest_cancel(JNIEnv *env, jobject thiz)
182 {
183 struct usb_request* request = get_request_from_object(env, thiz);
184 if (!request) {
185 ALOGE("request is closed in native_cancel");
186 return false;
187 }
188 return (usb_request_cancel(request) == 0);
189 }
190
191 static JNINativeMethod method_table[] = {
192 {"native_init", "(Landroid/hardware/usb/UsbDeviceConnection;IIII)Z",
193 (void *)android_hardware_UsbRequest_init},
194 {"native_close", "()V", (void *)android_hardware_UsbRequest_close},
195 {"native_queue_array", "([BIZ)Z", (void *)android_hardware_UsbRequest_queue_array},
196 {"native_dequeue_array", "([BIZ)I", (void *)android_hardware_UsbRequest_dequeue_array},
197 {"native_queue_direct", "(Ljava/nio/ByteBuffer;IZ)Z",
198 (void *)android_hardware_UsbRequest_queue_direct},
199 {"native_dequeue_direct", "()I", (void *)android_hardware_UsbRequest_dequeue_direct},
200 {"native_cancel", "()Z", (void *)android_hardware_UsbRequest_cancel},
201 };
202
register_android_hardware_UsbRequest(JNIEnv * env)203 int register_android_hardware_UsbRequest(JNIEnv *env)
204 {
205 jclass clazz = env->FindClass("android/hardware/usb/UsbRequest");
206 if (clazz == NULL) {
207 ALOGE("Can't find android/hardware/usb/UsbRequest");
208 return -1;
209 }
210 field_context = env->GetFieldID(clazz, "mNativeContext", "I");
211 if (field_context == NULL) {
212 ALOGE("Can't find UsbRequest.mNativeContext");
213 return -1;
214 }
215
216 return AndroidRuntime::registerNativeMethods(env, "android/hardware/usb/UsbRequest",
217 method_table, NELEM(method_table));
218 }
219
220