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 "core_jni_helpers.h"
24
25#include <usbhost/usbhost.h>
26
27#include <stdio.h>
28
29using namespace android;
30
31static jfieldID field_context;
32
33struct usb_request* get_request_from_object(JNIEnv* env, jobject java_request)
34{
35    return (struct usb_request*)env->GetLongField(java_request, field_context);
36}
37
38// in android_hardware_UsbDeviceConnection.cpp
39extern struct usb_device* get_device_from_object(JNIEnv* env, jobject connection);
40
41static jboolean
42android_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 JNI_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->SetLongField(thiz, field_context, (jlong)request);
65    return (request != NULL);
66}
67
68static void
69android_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->SetLongField(thiz, field_context, 0);
76    }
77}
78
79static jboolean
80android_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 JNI_FALSE;
87    }
88
89    if (buffer && length) {
90        request->buffer = malloc(length);
91        if (!request->buffer)
92            return JNI_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    // save a reference to ourselves so UsbDeviceConnection.waitRequest() can find us
104    request->client_data = (void *)env->NewGlobalRef(thiz);
105
106    if (usb_request_queue(request)) {
107        if (request->buffer) {
108            // free our buffer if usb_request_queue fails
109            free(request->buffer);
110            request->buffer = NULL;
111        }
112        env->DeleteGlobalRef((jobject)request->client_data);
113        return JNI_FALSE;
114    }
115    return JNI_TRUE;
116}
117
118static jint
119android_hardware_UsbRequest_dequeue_array(JNIEnv *env, jobject thiz,
120        jbyteArray buffer, jint length, jboolean out)
121{
122    struct usb_request* request = get_request_from_object(env, thiz);
123    if (!request) {
124        ALOGE("request is closed in native_dequeue");
125        return (jint) -1;
126    }
127
128    if (buffer && length && request->buffer && !out) {
129        // copy data from native buffer to Java buffer
130        env->SetByteArrayRegion(buffer, 0, length, (jbyte *)request->buffer);
131    }
132    free(request->buffer);
133    env->DeleteGlobalRef((jobject)request->client_data);
134    return (jint) request->actual_length;
135}
136
137static jboolean
138android_hardware_UsbRequest_queue_direct(JNIEnv *env, jobject thiz,
139        jobject buffer, jint length, jboolean out)
140{
141    struct usb_request* request = get_request_from_object(env, thiz);
142    if (!request) {
143        ALOGE("request is closed in native_queue");
144        return JNI_FALSE;
145    }
146
147    if (buffer && length) {
148        request->buffer = env->GetDirectBufferAddress(buffer);
149        if (!request->buffer)
150            return JNI_FALSE;
151    } else {
152        request->buffer = NULL;
153    }
154    request->buffer_length = length;
155
156    // save a reference to ourselves so UsbDeviceConnection.waitRequest() can find us
157    // we also need this to make sure our native buffer is not deallocated
158    // while IO is active
159    request->client_data = (void *)env->NewGlobalRef(thiz);
160
161    if (usb_request_queue(request)) {
162        request->buffer = NULL;
163        env->DeleteGlobalRef((jobject)request->client_data);
164        return JNI_FALSE;
165    }
166    return JNI_TRUE;
167}
168
169static jboolean
170android_hardware_UsbRequest_queue(JNIEnv *env, jobject thiz, jobject buffer, jint offset,
171        jint length)
172{
173    struct usb_request* request = get_request_from_object(env, thiz);
174    if (!request) {
175        ALOGE("request is closed in native_queue");
176        return JNI_FALSE;
177    }
178
179    if (buffer == NULL) {
180        request->buffer = NULL;
181        request->buffer_length = 0;
182    } else {
183        request->buffer = (void *)((char *)env->GetDirectBufferAddress(buffer) + offset);
184        request->buffer_length = length;
185    }
186
187    // Save a reference to ourselves so UsbDeviceConnection.waitRequest() can find us.
188    // We also need this to make sure our native buffer is not deallocated while IO is active.
189    request->client_data = (void *)env->NewGlobalRef(thiz);
190
191    int err = usb_request_queue(request);
192
193    if (err != 0) {
194        request->buffer = NULL;
195        env->DeleteGlobalRef((jobject)request->client_data);
196        return JNI_FALSE;
197    }
198    return JNI_TRUE;
199}
200
201static jint
202android_hardware_UsbRequest_dequeue_direct(JNIEnv *env, jobject thiz)
203{
204    struct usb_request* request = get_request_from_object(env, thiz);
205    if (!request) {
206        ALOGE("request is closed in native_dequeue");
207        return (jint) -1;
208    }
209    // all we need to do is delete our global ref
210    env->DeleteGlobalRef((jobject)request->client_data);
211    return (jint) request->actual_length;
212}
213
214static jboolean
215android_hardware_UsbRequest_cancel(JNIEnv *env, jobject thiz)
216{
217    struct usb_request* request = get_request_from_object(env, thiz);
218    if (!request) {
219        ALOGE("request is closed in native_cancel");
220        return JNI_FALSE;
221    }
222    return (usb_request_cancel(request) == 0);
223}
224
225static const JNINativeMethod method_table[] = {
226    {"native_init",             "(Landroid/hardware/usb/UsbDeviceConnection;IIII)Z",
227                                            (void *)android_hardware_UsbRequest_init},
228    {"native_close",            "()V",      (void *)android_hardware_UsbRequest_close},
229    {"native_queue",            "(Ljava/nio/ByteBuffer;II)Z",
230                                            (void *)android_hardware_UsbRequest_queue},
231    {"native_queue_array",      "([BIZ)Z",  (void *)android_hardware_UsbRequest_queue_array},
232    {"native_dequeue_array",    "([BIZ)I",  (void *)android_hardware_UsbRequest_dequeue_array},
233    {"native_queue_direct",     "(Ljava/nio/ByteBuffer;IZ)Z",
234                                            (void *)android_hardware_UsbRequest_queue_direct},
235    {"native_dequeue_direct",   "()I",      (void *)android_hardware_UsbRequest_dequeue_direct},
236    {"native_cancel",           "()Z",      (void *)android_hardware_UsbRequest_cancel},
237};
238
239int register_android_hardware_UsbRequest(JNIEnv *env)
240{
241    jclass clazz = env->FindClass("android/hardware/usb/UsbRequest");
242    if (clazz == NULL) {
243        ALOGE("Can't find android/hardware/usb/UsbRequest");
244        return -1;
245    }
246    field_context = env->GetFieldID(clazz, "mNativeContext", "J");
247    if (field_context == NULL) {
248        ALOGE("Can't find UsbRequest.mNativeContext");
249        return -1;
250    }
251
252    return RegisterMethodsOrDie(env, "android/hardware/usb/UsbRequest",
253            method_table, NELEM(method_table));
254}
255
256