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