xref: /haiku/src/libs/compat/freebsd_network/usb.cpp (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
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 usb_module_info* sUSB = NULL;
30 struct taskqueue* sUSBTaskqueue = NULL;
31 
32 
33 status_t
34 init_usb()
35 {
36 	if (sUSB != NULL)
37 		return B_OK;
38 
39 	if (get_module(B_USB_MODULE_NAME, (module_info**)&sUSB) != B_OK) {
40 		dprintf("cannot get module \"%s\"\n", B_USB_MODULE_NAME);
41 		return B_ERROR;
42 	}
43 	return B_OK;
44 }
45 
46 
47 void
48 uninit_usb()
49 {
50 	if (sUSB != NULL)
51 		put_module(B_USB_MODULE_NAME);
52 	if (sUSBTaskqueue != NULL)
53 		taskqueue_free(sUSBTaskqueue);
54 
55 	sUSB = NULL;
56 	sUSBTaskqueue = NULL;
57 }
58 
59 
60 status_t
61 get_next_usb_device(uint32* cookie, freebsd_usb_device* result)
62 {
63 	// We cheat here: since USB IDs are sequential, instead of doing a
64 	// complicated parent/child iteration dance, we simply request device
65 	// descriptors and let the USB stack figure out the rest.
66 	//
67 	// It would be better if we used USB->register_driver, but that is not
68 	// an option at present for a variety of reasons...
69 	const usb_configuration_info* config;
70 	usb_device current;
71 	while (*cookie < 1024) {
72 		current = *cookie;
73 		*cookie = *cookie + 1;
74 
75 		config = sUSB->get_configuration(current);
76 		if (config != NULL)
77 			break;
78 	}
79 	if (config == NULL)
80 		return ENODEV;
81 
82 	result->haiku_usb_device = current;
83 	result->endpoints_max = 0;
84 	for (size_t i = 0; i < config->interface_count; i++) {
85 		usb_interface_info* iface = config->interface[i].active;
86 		for (size_t j = 0; j < iface->endpoint_count; j++) {
87 			const int rep = result->endpoints_max++;
88 			result->endpoints[rep].iface_index = i;
89 
90 			static_assert(sizeof(freebsd_usb_endpoint_descriptor)
91 				== sizeof(usb_endpoint_descriptor), "size mismatch");
92 
93 			if (result->endpoints[rep].edesc == NULL)
94 				result->endpoints[rep].edesc = new freebsd_usb_endpoint_descriptor;
95 
96 			memcpy(result->endpoints[rep].edesc, iface->endpoint[j].descr,
97 				sizeof(usb_endpoint_descriptor));
98 		}
99 	}
100 
101 	return B_OK;
102 }
103 
104 
105 status_t
106 get_usb_device_attach_arg(struct freebsd_usb_device* device, struct usb_attach_arg* uaa)
107 {
108 	memset(uaa, 0, sizeof(struct usb_attach_arg));
109 
110 	const usb_device_descriptor* device_desc =
111 		sUSB->get_device_descriptor(device->haiku_usb_device);
112 	if (!device_desc)
113 		return B_BAD_VALUE;
114 
115 	uaa->info.idVendor = device_desc->vendor_id;
116 	uaa->info.idProduct = device_desc->product_id;
117 	uaa->info.bcdDevice = device_desc->device_version;
118 	uaa->info.bDeviceClass = device_desc->device_class;
119 	uaa->info.bDeviceSubClass = device_desc->device_subclass;
120 	uaa->info.bDeviceProtocol = device_desc->device_protocol;
121 
122 	const usb_configuration_info* config = sUSB->get_configuration(device->haiku_usb_device);
123 	if (!device_desc)
124 		return B_BAD_VALUE;
125 
126 	// TODO: represent more than just interface[0], but how?
127 	usb_interface_info* iface = config->interface[0].active;
128 
129 	uaa->info.bInterfaceClass = iface->descr->interface_class;
130 	uaa->info.bInterfaceSubClass = iface->descr->interface_subclass;
131 	uaa->info.bInterfaceProtocol = iface->descr->interface_protocol;
132 
133 	// TODO: bIface{Index,Num}, bConfig{Index,Num}
134 
135 	uaa->device = device;
136 	uaa->iface = NULL;
137 
138 	// TODO: fetch values for these?
139 	uaa->usb_mode = USB_MODE_HOST;
140 	uaa->port = 1;
141 	uaa->dev_state = UAA_DEV_READY;
142 
143 	return B_OK;
144 }
145 
146 
147 void
148 usb_cleanup_device(freebsd_usb_device* udev)
149 {
150 	for (int i = 0; i < USB_MAX_EP_UNITS; i++) {
151 		delete udev->endpoints[i].edesc;
152 		udev->endpoints[i].edesc = NULL;
153 	}
154 }
155 
156 
157 static usb_error_t
158 map_usb_error(status_t err)
159 {
160 	switch (err) {
161 	case B_OK:			return USB_ERR_NORMAL_COMPLETION;
162 	case B_DEV_STALLED:	return USB_ERR_STALLED;
163 	case B_CANCELED:	return USB_ERR_CANCELLED;
164 	}
165 	return USB_ERR_INVAL;
166 }
167 
168 
169 extern "C" usb_error_t
170 usbd_do_request_flags(struct freebsd_usb_device* udev, struct mtx* mtx,
171 	struct usb_device_request* req, void* data, uint16_t flags,
172 	uint16_t* actlen, usb_timeout_t timeout)
173 {
174 	if (mtx != NULL)
175 		mtx_unlock(mtx);
176 
177 	// FIXME: timeouts
178 	// TODO: flags
179 
180 	size_t actualLen = 0;
181 	status_t ret = sUSB->send_request((usb_device)udev->haiku_usb_device,
182 		req->bmRequestType, req->bRequest,
183 		UGETW(req->wValue), UGETW(req->wIndex), UGETW(req->wLength),
184 		data, &actualLen);
185 	if (actlen)
186 		*actlen = actualLen;
187 
188 	if (mtx != NULL)
189 		mtx_lock(mtx);
190 
191 	return map_usb_error(ret);
192 }
193 
194 
195 enum usb_dev_speed
196 usbd_get_speed(struct freebsd_usb_device* udev)
197 {
198 	const usb_device_descriptor* descriptor = sUSB->get_device_descriptor(
199 		(usb_device)udev->haiku_usb_device);
200 	KASSERT(descriptor != NULL, ("no device"));
201 
202 	if (descriptor->usb_version >= 0x0300)
203 		return USB_SPEED_SUPER;
204 	else if (descriptor->usb_version >= 0x200)
205 		return USB_SPEED_HIGH;
206 	else if (descriptor->usb_version >= 0x110)
207 		return USB_SPEED_FULL;
208 	else if (descriptor->usb_version >= 0x100)
209 		return USB_SPEED_LOW;
210 
211 	panic("unknown USB version!");
212 	return (usb_dev_speed)-1;
213 }
214 
215 
216 struct usb_xfer {
217 	struct mtx* mutex;
218 	void* priv_sc;
219 	usb_callback_t* callback;
220 	usb_xfer_flags flags;
221 	usb_frlength_t max_data_length;
222 
223 	usb_device device;
224 	uint8 type;
225 	usb_pipe pipe;
226 	iovec* frames;
227 	int nframes;
228 
229 	uint8 usb_state;
230 	bool in_progress;
231 	status_t result;
232 	int transferred_length;
233 
234 	struct task invoker;
235 	struct cv condition;
236 };
237 
238 
239 extern "C" usb_error_t
240 usbd_transfer_setup(struct freebsd_usb_device* udev,
241 	const uint8_t* ifaces, struct usb_xfer** ppxfer,
242 	const struct usb_config* setup_start, uint16_t n_setup,
243 	void* priv_sc, struct mtx* xfer_mtx)
244 {
245 	if (xfer_mtx == NULL)
246 		xfer_mtx = &Giant;
247 
248 	// Make sure the taskqueue exists.
249 	if (sUSBTaskqueue == NULL) {
250 		mtx_lock(&Giant);
251 		if (sUSBTaskqueue == NULL) {
252 			sUSBTaskqueue = taskqueue_create("usb taskq", 0,
253 				taskqueue_thread_enqueue, &sUSBTaskqueue);
254 			taskqueue_start_threads(&sUSBTaskqueue, 1, PZERO, "usb taskq");
255 		}
256 		mtx_unlock(&Giant);
257 	}
258 
259 	const usb_configuration_info* device_config = sUSB->get_configuration(
260 		(usb_device)udev->haiku_usb_device);
261 
262 	for (const struct usb_config* setup = setup_start;
263 			setup < (setup_start + n_setup); setup++) {
264 		/* skip transfers w/o callbacks */
265 		if (setup->callback == NULL)
266 			continue;
267 
268 		struct usb_xfer* xfer = new usb_xfer;
269 		xfer->mutex = xfer_mtx;
270 		xfer->priv_sc = priv_sc;
271 		xfer->callback = setup->callback;
272 		xfer->flags = setup->flags;
273 		xfer->max_data_length = setup->bufsize;
274 
275 		xfer->device = (usb_device)udev->haiku_usb_device;
276 		xfer->type = setup->type;
277 
278 		xfer->pipe = -1;
279 		uint8_t endpoint = setup->endpoint;
280 		uint8_t iface_index = ifaces[setup->if_index];
281 		if (endpoint == UE_ADDR_ANY) {
282 			for (int i = 0; i < udev->endpoints_max; i++) {
283 				if (UE_GET_XFERTYPE(udev->endpoints[i].edesc->bmAttributes) != xfer->type)
284 					continue;
285 
286 				endpoint = udev->endpoints[i].edesc->bEndpointAddress;
287 				break;
288 			}
289 		}
290 		usb_interface_info* iface = device_config->interface[iface_index].active;
291 		for (int i = 0; i < iface->endpoint_count; i++) {
292 			if (iface->endpoint[i].descr->endpoint_address != endpoint)
293 				continue;
294 
295 			xfer->pipe = iface->endpoint[i].handle;
296 			break;
297 		}
298 		if (xfer->pipe == -1)
299 			panic("failed to locate endpoint!");
300 
301 		xfer->nframes = setup->frames;
302 		if (xfer->nframes == 0)
303 			xfer->nframes = 1;
304 		xfer->frames = (iovec*)calloc(xfer->nframes, sizeof(iovec));
305 
306 		xfer->usb_state = USB_ST_SETUP;
307 		xfer->in_progress = false;
308 		xfer->transferred_length = 0;
309 		cv_init(&xfer->condition, "FreeBSD USB transfer");
310 
311 		if (xfer->flags.proxy_buffer)
312 			panic("not yet supported");
313 
314 		ppxfer[setup - setup_start] = xfer;
315 	}
316 
317 	return USB_ERR_NORMAL_COMPLETION;
318 }
319 
320 
321 extern "C" void
322 usbd_transfer_unsetup(struct usb_xfer** pxfer, uint16_t n_setup)
323 {
324 	for (int i = 0; i < n_setup; i++) {
325 		struct usb_xfer* xfer = pxfer[i];
326 		usbd_transfer_drain(xfer);
327 		cv_destroy(&xfer->condition);
328 		free(xfer->frames);
329 		delete xfer;
330 	}
331 }
332 
333 
334 extern "C" usb_frlength_t
335 usbd_xfer_max_len(struct usb_xfer* xfer)
336 {
337 	return xfer->max_data_length;
338 }
339 
340 
341 extern "C" void*
342 usbd_xfer_softc(struct usb_xfer* xfer)
343 {
344 	return xfer->priv_sc;
345 }
346 
347 
348 extern "C" uint8_t
349 usbd_xfer_state(struct usb_xfer* xfer)
350 {
351 	return xfer->usb_state;
352 }
353 
354 
355 extern "C" void
356 usbd_xfer_set_frame_data(struct usb_xfer* xfer,
357 	usb_frcount_t frindex, void* ptr, usb_frlength_t len)
358 {
359 	KASSERT(frindex < uint32_t(xfer->nframes), ("frame index overflow"));
360 
361 	xfer->frames[frindex].iov_base = ptr;
362 	xfer->frames[frindex].iov_len = len;
363 }
364 
365 
366 extern "C" void
367 usbd_xfer_set_stall(struct usb_xfer *xfer)
368 {
369 	// Not needed?
370 }
371 
372 
373 static void
374 usbd_invoker(void* arg, int pending)
375 {
376 	struct usb_xfer* xfer = (struct usb_xfer*)arg;
377 	mtx_lock(xfer->mutex);
378 	xfer->in_progress = false;
379 	xfer->usb_state = (xfer->result == B_OK) ? USB_ST_TRANSFERRED : USB_ST_ERROR;
380 	xfer->callback(xfer, map_usb_error(xfer->result));
381 	mtx_unlock(xfer->mutex);
382 	cv_signal(&xfer->condition);
383 }
384 
385 
386 static void
387 usbd_callback(void* arg, status_t status, void* data, size_t actualLength)
388 {
389 	struct usb_xfer* xfer = (struct usb_xfer*)arg;
390 	xfer->result = status;
391 	xfer->transferred_length = actualLength;
392 
393 	TASK_INIT(&xfer->invoker, 0, usbd_invoker, xfer);
394 	taskqueue_enqueue(sUSBTaskqueue, &xfer->invoker);
395 }
396 
397 
398 extern "C" void
399 usbd_transfer_start(struct usb_xfer* xfer)
400 {
401 	if (xfer->in_progress)
402 		return;
403 
404 	xfer->usb_state = USB_ST_SETUP;
405 	xfer->callback(xfer, USB_ERR_NOT_STARTED);
406 }
407 
408 
409 extern "C" void
410 usbd_transfer_submit(struct usb_xfer* xfer)
411 {
412 	KASSERT(!xfer->in_progress, ("cannot submit in-progress transfer!"));
413 
414 	xfer->transferred_length = 0;
415 	xfer->in_progress = true;
416 	status_t status = B_NOT_SUPPORTED;
417 	switch (xfer->type) {
418 	case UE_BULK:
419 		status = sUSB->queue_bulk_v(xfer->pipe, xfer->frames, xfer->nframes, usbd_callback, xfer);
420 		break;
421 
422 	case UE_INTERRUPT:
423 		KASSERT(xfer->nframes == 1, ("invalid frame count for interrupt transfer"));
424 		status = sUSB->queue_interrupt(xfer->pipe,
425 			xfer->frames[0].iov_base, xfer->frames[0].iov_len,
426 			usbd_callback, xfer);
427 		break;
428 
429 	default:
430 		panic("unhandled pipe type %d", xfer->type);
431 	}
432 
433 	if (status != B_OK)
434 		usbd_callback(xfer, status, NULL, 0);
435 }
436 
437 
438 extern "C" void
439 usbd_transfer_stop(struct usb_xfer* xfer)
440 {
441 	if (xfer == NULL)
442 		return;
443 	mtx_assert(xfer->mutex, MA_OWNED);
444 
445 	if (!xfer->in_progress)
446 		return;
447 
448 	// Unfortunately we have no way of cancelling just one transfer.
449 	sUSB->cancel_queued_transfers(xfer->pipe);
450 }
451 
452 
453 extern "C" void
454 usbd_transfer_drain(struct usb_xfer* xfer)
455 {
456 	if (xfer == NULL)
457 		return;
458 
459 	mtx_lock(xfer->mutex);
460 	usbd_transfer_stop(xfer);
461 	while (xfer->in_progress)
462 		cv_wait(&xfer->condition, xfer->mutex);
463 	mtx_unlock(xfer->mutex);
464 }
465 
466 
467 extern "C" void
468 usbd_xfer_status(struct usb_xfer* xfer, int* actlen, int* sumlen, int* aframes, int* nframes)
469 {
470 	if (actlen)
471 		*actlen = xfer->transferred_length;
472 	if (sumlen) {
473 		int sum = 0;
474 		for (int i = 0; i < xfer->nframes; i++)
475 			sum += xfer->frames[i].iov_len;
476 		*sumlen = sum;
477 	}
478 	if (aframes) {
479 		int length = xfer->transferred_length;
480 		int frames = 0;
481 		for (int i = 0; i < xfer->nframes && length > 0; i++) {
482 			length -= xfer->frames[i].iov_len;
483 			if (length >= 0)
484 				frames++;
485 		}
486 		*aframes = frames;
487 	}
488 	if (nframes)
489 		*nframes = xfer->nframes;
490 }
491