source: mainline/uspace/drv/bus/usb/uhci/transfer_list.c@ 351113f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 351113f was 0892663a, checked in by Ondřej Hlavatý <aearsis@…>, 8 years ago

usbhost: device removal and off/onlining moved into the library

Also, it is just not possible to make generic transfer abortion. So the
current semantics of endpoint unregistering is also aborting the pending
transfer. As it is not yet implemented in XHCI, and the stub in UHCI is
missing the magic, it breaks offlining interrupt devices, such as mouse.

When finishing this commit, I came across the fact we need some more
synchronization above device. Guard can protect internal structures, but
it cannot synchronize multiple calls to offline, or offline & removal
between each other - they both need to allow driver to unregister
endpoints, and as such must release the guard.

  • Property mode set to 100644
File size: 7.4 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
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 drvusbuhcihc
30 * @{
31 */
32/** @file
33 * @brief UHCI driver transfer list implementation
34 */
35
36#include <assert.h>
37#include <errno.h>
38#include <libarch/barrier.h>
39#include <stdint.h>
40#include <usb/debug.h>
41#include <usb/host/usb_transfer_batch.h>
42#include <usb/host/utils/malloc32.h>
43
44#include "hw_struct/link_pointer.h"
45#include "transfer_list.h"
46
47static void transfer_list_remove_batch(
48 transfer_list_t *instance, uhci_transfer_batch_t *uhci_batch);
49
50/** Initialize transfer list structures.
51 *
52 * @param[in] instance Memory place to use.
53 * @param[in] name Name of the new list.
54 * @return Error code
55 *
56 * Allocates memory for internal qh_t structure.
57 */
58int transfer_list_init(transfer_list_t *instance, const char *name)
59{
60 assert(instance);
61 instance->name = name;
62 instance->queue_head = malloc32(sizeof(qh_t));
63 if (!instance->queue_head) {
64 usb_log_error("Failed to allocate queue head.\n");
65 return ENOMEM;
66 }
67 const uint32_t queue_head_pa = addr_to_phys(instance->queue_head);
68 usb_log_debug2("Transfer list %s setup with QH: %p (%#" PRIx32" ).\n",
69 name, instance->queue_head, queue_head_pa);
70
71 qh_init(instance->queue_head);
72 list_initialize(&instance->batch_list);
73 fibril_mutex_initialize(&instance->guard);
74 return EOK;
75}
76
77/** Dispose transfer list structures.
78 *
79 * @param[in] instance Memory place to use.
80 *
81 * Frees memory of the internal qh_t structure.
82 */
83void transfer_list_fini(transfer_list_t *instance)
84{
85 assert(instance);
86 free32(instance->queue_head);
87}
88/** Set the next list in transfer list chain.
89 *
90 * @param[in] instance List to lead.
91 * @param[in] next List to append.
92 * @return Error code
93 *
94 * Does not check whether this replaces an existing list .
95 */
96void transfer_list_set_next(transfer_list_t *instance, transfer_list_t *next)
97{
98 assert(instance);
99 assert(instance->queue_head);
100 assert(next);
101 /* Set queue_head.next to point to the follower */
102 qh_set_next_qh(instance->queue_head, next->queue_head);
103}
104
105/** Add transfer batch to the list and queue.
106 *
107 * @param[in] instance List to use.
108 * @param[in] batch Transfer batch to submit.
109 *
110 * The batch is added to the end of the list and queue.
111 */
112void transfer_list_add_batch(
113 transfer_list_t *instance, uhci_transfer_batch_t *uhci_batch)
114{
115 assert(instance);
116 assert(uhci_batch);
117
118 endpoint_t *ep = uhci_batch->base.ep;
119
120 /* First, wait until the endpoint is free to use */
121 fibril_mutex_lock(&ep->guard);
122 endpoint_activate_locked(ep, &uhci_batch->base);
123 fibril_mutex_unlock(&ep->guard);
124
125 usb_log_debug2("Batch %p adding to queue %s.\n",
126 uhci_batch, instance->name);
127
128 fibril_mutex_lock(&instance->guard);
129
130 /* Assume there is nothing scheduled */
131 qh_t *last_qh = instance->queue_head;
132 /* There is something scheduled */
133 if (!list_empty(&instance->batch_list)) {
134 last_qh = uhci_transfer_batch_from_link(
135 list_last(&instance->batch_list))->qh;
136 }
137 /* Add to the hardware queue. */
138 const uint32_t pa = addr_to_phys(uhci_batch->qh);
139 assert((pa & LINK_POINTER_ADDRESS_MASK) == pa);
140
141 /* Make sure all data in the batch are written */
142 write_barrier();
143
144 /* keep link */
145 uhci_batch->qh->next = last_qh->next;
146 qh_set_next_qh(last_qh, uhci_batch->qh);
147
148 /* Make sure the pointer is updated */
149 write_barrier();
150
151 /* Add to the driver's list */
152 list_append(&uhci_batch->link, &instance->batch_list);
153
154 usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
155 " scheduled in queue %s.\n", uhci_batch,
156 USB_TRANSFER_BATCH_ARGS(uhci_batch->base), instance->name);
157 fibril_mutex_unlock(&instance->guard);
158}
159
160/** Add completed batches to the provided list.
161 *
162 * @param[in] instance List to use.
163 * @param[in] done list to fill
164 */
165void transfer_list_remove_finished(transfer_list_t *instance, list_t *done)
166{
167 assert(instance);
168 assert(done);
169
170 fibril_mutex_lock(&instance->guard);
171 link_t *current = list_first(&instance->batch_list);
172 while (current && current != &instance->batch_list.head) {
173 link_t * const next = current->next;
174 uhci_transfer_batch_t *batch =
175 uhci_transfer_batch_from_link(current);
176
177 if (uhci_transfer_batch_check_completed(batch)) {
178 /* Save for processing */
179 transfer_list_remove_batch(instance, batch);
180 list_append(current, done);
181 }
182 current = next;
183 }
184 fibril_mutex_unlock(&instance->guard);
185}
186
187/** Walk the list and finish all batches with EINTR.
188 *
189 * @param[in] instance List to use.
190 */
191void transfer_list_abort_all(transfer_list_t *instance)
192{
193 fibril_mutex_lock(&instance->guard);
194 while (!list_empty(&instance->batch_list)) {
195 link_t * const current = list_first(&instance->batch_list);
196 uhci_transfer_batch_t *batch = uhci_transfer_batch_from_link(current);
197 transfer_list_remove_batch(instance, batch);
198 }
199 fibril_mutex_unlock(&instance->guard);
200}
201
202/** Remove a transfer batch from the list and queue.
203 *
204 * @param[in] instance List to use.
205 * @param[in] batch Transfer batch to remove.
206 *
207 * Does not lock the transfer list, caller is responsible for that.
208 */
209void transfer_list_remove_batch(
210 transfer_list_t *instance, uhci_transfer_batch_t *uhci_batch)
211{
212 assert(instance);
213 assert(instance->queue_head);
214 assert(uhci_batch);
215 assert(uhci_batch->qh);
216 assert(fibril_mutex_is_locked(&instance->guard));
217
218 usb_log_debug2("Batch %p removing from queue %s.\n",
219 uhci_batch, instance->name);
220
221 /* Assume I'm the first */
222 const char *qpos = "FIRST";
223 qh_t *prev_qh = instance->queue_head;
224 /* Remove from the hardware queue */
225 if (list_first(&instance->batch_list) != &uhci_batch->link) {
226 /* There is a batch in front of me */
227 prev_qh =
228 uhci_transfer_batch_from_link(uhci_batch->link.prev)->qh;
229 qpos = "NOT FIRST";
230 }
231 assert((prev_qh->next & LINK_POINTER_ADDRESS_MASK)
232 == addr_to_phys(uhci_batch->qh));
233 prev_qh->next = uhci_batch->qh->next;
234
235 /* Make sure the pointer is updated */
236 write_barrier();
237
238 /* Remove from the batch list */
239 list_remove(&uhci_batch->link);
240 usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " removed (%s) "
241 "from %s, next: %x.\n", uhci_batch,
242 USB_TRANSFER_BATCH_ARGS(uhci_batch->base),
243 qpos, instance->name, uhci_batch->qh->next);
244}
245/**
246 * @}
247 */
Note: See TracBrowser for help on using the repository browser.