xref: /haiku/src/libs/compat/freebsd_network/usb.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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
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
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
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
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)
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)
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 
141 	uaa->info.bInterfaceClass = iface->descr->interface_class;
142 	uaa->info.bInterfaceSubClass = iface->descr->interface_subclass;
143 	uaa->info.bInterfaceProtocol = iface->descr->interface_protocol;
144 
145 	// TODO: bIface{Index,Num}, bConfig{Index,Num}
146 
147 	uaa->device = device;
148 	uaa->iface = NULL;
149 
150 	// TODO: fetch values for these?
151 	uaa->usb_mode = USB_MODE_HOST;
152 	uaa->port = 1;
153 	uaa->dev_state = UAA_DEV_READY;
154 
155 	return B_OK;
156 }
157 
158 
159 void
160 usb_cleanup_device(freebsd_usb_device* udev)
161 {
162 	for (int i = 0; i < USB_MAX_EP_UNITS; i++) {
163 		delete udev->endpoints[i].edesc;
164 		udev->endpoints[i].edesc = NULL;
165 	}
166 }
167 
168 
169 static usb_error_t
170 map_usb_error(status_t err)
171 {
172 	switch (err) {
173 	case B_OK:			return USB_ERR_NORMAL_COMPLETION;
174 	case B_DEV_STALLED:	return USB_ERR_STALLED;
175 	case B_CANCELED:	return USB_ERR_CANCELLED;
176 	case B_TIMED_OUT:	return USB_ERR_TIMEOUT;
177 	}
178 	return USB_ERR_INVAL;
179 }
180 
181 
182 extern "C" usb_error_t
183 usbd_do_request_flags(struct freebsd_usb_device* udev, struct mtx* mtx,
184 	struct usb_device_request* req, void* data, uint16_t flags,
185 	uint16_t* actlen, usb_timeout_t timeout)
186 {
187 	if (mtx != NULL)
188 		mtx_unlock(mtx);
189 
190 	// FIXME: timeouts
191 	// TODO: flags
192 
193 	size_t actualLen = 0;
194 	status_t ret = sUSB->send_request((usb_device)udev->haiku_usb_device,
195 		req->bmRequestType, req->bRequest,
196 		UGETW(req->wValue), UGETW(req->wIndex), UGETW(req->wLength),
197 		data, &actualLen);
198 	if (actlen)
199 		*actlen = actualLen;
200 
201 	if (mtx != NULL)
202 		mtx_lock(mtx);
203 
204 	return map_usb_error(ret);
205 }
206 
207 
208 enum usb_dev_speed
209 usbd_get_speed(struct freebsd_usb_device* udev)
210 {
211 	const usb_device_descriptor* descriptor = sUSB->get_device_descriptor(
212 		(usb_device)udev->haiku_usb_device);
213 	KASSERT(descriptor != NULL, ("no device"));
214 
215 	if (descriptor->usb_version >= 0x0300)
216 		return USB_SPEED_SUPER;
217 	else if (descriptor->usb_version >= 0x200)
218 		return USB_SPEED_HIGH;
219 	else if (descriptor->usb_version >= 0x110)
220 		return USB_SPEED_FULL;
221 	else if (descriptor->usb_version >= 0x100)
222 		return USB_SPEED_LOW;
223 
224 	panic("unknown USB version!");
225 	return (usb_dev_speed)-1;
226 }
227 
228 
229 struct usb_page_cache {
230 	void* buffer;
231 	size_t length;
232 };
233 
234 struct usb_xfer {
235 	struct mtx* mutex;
236 	void* priv_sc, *priv;
237 	usb_callback_t* callback;
238 	usb_xfer_flags flags;
239 	usb_frlength_t max_data_length;
240 
241 	usb_device device;
242 	uint8 type;
243 	usb_pipe pipe;
244 
245 	iovec* frames;
246 	usb_page_cache* buffers;
247 	int max_frame_count, nframes;
248 
249 	uint8 usb_state;
250 	bool in_progress;
251 	status_t result;
252 	int transferred_length;
253 
254 	struct task invoker;
255 	struct cv condition;
256 };
257 
258 
259 extern "C" usb_error_t
260 usbd_transfer_setup(struct freebsd_usb_device* udev,
261 	const uint8_t* ifaces, struct usb_xfer** ppxfer,
262 	const struct usb_config* setup_start, uint16_t n_setup,
263 	void* priv_sc, struct mtx* xfer_mtx)
264 {
265 	if (xfer_mtx == NULL)
266 		xfer_mtx = &Giant;
267 
268 	// Make sure the taskqueue exists.
269 	if (sUSBTaskqueue == NULL) {
270 		mtx_lock(&sUSBLock);
271 		if (sUSBTaskqueue == NULL) {
272 			sUSBTaskqueue = taskqueue_create("usb taskq", 0,
273 				taskqueue_thread_enqueue, &sUSBTaskqueue);
274 			taskqueue_start_threads(&sUSBTaskqueue, 1, PZERO, "usb taskq");
275 		}
276 		mtx_unlock(&sUSBLock);
277 	}
278 
279 	const usb_configuration_info* device_config = sUSB->get_configuration(
280 		(usb_device)udev->haiku_usb_device);
281 
282 	for (const struct usb_config* setup = setup_start;
283 			setup < (setup_start + n_setup); setup++) {
284 		/* skip transfers w/o callbacks */
285 		if (setup->callback == NULL)
286 			continue;
287 
288 		struct usb_xfer* xfer = new usb_xfer;
289 		xfer->mutex = xfer_mtx;
290 		xfer->priv_sc = priv_sc;
291 		xfer->priv = NULL;
292 		xfer->callback = setup->callback;
293 		xfer->flags = setup->flags;
294 		xfer->max_data_length = setup->bufsize;
295 
296 		xfer->device = (usb_device)udev->haiku_usb_device;
297 		xfer->type = setup->type;
298 
299 		xfer->pipe = -1;
300 		uint8_t endpoint = setup->endpoint;
301 		uint8_t iface_index = ifaces[setup->if_index];
302 		if (endpoint == UE_ADDR_ANY) {
303 			for (int i = 0; i < udev->endpoints_max; i++) {
304 				if (UE_GET_XFERTYPE(udev->endpoints[i].edesc->bmAttributes) != xfer->type)
305 					continue;
306 
307 				endpoint = udev->endpoints[i].edesc->bEndpointAddress;
308 				break;
309 			}
310 		}
311 		usb_interface_info* iface = device_config->interface[iface_index].active;
312 		for (int i = 0; i < iface->endpoint_count; i++) {
313 			if (iface->endpoint[i].descr->endpoint_address != endpoint)
314 				continue;
315 
316 			xfer->pipe = iface->endpoint[i].handle;
317 			break;
318 		}
319 		if (xfer->pipe == -1)
320 			panic("failed to locate endpoint!");
321 
322 		xfer->nframes = setup->frames;
323 		if (xfer->nframes == 0)
324 			xfer->nframes = 1;
325 		xfer->max_frame_count = xfer->nframes;
326 		xfer->frames = (iovec*)calloc(xfer->max_frame_count, sizeof(iovec));
327 		xfer->buffers = NULL;
328 
329 		xfer->usb_state = USB_ST_SETUP;
330 		xfer->in_progress = false;
331 		xfer->transferred_length = 0;
332 		cv_init(&xfer->condition, "FreeBSD USB transfer");
333 
334 		if (xfer->flags.proxy_buffer)
335 			panic("not yet supported");
336 
337 		ppxfer[setup - setup_start] = xfer;
338 	}
339 
340 	return USB_ERR_NORMAL_COMPLETION;
341 }
342 
343 
344 extern "C" void
345 usbd_transfer_unsetup(struct usb_xfer** pxfer, uint16_t n_setup)
346 {
347 	for (int i = 0; i < n_setup; i++) {
348 		struct usb_xfer* xfer = pxfer[i];
349 		usbd_transfer_drain(xfer);
350 		cv_destroy(&xfer->condition);
351 
352 		if (xfer->buffers != NULL) {
353 			for (int i = 0; i < xfer->max_frame_count; i++)
354 				free(xfer->buffers[i].buffer);
355 			free(xfer->buffers);
356 		}
357 		free(xfer->frames);
358 		delete xfer;
359 	}
360 }
361 
362 
363 extern "C" usb_frlength_t
364 usbd_xfer_max_len(struct usb_xfer* xfer)
365 {
366 	return xfer->max_data_length;
367 }
368 
369 
370 extern "C" void*
371 usbd_xfer_softc(struct usb_xfer* xfer)
372 {
373 	return xfer->priv_sc;
374 }
375 
376 
377 extern "C" void*
378 usbd_xfer_get_priv(struct usb_xfer* xfer)
379 {
380 	return xfer->priv;
381 }
382 
383 
384 extern "C" void
385 usbd_xfer_set_priv(struct usb_xfer* xfer, void* ptr)
386 {
387 	xfer->priv = ptr;
388 }
389 
390 
391 extern "C" uint8_t
392 usbd_xfer_state(struct usb_xfer* xfer)
393 {
394 	return xfer->usb_state;
395 }
396 
397 
398 extern "C" void
399 usbd_xfer_set_frames(struct usb_xfer* xfer, usb_frcount_t n)
400 {
401 	KASSERT(n <= uint32_t(xfer->max_frame_count), ("frame index overflow"));
402 	xfer->nframes = n;
403 }
404 
405 
406 extern "C" void
407 usbd_xfer_set_frame_data(struct usb_xfer* xfer,
408 	usb_frcount_t frindex, void* ptr, usb_frlength_t len)
409 {
410 	KASSERT(frindex < uint32_t(xfer->nframes), ("frame index overflow"));
411 
412 	xfer->frames[frindex].iov_base = ptr;
413 	xfer->frames[frindex].iov_len = len;
414 }
415 
416 
417 extern "C" void
418 usbd_xfer_set_frame_len(struct usb_xfer* xfer,
419 	usb_frcount_t frindex, usb_frlength_t len)
420 {
421 	KASSERT(frindex < uint32_t(xfer->nframes), ("frame index overflow"));
422 	KASSERT(len <= uint32_t(xfer->max_data_length), ("length overflow"));
423 
424 	// Trigger buffer allocation if necessary.
425 	if (xfer->frames[frindex].iov_base == NULL)
426 		usbd_xfer_get_frame(xfer, frindex);
427 
428 	xfer->frames[frindex].iov_len = len;
429 }
430 
431 
432 extern "C" struct usb_page_cache*
433 usbd_xfer_get_frame(struct usb_xfer* xfer, usb_frcount_t frindex)
434 {
435 	KASSERT(frindex < uint32_t(xfer->max_frame_count), ("frame index overflow"));
436 	if (xfer->buffers == NULL)
437 		xfer->buffers = (usb_page_cache*)calloc(xfer->max_frame_count, sizeof(usb_page_cache));
438 
439 	usb_page_cache* cache = &xfer->buffers[frindex];
440 	if (cache->buffer == NULL) {
441 		cache->buffer = malloc(xfer->max_data_length);
442 		cache->length = xfer->max_data_length;
443 	}
444 
445 	xfer->frames[frindex].iov_base = cache->buffer;
446 	return cache;
447 }
448 
449 
450 extern "C" void
451 usbd_frame_zero(struct usb_page_cache* cache,
452 	usb_frlength_t offset, usb_frlength_t len)
453 {
454 	KASSERT((offset + len) < uint32_t(cache->length), ("buffer overflow"));
455 	memset((uint8*)cache->buffer + offset, 0, len);
456 }
457 
458 
459 extern "C" void
460 usbd_copy_in(struct usb_page_cache* cache, usb_frlength_t offset,
461 	const void *ptr, usb_frlength_t len)
462 {
463 	KASSERT((offset + len) < uint32_t(cache->length), ("buffer overflow"));
464 	memcpy((uint8*)cache->buffer + offset, ptr, len);
465 }
466 
467 
468 extern "C" void
469 usbd_copy_out(struct usb_page_cache* cache, usb_frlength_t offset,
470 	void *ptr, usb_frlength_t len)
471 {
472 	KASSERT((offset + len) < uint32_t(cache->length), ("buffer overflow"));
473 	memcpy(ptr, (uint8*)cache->buffer + offset, len);
474 }
475 
476 
477 extern "C" void
478 usbd_m_copy_in(struct usb_page_cache* cache, usb_frlength_t dst_offset,
479 	struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len)
480 {
481 	m_copydata(m, src_offset, src_len, (caddr_t)((uint8*)cache->buffer + dst_offset));
482 }
483 
484 
485 extern "C" void
486 usbd_xfer_set_stall(struct usb_xfer *xfer)
487 {
488 	// Not needed?
489 }
490 
491 
492 static void
493 usbd_invoker(void* arg, int pending)
494 {
495 	struct usb_xfer* xfer = (struct usb_xfer*)arg;
496 	mtx_lock(xfer->mutex);
497 	xfer->in_progress = false;
498 	xfer->usb_state = (xfer->result == B_OK) ? USB_ST_TRANSFERRED : USB_ST_ERROR;
499 	xfer->callback(xfer, map_usb_error(xfer->result));
500 	mtx_unlock(xfer->mutex);
501 	cv_signal(&xfer->condition);
502 }
503 
504 
505 static void
506 usbd_callback(void* arg, status_t status, void* data, size_t actualLength)
507 {
508 	struct usb_xfer* xfer = (struct usb_xfer*)arg;
509 	xfer->result = status;
510 	xfer->transferred_length = actualLength;
511 
512 	TASK_INIT(&xfer->invoker, 0, usbd_invoker, xfer);
513 	taskqueue_enqueue(sUSBTaskqueue, &xfer->invoker);
514 }
515 
516 
517 extern "C" void
518 usbd_transfer_start(struct usb_xfer* xfer)
519 {
520 	if (xfer->in_progress)
521 		return;
522 
523 	xfer->usb_state = USB_ST_SETUP;
524 	xfer->callback(xfer, USB_ERR_NOT_STARTED);
525 }
526 
527 
528 extern "C" void
529 usbd_transfer_submit(struct usb_xfer* xfer)
530 {
531 	KASSERT(!xfer->in_progress, ("cannot submit in-progress transfer!"));
532 
533 	xfer->transferred_length = 0;
534 	xfer->in_progress = true;
535 	status_t status = B_NOT_SUPPORTED;
536 	switch (xfer->type) {
537 	case UE_BULK:
538 		status = sUSB->queue_bulk_v(xfer->pipe, xfer->frames, xfer->nframes, usbd_callback, xfer);
539 		break;
540 
541 	case UE_INTERRUPT:
542 		KASSERT(xfer->nframes == 1, ("invalid frame count for interrupt transfer"));
543 		status = sUSB->queue_interrupt(xfer->pipe,
544 			xfer->frames[0].iov_base, xfer->frames[0].iov_len,
545 			usbd_callback, xfer);
546 		break;
547 
548 	default:
549 		panic("unhandled pipe type %d", xfer->type);
550 	}
551 
552 	if (status != B_OK)
553 		usbd_callback(xfer, status, NULL, 0);
554 }
555 
556 
557 extern "C" void
558 usbd_transfer_stop(struct usb_xfer* xfer)
559 {
560 	if (xfer == NULL)
561 		return;
562 	mtx_assert(xfer->mutex, MA_OWNED);
563 
564 	if (!xfer->in_progress)
565 		return;
566 
567 	// Unfortunately we have no way of cancelling just one transfer.
568 	sUSB->cancel_queued_transfers(xfer->pipe);
569 }
570 
571 
572 extern "C" void
573 usbd_transfer_drain(struct usb_xfer* xfer)
574 {
575 	if (xfer == NULL)
576 		return;
577 
578 	mtx_lock(xfer->mutex);
579 	usbd_transfer_stop(xfer);
580 	while (xfer->in_progress)
581 		cv_wait(&xfer->condition, xfer->mutex);
582 	mtx_unlock(xfer->mutex);
583 }
584 
585 
586 extern "C" void
587 usbd_xfer_status(struct usb_xfer* xfer, int* actlen, int* sumlen, int* aframes, int* nframes)
588 {
589 	if (actlen)
590 		*actlen = xfer->transferred_length;
591 	if (sumlen) {
592 		int sum = 0;
593 		for (int i = 0; i < xfer->nframes; i++)
594 			sum += xfer->frames[i].iov_len;
595 		*sumlen = sum;
596 	}
597 	if (aframes) {
598 		int length = xfer->transferred_length;
599 		int frames = 0;
600 		for (int i = 0; i < xfer->nframes && length > 0; i++) {
601 			length -= xfer->frames[i].iov_len;
602 			if (length >= 0)
603 				frames++;
604 		}
605 		*aframes = frames;
606 	}
607 	if (nframes)
608 		*nframes = xfer->nframes;
609 }
610