source: mainline/uspace/lib/usb/src/request.c@ e2b58a63

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e2b58a63 was a6add7a, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

Comment and documentation fixes

In the process also removed several functions that are no longer
needed.

This commit shall not affect functionality in any way.

  • Property mode set to 100644
File size: 18.4 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libusb
30 * @{
31 */
32/** @file
33 * Standard USB requests (implementation).
34 */
35#include <usb/request.h>
36#include <errno.h>
37#include <assert.h>
38#include <usb/debug.h>
39
40#define MAX_DATA_LENGTH ((size_t)(0xFFFF))
41
42/** Generic wrapper for SET requests using standard control request format.
43 *
44 * @see usb_endpoint_pipe_control_write
45 *
46 * @param pipe Pipe used for the communication.
47 * @param request_type Request type (standard/class/vendor).
48 * @param recipient Request recipient (e.g. device or endpoint).
49 * @param request Actual request (e.g. GET_DESCRIPTOR).
50 * @param value Value of @c wValue field of setup packet
51 * (must be in USB endianness).
52 * @param index Value of @c wIndex field of setup packet
53 * (must be in USB endianness).
54 * @param data Data to be sent during DATA stage
55 * (expected to be in USB endianness).
56 * @param data_size Size of the @p data buffer (in native endianness).
57 * @return Error code.
58 * @retval EBADMEM @p pipe is NULL.
59 * @retval EBADMEM @p data is NULL and @p data_size is not zero.
60 * @retval ERANGE Data buffer too large.
61 */
62int usb_control_request_set(usb_endpoint_pipe_t *pipe,
63 usb_request_type_t request_type, usb_request_recipient_t recipient,
64 uint8_t request,
65 uint16_t value, uint16_t index,
66 void *data, size_t data_size)
67{
68 if (pipe == NULL) {
69 return EBADMEM;
70 }
71
72 if (data_size > MAX_DATA_LENGTH) {
73 return ERANGE;
74 }
75
76 if ((data_size > 0) && (data == NULL)) {
77 return EBADMEM;
78 }
79
80 /*
81 * TODO: check that @p request_type and @p recipient are
82 * within ranges.
83 */
84
85 usb_device_request_setup_packet_t setup_packet;
86 setup_packet.request_type = (request_type << 5) | recipient;
87 setup_packet.request = request;
88 setup_packet.value = value;
89 setup_packet.index = index;
90 setup_packet.length = (uint16_t) data_size;
91
92 int rc = usb_endpoint_pipe_control_write(pipe,
93 &setup_packet, sizeof(setup_packet),
94 data, data_size);
95
96 return rc;
97}
98
99 /** Generic wrapper for GET requests using standard control request format.
100 *
101 * @see usb_endpoint_pipe_control_read
102 *
103 * @param pipe Pipe used for the communication.
104 * @param request_type Request type (standard/class/vendor).
105 * @param recipient Request recipient (e.g. device or endpoint).
106 * @param request Actual request (e.g. GET_DESCRIPTOR).
107 * @param value Value of @c wValue field of setup packet
108 * (must be in USB endianness).
109 * @param index Value of @c wIndex field of setup packet
110 * (must be in USB endianness).
111 * @param data Buffer where to store data accepted during the DATA stage.
112 * (they will come in USB endianness).
113 * @param data_size Size of the @p data buffer
114 * (in native endianness).
115 * @param actual_data_size Actual size of transfered data
116 * (in native endianness).
117 * @return Error code.
118 * @retval EBADMEM @p pipe is NULL.
119 * @retval EBADMEM @p data is NULL and @p data_size is not zero.
120 * @retval ERANGE Data buffer too large.
121 */
122int usb_control_request_get(usb_endpoint_pipe_t *pipe,
123 usb_request_type_t request_type, usb_request_recipient_t recipient,
124 uint8_t request,
125 uint16_t value, uint16_t index,
126 void *data, size_t data_size, size_t *actual_data_size)
127{
128 if (pipe == NULL) {
129 return EBADMEM;
130 }
131
132 if (data_size > MAX_DATA_LENGTH) {
133 return ERANGE;
134 }
135
136 if ((data_size > 0) && (data == NULL)) {
137 return EBADMEM;
138 }
139
140 /*
141 * TODO: check that @p request_type and @p recipient are
142 * within ranges.
143 */
144
145 usb_device_request_setup_packet_t setup_packet;
146 setup_packet.request_type = 128 | (request_type << 5) | recipient;
147 setup_packet.request = request;
148 setup_packet.value = value;
149 setup_packet.index = index;
150 setup_packet.length = (uint16_t) data_size;
151
152 int rc = usb_endpoint_pipe_control_read(pipe,
153 &setup_packet, sizeof(setup_packet),
154 data, data_size, actual_data_size);
155
156 return rc;
157}
158
159/** Change address of connected device.
160 * This function automatically updates the backing connection to point to
161 * the new address.
162 *
163 * @param pipe Control endpoint pipe (session must be already started).
164 * @param new_address New USB address to be set (in native endianness).
165 * @return Error code.
166 */
167int usb_request_set_address(usb_endpoint_pipe_t *pipe,
168 usb_address_t new_address)
169{
170 if ((new_address < 0) || (new_address >= USB11_ADDRESS_MAX)) {
171 return EINVAL;
172 }
173
174 uint16_t addr = uint16_host2usb((uint16_t)new_address);
175
176 int rc = usb_control_request_set(pipe,
177 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
178 USB_DEVREQ_SET_ADDRESS,
179 addr, 0,
180 NULL, 0);
181
182 if (rc != EOK) {
183 return rc;
184 }
185
186 assert(pipe->wire != NULL);
187 /* TODO: prevent other from accessing wire now. */
188 pipe->wire->address = new_address;
189
190 return EOK;
191}
192
193/** Retrieve USB descriptor of a USB device.
194 *
195 * @param[in] pipe Control endpoint pipe (session must be already started).
196 * @param[in] request_type Request type (standard/class/vendor).
197 * @param[in] descriptor_type Descriptor type (device/configuration/HID/...).
198 * @param[in] descriptor_index Descriptor index.
199 * @param[in] language Language index.
200 * @param[out] buffer Buffer where to store the retrieved descriptor.
201 * @param[in] size Size of the @p buffer.
202 * @param[out] actual_size Number of bytes actually transferred.
203 * @return Error code.
204 */
205int usb_request_get_descriptor(usb_endpoint_pipe_t *pipe,
206 usb_request_type_t request_type, usb_request_recipient_t recipient,
207 uint8_t descriptor_type, uint8_t descriptor_index,
208 uint16_t language,
209 void *buffer, size_t size, size_t *actual_size)
210{
211 if (buffer == NULL) {
212 return EBADMEM;
213 }
214 if (size == 0) {
215 return EINVAL;
216 }
217
218 uint16_t wValue = descriptor_index | (descriptor_type << 8);
219
220 return usb_control_request_get(pipe,
221 request_type, recipient,
222 USB_DEVREQ_GET_DESCRIPTOR,
223 wValue, language,
224 buffer, size, actual_size);
225}
226
227/** Retrieve USB descriptor, allocate space for it.
228 *
229 * @param[in] pipe Control endpoint pipe (session must be already started).
230 * @param[in] request_type Request type (standard/class/vendor).
231 * @param[in] descriptor_type Descriptor type (device/configuration/HID/...).
232 * @param[in] descriptor_index Descriptor index.
233 * @param[in] language Language index.
234 * @param[out] buffer_ptr Where to store pointer to allocated buffer.
235 * @param[out] buffer_size Where to store the size of the descriptor.
236 * @return
237 */
238int usb_request_get_descriptor_alloc(usb_endpoint_pipe_t * pipe,
239 usb_request_type_t request_type, usb_request_recipient_t recipient,
240 uint8_t descriptor_type, uint8_t descriptor_index,
241 uint16_t language,
242 void **buffer_ptr, size_t *buffer_size)
243{
244 if (buffer_ptr == NULL) {
245 return EBADMEM;
246 }
247
248 int rc;
249
250 /*
251 * Get only first byte to retrieve descriptor length.
252 */
253 uint8_t tmp_buffer[1];
254 size_t bytes_transfered;
255 rc = usb_request_get_descriptor(pipe, request_type, recipient,
256 descriptor_type, descriptor_index, language,
257 &tmp_buffer, 1, &bytes_transfered);
258 if (rc != EOK) {
259 return rc;
260 }
261 if (bytes_transfered != 1) {
262 /* FIXME: some better error code? */
263 return ESTALL;
264 }
265
266 size_t size = tmp_buffer[0];
267 if (size == 0) {
268 /* FIXME: some better error code? */
269 return ESTALL;
270 }
271
272 /*
273 * Allocate buffer and get the descriptor again.
274 */
275 void *buffer = malloc(size);
276 if (buffer == NULL) {
277 return ENOMEM;
278 }
279
280 rc = usb_request_get_descriptor(pipe, request_type, recipient,
281 descriptor_type, descriptor_index, language,
282 buffer, size, &bytes_transfered);
283 if (rc != EOK) {
284 free(buffer);
285 return rc;
286 }
287 if (bytes_transfered != size) {
288 free(buffer);
289 /* FIXME: some better error code? */
290 return ESTALL;
291 }
292
293 *buffer_ptr = buffer;
294 if (buffer_size != NULL) {
295 *buffer_size = size;
296 }
297
298 return EOK;
299}
300
301/** Retrieve standard device descriptor of a USB device.
302 *
303 * @param[in] pipe Control endpoint pipe (session must be already started).
304 * @param[out] descriptor Storage for the device descriptor.
305 * @return Error code.
306 */
307int usb_request_get_device_descriptor(usb_endpoint_pipe_t *pipe,
308 usb_standard_device_descriptor_t *descriptor)
309{
310 if (descriptor == NULL) {
311 return EBADMEM;
312 }
313
314 size_t actually_transferred = 0;
315 usb_standard_device_descriptor_t descriptor_tmp;
316 int rc = usb_request_get_descriptor(pipe,
317 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
318 USB_DESCTYPE_DEVICE, 0, 0,
319 &descriptor_tmp, sizeof(descriptor_tmp),
320 &actually_transferred);
321
322 if (rc != EOK) {
323 return rc;
324 }
325
326 /* Verify that all data has been transferred. */
327 if (actually_transferred < sizeof(descriptor_tmp)) {
328 return ELIMIT;
329 }
330
331 /* Everything is okay, copy the descriptor. */
332 memcpy(descriptor, &descriptor_tmp,
333 sizeof(descriptor_tmp));
334
335 return EOK;
336}
337
338/** Retrieve configuration descriptor of a USB device.
339 *
340 * The function does not retrieve additional data binded with configuration
341 * descriptor (such as its interface and endpoint descriptors) - use
342 * usb_request_get_full_configuration_descriptor() instead.
343 *
344 * @param[in] pipe Control endpoint pipe (session must be already started).
345 * @param[in] index Descriptor index.
346 * @param[out] descriptor Storage for the device descriptor.
347 * @return Error code.
348 */
349int usb_request_get_bare_configuration_descriptor(usb_endpoint_pipe_t *pipe,
350 int index, usb_standard_configuration_descriptor_t *descriptor)
351{
352 if (descriptor == NULL) {
353 return EBADMEM;
354 }
355
356 if ((index < 0) || (index > 0xFF)) {
357 return ERANGE;
358 }
359
360 size_t actually_transferred = 0;
361 usb_standard_configuration_descriptor_t descriptor_tmp;
362 int rc = usb_request_get_descriptor(pipe,
363 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
364 USB_DESCTYPE_CONFIGURATION, index, 0,
365 &descriptor_tmp, sizeof(descriptor_tmp),
366 &actually_transferred);
367 if (rc != EOK) {
368 return rc;
369 }
370
371 /* Verify that all data has been transferred. */
372 if (actually_transferred < sizeof(descriptor_tmp)) {
373 return ELIMIT;
374 }
375
376 /* Everything is okay, copy the descriptor. */
377 memcpy(descriptor, &descriptor_tmp,
378 sizeof(descriptor_tmp));
379
380 return EOK;
381}
382
383/** Retrieve full configuration descriptor of a USB device.
384 *
385 * @warning The @p buffer might be touched (i.e. its contents changed)
386 * even when error occurs.
387 *
388 * @param[in] pipe Control endpoint pipe (session must be already started).
389 * @param[in] index Descriptor index.
390 * @param[out] descriptor Storage for the device descriptor.
391 * @param[in] descriptor_size Size of @p descriptor buffer.
392 * @param[out] actual_size Number of bytes actually transferred.
393 * @return Error code.
394 */
395int usb_request_get_full_configuration_descriptor(usb_endpoint_pipe_t *pipe,
396 int index, void *descriptor, size_t descriptor_size, size_t *actual_size)
397{
398 if ((index < 0) || (index > 0xFF)) {
399 return ERANGE;
400 }
401
402 return usb_request_get_descriptor(pipe,
403 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
404 USB_DESCTYPE_CONFIGURATION, index, 0,
405 descriptor, descriptor_size, actual_size);
406}
407
408/** Retrieve full configuration descriptor, allocate space for it.
409 *
410 * The function takes care that full configuration descriptor is returned
411 * (i.e. the function will fail when less data then descriptor.totalLength
412 * is returned).
413 *
414 * @param[in] pipe Control endpoint pipe (session must be already started).
415 * @param[in] index Configuration index.
416 * @param[out] descriptor_ptr Where to store pointer to allocated buffer.
417 * @param[out] descriptor_size Where to store the size of the descriptor.
418 * @return Error code.
419 */
420int usb_request_get_full_configuration_descriptor_alloc(
421 usb_endpoint_pipe_t *pipe, int index,
422 void **descriptor_ptr, size_t *descriptor_size)
423{
424 int rc;
425
426 if (descriptor_ptr == NULL) {
427 return EBADMEM;
428 }
429
430 usb_standard_configuration_descriptor_t bare_config;
431 rc = usb_request_get_bare_configuration_descriptor(pipe, index,
432 &bare_config);
433 if (rc != EOK) {
434 return rc;
435 }
436
437 if (bare_config.descriptor_type != USB_DESCTYPE_CONFIGURATION) {
438 return ENOENT;
439 }
440 if (bare_config.total_length < sizeof(bare_config)) {
441 return ELIMIT;
442 }
443
444 void *buffer = malloc(bare_config.total_length);
445 if (buffer == NULL) {
446 return ENOMEM;
447 }
448
449 size_t transferred = 0;
450 rc = usb_request_get_full_configuration_descriptor(pipe, index,
451 buffer, bare_config.total_length, &transferred);
452 if (rc != EOK) {
453 free(buffer);
454 return rc;
455 }
456
457 if (transferred != bare_config.total_length) {
458 free(buffer);
459 return ELIMIT;
460 }
461
462 /* Everything looks okay, copy the pointers. */
463
464 *descriptor_ptr = buffer;
465
466 if (descriptor_size != NULL) {
467 *descriptor_size = bare_config.total_length;
468 }
469
470 return EOK;
471}
472
473/** Set configuration of USB device.
474 *
475 * @param pipe Control endpoint pipe (session must be already started).
476 * @param configuration_value New configuration value.
477 * @return Error code.
478 */
479int usb_request_set_configuration(usb_endpoint_pipe_t *pipe,
480 uint8_t configuration_value)
481{
482 uint16_t config_value
483 = uint16_host2usb((uint16_t) configuration_value);
484
485 return usb_control_request_set(pipe,
486 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
487 USB_DEVREQ_SET_CONFIGURATION, config_value, 0,
488 NULL, 0);
489}
490
491/** Get list of supported languages by USB device.
492 *
493 * @param[in] pipe Control endpoint pipe (session must be already started).
494 * @param[out] languages_ptr Where to store pointer to allocated array of
495 * supported languages.
496 * @param[out] languages_count Number of supported languages.
497 * @return Error code.
498 */
499int usb_request_get_supported_languages(usb_endpoint_pipe_t *pipe,
500 l18_win_locales_t **languages_ptr, size_t *languages_count)
501{
502 int rc;
503
504 if (languages_ptr == NULL) {
505 return EBADMEM;
506 }
507 if (languages_count == NULL) {
508 return EBADMEM;
509 }
510
511 uint8_t *string_descriptor = NULL;
512 size_t string_descriptor_size = 0;
513 rc = usb_request_get_descriptor_alloc(pipe,
514 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
515 USB_DESCTYPE_STRING, 0, 0,
516 (void **) &string_descriptor, &string_descriptor_size);
517 if (rc != EOK) {
518 return rc;
519 }
520 if (string_descriptor_size <= 2) {
521 free(string_descriptor);
522 return EEMPTY;
523 }
524 /* Subtract first 2 bytes (length and descriptor type). */
525 string_descriptor_size -= 2;
526
527 /* Odd number of bytes - descriptor is broken? */
528 if ((string_descriptor_size % 2) != 0) {
529 /* FIXME: shall we return with error or silently ignore? */
530 free(string_descriptor);
531 return ESTALL;
532 }
533
534 size_t langs_count = string_descriptor_size / 2;
535 l18_win_locales_t *langs
536 = malloc(sizeof(l18_win_locales_t) * langs_count);
537 if (langs == NULL) {
538 free(string_descriptor);
539 return ENOMEM;
540 }
541
542 size_t i;
543 for (i = 0; i < langs_count; i++) {
544 /* Language code from the descriptor is in USB endianness. */
545 /* FIXME: is this really correct? */
546 uint16_t lang_code = (string_descriptor[2 + 2 * i + 1] << 8)
547 + string_descriptor[2 + 2 * i];
548 langs[i] = uint16_usb2host(lang_code);
549 }
550
551 free(string_descriptor);
552
553 *languages_ptr = langs;
554 *languages_count =langs_count;
555
556 return EOK;
557}
558
559/** Get string (descriptor) from USB device.
560 *
561 * The string is returned in native encoding of the operating system.
562 * For HelenOS, that is UTF-8.
563 *
564 * @param[in] pipe Control endpoint pipe (session must be already started).
565 * @param[in] index String index (in native endianness),
566 * first index has number 1 (index from descriptors can be used directly).
567 * @param[in] lang String language (in native endianness).
568 * @param[out] string_ptr Where to store allocated string in native encoding.
569 * @return Error code.
570 */
571int usb_request_get_string(usb_endpoint_pipe_t *pipe,
572 size_t index, l18_win_locales_t lang, char **string_ptr)
573{
574 if (string_ptr == NULL) {
575 return EBADMEM;
576 }
577 /*
578 * Index is actually one byte value and zero index is used
579 * to retrieve list of supported languages.
580 */
581 if ((index < 1) || (index > 0xFF)) {
582 return ERANGE;
583 }
584 /* Language is actually two byte value. */
585 if (lang > 0xFFFF) {
586 return ERANGE;
587 }
588
589 int rc;
590
591 /* Prepare dynamically allocated variables. */
592 uint8_t *string = NULL;
593 wchar_t *string_chars = NULL;
594
595 /* Get the actual descriptor. */
596 size_t string_size;
597 rc = usb_request_get_descriptor_alloc(pipe,
598 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
599 USB_DESCTYPE_STRING, index, uint16_host2usb(lang),
600 (void **) &string, &string_size);
601 if (rc != EOK) {
602 goto leave;
603 }
604
605 if (string_size <= 2) {
606 rc = EEMPTY;
607 goto leave;
608 }
609 /* Subtract first 2 bytes (length and descriptor type). */
610 string_size -= 2;
611
612 /* Odd number of bytes - descriptor is broken? */
613 if ((string_size % 2) != 0) {
614 /* FIXME: shall we return with error or silently ignore? */
615 rc = ESTALL;
616 goto leave;
617 }
618
619 size_t string_char_count = string_size / 2;
620 string_chars = malloc(sizeof(wchar_t) * (string_char_count + 1));
621 if (string_chars == NULL) {
622 rc = ENOMEM;
623 goto leave;
624 }
625
626 /*
627 * Build a wide string.
628 * And do not forget to set NULL terminator (string descriptors
629 * do not have them).
630 */
631 size_t i;
632 for (i = 0; i < string_char_count; i++) {
633 uint16_t uni_char = (string[2 + 2 * i + 1] << 8)
634 + string[2 + 2 * i];
635 string_chars[i] = uni_char;
636 }
637 string_chars[string_char_count] = 0;
638
639
640 /* Convert to normal string. */
641 char *str = wstr_to_astr(string_chars);
642 if (str == NULL) {
643 rc = ENOMEM;
644 goto leave;
645 }
646
647 *string_ptr = str;
648 rc = EOK;
649
650leave:
651 if (string != NULL) {
652 free(string);
653 }
654 if (string_chars != NULL) {
655 free(string_chars);
656 }
657
658 return rc;
659}
660
661/**
662 * @}
663 */
Note: See TracBrowser for help on using the repository browser.