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

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

Doxygen comments fixes

No change in functionality.

  • Property mode set to 100644
File size: 18.6 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] recipient Request recipient (device/interface/endpoint).
198 * @param[in] descriptor_type Descriptor type (device/configuration/HID/...).
199 * @param[in] descriptor_index Descriptor index.
200 * @param[in] language Language index.
201 * @param[out] buffer Buffer where to store the retrieved descriptor.
202 * @param[in] size Size of the @p buffer.
203 * @param[out] actual_size Number of bytes actually transferred.
204 * @return Error code.
205 */
206int usb_request_get_descriptor(usb_endpoint_pipe_t *pipe,
207 usb_request_type_t request_type, usb_request_recipient_t recipient,
208 uint8_t descriptor_type, uint8_t descriptor_index,
209 uint16_t language,
210 void *buffer, size_t size, size_t *actual_size)
211{
212 if (buffer == NULL) {
213 return EBADMEM;
214 }
215 if (size == 0) {
216 return EINVAL;
217 }
218
219 uint16_t wValue = descriptor_index | (descriptor_type << 8);
220
221 return usb_control_request_get(pipe,
222 request_type, recipient,
223 USB_DEVREQ_GET_DESCRIPTOR,
224 wValue, language,
225 buffer, size, actual_size);
226}
227
228/** Retrieve USB descriptor, allocate space for it.
229 *
230 * @param[in] pipe Control endpoint pipe (session must be already started).
231 * @param[in] request_type Request type (standard/class/vendor).
232 * @param[in] recipient Request recipient (device/interface/endpoint).
233 * @param[in] descriptor_type Descriptor type (device/configuration/HID/...).
234 * @param[in] descriptor_index Descriptor index.
235 * @param[in] language Language index.
236 * @param[out] buffer_ptr Where to store pointer to allocated buffer.
237 * @param[out] buffer_size Where to store the size of the descriptor.
238 * @return
239 */
240int usb_request_get_descriptor_alloc(usb_endpoint_pipe_t * pipe,
241 usb_request_type_t request_type, usb_request_recipient_t recipient,
242 uint8_t descriptor_type, uint8_t descriptor_index,
243 uint16_t language,
244 void **buffer_ptr, size_t *buffer_size)
245{
246 if (buffer_ptr == NULL) {
247 return EBADMEM;
248 }
249
250 int rc;
251
252 /*
253 * Get only first byte to retrieve descriptor length.
254 */
255 uint8_t tmp_buffer[1];
256 size_t bytes_transfered;
257 rc = usb_request_get_descriptor(pipe, request_type, recipient,
258 descriptor_type, descriptor_index, language,
259 &tmp_buffer, 1, &bytes_transfered);
260 if (rc != EOK) {
261 return rc;
262 }
263 if (bytes_transfered != 1) {
264 /* FIXME: some better error code? */
265 return ESTALL;
266 }
267
268 size_t size = tmp_buffer[0];
269 if (size == 0) {
270 /* FIXME: some better error code? */
271 return ESTALL;
272 }
273
274 /*
275 * Allocate buffer and get the descriptor again.
276 */
277 void *buffer = malloc(size);
278 if (buffer == NULL) {
279 return ENOMEM;
280 }
281
282 rc = usb_request_get_descriptor(pipe, request_type, recipient,
283 descriptor_type, descriptor_index, language,
284 buffer, size, &bytes_transfered);
285 if (rc != EOK) {
286 free(buffer);
287 return rc;
288 }
289 if (bytes_transfered != size) {
290 free(buffer);
291 /* FIXME: some better error code? */
292 return ESTALL;
293 }
294
295 *buffer_ptr = buffer;
296 if (buffer_size != NULL) {
297 *buffer_size = size;
298 }
299
300 return EOK;
301}
302
303/** Retrieve standard device descriptor of a USB device.
304 *
305 * @param[in] pipe Control endpoint pipe (session must be already started).
306 * @param[out] descriptor Storage for the device descriptor.
307 * @return Error code.
308 */
309int usb_request_get_device_descriptor(usb_endpoint_pipe_t *pipe,
310 usb_standard_device_descriptor_t *descriptor)
311{
312 if (descriptor == NULL) {
313 return EBADMEM;
314 }
315
316 size_t actually_transferred = 0;
317 usb_standard_device_descriptor_t descriptor_tmp;
318 int rc = usb_request_get_descriptor(pipe,
319 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
320 USB_DESCTYPE_DEVICE, 0, 0,
321 &descriptor_tmp, sizeof(descriptor_tmp),
322 &actually_transferred);
323
324 if (rc != EOK) {
325 return rc;
326 }
327
328 /* Verify that all data has been transferred. */
329 if (actually_transferred < sizeof(descriptor_tmp)) {
330 return ELIMIT;
331 }
332
333 /* Everything is okay, copy the descriptor. */
334 memcpy(descriptor, &descriptor_tmp,
335 sizeof(descriptor_tmp));
336
337 return EOK;
338}
339
340/** Retrieve configuration descriptor of a USB device.
341 *
342 * The function does not retrieve additional data binded with configuration
343 * descriptor (such as its interface and endpoint descriptors) - use
344 * usb_request_get_full_configuration_descriptor() instead.
345 *
346 * @param[in] pipe Control endpoint pipe (session must be already started).
347 * @param[in] index Descriptor index.
348 * @param[out] descriptor Storage for the device descriptor.
349 * @return Error code.
350 */
351int usb_request_get_bare_configuration_descriptor(usb_endpoint_pipe_t *pipe,
352 int index, usb_standard_configuration_descriptor_t *descriptor)
353{
354 if (descriptor == NULL) {
355 return EBADMEM;
356 }
357
358 if ((index < 0) || (index > 0xFF)) {
359 return ERANGE;
360 }
361
362 size_t actually_transferred = 0;
363 usb_standard_configuration_descriptor_t descriptor_tmp;
364 int rc = usb_request_get_descriptor(pipe,
365 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
366 USB_DESCTYPE_CONFIGURATION, index, 0,
367 &descriptor_tmp, sizeof(descriptor_tmp),
368 &actually_transferred);
369 if (rc != EOK) {
370 return rc;
371 }
372
373 /* Verify that all data has been transferred. */
374 if (actually_transferred < sizeof(descriptor_tmp)) {
375 return ELIMIT;
376 }
377
378 /* Everything is okay, copy the descriptor. */
379 memcpy(descriptor, &descriptor_tmp,
380 sizeof(descriptor_tmp));
381
382 return EOK;
383}
384
385/** Retrieve full configuration descriptor of a USB device.
386 *
387 * @warning The @p buffer might be touched (i.e. its contents changed)
388 * even when error occurs.
389 *
390 * @param[in] pipe Control endpoint pipe (session must be already started).
391 * @param[in] index Descriptor index.
392 * @param[out] descriptor Storage for the device descriptor.
393 * @param[in] descriptor_size Size of @p descriptor buffer.
394 * @param[out] actual_size Number of bytes actually transferred.
395 * @return Error code.
396 */
397int usb_request_get_full_configuration_descriptor(usb_endpoint_pipe_t *pipe,
398 int index, void *descriptor, size_t descriptor_size, size_t *actual_size)
399{
400 if ((index < 0) || (index > 0xFF)) {
401 return ERANGE;
402 }
403
404 return usb_request_get_descriptor(pipe,
405 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
406 USB_DESCTYPE_CONFIGURATION, index, 0,
407 descriptor, descriptor_size, actual_size);
408}
409
410/** Retrieve full configuration descriptor, allocate space for it.
411 *
412 * The function takes care that full configuration descriptor is returned
413 * (i.e. the function will fail when less data then descriptor.totalLength
414 * is returned).
415 *
416 * @param[in] pipe Control endpoint pipe (session must be already started).
417 * @param[in] index Configuration index.
418 * @param[out] descriptor_ptr Where to store pointer to allocated buffer.
419 * @param[out] descriptor_size Where to store the size of the descriptor.
420 * @return Error code.
421 */
422int usb_request_get_full_configuration_descriptor_alloc(
423 usb_endpoint_pipe_t *pipe, int index,
424 void **descriptor_ptr, size_t *descriptor_size)
425{
426 int rc;
427
428 if (descriptor_ptr == NULL) {
429 return EBADMEM;
430 }
431
432 usb_standard_configuration_descriptor_t bare_config;
433 rc = usb_request_get_bare_configuration_descriptor(pipe, index,
434 &bare_config);
435 if (rc != EOK) {
436 return rc;
437 }
438
439 if (bare_config.descriptor_type != USB_DESCTYPE_CONFIGURATION) {
440 return ENOENT;
441 }
442 if (bare_config.total_length < sizeof(bare_config)) {
443 return ELIMIT;
444 }
445
446 void *buffer = malloc(bare_config.total_length);
447 if (buffer == NULL) {
448 return ENOMEM;
449 }
450
451 size_t transferred = 0;
452 rc = usb_request_get_full_configuration_descriptor(pipe, index,
453 buffer, bare_config.total_length, &transferred);
454 if (rc != EOK) {
455 free(buffer);
456 return rc;
457 }
458
459 if (transferred != bare_config.total_length) {
460 free(buffer);
461 return ELIMIT;
462 }
463
464 /* Everything looks okay, copy the pointers. */
465
466 *descriptor_ptr = buffer;
467
468 if (descriptor_size != NULL) {
469 *descriptor_size = bare_config.total_length;
470 }
471
472 return EOK;
473}
474
475/** Set configuration of USB device.
476 *
477 * @param pipe Control endpoint pipe (session must be already started).
478 * @param configuration_value New configuration value.
479 * @return Error code.
480 */
481int usb_request_set_configuration(usb_endpoint_pipe_t *pipe,
482 uint8_t configuration_value)
483{
484 uint16_t config_value
485 = uint16_host2usb((uint16_t) configuration_value);
486
487 return usb_control_request_set(pipe,
488 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
489 USB_DEVREQ_SET_CONFIGURATION, config_value, 0,
490 NULL, 0);
491}
492
493/** Get list of supported languages by USB device.
494 *
495 * @param[in] pipe Control endpoint pipe (session must be already started).
496 * @param[out] languages_ptr Where to store pointer to allocated array of
497 * supported languages.
498 * @param[out] languages_count Number of supported languages.
499 * @return Error code.
500 */
501int usb_request_get_supported_languages(usb_endpoint_pipe_t *pipe,
502 l18_win_locales_t **languages_ptr, size_t *languages_count)
503{
504 int rc;
505
506 if (languages_ptr == NULL) {
507 return EBADMEM;
508 }
509 if (languages_count == NULL) {
510 return EBADMEM;
511 }
512
513 uint8_t *string_descriptor = NULL;
514 size_t string_descriptor_size = 0;
515 rc = usb_request_get_descriptor_alloc(pipe,
516 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
517 USB_DESCTYPE_STRING, 0, 0,
518 (void **) &string_descriptor, &string_descriptor_size);
519 if (rc != EOK) {
520 return rc;
521 }
522 if (string_descriptor_size <= 2) {
523 free(string_descriptor);
524 return EEMPTY;
525 }
526 /* Subtract first 2 bytes (length and descriptor type). */
527 string_descriptor_size -= 2;
528
529 /* Odd number of bytes - descriptor is broken? */
530 if ((string_descriptor_size % 2) != 0) {
531 /* FIXME: shall we return with error or silently ignore? */
532 free(string_descriptor);
533 return ESTALL;
534 }
535
536 size_t langs_count = string_descriptor_size / 2;
537 l18_win_locales_t *langs
538 = malloc(sizeof(l18_win_locales_t) * langs_count);
539 if (langs == NULL) {
540 free(string_descriptor);
541 return ENOMEM;
542 }
543
544 size_t i;
545 for (i = 0; i < langs_count; i++) {
546 /* Language code from the descriptor is in USB endianness. */
547 /* FIXME: is this really correct? */
548 uint16_t lang_code = (string_descriptor[2 + 2 * i + 1] << 8)
549 + string_descriptor[2 + 2 * i];
550 langs[i] = uint16_usb2host(lang_code);
551 }
552
553 free(string_descriptor);
554
555 *languages_ptr = langs;
556 *languages_count =langs_count;
557
558 return EOK;
559}
560
561/** Get string (descriptor) from USB device.
562 *
563 * The string is returned in native encoding of the operating system.
564 * For HelenOS, that is UTF-8.
565 *
566 * @param[in] pipe Control endpoint pipe (session must be already started).
567 * @param[in] index String index (in native endianness),
568 * first index has number 1 (index from descriptors can be used directly).
569 * @param[in] lang String language (in native endianness).
570 * @param[out] string_ptr Where to store allocated string in native encoding.
571 * @return Error code.
572 */
573int usb_request_get_string(usb_endpoint_pipe_t *pipe,
574 size_t index, l18_win_locales_t lang, char **string_ptr)
575{
576 if (string_ptr == NULL) {
577 return EBADMEM;
578 }
579 /*
580 * Index is actually one byte value and zero index is used
581 * to retrieve list of supported languages.
582 */
583 if ((index < 1) || (index > 0xFF)) {
584 return ERANGE;
585 }
586 /* Language is actually two byte value. */
587 if (lang > 0xFFFF) {
588 return ERANGE;
589 }
590
591 int rc;
592
593 /* Prepare dynamically allocated variables. */
594 uint8_t *string = NULL;
595 wchar_t *string_chars = NULL;
596
597 /* Get the actual descriptor. */
598 size_t string_size;
599 rc = usb_request_get_descriptor_alloc(pipe,
600 USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE,
601 USB_DESCTYPE_STRING, index, uint16_host2usb(lang),
602 (void **) &string, &string_size);
603 if (rc != EOK) {
604 goto leave;
605 }
606
607 if (string_size <= 2) {
608 rc = EEMPTY;
609 goto leave;
610 }
611 /* Subtract first 2 bytes (length and descriptor type). */
612 string_size -= 2;
613
614 /* Odd number of bytes - descriptor is broken? */
615 if ((string_size % 2) != 0) {
616 /* FIXME: shall we return with error or silently ignore? */
617 rc = ESTALL;
618 goto leave;
619 }
620
621 size_t string_char_count = string_size / 2;
622 string_chars = malloc(sizeof(wchar_t) * (string_char_count + 1));
623 if (string_chars == NULL) {
624 rc = ENOMEM;
625 goto leave;
626 }
627
628 /*
629 * Build a wide string.
630 * And do not forget to set NULL terminator (string descriptors
631 * do not have them).
632 */
633 size_t i;
634 for (i = 0; i < string_char_count; i++) {
635 uint16_t uni_char = (string[2 + 2 * i + 1] << 8)
636 + string[2 + 2 * i];
637 string_chars[i] = uni_char;
638 }
639 string_chars[string_char_count] = 0;
640
641
642 /* Convert to normal string. */
643 char *str = wstr_to_astr(string_chars);
644 if (str == NULL) {
645 rc = ENOMEM;
646 goto leave;
647 }
648
649 *string_ptr = str;
650 rc = EOK;
651
652leave:
653 if (string != NULL) {
654 free(string);
655 }
656 if (string_chars != NULL) {
657 free(string_chars);
658 }
659
660 return rc;
661}
662
663/**
664 * @}
665 */
Note: See TracBrowser for help on using the repository browser.