source: mainline/uspace/drv/uhci-hcd/uhci.c@ 6cd7b17

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6cd7b17 was 3cc5ccda, checked in by Jan Vesely <jano.vesely@…>, 15 years ago

Use mutex to guard the tracker_list
Do not resolve finished trackers immediately, create list for postprocessing instead (fixes list contention)

  • Property mode set to 100644
File size: 10.6 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/** @addtogroup usb
29 * @{
30 */
31/** @file
32 * @brief UHCI driver
33 */
34#include <errno.h>
35#include <adt/list.h>
36
37#include <usb/debug.h>
38#include <usb/usb.h>
39
40#include "uhci.h"
41
42static int uhci_init_transfer_lists(uhci_t *instance);
43static int uhci_clean_finished(void *arg);
44static int uhci_debug_checker(void *arg);
45static bool allowed_usb_packet(
46 bool low_speed, usb_transfer_type_t, size_t size);
47
48int uhci_init(uhci_t *instance, void *regs, size_t reg_size)
49{
50#define CHECK_RET_RETURN(message...) \
51 if (ret != EOK) { \
52 usb_log_error(message); \
53 return ret; \
54 } else (void) 0
55
56 /* init address keeper(libusb) */
57 usb_address_keeping_init(&instance->address_manager, USB11_ADDRESS_MAX);
58 usb_log_debug("Initialized address manager.\n");
59
60 /* allow access to hc control registers */
61 regs_t *io;
62 assert(reg_size >= sizeof(regs_t));
63 int ret = pio_enable(regs, reg_size, (void**)&io);
64 CHECK_RET_RETURN("Failed to gain access to registers at %p.\n", io);
65 instance->registers = io;
66 usb_log_debug("Device registers accessible.\n");
67
68 /* init transfer lists */
69 ret = uhci_init_transfer_lists(instance);
70 CHECK_RET_RETURN("Failed to initialize transfer lists.\n");
71 usb_log_debug("Transfer lists initialized.\n");
72
73
74 usb_log_debug("Initializing frame list.\n");
75 instance->frame_list = get_page();
76 ret = instance ? EOK : ENOMEM;
77 CHECK_RET_RETURN("Failed to get frame list page.\n");
78
79 /* initialize all frames to point to the first queue head */
80 const uint32_t queue =
81 instance->transfers_interrupt.queue_head_pa
82 | LINK_POINTER_QUEUE_HEAD_FLAG;
83 unsigned i = 0;
84 for(; i < UHCI_FRAME_LIST_COUNT; ++i) {
85 instance->frame_list[i] = queue;
86 }
87
88 const uintptr_t pa = (uintptr_t)addr_to_phys(instance->frame_list);
89 pio_write_32(&instance->registers->flbaseadd, (uint32_t)pa);
90
91 list_initialize(&instance->tracker_list);
92 fibril_mutex_initialize(&instance->tracker_list_mutex);
93
94 instance->cleaner = fibril_create(uhci_clean_finished, instance);
95 fibril_add_ready(instance->cleaner);
96
97 instance->debug_checker = fibril_create(uhci_debug_checker, instance);
98 fibril_add_ready(instance->debug_checker);
99
100 /* Start the hc with large(64B) packet FSBR */
101 pio_write_16(&instance->registers->usbcmd,
102 UHCI_CMD_RUN_STOP | UHCI_CMD_MAX_PACKET);
103 usb_log_debug("Started UHCI HC.\n");
104
105 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
106 cmd |= UHCI_CMD_DEBUG;
107 pio_write_16(&instance->registers->usbcmd, cmd);
108
109 return EOK;
110}
111/*----------------------------------------------------------------------------*/
112int uhci_init_transfer_lists(uhci_t *instance)
113{
114 assert(instance);
115
116 /* initialize */
117 int ret;
118 ret = transfer_list_init(&instance->transfers_bulk_full, "BULK_FULL");
119 assert(ret == EOK);
120 ret = transfer_list_init(&instance->transfers_control_full, "CONTROL_FULL");
121 assert(ret == EOK);
122 ret = transfer_list_init(&instance->transfers_control_slow, "CONTROL_SLOW");
123 assert(ret == EOK);
124 ret = transfer_list_init(&instance->transfers_interrupt, "INTERRUPT");
125 assert(ret == EOK);
126
127 transfer_list_set_next(&instance->transfers_control_full,
128 &instance->transfers_bulk_full);
129 transfer_list_set_next(&instance->transfers_control_slow,
130 &instance->transfers_control_full);
131 transfer_list_set_next(&instance->transfers_interrupt,
132 &instance->transfers_control_slow);
133
134 /*FSBR*/
135#ifdef FSBR
136 transfer_list_set_next(&instance->transfers_bulk_full,
137 &instance->transfers_control_full);
138#endif
139
140 instance->transfers[0][USB_TRANSFER_INTERRUPT] =
141 &instance->transfers_interrupt;
142 instance->transfers[1][USB_TRANSFER_INTERRUPT] =
143 &instance->transfers_interrupt;
144 instance->transfers[0][USB_TRANSFER_CONTROL] =
145 &instance->transfers_control_full;
146 instance->transfers[1][USB_TRANSFER_CONTROL] =
147 &instance->transfers_control_slow;
148 instance->transfers[0][USB_TRANSFER_CONTROL] =
149 &instance->transfers_control_full;
150
151 return EOK;
152}
153/*----------------------------------------------------------------------------*/
154int uhci_schedule(uhci_t *instance, tracker_t *tracker)
155{
156 assert(instance);
157 assert(tracker);
158 const int low_speed = (tracker->speed == LOW_SPEED);
159 if (!allowed_usb_packet(
160 low_speed, tracker->transfer_type, tracker->packet_size)) {
161 usb_log_warning("Invalid USB packet specified %s SPEED %d %zu.\n",
162 low_speed ? "LOW" : "FULL" , tracker->transfer_type,
163 tracker->packet_size);
164 return ENOTSUP;
165 }
166 /* TODO: check available bandwith here */
167
168 usb_log_debug2("Scheduler(%d) acquiring tracker list mutex.\n",
169 fibril_get_id());
170 fibril_mutex_lock(&instance->tracker_list_mutex);
171 usb_log_debug2("Scheduler(%d) acquired tracker list mutex.\n",
172 fibril_get_id());
173
174 transfer_list_t *list =
175 instance->transfers[low_speed][tracker->transfer_type];
176 assert(list);
177 transfer_list_add_tracker(list, tracker);
178 list_append(&tracker->link, &instance->tracker_list);
179
180 usb_log_debug2("Scheduler(%d) releasing tracker list mutex.\n",
181 fibril_get_id());
182 fibril_mutex_unlock(&instance->tracker_list_mutex);
183 usb_log_debug2("Scheduler(%d) released tracker list mutex.\n",
184 fibril_get_id());
185
186 return EOK;
187}
188/*----------------------------------------------------------------------------*/
189int uhci_clean_finished(void* arg)
190{
191 usb_log_debug("Started cleaning fibril.\n");
192 uhci_t *instance = (uhci_t*)arg;
193 assert(instance);
194
195 while(1) {
196 LIST_INITIALIZE(done_trackers);
197 /* tracker iteration */
198
199 usb_log_debug2("Cleaner(%d) acquiring tracker list mutex.\n",
200 fibril_get_id());
201 fibril_mutex_lock(&instance->tracker_list_mutex);
202 usb_log_debug2("Cleaner(%d) acquired tracker list mutex.\n",
203 fibril_get_id());
204
205 link_t *current = instance->tracker_list.next;
206 while (current != &instance->tracker_list)
207 {
208
209 link_t *next = current->next;
210 tracker_t *tracker = list_get_instance(current, tracker_t, link);
211
212 assert(current == &tracker->link);
213 assert(tracker);
214 assert(tracker->next_step);
215 assert(tracker->td);
216
217 if (!transfer_descriptor_is_active(tracker->td)) {
218 usb_log_info("Found inactive tracker with status: %x:%x.\n",
219 tracker->td->status, tracker->td->device);
220 list_remove(current);
221 list_append(current, &done_trackers);
222 }
223 current = next;
224 }
225
226 usb_log_debug2("Cleaner(%d) releasing tracker list mutex.\n",
227 fibril_get_id());
228 fibril_mutex_unlock(&instance->tracker_list_mutex);
229 usb_log_debug2("Cleaner(%d) released tracker list mutex.\n",
230 fibril_get_id());
231
232 while (!list_empty(&done_trackers)) {
233 tracker_t *tracker = list_get_instance(
234 done_trackers.next, tracker_t, link);
235 list_remove(&tracker->link);
236 tracker->next_step(tracker);
237 }
238 async_usleep(UHCI_CLEANER_TIMEOUT);
239 }
240 return EOK;
241}
242/*---------------------------------------------------------------------------*/
243int uhci_debug_checker(void *arg)
244{
245 uhci_t *instance = (uhci_t*)arg;
246 assert(instance);
247 while (1) {
248 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
249 uint16_t sts = pio_read_16(&instance->registers->usbsts);
250 usb_log_debug("Command register: %X Status register: %X\n", cmd, sts);
251
252 uintptr_t frame_list = pio_read_32(&instance->registers->flbaseadd);
253 if (frame_list != (uintptr_t)addr_to_phys(instance->frame_list)) {
254 usb_log_debug("Framelist address: %p vs. %p.\n",
255 frame_list, addr_to_phys(instance->frame_list));
256 }
257 int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff;
258 usb_log_debug2("Framelist item: %d \n", frnum );
259
260 queue_head_t* qh = instance->transfers_interrupt.queue_head;
261
262 if ((instance->frame_list[frnum] & (~0xf)) != (uintptr_t)addr_to_phys(qh)) {
263 usb_log_debug("Interrupt QH: %p vs. %p.\n",
264 instance->frame_list[frnum] & (~0xf), addr_to_phys(qh));
265 }
266
267 if ((qh->next_queue & (~0xf))
268 != (uintptr_t)addr_to_phys(instance->transfers_control_slow.queue_head)) {
269 usb_log_debug("Control Slow QH: %p vs. %p.\n", qh->next_queue & (~0xf),
270 addr_to_phys(instance->transfers_control_slow.queue_head));
271 }
272 qh = instance->transfers_control_slow.queue_head;
273
274 if ((qh->next_queue & (~0xf))
275 != (uintptr_t)addr_to_phys(instance->transfers_control_full.queue_head)) {
276 usb_log_debug("Control Full QH: %p vs. %p.\n", qh->next_queue & (~0xf),
277 addr_to_phys(instance->transfers_control_full.queue_head));\
278 }
279 qh = instance->transfers_control_full.queue_head;
280
281 if ((qh->next_queue & (~0xf))
282 != (uintptr_t)addr_to_phys(instance->transfers_bulk_full.queue_head)) {
283 usb_log_debug("Bulk QH: %p vs. %p.\n", qh->next_queue & (~0xf),
284 addr_to_phys(instance->transfers_bulk_full.queue_head));
285 }
286/*
287 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
288 cmd |= UHCI_CMD_RUN_STOP;
289 pio_write_16(&instance->registers->usbcmd, cmd);
290*/
291 async_usleep(UHCI_DEBUGER_TIMEOUT);
292 }
293 return 0;
294}
295/*----------------------------------------------------------------------------*/
296bool allowed_usb_packet(
297 bool low_speed, usb_transfer_type_t transfer, size_t size)
298{
299 /* see USB specification chapter 5.5-5.8 for magic numbers used here */
300 switch(transfer) {
301 case USB_TRANSFER_ISOCHRONOUS:
302 return (!low_speed && size < 1024);
303 case USB_TRANSFER_INTERRUPT:
304 return size <= (low_speed ? 8 : 64);
305 case USB_TRANSFER_CONTROL: /* device specifies its own max size */
306 return (size <= (low_speed ? 8 : 64));
307 case USB_TRANSFER_BULK: /* device specifies its own max size */
308 return (!low_speed && size <= 64);
309 }
310 return false;
311}
312/**
313 * @}
314 */
Note: See TracBrowser for help on using the repository browser.