source: mainline/uspace/lib/usbhost/src/hcd.c@ 53db806

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

usb: move toggle resetting to libusbhost

  • Property mode set to 100644
File size: 9.0 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
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 libusbhost
30 * @{
31 */
32/** @file
33 *
34 */
35
36#include <assert.h>
37#include <async.h>
38#include <errno.h>
39#include <macros.h>
40#include <str_error.h>
41#include <usb/debug.h>
42#include <usb/descriptor.h>
43#include <usb/request.h>
44#include <usb_iface.h>
45
46#include "bus.h"
47#include "endpoint.h"
48#include "usb_transfer_batch.h"
49
50#include "hcd.h"
51
52
53/** Initialize hcd_t structure.
54 * Initializes device and endpoint managers. Sets data and hook pointer to NULL.
55 *
56 * @param hcd hcd_t structure to initialize, non-null.
57 * @param max_speed Maximum supported USB speed (full, high).
58 * @param bandwidth Available bandwidth, passed to endpoint manager.
59 * @param bw_count Bandwidth compute function, passed to endpoint manager.
60 */
61void hcd_init(hcd_t *hcd) {
62 assert(hcd);
63
64 hcd_set_implementation(hcd, NULL, NULL, NULL);
65}
66
67/** Get max packet size for the control endpoint 0.
68 *
69 * For LS, HS, and SS devices this value is fixed. For FS devices we must fetch
70 * the first 8B of the device descriptor to determine it.
71 *
72 * @return Max packet size for EP 0
73 */
74int hcd_get_ep0_max_packet_size(uint16_t *mps, hcd_t *hcd, device_t *dev)
75{
76 assert(mps);
77
78 static const uint16_t mps_fixed [] = {
79 [USB_SPEED_LOW] = 8,
80 [USB_SPEED_HIGH] = 64,
81 [USB_SPEED_SUPER] = 512,
82 };
83
84 if (dev->speed < ARRAY_SIZE(mps_fixed) && mps_fixed[dev->speed] != 0) {
85 *mps = mps_fixed[dev->speed];
86 return EOK;
87 }
88
89 const usb_target_t control_ep = {{
90 .address = dev->address,
91 .endpoint = 0,
92 }};
93
94 usb_standard_device_descriptor_t desc = { 0 };
95 const usb_device_request_setup_packet_t get_device_desc_8 =
96 GET_DEVICE_DESC(CTRL_PIPE_MIN_PACKET_SIZE);
97
98 usb_log_debug("Requesting first 8B of device descriptor to determine MPS.");
99 ssize_t got = hcd_send_batch_sync(hcd, dev, control_ep, USB_DIRECTION_IN,
100 (char *) &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8,
101 "read first 8 bytes of dev descriptor");
102
103 if (got != CTRL_PIPE_MIN_PACKET_SIZE) {
104 const int err = got < 0 ? got : EOVERFLOW;
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
125/**
126 * Setup devices Transaction Translation.
127 *
128 * This applies for Low/Full speed devices under High speed hub only. Other
129 * devices just inherit TT from the hub.
130 *
131 * Roothub must be handled specially.
132 */
133void hcd_setup_device_tt(device_t *dev)
134{
135 if (!dev->hub)
136 return;
137
138 if (dev->hub->speed == USB_SPEED_HIGH && usb_speed_is_11(dev->speed)) {
139 /* For LS devices under HS hub */
140 dev->tt.address = dev->hub->address;
141 dev->tt.port = dev->port;
142 }
143 else {
144 /* Inherit hub's TT */
145 dev->tt = dev->hub->tt;
146 }
147}
148
149/** Check setup packet data for signs of toggle reset.
150 *
151 * @param[in] requst Setup requst data.
152 *
153 * @retval -1 No endpoints need reset.
154 * @retval 0 All endpoints need reset.
155 * @retval >0 Specified endpoint needs reset.
156 *
157 */
158static toggle_reset_mode_t hcd_get_request_toggle_reset_mode(
159 const usb_device_request_setup_packet_t *request)
160{
161 assert(request);
162 switch (request->request)
163 {
164 /* Clear Feature ENPOINT_STALL */
165 case USB_DEVREQ_CLEAR_FEATURE: /*resets only cleared ep */
166 /* 0x2 ( HOST to device | STANDART | TO ENPOINT) */
167 if ((request->request_type == 0x2) &&
168 (request->value == USB_FEATURE_ENDPOINT_HALT))
169 return RESET_EP;
170 break;
171 case USB_DEVREQ_SET_CONFIGURATION:
172 case USB_DEVREQ_SET_INTERFACE:
173 /* Recipient must be device, this resets all endpoints,
174 * In fact there should be no endpoints but EP 0 registered
175 * as different interfaces use different endpoints,
176 * unless you're changing configuration or alternative
177 * interface of an already setup device. */
178 if (!(request->request_type & SETUP_REQUEST_TYPE_DEVICE_TO_HOST))
179 return RESET_ALL;
180 break;
181 default:
182 break;
183 }
184
185 return RESET_NONE;
186}
187
188/** Prepare generic usb_transfer_batch and schedule it.
189 * @param hcd Host controller driver.
190 * @param target address and endpoint number.
191 * @param setup_data Data to use in setup stage (Control communication type)
192 * @param in Callback for device to host communication.
193 * @param out Callback for host to device communication.
194 * @param arg Callback parameter.
195 * @param name Communication identifier (for nicer output).
196 * @return Error code.
197 */
198int hcd_send_batch(hcd_t *hcd, device_t *device, usb_target_t target,
199 usb_direction_t direction, char *data, size_t size, uint64_t setup_data,
200 usbhc_iface_transfer_callback_t on_complete, void *arg, const char *name)
201{
202 assert(hcd);
203 assert(device->address == target.address);
204
205 if (!hcd->ops.schedule) {
206 usb_log_error("HCD does not implement scheduler.\n");
207 return ENOTSUP;
208 }
209
210 endpoint_t *ep = bus_find_endpoint(hcd->bus, device, target, direction);
211 if (ep == NULL) {
212 usb_log_error("Endpoint(%d:%d) not registered for %s.\n",
213 device->address, target.endpoint, name);
214 return ENOENT;
215 }
216
217 // TODO cut here aka provide helper to call with instance of endpoint_t in hand
218
219 usb_log_debug2("%s %d:%d %zu(%zu).\n",
220 name, target.address, target.endpoint, size, ep->max_packet_size);
221
222 const size_t bw = bus_count_bw(ep, size);
223 /* Check if we have enough bandwidth reserved */
224 if (ep->bandwidth < bw) {
225 usb_log_error("Endpoint(%d:%d) %s needs %zu bw "
226 "but only %zu is reserved.\n",
227 device->address, ep->endpoint, name, bw, ep->bandwidth);
228 return ENOSPC;
229 }
230
231 usb_transfer_batch_t *batch = usb_transfer_batch_create(ep);
232 if (!batch) {
233 usb_log_error("Failed to create transfer batch.\n");
234 return ENOMEM;
235 }
236
237 batch->target = target;
238 batch->buffer = data;
239 batch->buffer_size = size;
240 batch->setup.packed = setup_data;
241 batch->dir = direction;
242 batch->on_complete = on_complete;
243 batch->on_complete_data = arg;
244
245 /* Check for commands that reset toggle bit */
246 if (ep->transfer_type == USB_TRANSFER_CONTROL)
247 batch->toggle_reset_mode
248 = hcd_get_request_toggle_reset_mode(&batch->setup.packet);
249
250 const int ret = hcd->ops.schedule(hcd, batch);
251 if (ret != EOK) {
252 usb_log_warning("Batch %p failed to schedule: %s", batch, str_error(ret));
253 usb_transfer_batch_destroy(batch);
254 }
255
256 /* Drop our own reference to ep. */
257 endpoint_del_ref(ep);
258
259 return ret;
260}
261
262typedef struct {
263 fibril_mutex_t done_mtx;
264 fibril_condvar_t done_cv;
265 unsigned done;
266
267 size_t transfered_size;
268 int error;
269} sync_data_t;
270
271static int sync_transfer_complete(void *arg, int error, size_t transfered_size)
272{
273 sync_data_t *d = arg;
274 assert(d);
275 d->transfered_size = transfered_size;
276 d->error = error;
277 fibril_mutex_lock(&d->done_mtx);
278 d->done = 1;
279 fibril_condvar_broadcast(&d->done_cv);
280 fibril_mutex_unlock(&d->done_mtx);
281 return EOK;
282}
283
284ssize_t hcd_send_batch_sync(hcd_t *hcd, device_t *device, usb_target_t target,
285 usb_direction_t direction, char *data, size_t size, uint64_t setup_data,
286 const char *name)
287{
288 assert(hcd);
289 sync_data_t sd = { .done = 0 };
290 fibril_mutex_initialize(&sd.done_mtx);
291 fibril_condvar_initialize(&sd.done_cv);
292
293 const int ret = hcd_send_batch(hcd, device, target, direction,
294 data, size, setup_data,
295 sync_transfer_complete, &sd, name);
296 if (ret != EOK)
297 return ret;
298
299 fibril_mutex_lock(&sd.done_mtx);
300 while (!sd.done)
301 fibril_condvar_wait(&sd.done_cv, &sd.done_mtx);
302 fibril_mutex_unlock(&sd.done_mtx);
303
304 return (sd.error == EOK)
305 ? (ssize_t) sd.transfered_size
306 : (ssize_t) sd.error;
307}
308
309
310/**
311 * @}
312 */
Note: See TracBrowser for help on using the repository browser.