summaryrefslogtreecommitdiffstats
path: root/usb-linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'usb-linux.c')
-rw-r--r--usb-linux.c152
1 files changed, 144 insertions, 8 deletions
diff --git a/usb-linux.c b/usb-linux.c
index 0820ed9..27a7bb1 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -42,13 +42,13 @@ struct usb_device {
42 uint8_t bus, address; 42 uint8_t bus, address;
43 char serial[256]; 43 char serial[256];
44 int alive; 44 int alive;
45 struct libusb_transfer *rx_xfer;
45}; 46};
46 47
47int num_devs; 48static int num_devs;
48int device_id; 49static struct usb_device *device_list;
49struct usb_device *device_list;
50 50
51struct timeval next_dev_poll_time; 51static struct timeval next_dev_poll_time;
52 52
53static int alloc_device(void) 53static int alloc_device(void)
54{ 54{
@@ -68,11 +68,130 @@ static void usb_disconnect(struct usb_device *dev)
68 if(!dev->dev) { 68 if(!dev->dev) {
69 return; 69 return;
70 } 70 }
71 if(dev->rx_xfer) {
72 // kill the rx xfer and try to make sure the rx callback gets called before we free the device
73 struct timeval tv;
74 int res;
75 // TODO: BUG: outstanding TX xfers are not listed but we need to free them
76 libusb_cancel_transfer(dev->rx_xfer);
77 tv.tv_sec = tv.tv_usec = 0;
78 if((res = libusb_handle_events_timeout(NULL, &tv)) < 0) {
79 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout for device removal failed: %d", res);
80 }
81 }
71 libusb_release_interface(dev->dev, USB_INTERFACE); 82 libusb_release_interface(dev->dev, USB_INTERFACE);
72 libusb_close(dev->dev); 83 libusb_close(dev->dev);
73 dev->dev = NULL; 84 dev->dev = NULL;
74} 85}
75 86
87static void tx_callback(struct libusb_transfer *xfer)
88{
89 struct usb_device *dev = xfer->user_data;
90 usbmuxd_log(LL_SPEW, "TX callback dev %d-%d len %d -> %d status %d", dev->bus, dev->address, xfer->length, xfer->actual_length, xfer->status);
91 if(xfer->status != LIBUSB_TRANSFER_COMPLETED) {
92 switch(xfer->status) {
93 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
94 case LIBUSB_TRANSFER_ERROR:
95 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
96 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to error or disconnect", dev->bus, dev->address);
97 break;
98 case LIBUSB_TRANSFER_TIMED_OUT:
99 usbmuxd_log(LL_ERROR, "TX transfer timed out for device %d-%d", dev->bus, dev->address);
100 break;
101 case LIBUSB_TRANSFER_CANCELLED:
102 usbmuxd_log(LL_ERROR, "TX transfer cancelled for device %d-%d", dev->bus, dev->address);
103 break;
104 case LIBUSB_TRANSFER_STALL:
105 usbmuxd_log(LL_ERROR, "TX transfer stalled for device %d-%d", dev->bus, dev->address);
106 break;
107 case LIBUSB_TRANSFER_NO_DEVICE:
108 // other times, this happens, and also even when we abort the transfer after device removal
109 usbmuxd_log(LL_INFO, "Device %d-%d TX aborted due to disconnect", dev->bus, dev->address);
110 break;
111 case LIBUSB_TRANSFER_OVERFLOW:
112 usbmuxd_log(LL_ERROR, "TX transfer overflow for device %d-%d", dev->bus, dev->address);
113 break;
114 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
115 }
116 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
117 // we'll do device_remove there too
118 dev->alive = 0;
119 }
120 free(xfer->buffer);
121 libusb_free_transfer(xfer);
122}
123
124int usb_send(struct usb_device *dev, const unsigned char *buf, int length)
125{
126 int res;
127 struct libusb_transfer *xfer = libusb_alloc_transfer(0);
128 libusb_fill_bulk_transfer(xfer, dev->dev, BULK_OUT, (void*)buf, length, tx_callback, dev, 0);
129 xfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
130 if((res = libusb_submit_transfer(xfer)) < 0) {
131 usbmuxd_log(LL_ERROR, "Failed to submit TX transfer %p len %d to device %d-%d: %d", buf, length, dev->bus, dev->address, res);
132 libusb_free_transfer(xfer);
133 return res;
134 }
135 return 0;
136}
137
138static void rx_callback(struct libusb_transfer *xfer)
139{
140 struct usb_device *dev = xfer->user_data;
141 usbmuxd_log(LL_SPEW, "RX callback dev %d-%d len %d status %d", dev->bus, dev->address, xfer->actual_length, xfer->status);
142 if(xfer->status == LIBUSB_TRANSFER_COMPLETED) {
143 device_data_input(dev, xfer->buffer, xfer->actual_length);
144 libusb_submit_transfer(xfer);
145 } else {
146 switch(xfer->status) {
147 case LIBUSB_TRANSFER_COMPLETED: //shut up compiler
148 case LIBUSB_TRANSFER_ERROR:
149 // funny, this happens when we disconnect the device while waiting for a transfer, sometimes
150 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to error or disconnect", dev->bus, dev->address);
151 break;
152 case LIBUSB_TRANSFER_TIMED_OUT:
153 usbmuxd_log(LL_ERROR, "RX transfer timed out for device %d-%d", dev->bus, dev->address);
154 break;
155 case LIBUSB_TRANSFER_CANCELLED:
156 usbmuxd_log(LL_ERROR, "RX transfer cancelled for device %d-%d", dev->bus, dev->address);
157 break;
158 case LIBUSB_TRANSFER_STALL:
159 usbmuxd_log(LL_ERROR, "RX transfer stalled for device %d-%d", dev->bus, dev->address);
160 break;
161 case LIBUSB_TRANSFER_NO_DEVICE:
162 // other times, this happens, and also even when we abort the transfer after device removal
163 usbmuxd_log(LL_INFO, "Device %d-%d RX aborted due to disconnect", dev->bus, dev->address);
164 break;
165 case LIBUSB_TRANSFER_OVERFLOW:
166 usbmuxd_log(LL_ERROR, "RX transfer overflow for device %d-%d", dev->bus, dev->address);
167 break;
168 // and nothing happens (this never gets called) if the device is freed after a disconnect! (bad)
169 }
170 free(xfer->buffer);
171 dev->rx_xfer = NULL;
172 libusb_free_transfer(xfer);
173 // we can't usb_disconnect here due to a deadlock, so instead mark it as dead and reap it after processing events
174 // we'll do device_remove there too
175 dev->alive = 0;
176 }
177}
178
179static int start_rx(struct usb_device *dev)
180{
181 int res;
182 void *buf;
183 dev->rx_xfer = libusb_alloc_transfer(0);
184 buf = malloc(USB_MTU);
185 libusb_fill_bulk_transfer(dev->rx_xfer, dev->dev, BULK_IN, buf, USB_MTU, rx_callback, dev, 0);
186 if((res = libusb_submit_transfer(dev->rx_xfer)) != 0) {
187 usbmuxd_log(LL_ERROR, "Failed to submit RX transfer to device %d-%d: %d", dev->bus, dev->address, res);
188 libusb_free_transfer(dev->rx_xfer);
189 dev->rx_xfer = NULL;
190 return res;
191 }
192 return 0;
193}
194
76static int usb_discover(void) 195static int usb_discover(void)
77{ 196{
78 int cnt, i, j, res; 197 int cnt, i, j, res;
@@ -135,7 +254,8 @@ static int usb_discover(void)
135 int idx = alloc_device(); 254 int idx = alloc_device();
136 255
137 if((res = libusb_get_string_descriptor_ascii(handle, devdesc.iSerialNumber, (uint8_t *)device_list[idx].serial, 256)) <= 0) { 256 if((res = libusb_get_string_descriptor_ascii(handle, devdesc.iSerialNumber, (uint8_t *)device_list[idx].serial, 256)) <= 0) {
138 usbmuxd_log(LL_WARNING, "Could not get serial number for device %d-%d: %d", USB_INTERFACE, bus, address, res); 257 usbmuxd_log(LL_WARNING, "Could not get serial number for device %d-%d: %d", bus, address, res);
258 libusb_release_interface(handle, USB_INTERFACE);
139 libusb_close(handle); 259 libusb_close(handle);
140 continue; 260 continue;
141 } 261 }
@@ -145,7 +265,15 @@ static int usb_discover(void)
145 device_list[idx].dev = handle; 265 device_list[idx].dev = handle;
146 device_list[idx].alive = 1; 266 device_list[idx].alive = 1;
147 267
148 device_add(&device_list[idx]); 268 if(device_add(&device_list[idx]) < 0) {
269 usb_disconnect(&device_list[j]);
270 continue;
271 }
272 if(start_rx(&device_list[idx]) < 0) {
273 device_remove(&device_list[j]);
274 usb_disconnect(&device_list[j]);
275 continue;
276 }
149 valid_count++; 277 valid_count++;
150 } 278 }
151 for(j=0; j<num_devs; j++) { 279 for(j=0; j<num_devs; j++) {
@@ -232,7 +360,7 @@ int usb_get_timeout(void)
232 360
233int usb_process(void) 361int usb_process(void)
234{ 362{
235 int res; 363 int i, res;
236 struct timeval tv; 364 struct timeval tv;
237 tv.tv_sec = tv.tv_usec = 0; 365 tv.tv_sec = tv.tv_usec = 0;
238 res = libusb_handle_events_timeout(NULL, &tv); 366 res = libusb_handle_events_timeout(NULL, &tv);
@@ -240,6 +368,14 @@ int usb_process(void)
240 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res); 368 usbmuxd_log(LL_ERROR, "libusb_handle_events_timeout failed: %d", res);
241 return res; 369 return res;
242 } 370 }
371 // reap devices marked dead due to an RX error
372 for(i=0; i<num_devs; i++) {
373 if(device_list[i].dev && !device_list[i].alive) {
374 device_remove(&device_list[i]);
375 usb_disconnect(&device_list[i]);
376 }
377 }
378
243 if(dev_poll_remain_ms() <= 0) { 379 if(dev_poll_remain_ms() <= 0) {
244 res = usb_discover(); 380 res = usb_discover();
245 if(res < 0) { 381 if(res < 0) {
@@ -256,12 +392,12 @@ int usb_init(void)
256 usbmuxd_log(LL_DEBUG, "usb_init for linux / libusb 1.0"); 392 usbmuxd_log(LL_DEBUG, "usb_init for linux / libusb 1.0");
257 393
258 res = libusb_init(NULL); 394 res = libusb_init(NULL);
395 //libusb_set_debug(NULL, 3);
259 if(res != 0) { 396 if(res != 0) {
260 usbmuxd_log(LL_FATAL, "libusb_init failed: %d", res); 397 usbmuxd_log(LL_FATAL, "libusb_init failed: %d", res);
261 return -1; 398 return -1;
262 } 399 }
263 400
264 device_id = 1;
265 num_devs = 1; 401 num_devs = 1;
266 device_list = malloc(sizeof(*device_list) * num_devs); 402 device_list = malloc(sizeof(*device_list) * num_devs);
267 memset(device_list, 0, sizeof(*device_list) * num_devs); 403 memset(device_list, 0, sizeof(*device_list) * num_devs);