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#include <unistd.h> 18#include <stdio.h> 19#include <string.h> 20#include <stdlib.h> 21 22#include <sys/types.h> 23#include <sys/stat.h> 24#include <fcntl.h> 25#include <errno.h> 26#include <pthread.h> 27#include <time.h> 28 29#include <usbhost/usbhost.h> 30#include <linux/usb/f_accessory.h> 31 32struct usb_device *sDevice = NULL; 33 34static void* read_thread(void* arg) { 35 int endpoint = (int)(uintptr_t)arg; 36 int ret = 0; 37 38 while (sDevice && ret >= 0) { 39 char buffer[16384]; 40 41 ret = usb_device_bulk_transfer(sDevice, endpoint, buffer, sizeof(buffer), 1000); 42 if (ret < 0 && errno == ETIMEDOUT) 43 ret = 0; 44 if (ret > 0) { 45 fwrite(buffer, 1, ret, stdout); 46 printf("\n"); 47 fflush(stdout); 48 } 49 } 50 51 return NULL; 52} 53 54static void* write_thread(void* arg) { 55 int endpoint = (int)(uintptr_t)arg; 56 int ret = 0; 57 58 while (ret >= 0) { 59 char buffer[16384]; 60 char *line = fgets(buffer, sizeof(buffer), stdin); 61 if (!line || !sDevice) 62 break; 63 ret = usb_device_bulk_transfer(sDevice, endpoint, line, strlen(line), 1000); 64 } 65 66 return NULL; 67} 68 69static void milli_sleep(int millis) { 70 struct timespec tm; 71 72 tm.tv_sec = 0; 73 tm.tv_nsec = millis * 1000000; 74 nanosleep(&tm, NULL); 75} 76 77static void send_string(struct usb_device *device, int index, const char* string) { 78 int ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR, 79 ACCESSORY_SEND_STRING, 0, index, (void *)string, strlen(string) + 1, 0); 80 81 // some devices can't handle back-to-back requests, so delay a bit 82 milli_sleep(10); 83} 84 85static int usb_device_added(const char *devname, void* client_data) { 86 struct usb_descriptor_header* desc; 87 struct usb_descriptor_iter iter; 88 uint16_t vendorId, productId; 89 int ret; 90 pthread_t th; 91 92 struct usb_device *device = usb_device_open(devname); 93 if (!device) { 94 fprintf(stderr, "usb_device_open failed\n"); 95 return 0; 96 } 97 98 vendorId = usb_device_get_vendor_id(device); 99 productId = usb_device_get_product_id(device); 100 101 if (vendorId == 0x18D1 || vendorId == 0x22B8 || vendorId == 0x04e8) { 102 if (!sDevice && (productId == 0x2D00 || productId == 0x2D01)) { 103 struct usb_descriptor_header* desc; 104 struct usb_descriptor_iter iter; 105 struct usb_interface_descriptor *intf = NULL; 106 struct usb_endpoint_descriptor *ep1 = NULL; 107 struct usb_endpoint_descriptor *ep2 = NULL; 108 109 printf("Found android device in accessory mode\n"); 110 sDevice = device; 111 112 usb_descriptor_iter_init(device, &iter); 113 while ((desc = usb_descriptor_iter_next(&iter)) != NULL && (!intf || !ep1 || !ep2)) { 114 if (desc->bDescriptorType == USB_DT_INTERFACE) { 115 intf = (struct usb_interface_descriptor *)desc; 116 } else if (desc->bDescriptorType == USB_DT_ENDPOINT) { 117 if (ep1) 118 ep2 = (struct usb_endpoint_descriptor *)desc; 119 else 120 ep1 = (struct usb_endpoint_descriptor *)desc; 121 } 122 } 123 124 if (!intf) { 125 fprintf(stderr, "interface not found\n"); 126 exit(1); 127 } 128 if (!ep1 || !ep2) { 129 fprintf(stderr, "endpoints not found\n"); 130 exit(1); 131 } 132 133 if (usb_device_claim_interface(device, intf->bInterfaceNumber)) { 134 fprintf(stderr, "usb_device_claim_interface failed errno: %d\n", errno); 135 exit(1); 136 } 137 138 if ((ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { 139 pthread_create(&th, NULL, read_thread, (void *)(uintptr_t)ep1->bEndpointAddress); 140 pthread_create(&th, NULL, write_thread, (void *)(uintptr_t)ep2->bEndpointAddress); 141 } else { 142 pthread_create(&th, NULL, read_thread, (void *)(uintptr_t)ep2->bEndpointAddress); 143 pthread_create(&th, NULL, write_thread, (void *)(uintptr_t)ep1->bEndpointAddress); 144 } 145 } else { 146 printf("Found possible android device - attempting to switch to accessory mode\n"); 147 148 uint16_t protocol; 149 ret = usb_device_control_transfer(device, USB_DIR_IN | USB_TYPE_VENDOR, 150 ACCESSORY_GET_PROTOCOL, 0, 0, &protocol, sizeof(protocol), 0); 151 if (ret == 2) 152 printf("device supports protocol version %d\n", protocol); 153 else 154 fprintf(stderr, "failed to read protocol version\n"); 155 156 send_string(device, ACCESSORY_STRING_MANUFACTURER, "Google, Inc."); 157 send_string(device, ACCESSORY_STRING_MODEL, "AccessoryChat"); 158 send_string(device, ACCESSORY_STRING_DESCRIPTION, "Accessory Chat"); 159 send_string(device, ACCESSORY_STRING_VERSION, "1.0"); 160 send_string(device, ACCESSORY_STRING_URI, "http://www.android.com"); 161 send_string(device, ACCESSORY_STRING_SERIAL, "1234567890"); 162 163 ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR, 164 ACCESSORY_START, 0, 0, 0, 0, 0); 165 return 0; 166 } 167 } 168 169 if (device != sDevice) 170 usb_device_close(device); 171 172 return 0; 173} 174 175static int usb_device_removed(const char *devname, void* client_data) { 176 if (sDevice && !strcmp(usb_device_get_name(sDevice), devname)) { 177 usb_device_close(sDevice); 178 sDevice = NULL; 179 // exit when we are disconnected 180 return 1; 181 } 182 return 0; 183} 184 185 186int main(int argc, char* argv[]) { 187 struct usb_host_context* context = usb_host_init(); 188 if (!context) { 189 fprintf(stderr, "usb_host_init failed"); 190 return 1; 191 } 192 193 // this will never return so it is safe to pass thiz directly 194 usb_host_run(context, usb_device_added, usb_device_removed, NULL, NULL); 195 return 0; 196} 197