1/* 2 * Copyright (C) 2012 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#include <stdio.h> 18#include <stdlib.h> 19#include <stdint.h> 20#include <signal.h> 21#include <pthread.h> 22#include <errno.h> 23#include <string.h> 24 25#include <usbhost/usbhost.h> 26#include "f_accessory.h" 27 28#include "accessory.h" 29 30static struct usb_device *current_device = NULL; 31static uint8_t read_ep; 32static uint8_t write_ep; 33 34static pthread_mutex_t device_mutex = PTHREAD_MUTEX_INITIALIZER; 35static pthread_cond_t device_cond = PTHREAD_COND_INITIALIZER; 36 37static void milli_sleep(int millis) { 38 struct timespec tm; 39 40 tm.tv_sec = 0; 41 tm.tv_nsec = millis * 1000000; 42 nanosleep(&tm, NULL); 43} 44 45static void* read_thread(void* arg) { 46 int ret = 0; 47 48 while (current_device && ret >= 0) { 49 char buffer[16384]; 50 51 ret = usb_device_bulk_transfer(current_device, read_ep, buffer, sizeof(buffer), 1000); 52 if (ret < 0 && errno == ETIMEDOUT) 53 ret = 0; 54 if (ret > 0) { 55 fwrite(buffer, 1, ret, stdout); 56 fprintf(stderr, "\n"); 57 fflush(stdout); 58 } 59 } 60 61 return NULL; 62} 63 64static void* write_thread(void* arg) { 65 int ret = 0; 66 67 while (ret >= 0) { 68 char buffer[16384]; 69 char *line = fgets(buffer, sizeof(buffer), stdin); 70 if (!line || !current_device) 71 break; 72 ret = usb_device_bulk_transfer(current_device, write_ep, line, strlen(line), 1000); 73 } 74 75 return NULL; 76} 77 78static void send_string(struct usb_device *device, int index, const char* string) { 79 usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR, 80 ACCESSORY_SEND_STRING, 0, index, (void *)string, strlen(string) + 1, 0); 81 82 // some devices can't handle back-to-back requests, so delay a bit 83 milli_sleep(10); 84} 85 86static int usb_device_added(const char *devname, void* client_data) { 87 uint16_t vendorId, productId; 88 int ret; 89 int enable_accessory = (int)client_data; 90 91 struct usb_device *device = usb_device_open(devname); 92 if (!device) { 93 fprintf(stderr, "usb_device_open failed\n"); 94 return 0; 95 } 96 97 vendorId = usb_device_get_vendor_id(device); 98 productId = usb_device_get_product_id(device); 99 100 if (!current_device && vendorId == 0x18D1 && productId >= 0x2D00 && productId <= 0x2D05) { 101 102 pthread_mutex_lock(&device_mutex); 103 fprintf(stderr, "Found android device in accessory mode\n"); 104 current_device = device; 105 pthread_cond_broadcast(&device_cond); 106 pthread_mutex_unlock(&device_mutex); 107 108 if (enable_accessory) { 109 struct usb_descriptor_header* desc; 110 struct usb_descriptor_iter iter; 111 struct usb_interface_descriptor *intf = NULL; 112 struct usb_endpoint_descriptor *ep1 = NULL; 113 struct usb_endpoint_descriptor *ep2 = NULL; 114 pthread_t th; 115 116 usb_descriptor_iter_init(device, &iter); 117 while ((desc = usb_descriptor_iter_next(&iter)) != NULL && (!intf || !ep1 || !ep2)) { 118 if (desc->bDescriptorType == USB_DT_INTERFACE) { 119 intf = (struct usb_interface_descriptor *)desc; 120 } else if (desc->bDescriptorType == USB_DT_ENDPOINT) { 121 if (ep1) 122 ep2 = (struct usb_endpoint_descriptor *)desc; 123 else 124 ep1 = (struct usb_endpoint_descriptor *)desc; 125 } 126 } 127 128 if (!intf) { 129 fprintf(stderr, "interface not found\n"); 130 exit(1); 131 } 132 if (!ep1 || !ep2) { 133 fprintf(stderr, "endpoints not found\n"); 134 exit(1); 135 } 136 137 if (usb_device_claim_interface(device, intf->bInterfaceNumber)) { 138 fprintf(stderr, "usb_device_claim_interface failed errno: %d\n", errno); 139 exit(1); 140 } 141 142 if ((ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { 143 read_ep = ep1->bEndpointAddress; 144 write_ep = ep2->bEndpointAddress; 145 } else { 146 read_ep = ep2->bEndpointAddress; 147 write_ep = ep1->bEndpointAddress; 148 } 149 150 pthread_create(&th, NULL, read_thread, NULL); 151 pthread_create(&th, NULL, write_thread, NULL); 152 } 153 } else { 154// fprintf(stderr, "Found new device - attempting to switch to accessory mode\n"); 155 156 uint16_t protocol = -1; 157 ret = usb_device_control_transfer(device, USB_DIR_IN | USB_TYPE_VENDOR, 158 ACCESSORY_GET_PROTOCOL, 0, 0, &protocol, sizeof(protocol), 1000); 159 if (ret < 0) { 160 // fprintf(stderr, "ACCESSORY_GET_PROTOCOL returned %d errno: %d\n", ret, errno); 161 } else { 162 fprintf(stderr, "device supports protocol version %d\n", protocol); 163 if (protocol >= 2) { 164 if (enable_accessory) { 165 send_string(device, ACCESSORY_STRING_MANUFACTURER, "Google, Inc."); 166 send_string(device, ACCESSORY_STRING_MODEL, "AccessoryChat"); 167 send_string(device, ACCESSORY_STRING_DESCRIPTION, "Accessory Chat"); 168 send_string(device, ACCESSORY_STRING_VERSION, "1.0"); 169 send_string(device, ACCESSORY_STRING_URI, "http://www.android.com"); 170 send_string(device, ACCESSORY_STRING_SERIAL, "1234567890"); 171 } 172 173 fprintf(stderr, "sending ACCESSORY_SET_AUDIO_MODE\n"); 174 ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR, 175 ACCESSORY_SET_AUDIO_MODE, 1, 0, NULL, 0, 1000); 176 if (ret < 0) 177 fprintf(stderr, "ACCESSORY_SET_AUDIO_MODE returned %d errno: %d\n", ret, errno); 178 179 fprintf(stderr, "sending ACCESSORY_START\n"); 180 ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR, 181 ACCESSORY_START, 0, 0, NULL, 0, 1000); 182 fprintf(stderr, "did ACCESSORY_START\n"); 183 if (ret < 0) 184 fprintf(stderr, "ACCESSORY_START returned %d errno: %d\n", ret, errno); 185 } 186 } 187 188 return 0; 189 } 190 191 if (device != current_device) 192 usb_device_close(device); 193 194 return 0; 195} 196 197static int usb_device_removed(const char *devname, void* client_data) { 198 pthread_mutex_lock(&device_mutex); 199 200 if (current_device && !strcmp(usb_device_get_name(current_device), devname)) { 201 fprintf(stderr, "current device disconnected\n"); 202 usb_device_close(current_device); 203 current_device = NULL; 204 } 205 206 pthread_mutex_unlock(&device_mutex); 207 return 0; 208} 209 210struct usb_device* usb_wait_for_device() { 211 struct usb_device* device = NULL; 212 213 pthread_mutex_lock(&device_mutex); 214 while (!current_device) 215 pthread_cond_wait(&device_cond, &device_mutex); 216 device = current_device; 217 pthread_mutex_unlock(&device_mutex); 218 219 return device; 220} 221 222void usb_run(uintptr_t enable_accessory) { 223 struct usb_host_context* context = usb_host_init(); 224 225 usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)enable_accessory); 226} 227 228