1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <ddk/debug.h>
6 #include <ddk/protocol/usb.h>
7 #include <usb/usb-request.h>
8 #include <endian.h>
9 #include <lib/sync/completion.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <utf_conversion/utf_conversion.h>
13 
14 #include "usb-bus.h"
15 #include "usb-device.h"
16 #include "util.h"
17 
18 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
19 
20 typedef struct usb_langid_desc {
21     uint8_t bLength;
22     uint8_t bDescriptorType;
23     uint16_t wLangIds[127];
24 } __PACKED usb_langid_desc_t;
25 
usb_util_control_complete(void * ctx,usb_request_t * req)26 static void usb_util_control_complete(void* ctx, usb_request_t* req) {
27     sync_completion_signal((sync_completion_t*)ctx);
28 }
29 
usb_util_control(usb_device_t * dev,uint8_t request_type,uint8_t request,uint16_t value,uint16_t index,void * data,size_t length,size_t * out_actual)30 zx_status_t usb_util_control(usb_device_t* dev, uint8_t request_type, uint8_t request,
31                              uint16_t value, uint16_t index, void* data, size_t length,
32                              size_t* out_actual) {
33     usb_request_t* req = NULL;
34     bool use_free_list = length == 0;
35     if (use_free_list) {
36         req = usb_request_pool_get(&dev->free_reqs, length);
37     }
38     if (req == NULL) {
39         zx_status_t status = usb_request_alloc(&req, length, 0, dev->req_size);
40         if (status != ZX_OK) return status;
41     }
42 
43     // fill in protocol data
44     usb_setup_t* setup = &req->setup;
45     setup->bmRequestType = request_type;
46     setup->bRequest = request;
47     setup->wValue = value;
48     setup->wIndex = index;
49     setup->wLength = length;
50     req->header.device_id = dev->device_id;
51 
52     bool out = !!((request_type & USB_DIR_MASK) == USB_DIR_OUT);
53     if (length > 0 && out) {
54         usb_request_copy_to(req, data, length, 0);
55     }
56 
57     sync_completion_t completion = SYNC_COMPLETION_INIT;
58 
59     req->header.length = length;
60 
61     usb_request_complete_t complete = {
62         .callback = usb_util_control_complete,
63         .ctx = &completion,
64     };
65     usb_hci_request_queue(&dev->hci, req, &complete);
66     zx_status_t status = sync_completion_wait(&completion, ZX_SEC(1));
67     if (status == ZX_OK) {
68         status = req->response.status;
69     } else if (status == ZX_ERR_TIMED_OUT) {
70         sync_completion_reset(&completion);
71         status = usb_hci_cancel_all(&dev->hci, dev->device_id, 0);
72         if (status == ZX_OK) {
73             sync_completion_wait(&completion, ZX_TIME_INFINITE);
74             status = ZX_ERR_TIMED_OUT;
75         }
76     }
77 
78     if (status == ZX_OK) {
79         if (out_actual) {
80             *out_actual = req->response.actual;
81         }
82 
83         if (length > 0 && !out) {
84             usb_request_copy_from(req, data, req->response.actual, 0);
85         }
86     }
87     if (use_free_list) {
88         if (usb_request_pool_add(&dev->free_reqs, req) != ZX_OK) {
89             zxlogf(TRACE, "Unable to add back request to the free pool\n");
90             usb_request_release(req);
91         }
92     } else {
93         usb_request_release(req);
94     }
95     return status;
96 }
97 
usb_util_get_descriptor(usb_device_t * dev,uint16_t type,uint16_t index,uint16_t language,void * data,size_t length,size_t * out_actual)98 zx_status_t usb_util_get_descriptor(usb_device_t* dev, uint16_t type, uint16_t index,
99                                     uint16_t language, void* data, size_t length,
100                                     size_t* out_actual) {
101     return usb_util_control(dev, USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
102                             USB_REQ_GET_DESCRIPTOR, type << 8 | index, language, data, length,
103                             out_actual);
104 }
105 
usb_util_get_string_descriptor(usb_device_t * dev,uint8_t desc_id,uint16_t lang_id,uint8_t * buf,size_t buflen,size_t * out_actual,uint16_t * out_actual_lang_id)106 zx_status_t usb_util_get_string_descriptor(usb_device_t* dev, uint8_t desc_id, uint16_t lang_id,
107                                            uint8_t* buf, size_t buflen, size_t* out_actual,
108                                            uint16_t* out_actual_lang_id) {
109     //  If we have never attempted to load our language ID table, do so now.
110     zx_status_t result;
111     size_t actual;
112     if (!atomic_load_explicit(&dev->langids_fetched, memory_order_relaxed)) {
113         usb_langid_desc_t* id_desc = calloc(1, sizeof(usb_langid_desc_t));
114 
115         if (id_desc != NULL) {
116             result = usb_util_get_descriptor(dev, USB_DT_STRING, 0, 0, id_desc, sizeof(*id_desc),
117                                              &actual);
118             if (result == ZX_ERR_IO_REFUSED || result == ZX_ERR_IO_INVALID) {
119                 // some devices do not support fetching language list
120                 // in that case assume US English (0x0409)
121                 usb_hci_reset_endpoint(&dev->hci, dev->device_id, 0);
122                 id_desc->bLength = 4;
123                 id_desc->wLangIds[0] = htole16(0x0409);
124                 result = ZX_OK;
125                 actual = 4;
126             } else if ((result == ZX_OK) &&
127                       ((actual < 4) || (actual != id_desc->bLength) || (actual & 0x1))) {
128                 result = ZX_ERR_INTERNAL;
129             }
130 
131             // So, if we have managed to fetch/synthesize a language ID table,
132             // go ahead and perform a bit of fixup.  Redefine bLength to be the
133             // valid number of entires in the table, and fixup the endianness of
134             // all the entires in the table.  Then, attempt to swap in the new
135             // language ID table.
136             if (result == ZX_OK) {
137                 id_desc->bLength = (id_desc->bLength - 2) >> 1;
138 #if BYTE_ORDER != LITTLE_ENDIAN
139                 for (uint8_t i = 0; i < id_desc->bLength; ++i) {
140                     id_desc->wLangIds[i] = letoh16(id_desc->wLangIds[i]);
141                 }
142 #endif
143                 uintptr_t expected = 0;
144                 if (atomic_compare_exchange_strong(&dev->lang_ids, &expected, (uintptr_t)id_desc)) {
145                     id_desc = NULL;
146                 }
147             }
148 
149             // Make sure that we free any table that we allocated, but did not
150             // end up swapping into place.
151             free(id_desc);
152         } else {
153             result = ZX_ERR_NO_MEMORY;
154         }
155 
156         atomic_store_explicit(&dev->langids_fetched, true, memory_order_relaxed);
157         if (result != ZX_OK) {
158             return result;
159         }
160     }
161 
162     // At this point in time, if we don't have a language id table, but we have
163     // tried to obtain or synthesize one in the past, we are not going to get
164     // one.  Just fail.
165     usb_langid_desc_t* lang_ids =
166         (usb_langid_desc_t*)(atomic_load_explicit(&dev->lang_ids, memory_order_relaxed));
167     if (!lang_ids) {
168         return ZX_ERR_BAD_STATE;
169     }
170 
171     // Handle the special case that the user asked for the language ID table.
172     if (desc_id == 0) {
173         size_t table_sz = (lang_ids->bLength << 1);
174         buflen &= ~1;
175         size_t actual = MIN(table_sz, buflen);
176         memcpy(buf, lang_ids->wLangIds, actual);
177         *out_actual = actual;
178         return ZX_OK;
179     }
180 
181     // Search for the requested language ID.
182     uint32_t lang_ndx;
183     for (lang_ndx = 0; lang_ndx < lang_ids->bLength; ++ lang_ndx) {
184         if (lang_id == lang_ids->wLangIds[lang_ndx]) {
185             break;
186         }
187     }
188 
189     // If we didn't find it, default to the first entry in the table.
190     if (lang_ndx >= lang_ids->bLength) {
191         ZX_DEBUG_ASSERT(lang_ids->bLength >= 1);
192         lang_id = lang_ids->wLangIds[0];
193     }
194 
195     struct {
196         uint8_t bLength;
197         uint8_t bDescriptorType;
198         uint16_t code_points[127];
199     } string_desc;
200 
201     result = usb_util_get_descriptor(dev, USB_DT_STRING, desc_id, le16toh(lang_id),
202                                      &string_desc, sizeof(string_desc), &actual);
203 
204     if (result == ZX_ERR_IO_REFUSED || result == ZX_ERR_IO_INVALID) {
205         zx_status_t reset_result = usb_hci_reset_endpoint(&dev->hci, dev->device_id, 0);
206         if (reset_result != ZX_OK) {
207             zxlogf(ERROR, "failed to reset endpoint, err: %d\n", reset_result);
208             return result;
209         }
210         result = usb_util_get_descriptor(dev, USB_DT_STRING, desc_id, le16toh(lang_id),
211                                          &string_desc, sizeof(string_desc), &actual);
212         if (result == ZX_ERR_IO_REFUSED || result == ZX_ERR_IO_INVALID) {
213             reset_result = usb_hci_reset_endpoint(&dev->hci, dev->device_id, 0);
214             if (reset_result != ZX_OK) {
215                 zxlogf(ERROR, "failed to reset endpoint, err: %d\n", reset_result);
216                 return result;
217             }
218         }
219     }
220 
221     if (result != ZX_OK) {
222         return result;
223     }
224 
225     if ((actual < 2) || (actual != string_desc.bLength)) {
226         result = ZX_ERR_INTERNAL;
227     } else  {
228         // Success! Convert this result from UTF16LE to UTF8 and store the
229         // language ID we actually fetched (if it was not what the user
230         // requested).
231         *out_actual = buflen;
232         *out_actual_lang_id = lang_id;
233         utf16_to_utf8(string_desc.code_points, (string_desc.bLength >> 1) - 1,
234                       buf, out_actual,
235                       UTF_CONVERT_FLAG_FORCE_LITTLE_ENDIAN);
236         return ZX_OK;
237     }
238 
239     return result;
240 }
241