source: mainline/uspace/lib/usbdev/src/devpoll.c@ 8624d1f

Last change on this file since 8624d1f was d7f7a4a, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 3 years ago

Replace some license headers with SPDX identifier

Headers are replaced using tools/transorm-copyright.sh only
when it can be matched verbatim with the license header used
throughout most of the codebase.

  • Property mode set to 100644
File size: 7.1 KB
Line 
1/*
2 * SPDX-FileCopyrightText: 2011 Vojtech Horky
3 * SPDX-FileCopyrightText: 2017 Petr Manek
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8/** @addtogroup libusbdev
9 * @{
10 */
11/** @file
12 * USB device driver framework - automatic interrupt polling.
13 */
14
15#include <usb/dev/device.h>
16#include <usb/dev/pipes.h>
17#include <usb/dev/poll.h>
18#include <usb/dev/request.h>
19#include <usb/classes/classes.h>
20#include <usb/debug.h>
21#include <usb/descriptor.h>
22#include <usb/usb.h>
23
24#include <assert.h>
25#include <async.h>
26#include <errno.h>
27#include <fibril.h>
28#include <fibril_synch.h>
29#include <stdbool.h>
30#include <stdlib.h>
31#include <str_error.h>
32#include <stddef.h>
33#include <stdint.h>
34
35/** Initialize the polling data structure, its internals and configuration
36 * with default values.
37 *
38 * @param polling Valid polling data structure.
39 * @return Error code.
40 * @retval EOK Polling data structure is ready to be used.
41 */
42int usb_polling_init(usb_polling_t *polling)
43{
44 if (!polling)
45 return EBADMEM;
46
47 /* Zero out everything */
48 memset(polling, 0, sizeof(usb_polling_t));
49
50 /* Internal initializers. */
51 fibril_mutex_initialize(&polling->guard);
52 fibril_condvar_initialize(&polling->cv);
53
54 /* Default configuration. */
55 polling->auto_clear_halt = true;
56 polling->delay = -1;
57 polling->max_failures = 3;
58
59 return EOK;
60}
61
62/** Destroy the polling data structure.
63 * This function does nothing but a safety check whether the polling
64 * was joined successfully.
65 *
66 * @param polling Polling data structure.
67 */
68void usb_polling_fini(usb_polling_t *polling)
69{
70 /* Nothing done at the moment. */
71 assert(polling);
72 assert(!polling->running);
73}
74
75/** Polling fibril.
76 *
77 * @param arg Pointer to usb_polling_t.
78 * @return Always EOK.
79 */
80static errno_t polling_fibril(void *arg)
81{
82 assert(arg);
83 usb_polling_t *polling = arg;
84
85 fibril_mutex_lock(&polling->guard);
86 polling->running = true;
87 fibril_mutex_unlock(&polling->guard);
88
89 usb_pipe_t *pipe = &polling->ep_mapping->pipe;
90
91 if (polling->debug > 0) {
92 const usb_endpoint_mapping_t *mapping =
93 polling->ep_mapping;
94 usb_log_debug("Poll (%p): started polling of `%s' - "
95 "interface %d (%s,%d,%d), %zuB/%zu.\n",
96 polling, usb_device_get_name(polling->device),
97 (int) mapping->interface->interface_number,
98 usb_str_class(mapping->interface->interface_class),
99 (int) mapping->interface->interface_subclass,
100 (int) mapping->interface->interface_protocol,
101 polling->request_size, pipe->desc.max_transfer_size);
102 }
103
104 size_t failed_attempts = 0;
105 while (failed_attempts <= polling->max_failures) {
106 size_t actual_size;
107 const errno_t rc = usb_pipe_read(pipe, polling->buffer,
108 polling->request_size, &actual_size);
109
110 if (rc == EOK) {
111 if (polling->debug > 1) {
112 usb_log_debug(
113 "Poll%p: received: '%s' (%zuB).\n",
114 polling,
115 usb_debug_str_buffer(polling->buffer,
116 actual_size, 16),
117 actual_size);
118 }
119 } else {
120 usb_log_debug(
121 "Poll%p: polling failed: %s.\n",
122 polling, str_error(rc));
123 }
124
125 /* If the pipe stalled, we can try to reset the stall. */
126 if (rc == ESTALL && polling->auto_clear_halt) {
127 /*
128 * We ignore error here as this is usually a futile
129 * attempt anyway.
130 */
131 usb_pipe_clear_halt(
132 usb_device_get_default_pipe(polling->device), pipe);
133 }
134
135 if (rc != EOK) {
136 ++failed_attempts;
137 const bool carry_on = !polling->on_error ? true :
138 polling->on_error(polling->device, rc, polling->arg);
139
140 if (!carry_on || polling->joining) {
141 /* This is user requested abort, erases failures. */
142 failed_attempts = 0;
143 break;
144 }
145 continue;
146 }
147
148 /* We have the data, execute the callback now. */
149 assert(polling->on_data);
150 const bool carry_on = polling->on_data(polling->device,
151 polling->buffer, actual_size, polling->arg);
152
153 if (!carry_on) {
154 /* This is user requested abort, erases failures. */
155 failed_attempts = 0;
156 break;
157 }
158
159 /* Reset as something might be only a temporary problem. */
160 failed_attempts = 0;
161
162 /* Take a rest before next request. */
163
164 // FIXME TODO: This is broken, the time is in ms not us.
165 // but first we need to fix drivers to actually stop using this,
166 // since polling delay should be implemented in HC schedule
167 fibril_usleep(polling->delay);
168 }
169
170 const bool failed = failed_attempts > 0;
171
172 if (polling->on_polling_end)
173 polling->on_polling_end(polling->device, failed, polling->arg);
174
175 if (polling->debug > 0) {
176 if (failed) {
177 usb_log_error("Polling of device `%s' terminated: "
178 "recurring failures.\n",
179 usb_device_get_name(polling->device));
180 } else {
181 usb_log_debug("Polling of device `%s' terminated: "
182 "driver request.\n",
183 usb_device_get_name(polling->device));
184 }
185 }
186
187 fibril_mutex_lock(&polling->guard);
188 polling->running = false;
189 fibril_mutex_unlock(&polling->guard);
190
191 /* Notify joiners, if any. */
192 fibril_condvar_broadcast(&polling->cv);
193 return EOK;
194}
195
196/** Start automatic device polling over interrupt in pipe.
197 *
198 * The polling settings is copied thus it is okay to destroy the structure
199 * after this function returns.
200 *
201 * @warning There is no guarantee when the request to the device
202 * will be sent for the first time (it is possible that this
203 * first request would be executed prior to return from this function).
204 *
205 * @param polling Polling data structure.
206 * @return Error code.
207 * @retval EOK New fibril polling the device was already started.
208 */
209errno_t usb_polling_start(usb_polling_t *polling)
210{
211 if (!polling || !polling->device || !polling->ep_mapping || !polling->on_data)
212 return EBADMEM;
213
214 if (!polling->request_size)
215 return EINVAL;
216
217 if (!polling->ep_mapping || (polling->ep_mapping->pipe.desc.transfer_type != USB_TRANSFER_INTERRUPT) ||
218 (polling->ep_mapping->pipe.desc.direction != USB_DIRECTION_IN))
219 return EINVAL;
220
221 /* Negative value means use descriptor provided value. */
222 if (polling->delay < 0)
223 polling->delay = polling->ep_mapping->descriptor->poll_interval;
224
225 polling->fibril = fibril_create(polling_fibril, polling);
226 if (!polling->fibril)
227 return ENOMEM;
228
229 fibril_add_ready(polling->fibril);
230
231 /* Fibril launched. That fibril will free the allocated data. */
232 return EOK;
233}
234
235/** Close the polling pipe permanently and synchronously wait
236 * until the automatic polling fibril terminates.
237 *
238 * It is safe to deallocate the polling data structure (and its
239 * data buffer) only after a successful call to this function.
240 *
241 * @warning Call to this function will trigger execution of the
242 * on_error() callback with EINTR error code.
243 *
244 * @parram polling Polling data structure.
245 * @return Error code.
246 * @retval EOK Polling fibril has been successfully terminated.
247 */
248errno_t usb_polling_join(usb_polling_t *polling)
249{
250 errno_t rc;
251 if (!polling)
252 return EBADMEM;
253
254 /* Check if the fibril already terminated. */
255 if (!polling->running)
256 return EOK;
257
258 /* Set the flag */
259 polling->joining = true;
260
261 /* Unregister the pipe. */
262 rc = usb_device_unmap_ep(polling->ep_mapping);
263 if (rc != EOK && rc != ENOENT && rc != EHANGUP)
264 return rc;
265
266 /* Wait for the fibril to terminate. */
267 fibril_mutex_lock(&polling->guard);
268 while (polling->running)
269 fibril_condvar_wait(&polling->cv, &polling->guard);
270 fibril_mutex_unlock(&polling->guard);
271
272 return EOK;
273}
274
275/**
276 * @}
277 */
Note: See TracBrowser for help on using the repository browser.