source: mainline/uspace/lib/usbdev/src/devpoll.c@ 7dddd7b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7dddd7b was 7dddd7b, checked in by Petr Manek <petr.manek@…>, 7 years ago

usbdev: refactor polling

Until now, device polling had to be executed by calling one of four
proxy functions, which served as a syntax sugar and had no clear
distinction between each other (not to mention misleading names and high
number of arguments).

In this commit, the four mentioned functions are discarded in favor of
one main function, which was proxied by them either way. The number of
arguments have decreased in favor of named struct fields in the auto
polling config structure.

Drivers, which make use of polling, such as usbhid and usbhub were
updated to the latest API design.

  • Property mode set to 100644
File size: 7.4 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 /* This is user requested abort, erases failures. */
139 failed_attempts = 0;
140 break;
141 }
142 continue;
143 }
144
145 /* We have the data, execute the callback now. */
146 assert(params->on_data);
147 const bool carry_on = params->on_data(
148 data->dev, data->buffer, actual_size, params->arg);
149
150 if (!carry_on) {
151 /* This is user requested abort, erases failures. */
152 failed_attempts = 0;
153 break;
154 }
155
156 /* Reset as something might be only a temporary problem. */
157 failed_attempts = 0;
158
159 /* Take a rest before next request. */
160
161 // FIXME TODO: This is broken, the time is in ms not us.
162 // but first we need to fix drivers to actually stop using this,
163 // since polling delay should be implemented in HC schedule
164 async_usleep(params->delay);
165 }
166
167 const bool failed = failed_attempts > 0;
168
169 if (params->on_polling_end != NULL) {
170 params->on_polling_end(data->dev, failed, params->arg);
171 }
172
173 if (params->debug > 0) {
174 if (failed) {
175 usb_log_error("Polling of device `%s' terminated: "
176 "recurring failures.\n",
177 usb_device_get_name(data->dev));
178 } else {
179 usb_log_debug("Polling of device `%s' terminated: "
180 "driver request.\n",
181 usb_device_get_name(data->dev));
182 }
183 }
184
185 /* Free the allocated memory. */
186 free(data->buffer);
187 free(data);
188
189 return EOK;
190}
191
192
193/** Start automatic device polling over interrupt in pipe.
194 *
195 * The polling settings is copied thus it is okay to destroy the structure
196 * after this function returns.
197 *
198 * @warning There is no guarantee when the request to the device
199 * will be sent for the first time (it is possible that this
200 * first request would be executed prior to return from this function).
201 *
202 * @param dev Device to be periodically polled.
203 * @param epm Endpoint mapping to use.
204 * @param polling Polling settings.
205 * @param req_size How many bytes to ask for in each request.
206 * @return Error code.
207 * @retval EOK New fibril polling the device was already started.
208 */
209int usb_device_auto_polling(usb_device_t *dev, usb_endpoint_mapping_t *epm,
210 const usb_device_auto_polling_t *polling, size_t req_size)
211{
212 int rc;
213 if (!dev || !polling || !polling->on_data)
214 return EBADMEM;
215
216 if (!req_size)
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 polling_data_t *polling_data = malloc(sizeof(polling_data_t));
224 if (!polling_data)
225 return ENOMEM;
226
227 /* Fill-in the data. */
228 polling_data->buffer = malloc(req_size);
229 if (polling_data->buffer == NULL) {
230 rc = ENOMEM;
231 goto err_polling_data;
232 }
233 polling_data->request_size = req_size;
234 polling_data->dev = dev;
235 polling_data->polling_mapping = epm;
236
237 /* Copy provided settings. */
238 polling_data->auto_polling = *polling;
239
240 /* Negative value means use descriptor provided value. */
241 if (polling->delay < 0) {
242 polling_data->auto_polling.delay =
243 epm->descriptor->poll_interval;
244 }
245
246 fid_t fibril = fibril_create(polling_fibril, polling_data);
247 if (!fibril) {
248 rc = ENOMEM;
249 goto err_buffer;
250 }
251 fibril_add_ready(fibril);
252
253 /* Fibril launched. That fibril will free the allocated data. */
254 return EOK;
255
256err_buffer:
257 free(polling_data->buffer);
258err_polling_data:
259 free(polling_data);
260 return rc;
261}
262
263/**
264 * @}
265 */
Note: See TracBrowser for help on using the repository browser.