source: mainline/uspace/lib/usbhost/src/utility.c@ ab6edb6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ab6edb6 was 904b1bc, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix remaining ccheck issues.

  • Property mode set to 100644
File size: 10.2 KB
Line 
1/*
2 * Copyright (c) 2013 Jan Vesely
3 * Copyright (c) 2018 Ondrej Hlavaty, Jan Hrach
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/** @addtogroup libusbhost
30 * @{
31 */
32/** @file
33 */
34
35#include <macros.h>
36#include <str_error.h>
37#include <usb/debug.h>
38#include <usb/descriptor.h>
39#include <usb/request.h>
40
41#include "ddf_helpers.h"
42#include "utility.h"
43
44/**
45 * Get initial max packet size for the control endpoint 0.
46 *
47 * For LS, HS, and SS devices this value is final and fixed.
48 * For FS devices, the default value of 8 is returned. The caller needs
49 * to fetch the first 8B of the device descriptor later in the initialization
50 * process and determine whether it should be increased.
51 *
52 * @return Max packet size for EP 0 (in bytes)
53 */
54uint16_t hc_get_ep0_initial_mps(usb_speed_t speed)
55{
56 static const uint16_t mps_fixed [] = {
57 [USB_SPEED_LOW] = 8,
58 [USB_SPEED_HIGH] = 64,
59 [USB_SPEED_SUPER] = 512,
60 };
61
62 if (speed < ARRAY_SIZE(mps_fixed) && mps_fixed[speed] != 0) {
63 return mps_fixed[speed];
64 }
65 return 8; // USB_SPEED_FULL default
66}
67
68/**
69 * Get max packet size for the control endpoint 0.
70 *
71 * For LS, HS, and SS devices the corresponding fixed value is obtained.
72 * For FS devices the first 8B of the device descriptor are fetched to
73 * determine it.
74 *
75 * @return Max packet size for EP 0 (in bytes)
76 */
77int hc_get_ep0_max_packet_size(uint16_t *mps, device_t *dev)
78{
79 assert(mps);
80
81 *mps = hc_get_ep0_initial_mps(dev->speed);
82 if (dev->speed != USB_SPEED_FULL) {
83 return EOK;
84 }
85
86 const usb_target_t control_ep = {
87 {
88 .address = dev->address,
89 .endpoint = 0,
90 }
91 };
92
93 usb_standard_device_descriptor_t desc = { 0 };
94 const usb_device_request_setup_packet_t get_device_desc_8 =
95 GET_DEVICE_DESC(CTRL_PIPE_MIN_PACKET_SIZE);
96
97 usb_log_debug("Requesting first 8B of device descriptor to determine MPS.");
98 size_t got;
99 const errno_t err = bus_device_send_batch_sync(dev, control_ep,
100 USB_DIRECTION_IN, (char *) &desc, CTRL_PIPE_MIN_PACKET_SIZE,
101 *(uint64_t *)&get_device_desc_8,
102 "read first 8 bytes of dev descriptor", &got);
103
104 if (got != CTRL_PIPE_MIN_PACKET_SIZE) {
105 usb_log_error("Failed to get 8B of dev descr: %s.", str_error(err));
106 return err;
107 }
108
109 if (desc.descriptor_type != USB_DESCTYPE_DEVICE) {
110 usb_log_error("The device responded with wrong device descriptor.");
111 return EIO;
112 }
113
114 uint16_t version = uint16_usb2host(desc.usb_spec_version);
115 if (version < 0x0300) {
116 /* USB 2 and below have MPS raw in the field */
117 *mps = desc.max_packet_size;
118 } else {
119 /* USB 3 have MPS as an 2-based exponent */
120 *mps = (1 << desc.max_packet_size);
121 }
122 return EOK;
123}
124
125int hc_get_device_desc(device_t *device, usb_standard_device_descriptor_t *desc)
126{
127 const usb_target_t control_ep = {
128 {
129 .address = device->address,
130 .endpoint = 0,
131 }
132 };
133
134 /* Get std device descriptor */
135 const usb_device_request_setup_packet_t get_device_desc =
136 GET_DEVICE_DESC(sizeof(*desc));
137
138 usb_log_debug("Device(%d): Requesting full device descriptor.",
139 device->address);
140 size_t got;
141 errno_t err = bus_device_send_batch_sync(device, control_ep,
142 USB_DIRECTION_IN, (char *) desc, sizeof(*desc),
143 *(uint64_t *)&get_device_desc, "read device descriptor", &got);
144
145 if (!err && got != sizeof(*desc))
146 err = EOVERFLOW;
147
148 return err;
149}
150
151int hc_get_hub_desc(device_t *device, usb_hub_descriptor_header_t *desc)
152{
153 const usb_target_t control_ep = {
154 {
155 .address = device->address,
156 .endpoint = 0,
157 }
158 };
159
160 const usb_descriptor_type_t type = device->speed >= USB_SPEED_SUPER ?
161 USB_DESCTYPE_SSPEED_HUB : USB_DESCTYPE_HUB;
162
163 const usb_device_request_setup_packet_t get_hub_desc = {
164 .request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST |
165 (USB_REQUEST_TYPE_CLASS << 5) |
166 USB_REQUEST_RECIPIENT_DEVICE,
167 .request = USB_DEVREQ_GET_DESCRIPTOR,
168 .value = uint16_host2usb(type << 8),
169 .length = sizeof(*desc),
170 };
171
172 usb_log_debug("Device(%d): Requesting hub descriptor.",
173 device->address);
174
175 size_t got;
176 errno_t err = bus_device_send_batch_sync(device, control_ep,
177 USB_DIRECTION_IN, (char *) desc, sizeof(*desc),
178 *(uint64_t *)&get_hub_desc, "get hub descriptor", &got);
179
180 if (!err && got != sizeof(*desc))
181 err = EOVERFLOW;
182
183 return err;
184}
185
186int hc_device_explore(device_t *device)
187{
188 int err;
189 usb_standard_device_descriptor_t desc = { 0 };
190
191 if ((err = hc_get_device_desc(device, &desc))) {
192 usb_log_error("Device(%d): Failed to get dev descriptor: %s",
193 device->address, str_error(err));
194 return err;
195 }
196
197 if ((err = hcd_ddf_setup_match_ids(device, &desc))) {
198 usb_log_error("Device(%d): Failed to setup match ids: %s", device->address, str_error(err));
199 return err;
200 }
201
202 return EOK;
203}
204
205/** Announce root hub to the DDF
206 *
207 * @param[in] device Host controller ddf device
208 * @return Error code
209 */
210int hc_setup_virtual_root_hub(hc_device_t *hcd, usb_speed_t rh_speed)
211{
212 int err;
213
214 assert(hcd);
215
216 device_t *dev = hcd_ddf_fun_create(hcd, rh_speed);
217 if (!dev) {
218 usb_log_error("Failed to create function for the root hub.");
219 return ENOMEM;
220 }
221
222 ddf_fun_set_name(dev->fun, "roothub");
223
224 /* Assign an address to the device */
225 if ((err = bus_device_enumerate(dev))) {
226 usb_log_error("Failed to enumerate roothub device: %s", str_error(err));
227 goto err_usb_dev;
228 }
229
230 if ((err = ddf_fun_bind(dev->fun))) {
231 usb_log_error("Failed to register roothub: %s.", str_error(err));
232 goto err_enumerated;
233 }
234
235 return EOK;
236
237err_enumerated:
238 bus_device_gone(dev);
239err_usb_dev:
240 hcd_ddf_fun_destroy(dev);
241 return err;
242}
243
244/**
245 * Check setup packet data for signs of toggle reset.
246 *
247 * @param[in] batch USB batch
248 * @param[in] reset_cb Callback to reset an endpoint
249 */
250void hc_reset_toggles(const usb_transfer_batch_t *batch, endpoint_reset_toggle_t reset_cb)
251{
252 if (batch->ep->transfer_type != USB_TRANSFER_CONTROL ||
253 batch->dir != USB_DIRECTION_OUT)
254 return;
255
256 const usb_device_request_setup_packet_t *request = &batch->setup.packet;
257 device_t *const dev = batch->ep->device;
258
259 switch (request->request) {
260 /* Clear Feature ENPOINT_STALL */
261 case USB_DEVREQ_CLEAR_FEATURE: /*resets only cleared ep */
262 /* 0x2 ( HOST to device | STANDART | TO ENPOINT) */
263 if ((request->request_type == 0x2) &&
264 (request->value == USB_FEATURE_ENDPOINT_HALT)) {
265 const uint16_t index = uint16_usb2host(request->index);
266 const usb_endpoint_t ep_num = index & 0xf;
267 const usb_direction_t dir = (index >> 7) ? USB_DIRECTION_IN : USB_DIRECTION_OUT;
268
269 endpoint_t *ep = bus_find_endpoint(dev, ep_num, dir);
270 if (ep) {
271 reset_cb(ep);
272 endpoint_del_ref(ep);
273 } else {
274 usb_log_warning("Device(%u): Resetting unregistered endpoint %u %s.", dev->address, ep_num, usb_str_direction(dir));
275 }
276 }
277 break;
278 case USB_DEVREQ_SET_CONFIGURATION:
279 case USB_DEVREQ_SET_INTERFACE:
280 /*
281 * Recipient must be device, this resets all endpoints,
282 * In fact there should be no endpoints but EP 0 registered
283 * as different interfaces use different endpoints,
284 * unless you're changing configuration or alternative
285 * interface of an already setup device.
286 */
287 if (!(request->request_type & SETUP_REQUEST_TYPE_DEVICE_TO_HOST))
288 for (usb_endpoint_t i = 0; i < 2 * USB_ENDPOINT_MAX; ++i)
289 if (dev->endpoints[i])
290 reset_cb(dev->endpoints[i]);
291 break;
292 default:
293 break;
294 }
295}
296
297typedef struct joinable_fibril {
298 fid_t fid;
299 void *arg;
300 fibril_worker_t worker;
301
302 bool running;
303 fibril_mutex_t guard;
304 fibril_condvar_t dead_cv;
305} joinable_fibril_t;
306
307static int joinable_fibril_worker(void *arg)
308{
309 joinable_fibril_t *jf = arg;
310
311 jf->worker(jf->arg);
312
313 fibril_mutex_lock(&jf->guard);
314 jf->running = false;
315 fibril_mutex_unlock(&jf->guard);
316 fibril_condvar_broadcast(&jf->dead_cv);
317 return 0;
318}
319
320/**
321 * Create a fibril that is joinable. Similar to fibril_create.
322 */
323joinable_fibril_t *joinable_fibril_create(fibril_worker_t worker, void *arg)
324{
325 joinable_fibril_t *jf = calloc(1, sizeof(joinable_fibril_t));
326 if (!jf)
327 return NULL;
328
329 jf->worker = worker;
330 jf->arg = arg;
331 fibril_mutex_initialize(&jf->guard);
332 fibril_condvar_initialize(&jf->dead_cv);
333
334 if (joinable_fibril_recreate(jf)) {
335 free(jf);
336 return NULL;
337 }
338
339 return jf;
340}
341
342/**
343 * Start a joinable fibril. Similar to fibril_add_ready.
344 */
345void joinable_fibril_start(joinable_fibril_t *jf)
346{
347 assert(jf);
348 assert(!jf->running);
349
350 jf->running = true;
351 fibril_add_ready(jf->fid);
352}
353
354/**
355 * Join a joinable fibril. Not similar to anything, obviously.
356 */
357void joinable_fibril_join(joinable_fibril_t *jf)
358{
359 assert(jf);
360
361 fibril_mutex_lock(&jf->guard);
362 while (jf->running)
363 fibril_condvar_wait(&jf->dead_cv, &jf->guard);
364 fibril_mutex_unlock(&jf->guard);
365
366 jf->fid = 0;
367}
368
369/**
370 * Reinitialize a joinable fibril.
371 */
372errno_t joinable_fibril_recreate(joinable_fibril_t *jf)
373{
374 assert(!jf->fid);
375
376 jf->fid = fibril_create(joinable_fibril_worker, jf);
377 return jf->fid ? EOK : ENOMEM;
378}
379
380/**
381 * Regular fibrils clean after themselves, joinable fibrils cannot.
382 */
383void joinable_fibril_destroy(joinable_fibril_t *jf)
384{
385 if (jf) {
386 joinable_fibril_join(jf);
387 free(jf);
388 }
389}
390
391/**
392 * @}
393 */
Note: See TracBrowser for help on using the repository browser.