source: mainline/uspace/lib/usbdev/src/devpoll.c@ 34b2f54d

Last change on this file since 34b2f54d 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
RevLine 
[4bf94df]1/*
[d7f7a4a]2 * SPDX-FileCopyrightText: 2011 Vojtech Horky
3 * SPDX-FileCopyrightText: 2017 Petr Manek
[4bf94df]4 *
[d7f7a4a]5 * SPDX-License-Identifier: BSD-3-Clause
[4bf94df]6 */
7
[160b75e]8/** @addtogroup libusbdev
[4bf94df]9 * @{
10 */
11/** @file
12 * USB device driver framework - automatic interrupt polling.
13 */
[58563585]14
[c01987c]15#include <usb/dev/device.h>
16#include <usb/dev/pipes.h>
[7d521e24]17#include <usb/dev/poll.h>
18#include <usb/dev/request.h>
[5e168be1]19#include <usb/classes/classes.h>
[c01987c]20#include <usb/debug.h>
21#include <usb/descriptor.h>
22#include <usb/usb.h>
23
24#include <assert.h>
25#include <async.h>
[4bf94df]26#include <errno.h>
[c01987c]27#include <fibril.h>
[8a0c52a]28#include <fibril_synch.h>
[c01987c]29#include <stdbool.h>
30#include <stdlib.h>
[4bf94df]31#include <str_error.h>
[8d2dd7f2]32#include <stddef.h>
33#include <stdint.h>
[4bf94df]34
[8b71f3e]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;
[71f211f]46
[8b71f3e]47 /* Zero out everything */
48 memset(polling, 0, sizeof(usb_polling_t));
[8a0c52a]49
[8b71f3e]50 /* Internal initializers. */
51 fibril_mutex_initialize(&polling->guard);
52 fibril_condvar_initialize(&polling->cv);
[8a0c52a]53
[8b71f3e]54 /* Default configuration. */
55 polling->auto_clear_halt = true;
56 polling->delay = -1;
57 polling->max_failures = 3;
[8a0c52a]58
[8b71f3e]59 return EOK;
60}
[4bf94df]61
[8b71f3e]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)
[8a0c52a]69{
[8b71f3e]70 /* Nothing done at the moment. */
71 assert(polling);
72 assert(!polling->running);
[8a0c52a]73}
74
[4bf94df]75/** Polling fibril.
76 *
[8b71f3e]77 * @param arg Pointer to usb_polling_t.
[4bf94df]78 * @return Always EOK.
79 */
[5a6cc679]80static errno_t polling_fibril(void *arg)
[4bf94df]81{
[9dbfd288]82 assert(arg);
[8b71f3e]83 usb_polling_t *polling = arg;
[4e44f5d]84
85 fibril_mutex_lock(&polling->guard);
[8b71f3e]86 polling->running = true;
[4e44f5d]87 fibril_mutex_unlock(&polling->guard);
[71f211f]88
[8b71f3e]89 usb_pipe_t *pipe = &polling->ep_mapping->pipe;
[4bf94df]90
[8b71f3e]91 if (polling->debug > 0) {
[58563585]92 const usb_endpoint_mapping_t *mapping =
[8b71f3e]93 polling->ep_mapping;
[5ef16903]94 usb_log_debug("Poll (%p): started polling of `%s' - "
[5e168be1]95 "interface %d (%s,%d,%d), %zuB/%zu.\n",
[8b71f3e]96 polling, usb_device_get_name(polling->device),
[5e168be1]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,
[8b71f3e]101 polling->request_size, pipe->desc.max_transfer_size);
[5e168be1]102 }
[4bf94df]103
104 size_t failed_attempts = 0;
[8b71f3e]105 while (failed_attempts <= polling->max_failures) {
[4bf94df]106 size_t actual_size;
[5a6cc679]107 const errno_t rc = usb_pipe_read(pipe, polling->buffer,
[8b71f3e]108 polling->request_size, &actual_size);
[4bf94df]109
[51cc6cef]110 if (rc == EOK) {
[8b71f3e]111 if (polling->debug > 1) {
[5e168be1]112 usb_log_debug(
[4125b7d]113 "Poll%p: received: '%s' (%zuB).\n",
[8b71f3e]114 polling,
115 usb_debug_str_buffer(polling->buffer,
[3bacee1]116 actual_size, 16),
[5e168be1]117 actual_size);
[51cc6cef]118 }
119 } else {
[3bacee1]120 usb_log_debug(
121 "Poll%p: polling failed: %s.\n",
122 polling, str_error(rc));
[5e168be1]123 }
124
[8989f48f]125 /* If the pipe stalled, we can try to reset the stall. */
[8b71f3e]126 if (rc == ESTALL && polling->auto_clear_halt) {
[8989f48f]127 /*
128 * We ignore error here as this is usually a futile
129 * attempt anyway.
130 */
[d08aa42d]131 usb_pipe_clear_halt(
132 usb_device_get_default_pipe(polling->device), pipe);
[8989f48f]133 }
[4bf94df]134
135 if (rc != EOK) {
[9dbfd288]136 ++failed_attempts;
[8b71f3e]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) {
[a4eb520f]141 /* This is user requested abort, erases failures. */
142 failed_attempts = 0;
143 break;
[8989f48f]144 }
[4bf94df]145 continue;
146 }
147
148 /* We have the data, execute the callback now. */
[8b71f3e]149 assert(polling->on_data);
150 const bool carry_on = polling->on_data(polling->device,
151 polling->buffer, actual_size, polling->arg);
[4bf94df]152
153 if (!carry_on) {
[9dbfd288]154 /* This is user requested abort, erases failures. */
[4bf94df]155 failed_attempts = 0;
156 break;
157 }
158
159 /* Reset as something might be only a temporary problem. */
160 failed_attempts = 0;
[8989f48f]161
162 /* Take a rest before next request. */
[816f5f4]163
[58563585]164 // FIXME TODO: This is broken, the time is in ms not us.
[ff0258f]165 // but first we need to fix drivers to actually stop using this,
[58563585]166 // since polling delay should be implemented in HC schedule
[5f97ef44]167 fibril_usleep(polling->delay);
[4bf94df]168 }
169
[9dbfd288]170 const bool failed = failed_attempts > 0;
171
[8b71f3e]172 if (polling->on_polling_end)
173 polling->on_polling_end(polling->device, failed, polling->arg);
[4bf94df]174
[8b71f3e]175 if (polling->debug > 0) {
[9dbfd288]176 if (failed) {
177 usb_log_error("Polling of device `%s' terminated: "
[f6b2a76b]178 "recurring failures.\n",
[8b71f3e]179 usb_device_get_name(polling->device));
[5e168be1]180 } else {
[9dbfd288]181 usb_log_debug("Polling of device `%s' terminated: "
[f6b2a76b]182 "driver request.\n",
[8b71f3e]183 usb_device_get_name(polling->device));
[5e168be1]184 }
185 }
186
[4e44f5d]187 fibril_mutex_lock(&polling->guard);
[8b71f3e]188 polling->running = false;
[4e44f5d]189 fibril_mutex_unlock(&polling->guard);
[8a0c52a]190
[edc51615]191 /* Notify joiners, if any. */
[8b71f3e]192 fibril_condvar_broadcast(&polling->cv);
[4bf94df]193 return EOK;
194}
195
[8989f48f]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 *
[8b71f3e]205 * @param polling Polling data structure.
[8989f48f]206 * @return Error code.
207 * @retval EOK New fibril polling the device was already started.
208 */
[5a6cc679]209errno_t usb_polling_start(usb_polling_t *polling)
[8989f48f]210{
[8b71f3e]211 if (!polling || !polling->device || !polling->ep_mapping || !polling->on_data)
[8989f48f]212 return EBADMEM;
[b81410f]213
[8b71f3e]214 if (!polling->request_size)
[8989f48f]215 return EINVAL;
[816f5f4]216
[3bacee1]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))
[8989f48f]219 return EINVAL;
[816f5f4]220
[2703331b]221 /* Negative value means use descriptor provided value. */
[8b71f3e]222 if (polling->delay < 0)
223 polling->delay = polling->ep_mapping->descriptor->poll_interval;
[8989f48f]224
[8b71f3e]225 polling->fibril = fibril_create(polling_fibril, polling);
226 if (!polling->fibril)
227 return ENOMEM;
[4bf94df]228
[8b71f3e]229 fibril_add_ready(polling->fibril);
[71f211f]230
[8989f48f]231 /* Fibril launched. That fibril will free the allocated data. */
[4bf94df]232 return EOK;
[bb70637]233}
[4bf94df]234
[8b71f3e]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 */
[5a6cc679]248errno_t usb_polling_join(usb_polling_t *polling)
[8a0c52a]249{
[5a6cc679]250 errno_t rc;
[8a0c52a]251 if (!polling)
252 return EBADMEM;
253
[8b71f3e]254 /* Check if the fibril already terminated. */
255 if (!polling->running)
256 return EOK;
257
[8a0c52a]258 /* Set the flag */
259 polling->joining = true;
260
261 /* Unregister the pipe. */
[338729c]262 rc = usb_device_unmap_ep(polling->ep_mapping);
[3f44312]263 if (rc != EOK && rc != ENOENT && rc != EHANGUP)
[8a0c52a]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
[4bf94df]275/**
276 * @}
277 */
Note: See TracBrowser for help on using the repository browser.