source: mainline/uspace/lib/usbdev/src/devpoll.c@ 5e2b1ae6

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

usb: move endpoint descriptor parsing to HC

This better separates responsibilities. Now the device driver does not
care about the contents of an endpoint descriptor, and HC can parse the
values according to device's actual speed.

Currently, it is device driver's responsibility to fetch endpoint
descriptors, map them and register pipes - sending the endpoint
descriptor back to HC. HC then parses it, and fills the pipe
description, which then sends back to device driver. We shall probably
fetch the endpoint descriptor from inside the HC (also fixing the USB
spec violation of communication with EP0).

  • Property mode set to 100644
File size: 10.9 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
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 libusbdev
30 * @{
31 */
32/** @file
33 * USB device driver framework - automatic interrupt polling.
34 */
35
36#include <usb/dev/device.h>
37#include <usb/dev/pipes.h>
38#include <usb/dev/poll.h>
39#include <usb/dev/request.h>
40#include <usb/classes/classes.h>
41#include <usb/debug.h>
42#include <usb/descriptor.h>
43#include <usb/usb.h>
44
45#include <assert.h>
46#include <async.h>
47#include <errno.h>
48#include <fibril.h>
49#include <stdbool.h>
50#include <stdlib.h>
51#include <str_error.h>
52#include <stddef.h>
53#include <stdint.h>
54
55/** Maximum number of failed consecutive requests before announcing failure. */
56#define MAX_FAILED_ATTEMPTS 3
57
58/** Data needed for polling. */
59typedef struct {
60 /** Parameters for automated polling. */
61 usb_device_auto_polling_t auto_polling;
62
63 /** USB device to poll. */
64 usb_device_t *dev;
65 /** Device enpoint mapping to use for polling. */
66 usb_endpoint_mapping_t *polling_mapping;
67 /** Size of the recieved data. */
68 size_t request_size;
69 /** Data buffer. */
70 uint8_t *buffer;
71} polling_data_t;
72
73
74/** Polling fibril.
75 *
76 * @param arg Pointer to polling_data_t.
77 * @return Always EOK.
78 */
79static int polling_fibril(void *arg)
80{
81 assert(arg);
82 const polling_data_t *data = arg;
83 /* Helper to reduce typing. */
84 const usb_device_auto_polling_t *params = &data->auto_polling;
85
86 usb_pipe_t *pipe = &data->polling_mapping->pipe;
87
88 if (params->debug > 0) {
89 const usb_endpoint_mapping_t *mapping =
90 data->polling_mapping;
91 usb_log_debug("Poll (%p): started polling of `%s' - " \
92 "interface %d (%s,%d,%d), %zuB/%zu.\n",
93 data, usb_device_get_name(data->dev),
94 (int) mapping->interface->interface_number,
95 usb_str_class(mapping->interface->interface_class),
96 (int) mapping->interface->interface_subclass,
97 (int) mapping->interface->interface_protocol,
98 data->request_size, pipe->desc.max_transfer_size);
99 }
100
101 size_t failed_attempts = 0;
102 while (failed_attempts <= params->max_failures) {
103 size_t actual_size;
104 const int rc = usb_pipe_read(pipe, data->buffer,
105 data->request_size, &actual_size);
106
107 if (rc == EOK) {
108 if (params->debug > 1) {
109 usb_log_debug(
110 "Poll%p: received: '%s' (%zuB).\n",
111 data,
112 usb_debug_str_buffer(data->buffer,
113 actual_size, 16),
114 actual_size);
115 }
116 } else {
117 usb_log_debug(
118 "Poll%p: polling failed: %s.\n",
119 data, str_error(rc));
120 }
121
122 /* If the pipe stalled, we can try to reset the stall. */
123 if ((rc == ESTALL) && (params->auto_clear_halt)) {
124 /*
125 * We ignore error here as this is usually a futile
126 * attempt anyway.
127 */
128 usb_request_clear_endpoint_halt(
129 usb_device_get_default_pipe(data->dev),
130 pipe->desc.endpoint_no);
131 }
132
133 if (rc != EOK) {
134 ++failed_attempts;
135 const bool cont = (params->on_error == NULL) ? true :
136 params->on_error(data->dev, rc, params->arg);
137 if (!cont) {
138 failed_attempts = params->max_failures;
139 }
140 continue;
141 }
142
143 /* We have the data, execute the callback now. */
144 assert(params->on_data);
145 const bool carry_on = params->on_data(
146 data->dev, data->buffer, actual_size, params->arg);
147
148 if (!carry_on) {
149 /* This is user requested abort, erases failures. */
150 failed_attempts = 0;
151 break;
152 }
153
154 /* Reset as something might be only a temporary problem. */
155 failed_attempts = 0;
156
157 /* Take a rest before next request. */
158
159 // FIXME TODO: This is broken, the time is in ms not us.
160 // but first we need to fix drivers to actually stop using this,
161 // since polling delay should be implemented in HC schedule
162 async_usleep(params->delay);
163 }
164
165 const bool failed = failed_attempts > 0;
166
167 if (params->on_polling_end != NULL) {
168 params->on_polling_end(data->dev, failed, params->arg);
169 }
170
171 if (params->debug > 0) {
172 if (failed) {
173 usb_log_error("Polling of device `%s' terminated: "
174 "recurring failures.\n",
175 usb_device_get_name(data->dev));
176 } else {
177 usb_log_debug("Polling of device `%s' terminated: "
178 "driver request.\n",
179 usb_device_get_name(data->dev));
180 }
181 }
182
183 /* Free the allocated memory. */
184 free(data->buffer);
185 free(data);
186
187 return EOK;
188}
189
190
191/** Start automatic device polling over interrupt in pipe.
192 *
193 * The polling settings is copied thus it is okay to destroy the structure
194 * after this function returns.
195 *
196 * @warning There is no guarantee when the request to the device
197 * will be sent for the first time (it is possible that this
198 * first request would be executed prior to return from this function).
199 *
200 * @param dev Device to be periodically polled.
201 * @param epm Endpoint mapping to use.
202 * @param polling Polling settings.
203 * @param request_size How many bytes to ask for in each request.
204 * @param arg Custom argument (passed as is to the callbacks).
205 * @return Error code.
206 * @retval EOK New fibril polling the device was already started.
207 */
208static int usb_device_auto_polling_internal(usb_device_t *dev,
209 usb_endpoint_mapping_t *epm, const usb_device_auto_polling_t *polling,
210 size_t request_size)
211{
212 if ((dev == NULL) || (polling == NULL) || (polling->on_data == NULL)) {
213 return EBADMEM;
214 }
215
216 if (request_size == 0)
217 return EINVAL;
218
219 if (!epm || (epm->pipe.desc.transfer_type != USB_TRANSFER_INTERRUPT) ||
220 (epm->pipe.desc.direction != USB_DIRECTION_IN))
221 return EINVAL;
222
223
224 polling_data_t *polling_data = malloc(sizeof(polling_data_t));
225 if (polling_data == NULL) {
226 return ENOMEM;
227 }
228
229 /* Fill-in the data. */
230 polling_data->buffer = malloc(sizeof(request_size));
231 if (polling_data->buffer == NULL) {
232 free(polling_data);
233 return ENOMEM;
234 }
235 polling_data->request_size = request_size;
236 polling_data->dev = dev;
237 polling_data->polling_mapping = epm;
238
239 /* Copy provided settings. */
240 polling_data->auto_polling = *polling;
241
242 /* Negative value means use descriptor provided value. */
243 if (polling->delay < 0) {
244 polling_data->auto_polling.delay =
245 epm->descriptor->poll_interval;
246 }
247
248 fid_t fibril = fibril_create(polling_fibril, polling_data);
249 if (fibril == 0) {
250 free(polling_data->buffer);
251 free(polling_data);
252 return ENOMEM;
253 }
254 fibril_add_ready(fibril);
255
256 /* Fibril launched. That fibril will free the allocated data. */
257
258 return EOK;
259}
260/** Start automatic device polling over interrupt in pipe.
261 *
262 * The polling settings is copied thus it is okay to destroy the structure
263 * after this function returns.
264 *
265 * @warning There is no guarantee when the request to the device
266 * will be sent for the first time (it is possible that this
267 * first request would be executed prior to return from this function).
268 *
269 * @param dev Device to be periodically polled.
270 * @param pipe_index Index of the endpoint pipe used for polling.
271 * @param polling Polling settings.
272 * @param req_size How many bytes to ask for in each request.
273 * @param arg Custom argument (passed as is to the callbacks).
274 * @return Error code.
275 * @retval EOK New fibril polling the device was already started.
276 */
277int usb_device_auto_polling(usb_device_t *usb_dev, usb_endpoint_t ep,
278 const usb_device_auto_polling_t *polling, size_t req_size)
279{
280 usb_endpoint_mapping_t *epm = usb_device_get_mapped_ep(usb_dev, ep);
281 return usb_device_auto_polling_internal(usb_dev, epm, polling, req_size);
282}
283
284/** Start automatic device polling over interrupt in pipe.
285 *
286 * @warning It is up to the callback to produce delays between individual
287 * requests.
288 *
289 * @warning There is no guarantee when the request to the device
290 * will be sent for the first time (it is possible that this
291 * first request would be executed prior to return from this function).
292 *
293 * @param dev Device to be periodically polled.
294 * @param ep Endpoint used for polling.
295 * @param callback Callback when data are available.
296 * @param request_size How many bytes to ask for in each request.
297 * @param delay NUmber of ms to wait between queries, -1 to use descriptor val.
298 * @param terminated_callback Callback when polling is terminated.
299 * @param arg Custom argument (passed as is to the callbacks).
300 * @return Error code.
301 * @retval EOK New fibril polling the device was already started.
302 */
303int usb_device_auto_poll(usb_device_t *dev, usb_endpoint_t ep,
304 usb_polling_callback_t callback, size_t request_size, int delay,
305 usb_polling_terminted_callback_t terminated_callback, void *arg)
306{
307 const usb_device_auto_polling_t auto_polling = {
308 .debug = 1,
309 .auto_clear_halt = true,
310 .delay = delay,
311 .max_failures = MAX_FAILED_ATTEMPTS,
312 .on_data = callback,
313 .on_polling_end = terminated_callback,
314 .on_error = NULL,
315 .arg = arg,
316 };
317
318 usb_endpoint_mapping_t *epm = usb_device_get_mapped_ep(dev, ep);
319 return usb_device_auto_polling_internal(
320 dev, epm, &auto_polling, request_size);
321}
322
323int usb_device_auto_polling_desc(usb_device_t *usb_dev,
324 const usb_endpoint_description_t *desc,
325 const usb_device_auto_polling_t *polling, size_t req_size)
326{
327 usb_endpoint_mapping_t *epm =
328 usb_device_get_mapped_ep_desc(usb_dev, desc);
329 return usb_device_auto_polling_internal(usb_dev, epm, polling, req_size);
330}
331
332int usb_device_auto_poll_desc(usb_device_t * usb_dev,
333 const usb_endpoint_description_t *desc, usb_polling_callback_t callback,
334 size_t req_size, int delay,
335 usb_polling_terminted_callback_t terminated_callback, void *arg)
336{
337 const usb_device_auto_polling_t auto_polling = {
338 .debug = 1,
339 .auto_clear_halt = true,
340 .delay = delay,
341 .max_failures = MAX_FAILED_ATTEMPTS,
342 .on_data = callback,
343 .on_polling_end = terminated_callback,
344 .on_error = NULL,
345 .arg = arg,
346 };
347
348 usb_endpoint_mapping_t *epm =
349 usb_device_get_mapped_ep_desc(usb_dev, desc);
350 return usb_device_auto_polling_internal(
351 usb_dev, epm, &auto_polling, req_size);
352}
353
354/**
355 * @}
356 */
Note: See TracBrowser for help on using the repository browser.