source: mainline/uspace/lib/usbdev/src/devpoll.c@ 71f211f

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

usbdev: refactor polling data structs

Symbols related to USB device endpoint polling have been moved around
and renamed in this commit.

usb_device_auto_polling_t, which has the semantics of a configuration
parameter struct, has been renamed to usb_device_polling_config_t.

usb_device_auto_polling() is now called usb_device_poll().

A new data structure, usb_device_polling_t, has been introduced to
serve as a user handle to the active polling process (WIP).

  • Property mode set to 100644
File size: 7.3 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/** Private automated polling instance data. */
56struct usb_device_polling {
57 /** Parameters for automated polling. */
58 usb_device_polling_config_t config;
59
60 /** USB device to poll. */
61 usb_device_t *dev;
62
63 /** Device enpoint mapping to use for polling. */
64 usb_endpoint_mapping_t *polling_mapping;
65
66 /** Size of the recieved data. */
67 size_t request_size;
68
69 /** Data buffer. */
70 uint8_t *buffer;
71};
72
73
74/** Polling fibril.
75 *
76 * @param arg Pointer to usb_device_polling_t.
77 * @return Always EOK.
78 */
79static int polling_fibril(void *arg)
80{
81 assert(arg);
82 const usb_device_polling_t *data = arg;
83
84 /* Helper to reduce typing. */
85 const usb_device_polling_config_t *params = &data->config;
86
87 usb_pipe_t *pipe = &data->polling_mapping->pipe;
88
89 if (params->debug > 0) {
90 const usb_endpoint_mapping_t *mapping =
91 data->polling_mapping;
92 usb_log_debug("Poll (%p): started polling of `%s' - " \
93 "interface %d (%s,%d,%d), %zuB/%zu.\n",
94 data, usb_device_get_name(data->dev),
95 (int) mapping->interface->interface_number,
96 usb_str_class(mapping->interface->interface_class),
97 (int) mapping->interface->interface_subclass,
98 (int) mapping->interface->interface_protocol,
99 data->request_size, pipe->desc.max_transfer_size);
100 }
101
102 size_t failed_attempts = 0;
103 while (failed_attempts <= params->max_failures) {
104 size_t actual_size;
105 const int rc = usb_pipe_read(pipe, data->buffer,
106 data->request_size, &actual_size);
107
108 if (rc == EOK) {
109 if (params->debug > 1) {
110 usb_log_debug(
111 "Poll%p: received: '%s' (%zuB).\n",
112 data,
113 usb_debug_str_buffer(data->buffer,
114 actual_size, 16),
115 actual_size);
116 }
117 } else {
118 usb_log_debug(
119 "Poll%p: polling failed: %s.\n",
120 data, str_error(rc));
121 }
122
123 /* If the pipe stalled, we can try to reset the stall. */
124 if ((rc == ESTALL) && (params->auto_clear_halt)) {
125 /*
126 * We ignore error here as this is usually a futile
127 * attempt anyway.
128 */
129 usb_request_clear_endpoint_halt(
130 usb_device_get_default_pipe(data->dev),
131 pipe->desc.endpoint_no);
132 }
133
134 if (rc != EOK) {
135 ++failed_attempts;
136 const bool cont = (params->on_error == NULL) ? true :
137 params->on_error(data->dev, rc, params->arg);
138 if (!cont) {
139 /* This is user requested abort, erases failures. */
140 failed_attempts = 0;
141 break;
142 }
143 continue;
144 }
145
146 /* We have the data, execute the callback now. */
147 assert(params->on_data);
148 const bool carry_on = params->on_data(
149 data->dev, data->buffer, actual_size, params->arg);
150
151 if (!carry_on) {
152 /* This is user requested abort, erases failures. */
153 failed_attempts = 0;
154 break;
155 }
156
157 /* Reset as something might be only a temporary problem. */
158 failed_attempts = 0;
159
160 /* Take a rest before next request. */
161
162 // FIXME TODO: This is broken, the time is in ms not us.
163 // but first we need to fix drivers to actually stop using this,
164 // since polling delay should be implemented in HC schedule
165 async_usleep(params->delay);
166 }
167
168 const bool failed = failed_attempts > 0;
169
170 if (params->on_polling_end != NULL) {
171 params->on_polling_end(data->dev, failed, params->arg);
172 }
173
174 if (params->debug > 0) {
175 if (failed) {
176 usb_log_error("Polling of device `%s' terminated: "
177 "recurring failures.\n",
178 usb_device_get_name(data->dev));
179 } else {
180 usb_log_debug("Polling of device `%s' terminated: "
181 "driver request.\n",
182 usb_device_get_name(data->dev));
183 }
184 }
185
186 /* Free the allocated memory. */
187 free(data->buffer);
188 free(data);
189
190 return EOK;
191}
192
193
194/** Start automatic device polling over interrupt in pipe.
195 *
196 * The polling settings is copied thus it is okay to destroy the structure
197 * after this function returns.
198 *
199 * @warning There is no guarantee when the request to the device
200 * will be sent for the first time (it is possible that this
201 * first request would be executed prior to return from this function).
202 *
203 * @param dev Device to be periodically polled.
204 * @param epm Endpoint mapping to use.
205 * @param config Polling settings.
206 * @param req_size How many bytes to ask for in each request.
207 * @return Error code.
208 * @retval EOK New fibril polling the device was already started.
209 */
210int usb_device_poll(usb_device_t *dev, usb_endpoint_mapping_t *epm,
211 const usb_device_polling_config_t *config, size_t req_size,
212 usb_device_polling_t **handle)
213{
214 int rc;
215 if (!dev || !config || !config->on_data)
216 return EBADMEM;
217
218 if (!req_size)
219 return EINVAL;
220
221 if (!epm || (epm->pipe.desc.transfer_type != USB_TRANSFER_INTERRUPT) ||
222 (epm->pipe.desc.direction != USB_DIRECTION_IN))
223 return EINVAL;
224
225 usb_device_polling_t *instance = malloc(sizeof(usb_device_polling_t));
226 if (!instance)
227 return ENOMEM;
228
229 /* Fill-in the data. */
230 instance->buffer = malloc(req_size);
231 if (!instance->buffer) {
232 rc = ENOMEM;
233 goto err_instance;
234 }
235 instance->request_size = req_size;
236 instance->dev = dev;
237 instance->polling_mapping = epm;
238
239 /* Copy provided settings. */
240 instance->config = *config;
241
242 /* Negative value means use descriptor provided value. */
243 if (config->delay < 0) {
244 instance->config.delay = epm->descriptor->poll_interval;
245 }
246
247 fid_t fibril = fibril_create(polling_fibril, instance);
248 if (!fibril) {
249 rc = ENOMEM;
250 goto err_buffer;
251 }
252 fibril_add_ready(fibril);
253
254 if (handle)
255 *handle = instance;
256
257 /* Fibril launched. That fibril will free the allocated data. */
258 return EOK;
259
260err_buffer:
261 free(instance->buffer);
262err_instance:
263 free(instance);
264 return rc;
265}
266
267/**
268 * @}
269 */
Note: See TracBrowser for help on using the repository browser.