1 /*
2 * Copyright 2022, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
4 */
5
6 #include <sys/condvar.h>
7
8 extern "C" {
9 #include <sys/mutex.h>
10 #include <sys/systm.h>
11 #include <sys/taskqueue.h>
12 #include <sys/priority.h>
13
14 #include <dev/usb/usb.h>
15 #include <dev/usb/usbdi.h>
16 #include <dev/usb/usb_device.h>
17
18 #include "device.h"
19 }
20
21 // undo name remappings, so we can use both FreeBSD and Haiku ones in this file
22 #undef usb_device
23 #undef usb_interface
24 #undef usb_endpoint_descriptor
25
26 #include <USB3.h>
27
28
29 struct mtx sUSBLock;
30 usb_module_info* sUSB = NULL;
31 struct taskqueue* sUSBTaskqueue = NULL;
32
33
34 status_t
init_usb()35 init_usb()
36 {
37 if (sUSB != NULL)
38 return B_OK;
39
40 if (get_module(B_USB_MODULE_NAME, (module_info**)&sUSB) != B_OK) {
41 dprintf("cannot get module \"%s\"\n", B_USB_MODULE_NAME);
42 return B_ERROR;
43 }
44
45 mtx_init(&sUSBLock, "fbsd usb", NULL, MTX_DEF);
46 return B_OK;
47 }
48
49
50 void
uninit_usb()51 uninit_usb()
52 {
53 if (sUSB == NULL)
54 return;
55
56 put_module(B_USB_MODULE_NAME);
57 if (sUSBTaskqueue != NULL)
58 taskqueue_free(sUSBTaskqueue);
59
60 sUSB = NULL;
61 sUSBTaskqueue = NULL;
62 mtx_destroy(&sUSBLock);
63 }
64
65
66 status_t
get_next_usb_device(uint32 * cookie,freebsd_usb_device * result)67 get_next_usb_device(uint32* cookie, freebsd_usb_device* result)
68 {
69 // We cheat here: since USB IDs are sequential, instead of doing a
70 // complicated parent/child iteration dance, we simply request device
71 // descriptors and let the USB stack figure out the rest.
72 //
73 // It would be better if we used USB->register_driver, but that is not
74 // an option at present for a variety of reasons...
75 const usb_configuration_info* config;
76 usb_device current;
77 while (*cookie < 1024) {
78 current = *cookie;
79 *cookie = *cookie + 1;
80
81 config = sUSB->get_configuration(current);
82 if (config != NULL)
83 break;
84 }
85 if (config == NULL)
86 return ENODEV;
87
88 result->haiku_usb_device = current;
89 result->endpoints_max = 0;
90 for (size_t i = 0; i < config->interface_count; i++) {
91 usb_interface_info* iface = config->interface[i].active;
92 if (iface == NULL)
93 continue;
94
95 for (size_t j = 0; j < iface->endpoint_count; j++) {
96 if (iface->endpoint[j].descr == NULL)
97 continue;
98
99 const int rep = result->endpoints_max++;
100 result->endpoints[rep].iface_index = i;
101
102 static_assert(sizeof(freebsd_usb_endpoint_descriptor)
103 == sizeof(usb_endpoint_descriptor), "size mismatch");
104
105 if (result->endpoints[rep].edesc == NULL)
106 result->endpoints[rep].edesc = new freebsd_usb_endpoint_descriptor;
107
108 memcpy(result->endpoints[rep].edesc, iface->endpoint[j].descr,
109 sizeof(usb_endpoint_descriptor));
110 }
111 }
112
113 return B_OK;
114 }
115
116
117 status_t
get_usb_device_attach_arg(struct freebsd_usb_device * device,struct usb_attach_arg * uaa)118 get_usb_device_attach_arg(struct freebsd_usb_device* device, struct usb_attach_arg* uaa)
119 {
120 memset(uaa, 0, sizeof(struct usb_attach_arg));
121
122 const usb_device_descriptor* device_desc =
123 sUSB->get_device_descriptor(device->haiku_usb_device);
124 if (device_desc == NULL)
125 return B_BAD_VALUE;
126
127 uaa->info.idVendor = device_desc->vendor_id;
128 uaa->info.idProduct = device_desc->product_id;
129 uaa->info.bcdDevice = device_desc->device_version;
130 uaa->info.bDeviceClass = device_desc->device_class;
131 uaa->info.bDeviceSubClass = device_desc->device_subclass;
132 uaa->info.bDeviceProtocol = device_desc->device_protocol;
133
134 const usb_configuration_info* config = sUSB->get_configuration(device->haiku_usb_device);
135 if (device_desc == NULL)
136 return B_BAD_VALUE;
137
138 // TODO: represent more than just interface[0], but how?
139 usb_interface_info* iface = config->interface[0].active;
140 if (iface == NULL)
141 return B_NO_INIT;
142
143 uaa->info.bInterfaceClass = iface->descr->interface_class;
144 uaa->info.bInterfaceSubClass = iface->descr->interface_subclass;
145 uaa->info.bInterfaceProtocol = iface->descr->interface_protocol;
146
147 // TODO: bIface{Index,Num}, bConfig{Index,Num}
148
149 uaa->device = device;
150 uaa->iface = NULL;
151
152 // TODO: fetch values for these?
153 uaa->usb_mode = USB_MODE_HOST;
154 uaa->port = 1;
155 uaa->dev_state = UAA_DEV_READY;
156
157 return B_OK;
158 }
159
160
161 void
usb_cleanup_device(freebsd_usb_device * udev)162 usb_cleanup_device(freebsd_usb_device* udev)
163 {
164 for (int i = 0; i < USB_MAX_EP_UNITS; i++) {
165 delete udev->endpoints[i].edesc;
166 udev->endpoints[i].edesc = NULL;
167 }
168 }
169
170
171 static usb_error_t
map_usb_error(status_t err)172 map_usb_error(status_t err)
173 {
174 switch (err) {
175 case B_OK: return USB_ERR_NORMAL_COMPLETION;
176 case B_DEV_STALLED: return USB_ERR_STALLED;
177 case B_CANCELED: return USB_ERR_CANCELLED;
178 case B_TIMED_OUT: return USB_ERR_TIMEOUT;
179 }
180 return USB_ERR_INVAL;
181 }
182
183
184 extern "C" usb_error_t
usbd_do_request_flags(struct freebsd_usb_device * udev,struct mtx * mtx,struct usb_device_request * req,void * data,uint16_t flags,uint16_t * actlen,usb_timeout_t timeout)185 usbd_do_request_flags(struct freebsd_usb_device* udev, struct mtx* mtx,
186 struct usb_device_request* req, void* data, uint16_t flags,
187 uint16_t* actlen, usb_timeout_t timeout)
188 {
189 if (mtx != NULL)
190 mtx_unlock(mtx);
191
192 // FIXME: timeouts
193 // TODO: flags
194
195 size_t actualLen = 0;
196 status_t ret = sUSB->send_request((usb_device)udev->haiku_usb_device,
197 req->bmRequestType, req->bRequest,
198 UGETW(req->wValue), UGETW(req->wIndex), UGETW(req->wLength),
199 data, &actualLen);
200 if (actlen)
201 *actlen = actualLen;
202
203 if (mtx != NULL)
204 mtx_lock(mtx);
205
206 return map_usb_error(ret);
207 }
208
209
210 enum usb_dev_speed
usbd_get_speed(struct freebsd_usb_device * udev)211 usbd_get_speed(struct freebsd_usb_device* udev)
212 {
213 const usb_device_descriptor* descriptor = sUSB->get_device_descriptor(
214 (usb_device)udev->haiku_usb_device);
215 KASSERT(descriptor != NULL, ("no device"));
216
217 if (descriptor->usb_version >= 0x0300)
218 return USB_SPEED_SUPER;
219 else if (descriptor->usb_version >= 0x200)
220 return USB_SPEED_HIGH;
221 else if (descriptor->usb_version >= 0x110)
222 return USB_SPEED_FULL;
223 else if (descriptor->usb_version >= 0x100)
224 return USB_SPEED_LOW;
225
226 panic("unknown USB version!");
227 return (usb_dev_speed)-1;
228 }
229
230
231 struct usb_page_cache {
232 void* buffer;
233 size_t length;
234 };
235
236 struct usb_xfer {
237 struct mtx* mutex;
238 void* priv_sc, *priv;
239 usb_callback_t* callback;
240 usb_xfer_flags flags;
241 usb_frlength_t max_data_length;
242
243 usb_device device;
244 uint8 type;
245 usb_pipe pipe;
246
247 iovec* frames;
248 usb_page_cache* buffers;
249 int max_frame_count, nframes;
250
251 uint8 usb_state;
252 bool in_progress;
253 status_t result;
254 int transferred_length;
255
256 struct task invoker;
257 struct cv condition;
258 };
259
260
261 extern "C" usb_error_t
usbd_transfer_setup(struct freebsd_usb_device * udev,const uint8_t * ifaces,struct usb_xfer ** ppxfer,const struct usb_config * setup_start,uint16_t n_setup,void * priv_sc,struct mtx * xfer_mtx)262 usbd_transfer_setup(struct freebsd_usb_device* udev,
263 const uint8_t* ifaces, struct usb_xfer** ppxfer,
264 const struct usb_config* setup_start, uint16_t n_setup,
265 void* priv_sc, struct mtx* xfer_mtx)
266 {
267 if (xfer_mtx == NULL)
268 xfer_mtx = &Giant;
269
270 // Make sure the taskqueue exists.
271 if (sUSBTaskqueue == NULL) {
272 mtx_lock(&sUSBLock);
273 if (sUSBTaskqueue == NULL) {
274 sUSBTaskqueue = taskqueue_create("usb taskq", 0,
275 taskqueue_thread_enqueue, &sUSBTaskqueue);
276 taskqueue_start_threads(&sUSBTaskqueue, 1, PZERO, "usb taskq");
277 }
278 mtx_unlock(&sUSBLock);
279 }
280
281 const usb_configuration_info* device_config = sUSB->get_configuration(
282 (usb_device)udev->haiku_usb_device);
283
284 for (const struct usb_config* setup = setup_start;
285 setup < (setup_start + n_setup); setup++) {
286 /* skip transfers w/o callbacks */
287 if (setup->callback == NULL)
288 continue;
289
290 struct usb_xfer* xfer = new usb_xfer;
291 xfer->mutex = xfer_mtx;
292 xfer->priv_sc = priv_sc;
293 xfer->priv = NULL;
294 xfer->callback = setup->callback;
295 xfer->flags = setup->flags;
296 xfer->max_data_length = setup->bufsize;
297
298 xfer->device = (usb_device)udev->haiku_usb_device;
299 xfer->type = setup->type;
300
301 xfer->pipe = -1;
302 uint8_t endpoint = setup->endpoint;
303 uint8_t iface_index = ifaces[setup->if_index];
304 if (endpoint == UE_ADDR_ANY) {
305 for (int i = 0; i < udev->endpoints_max; i++) {
306 if (UE_GET_XFERTYPE(udev->endpoints[i].edesc->bmAttributes) != xfer->type)
307 continue;
308
309 endpoint = udev->endpoints[i].edesc->bEndpointAddress;
310 break;
311 }
312 }
313 usb_interface_info* iface = device_config->interface[iface_index].active;
314 for (int i = 0; i < iface->endpoint_count; i++) {
315 if (iface->endpoint[i].descr->endpoint_address != endpoint)
316 continue;
317
318 xfer->pipe = iface->endpoint[i].handle;
319 break;
320 }
321 if (xfer->pipe == -1)
322 panic("failed to locate endpoint!");
323
324 xfer->nframes = setup->frames;
325 if (xfer->nframes == 0)
326 xfer->nframes = 1;
327 xfer->max_frame_count = xfer->nframes;
328 xfer->frames = (iovec*)calloc(xfer->max_frame_count, sizeof(iovec));
329 xfer->buffers = NULL;
330
331 xfer->usb_state = USB_ST_SETUP;
332 xfer->in_progress = false;
333 xfer->transferred_length = 0;
334 cv_init(&xfer->condition, "FreeBSD USB transfer");
335
336 if (xfer->flags.proxy_buffer)
337 panic("not yet supported");
338
339 ppxfer[setup - setup_start] = xfer;
340 }
341
342 return USB_ERR_NORMAL_COMPLETION;
343 }
344
345
346 extern "C" void
usbd_transfer_unsetup(struct usb_xfer ** pxfer,uint16_t n_setup)347 usbd_transfer_unsetup(struct usb_xfer** pxfer, uint16_t n_setup)
348 {
349 for (int i = 0; i < n_setup; i++) {
350 struct usb_xfer* xfer = pxfer[i];
351 usbd_transfer_drain(xfer);
352 cv_destroy(&xfer->condition);
353
354 if (xfer->buffers != NULL) {
355 for (int i = 0; i < xfer->max_frame_count; i++)
356 free(xfer->buffers[i].buffer);
357 free(xfer->buffers);
358 }
359 free(xfer->frames);
360 delete xfer;
361 }
362 }
363
364
365 extern "C" usb_frlength_t
usbd_xfer_max_len(struct usb_xfer * xfer)366 usbd_xfer_max_len(struct usb_xfer* xfer)
367 {
368 return xfer->max_data_length;
369 }
370
371
372 extern "C" void*
usbd_xfer_softc(struct usb_xfer * xfer)373 usbd_xfer_softc(struct usb_xfer* xfer)
374 {
375 return xfer->priv_sc;
376 }
377
378
379 extern "C" void*
usbd_xfer_get_priv(struct usb_xfer * xfer)380 usbd_xfer_get_priv(struct usb_xfer* xfer)
381 {
382 return xfer->priv;
383 }
384
385
386 extern "C" void
usbd_xfer_set_priv(struct usb_xfer * xfer,void * ptr)387 usbd_xfer_set_priv(struct usb_xfer* xfer, void* ptr)
388 {
389 xfer->priv = ptr;
390 }
391
392
393 extern "C" uint8_t
usbd_xfer_state(struct usb_xfer * xfer)394 usbd_xfer_state(struct usb_xfer* xfer)
395 {
396 return xfer->usb_state;
397 }
398
399
400 extern "C" void
usbd_xfer_set_frames(struct usb_xfer * xfer,usb_frcount_t n)401 usbd_xfer_set_frames(struct usb_xfer* xfer, usb_frcount_t n)
402 {
403 KASSERT(n <= uint32_t(xfer->max_frame_count), ("frame index overflow"));
404 xfer->nframes = n;
405 }
406
407
408 extern "C" void
usbd_xfer_set_frame_data(struct usb_xfer * xfer,usb_frcount_t frindex,void * ptr,usb_frlength_t len)409 usbd_xfer_set_frame_data(struct usb_xfer* xfer,
410 usb_frcount_t frindex, void* ptr, usb_frlength_t len)
411 {
412 KASSERT(frindex < uint32_t(xfer->nframes), ("frame index overflow"));
413
414 xfer->frames[frindex].iov_base = ptr;
415 xfer->frames[frindex].iov_len = len;
416 }
417
418
419 extern "C" void
usbd_xfer_set_frame_len(struct usb_xfer * xfer,usb_frcount_t frindex,usb_frlength_t len)420 usbd_xfer_set_frame_len(struct usb_xfer* xfer,
421 usb_frcount_t frindex, usb_frlength_t len)
422 {
423 KASSERT(frindex < uint32_t(xfer->nframes), ("frame index overflow"));
424 KASSERT(len <= uint32_t(xfer->max_data_length), ("length overflow"));
425
426 // Trigger buffer allocation if necessary.
427 if (xfer->frames[frindex].iov_base == NULL)
428 usbd_xfer_get_frame(xfer, frindex);
429
430 xfer->frames[frindex].iov_len = len;
431 }
432
433
434 extern "C" struct usb_page_cache*
usbd_xfer_get_frame(struct usb_xfer * xfer,usb_frcount_t frindex)435 usbd_xfer_get_frame(struct usb_xfer* xfer, usb_frcount_t frindex)
436 {
437 KASSERT(frindex < uint32_t(xfer->max_frame_count), ("frame index overflow"));
438 if (xfer->buffers == NULL)
439 xfer->buffers = (usb_page_cache*)calloc(xfer->max_frame_count, sizeof(usb_page_cache));
440
441 usb_page_cache* cache = &xfer->buffers[frindex];
442 if (cache->buffer == NULL) {
443 cache->buffer = malloc(xfer->max_data_length);
444 cache->length = xfer->max_data_length;
445 }
446
447 xfer->frames[frindex].iov_base = cache->buffer;
448 return cache;
449 }
450
451
452 extern "C" void
usbd_frame_zero(struct usb_page_cache * cache,usb_frlength_t offset,usb_frlength_t len)453 usbd_frame_zero(struct usb_page_cache* cache,
454 usb_frlength_t offset, usb_frlength_t len)
455 {
456 KASSERT((offset + len) < uint32_t(cache->length), ("buffer overflow"));
457 memset((uint8*)cache->buffer + offset, 0, len);
458 }
459
460
461 extern "C" void
usbd_copy_in(struct usb_page_cache * cache,usb_frlength_t offset,const void * ptr,usb_frlength_t len)462 usbd_copy_in(struct usb_page_cache* cache, usb_frlength_t offset,
463 const void *ptr, usb_frlength_t len)
464 {
465 KASSERT((offset + len) < uint32_t(cache->length), ("buffer overflow"));
466 memcpy((uint8*)cache->buffer + offset, ptr, len);
467 }
468
469
470 extern "C" void
usbd_copy_out(struct usb_page_cache * cache,usb_frlength_t offset,void * ptr,usb_frlength_t len)471 usbd_copy_out(struct usb_page_cache* cache, usb_frlength_t offset,
472 void *ptr, usb_frlength_t len)
473 {
474 KASSERT((offset + len) < uint32_t(cache->length), ("buffer overflow"));
475 memcpy(ptr, (uint8*)cache->buffer + offset, len);
476 }
477
478
479 extern "C" void
usbd_m_copy_in(struct usb_page_cache * cache,usb_frlength_t dst_offset,struct mbuf * m,usb_size_t src_offset,usb_frlength_t src_len)480 usbd_m_copy_in(struct usb_page_cache* cache, usb_frlength_t dst_offset,
481 struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len)
482 {
483 m_copydata(m, src_offset, src_len, (caddr_t)((uint8*)cache->buffer + dst_offset));
484 }
485
486
487 extern "C" void
usbd_xfer_set_stall(struct usb_xfer * xfer)488 usbd_xfer_set_stall(struct usb_xfer *xfer)
489 {
490 // Not needed?
491 }
492
493
494 static void
usbd_invoker(void * arg,int pending)495 usbd_invoker(void* arg, int pending)
496 {
497 struct usb_xfer* xfer = (struct usb_xfer*)arg;
498 mtx_lock(xfer->mutex);
499 xfer->in_progress = false;
500 xfer->usb_state = (xfer->result == B_OK) ? USB_ST_TRANSFERRED : USB_ST_ERROR;
501 xfer->callback(xfer, map_usb_error(xfer->result));
502 mtx_unlock(xfer->mutex);
503 cv_signal(&xfer->condition);
504 }
505
506
507 static void
usbd_callback(void * arg,status_t status,void * data,size_t actualLength)508 usbd_callback(void* arg, status_t status, void* data, size_t actualLength)
509 {
510 struct usb_xfer* xfer = (struct usb_xfer*)arg;
511 xfer->result = status;
512 xfer->transferred_length = actualLength;
513
514 TASK_INIT(&xfer->invoker, 0, usbd_invoker, xfer);
515 taskqueue_enqueue(sUSBTaskqueue, &xfer->invoker);
516 }
517
518
519 extern "C" void
usbd_transfer_start(struct usb_xfer * xfer)520 usbd_transfer_start(struct usb_xfer* xfer)
521 {
522 if (xfer->in_progress)
523 return;
524
525 xfer->usb_state = USB_ST_SETUP;
526 xfer->callback(xfer, USB_ERR_NOT_STARTED);
527 }
528
529
530 extern "C" void
usbd_transfer_submit(struct usb_xfer * xfer)531 usbd_transfer_submit(struct usb_xfer* xfer)
532 {
533 KASSERT(!xfer->in_progress, ("cannot submit in-progress transfer!"));
534
535 xfer->transferred_length = 0;
536 xfer->in_progress = true;
537 status_t status = B_NOT_SUPPORTED;
538 switch (xfer->type) {
539 case UE_BULK:
540 status = sUSB->queue_bulk_v(xfer->pipe, xfer->frames, xfer->nframes, usbd_callback, xfer);
541 break;
542
543 case UE_INTERRUPT:
544 KASSERT(xfer->nframes == 1, ("invalid frame count for interrupt transfer"));
545 status = sUSB->queue_interrupt(xfer->pipe,
546 xfer->frames[0].iov_base, xfer->frames[0].iov_len,
547 usbd_callback, xfer);
548 break;
549
550 default:
551 panic("unhandled pipe type %d", xfer->type);
552 }
553
554 if (status != B_OK)
555 usbd_callback(xfer, status, NULL, 0);
556 }
557
558
559 extern "C" void
usbd_transfer_stop(struct usb_xfer * xfer)560 usbd_transfer_stop(struct usb_xfer* xfer)
561 {
562 if (xfer == NULL)
563 return;
564 mtx_assert(xfer->mutex, MA_OWNED);
565
566 if (!xfer->in_progress)
567 return;
568
569 // Unfortunately we have no way of cancelling just one transfer.
570 sUSB->cancel_queued_transfers(xfer->pipe);
571 }
572
573
574 extern "C" void
usbd_transfer_drain(struct usb_xfer * xfer)575 usbd_transfer_drain(struct usb_xfer* xfer)
576 {
577 if (xfer == NULL)
578 return;
579
580 mtx_lock(xfer->mutex);
581 usbd_transfer_stop(xfer);
582 while (xfer->in_progress)
583 cv_wait(&xfer->condition, xfer->mutex);
584 mtx_unlock(xfer->mutex);
585 }
586
587
588 extern "C" void
usbd_xfer_status(struct usb_xfer * xfer,int * actlen,int * sumlen,int * aframes,int * nframes)589 usbd_xfer_status(struct usb_xfer* xfer, int* actlen, int* sumlen, int* aframes, int* nframes)
590 {
591 if (actlen)
592 *actlen = xfer->transferred_length;
593 if (sumlen) {
594 int sum = 0;
595 for (int i = 0; i < xfer->nframes; i++)
596 sum += xfer->frames[i].iov_len;
597 *sumlen = sum;
598 }
599 if (aframes) {
600 int length = xfer->transferred_length;
601 int frames = 0;
602 for (int i = 0; i < xfer->nframes && length > 0; i++) {
603 length -= xfer->frames[i].iov_len;
604 if (length >= 0)
605 frames++;
606 }
607 *aframes = frames;
608 }
609 if (nframes)
610 *nframes = xfer->nframes;
611 }
612