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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c280d7e was b5c92d7d, checked in by Ondřej Hlavatý <aearsis@…>, 7 years ago

libusbdev: pass correct endpoint index to reset endpoint

… and do not trust that the index is correct in libusbhost.

  • Property mode set to 100644
File size: 7.7 KB
Line 
1/*
2 * Copyright (c) 2013 Jan Vesely
3 * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
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/**
46 * Get max packet size for the control endpoint 0.
47 *
48 * For LS, HS, and SS devices this value is fixed. For FS devices we must fetch
49 * the first 8B of the device descriptor to determine it.
50 *
51 * @return Max packet size for EP 0
52 */
53int hc_get_ep0_max_packet_size(uint16_t *mps, bus_t *bus, device_t *dev)
54{
55 assert(mps);
56
57 static const uint16_t mps_fixed [] = {
58 [USB_SPEED_LOW] = 8,
59 [USB_SPEED_HIGH] = 64,
60 [USB_SPEED_SUPER] = 512,
61 };
62
63 if (dev->speed < ARRAY_SIZE(mps_fixed) && mps_fixed[dev->speed] != 0) {
64 *mps = mps_fixed[dev->speed];
65 return EOK;
66 }
67
68 const usb_target_t control_ep = {{
69 .address = dev->address,
70 .endpoint = 0,
71 }};
72
73 usb_standard_device_descriptor_t desc = { 0 };
74 const usb_device_request_setup_packet_t get_device_desc_8 =
75 GET_DEVICE_DESC(CTRL_PIPE_MIN_PACKET_SIZE);
76
77 usb_log_debug("Requesting first 8B of device descriptor to determine MPS.");
78 ssize_t got = bus_device_send_batch_sync(dev, control_ep, USB_DIRECTION_IN,
79 (char *) &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8,
80 "read first 8 bytes of dev descriptor");
81
82 if (got != CTRL_PIPE_MIN_PACKET_SIZE) {
83 const int err = got < 0 ? got : EOVERFLOW;
84 usb_log_error("Failed to get 8B of dev descr: %s.", str_error(err));
85 return err;
86 }
87
88 if (desc.descriptor_type != USB_DESCTYPE_DEVICE) {
89 usb_log_error("The device responded with wrong device descriptor.");
90 return EIO;
91 }
92
93 uint16_t version = uint16_usb2host(desc.usb_spec_version);
94 if (version < 0x0300) {
95 /* USB 2 and below have MPS raw in the field */
96 *mps = desc.max_packet_size;
97 } else {
98 /* USB 3 have MPS as an 2-based exponent */
99 *mps = (1 << desc.max_packet_size);
100 }
101 return EOK;
102}
103
104int hc_get_device_desc(device_t *device, usb_standard_device_descriptor_t *desc)
105{
106 const usb_target_t control_ep = {{
107 .address = device->address,
108 .endpoint = 0,
109 }};
110
111 /* Get std device descriptor */
112 const usb_device_request_setup_packet_t get_device_desc =
113 GET_DEVICE_DESC(sizeof(*desc));
114
115 usb_log_debug("Device(%d): Requesting full device descriptor.",
116 device->address);
117 ssize_t got = bus_device_send_batch_sync(device, control_ep, USB_DIRECTION_IN,
118 (char *) desc, sizeof(*desc), *(uint64_t *)&get_device_desc,
119 "read device descriptor");
120
121 if (got < 0)
122 return got;
123
124 return got == sizeof(*desc) ? EOK : EOVERFLOW;
125}
126
127int hc_get_hub_desc(device_t *device, usb_hub_descriptor_header_t *desc)
128{
129 const usb_target_t control_ep = {{
130 .address = device->address,
131 .endpoint = 0,
132 }};
133
134 const usb_device_request_setup_packet_t get_hub_desc = {
135 .request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST
136 | (USB_REQUEST_TYPE_CLASS << 5)
137 | USB_REQUEST_RECIPIENT_DEVICE,
138 .request = USB_DEVREQ_GET_DESCRIPTOR, \
139 .value = uint16_host2usb(USB_DESCTYPE_HUB << 8), \
140 .length = sizeof(*desc),
141 };
142
143 usb_log_debug("Device(%d): Requesting hub descriptor.",
144 device->address);
145 ssize_t got = bus_device_send_batch_sync(device, control_ep, USB_DIRECTION_IN,
146 (char *) desc, sizeof(*desc), *(uint64_t *)&get_hub_desc,
147 "get hub descriptor");
148
149 if (got < 0)
150 return got;
151
152 return got == sizeof(*desc) ? EOK : EOVERFLOW;
153}
154
155int hc_device_explore(device_t *device)
156{
157 int err;
158 usb_standard_device_descriptor_t desc = { 0 };
159
160 if ((err = hc_get_device_desc(device, &desc))) {
161 usb_log_error("Device(%d): Failed to get dev descriptor: %s",
162 device->address, str_error(err));
163 return err;
164 }
165
166 if ((err = hcd_ddf_setup_match_ids(device, &desc))) {
167 usb_log_error("Device(%d): Failed to setup match ids: %s", device->address, str_error(err));
168 return err;
169 }
170
171 return EOK;
172}
173
174/** Announce root hub to the DDF
175 *
176 * @param[in] device Host controller ddf device
177 * @return Error code
178 */
179int hc_setup_virtual_root_hub(hc_device_t *hcd)
180{
181 int err;
182
183 assert(hcd);
184
185 device_t *dev = hcd_ddf_fun_create(hcd, USB_SPEED_MAX);
186 if (!dev) {
187 usb_log_error("Failed to create function for the root hub.");
188 return ENOMEM;
189 }
190
191 ddf_fun_set_name(dev->fun, "roothub");
192
193 /* Assign an address to the device */
194 if ((err = bus_device_enumerate(dev))) {
195 usb_log_error("Failed to enumerate roothub device: %s", str_error(err));
196 goto err_usb_dev;
197 }
198
199 if ((err = ddf_fun_bind(dev->fun))) {
200 usb_log_error("Failed to register roothub: %s.", str_error(err));
201 goto err_enumerated;
202 }
203
204 return EOK;
205
206err_enumerated:
207 bus_device_gone(dev);
208err_usb_dev:
209 hcd_ddf_fun_destroy(dev);
210 return err;
211}
212
213/**
214 * Check setup packet data for signs of toggle reset.
215 *
216 * @param[in] batch USB batch
217 * @param[in] reset_cb Callback to reset an endpoint
218 */
219void hc_reset_toggles(const usb_transfer_batch_t *batch, endpoint_reset_toggle_t reset_cb)
220{
221 if (batch->ep->transfer_type != USB_TRANSFER_CONTROL
222 || batch->dir != USB_DIRECTION_OUT)
223 return;
224
225 const usb_device_request_setup_packet_t *request = &batch->setup.packet;
226 device_t * const dev = batch->ep->device;
227
228 switch (request->request)
229 {
230 /* Clear Feature ENPOINT_STALL */
231 case USB_DEVREQ_CLEAR_FEATURE: /*resets only cleared ep */
232 /* 0x2 ( HOST to device | STANDART | TO ENPOINT) */
233 if ((request->request_type == 0x2) &&
234 (request->value == USB_FEATURE_ENDPOINT_HALT)) {
235 const uint16_t index = uint16_usb2host(request->index);
236 const usb_endpoint_t ep_num = index & 0xf;
237 const usb_direction_t dir = (index >> 7) ? USB_DIRECTION_IN : USB_DIRECTION_OUT;
238
239 endpoint_t *ep = bus_find_endpoint(dev, ep_num, dir);
240 if (ep) {
241 reset_cb(ep);
242 endpoint_del_ref(ep);
243 } else {
244 usb_log_warning("Device(%u): Resetting unregistered endpoint %u %s.", dev->address, ep_num, usb_str_direction(dir));
245 }
246 }
247 break;
248 case USB_DEVREQ_SET_CONFIGURATION:
249 case USB_DEVREQ_SET_INTERFACE:
250 /* Recipient must be device, this resets all endpoints,
251 * In fact there should be no endpoints but EP 0 registered
252 * as different interfaces use different endpoints,
253 * unless you're changing configuration or alternative
254 * interface of an already setup device. */
255 if (!(request->request_type & SETUP_REQUEST_TYPE_DEVICE_TO_HOST))
256 for (usb_endpoint_t i = 0; i < 2 * USB_ENDPOINT_MAX; ++i)
257 if (dev->endpoints[i])
258 reset_cb(dev->endpoints[i]);
259 break;
260 default:
261 break;
262 }
263}
264
265/**
266 * @}
267 */
Note: See TracBrowser for help on using the repository browser.