source: mainline/uspace/drv/uhci-hcd/uhci.c@ 9e80904

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

Fixes: report proper size in tracker call_in

Adds few debug lines

  • Property mode set to 100644
File size: 12.0 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
36#include <usb/debug.h>
37#include <usb/usb.h>
38
39#include "uhci.h"
40
41static int uhci_init_transfer_lists(uhci_t *instance);
42static int uhci_clean_finished(void *arg);
43static int uhci_debug_checker(void *arg);
44static bool allowed_usb_packet(
45 bool low_speed, usb_transfer_type_t, size_t size);
46
47int uhci_init(uhci_t *instance, void *regs, size_t reg_size)
48{
49#define CHECK_RET_RETURN(message...) \
50 if (ret != EOK) { \
51 usb_log_error(message); \
52 return ret; \
53 } else (void) 0
54
55 /* init address keeper(libusb) */
56 usb_address_keeping_init(&instance->address_manager, USB11_ADDRESS_MAX);
57 usb_log_debug("Initialized address manager.\n");
58
59 /* allow access to hc control registers */
60 regs_t *io;
61 assert(reg_size >= sizeof(regs_t));
62 int ret = pio_enable(regs, reg_size, (void**)&io);
63 CHECK_RET_RETURN("Failed to gain access to registers at %p.\n", io);
64 instance->registers = io;
65 usb_log_debug("Device registers accessible.\n");
66
67 /* init transfer lists */
68 ret = uhci_init_transfer_lists(instance);
69 CHECK_RET_RETURN("Failed to initialize transfer lists.\n");
70 usb_log_debug("Transfer lists initialized.\n");
71
72
73 usb_log_debug("Initializing frame list.\n");
74 instance->frame_list = get_page();
75 ret = instance ? EOK : ENOMEM;
76 CHECK_RET_RETURN("Failed to get frame list page.\n");
77
78 /* initialize all frames to point to the first queue head */
79 const uint32_t queue =
80 instance->transfers_interrupt.queue_head_pa
81 | LINK_POINTER_QUEUE_HEAD_FLAG;
82 unsigned i = 0;
83 for(; i < UHCI_FRAME_LIST_COUNT; ++i) {
84 instance->frame_list[i] = queue;
85 }
86
87 const uintptr_t pa = (uintptr_t)addr_to_phys(instance->frame_list);
88 pio_write_32(&instance->registers->flbaseadd, (uint32_t)pa);
89
90 list_initialize(&instance->tracker_list);
91
92 instance->cleaner = fibril_create(uhci_clean_finished, instance);
93 fibril_add_ready(instance->cleaner);
94
95 instance->debug_checker = fibril_create(uhci_debug_checker, instance);
96 fibril_add_ready(instance->debug_checker);
97
98 /* Start the hc with large(64b) packet FSBR */
99 pio_write_16(&instance->registers->usbcmd,
100 UHCI_CMD_RUN_STOP | UHCI_CMD_MAX_PACKET);
101 usb_log_debug("Started UHCI HC.\n");
102
103 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
104 cmd |= UHCI_CMD_DEBUG;
105 pio_write_16(&instance->registers->usbcmd, cmd);
106
107 return EOK;
108}
109/*----------------------------------------------------------------------------*/
110int uhci_init_transfer_lists(uhci_t *instance)
111{
112 assert(instance);
113
114 /* initialize */
115 int ret;
116 ret = transfer_list_init(&instance->transfers_bulk_full, "BULK_FULL");
117 assert(ret == EOK);
118 ret = transfer_list_init(&instance->transfers_control_full, "CONTROL_FULL");
119 assert(ret == EOK);
120 ret = transfer_list_init(&instance->transfers_control_slow, "CONTROL_SLOW");
121 assert(ret == EOK);
122 ret = transfer_list_init(&instance->transfers_interrupt, "INTERRUPT");
123 assert(ret == EOK);
124
125 transfer_list_set_next(&instance->transfers_control_full,
126 &instance->transfers_bulk_full);
127 transfer_list_set_next(&instance->transfers_control_slow,
128 &instance->transfers_control_full);
129 transfer_list_set_next(&instance->transfers_interrupt,
130 &instance->transfers_control_slow);
131
132 /*FSBR*/
133#ifdef FSBR
134 transfer_list_set_next(&instance->transfers_bulk_full,
135 &instance->transfers_control_full);
136#endif
137
138 instance->transfers[0][USB_TRANSFER_INTERRUPT] =
139 &instance->transfers_interrupt;
140 instance->transfers[1][USB_TRANSFER_INTERRUPT] =
141 &instance->transfers_interrupt;
142 instance->transfers[0][USB_TRANSFER_CONTROL] =
143 &instance->transfers_control_full;
144 instance->transfers[1][USB_TRANSFER_CONTROL] =
145 &instance->transfers_control_slow;
146 instance->transfers[0][USB_TRANSFER_CONTROL] =
147 &instance->transfers_control_full;
148
149 return EOK;
150}
151/*----------------------------------------------------------------------------*/
152int uhci_transfer(
153 uhci_t *instance,
154 device_t *dev,
155 usb_target_t target,
156 usb_transfer_type_t transfer_type,
157 bool toggle,
158 usb_packet_id pid,
159 bool low_speed,
160 void *buffer, size_t size,
161 usbhc_iface_transfer_out_callback_t callback_out,
162 usbhc_iface_transfer_in_callback_t callback_in,
163 void *arg)
164{
165 if (!allowed_usb_packet(low_speed, transfer_type, size)) {
166 usb_log_warning("Invalid USB packet specified %s SPEED %d %zu.\n",
167 low_speed ? "LOW" : "FULL" , transfer_type, size);
168 return ENOTSUP;
169 }
170
171 // TODO: Add support for isochronous transfers
172 if (transfer_type == USB_TRANSFER_ISOCHRONOUS) {
173 usb_log_warning("ISO transfer not supported.\n");
174 return ENOTSUP;
175 }
176
177 transfer_list_t *list = instance->transfers[low_speed][transfer_type];
178 assert(list);
179
180 transfer_descriptor_t *td = NULL;
181 callback_t *job = NULL;
182 int ret = EOK;
183 assert(dev);
184
185#define CHECK_RET_TRANS_FREE_JOB_TD(message) \
186 if (ret != EOK) { \
187 usb_log_error(message); \
188 if (job) { \
189 callback_dispose(job); \
190 } \
191 if (td) { free32(td); } \
192 return ret; \
193 } else (void) 0
194
195 job = callback_get(dev, buffer, size, callback_in, callback_out, arg);
196 ret = job ? EOK : ENOMEM;
197 CHECK_RET_TRANS_FREE_JOB_TD("Failed to allocate callback structure.\n");
198
199 td = transfer_descriptor_get(3, size, false, target, pid, job->new_buffer);
200 ret = td ? EOK : ENOMEM;
201 CHECK_RET_TRANS_FREE_JOB_TD("Failed to setup transfer descriptor.\n");
202
203 td->callback = job;
204
205 usb_log_debug("Appending a new transfer to queue %s.\n", list->name);
206
207 ret = transfer_list_append(list, td);
208 CHECK_RET_TRANS_FREE_JOB_TD("Failed to append transfer descriptor.\n");
209
210 return EOK;
211}
212/*----------------------------------------------------------------------------*/
213int uhci_schedule(uhci_t *instance, tracker_t *tracker)
214{
215 assert(instance);
216 assert(tracker);
217 const int low_speed = (tracker->speed == LOW_SPEED);
218 if (!allowed_usb_packet(
219 low_speed, tracker->transfer_type, tracker->packet_size)) {
220 usb_log_warning("Invalid USB packet specified %s SPEED %d %zu.\n",
221 low_speed ? "LOW" : "FULL" , tracker->transfer_type,
222 tracker->packet_size);
223 return ENOTSUP;
224 }
225 /* TODO: check available bandwith here */
226
227 transfer_list_t *list =
228 instance->transfers[low_speed][tracker->transfer_type];
229 assert(list);
230 transfer_list_add_tracker(list, tracker);
231 list_append(&tracker->link, &instance->tracker_list);
232
233 return EOK;
234}
235/*----------------------------------------------------------------------------*/
236int uhci_clean_finished(void* arg)
237{
238 usb_log_debug("Started cleaning fibril.\n");
239 uhci_t *instance = (uhci_t*)arg;
240 assert(instance);
241
242 while(1) {
243 /* tracker iteration */
244 link_t *current = instance->tracker_list.next;
245 while (current != &instance->tracker_list)
246 {
247 link_t *next = current->next;
248 tracker_t *tracker = list_get_instance(current, tracker_t, link);
249 assert(current == &tracker->link);
250 if (!transfer_descriptor_is_active(tracker->td)) {
251 usb_log_debug("Found inactive tracker with status: %x.\n",
252 tracker->td->status);
253 list_remove(current);
254 tracker->next_step(tracker);
255 }
256 current = next;
257 }
258 /* iterate all transfer queues */
259 transfer_list_t *current_list = &instance->transfers_interrupt;
260 while (current_list) {
261 /* Remove inactive transfers from the top of the queue
262 * TODO: should I reach queue head or is this enough? */
263 volatile transfer_descriptor_t * it =
264 current_list->first;
265 usb_log_debug("Running cleaning fibril on queue: %s (%s).\n",
266 current_list->name, it ? "SOMETHING" : "EMPTY");
267
268 if (it) {
269 usb_log_debug("First in queue: %p (%x) PA:%x.\n",
270 it, it->status, addr_to_phys((void*)it) );
271 usb_log_debug("First to send: %x\n",
272 (current_list->queue_head->element) );
273 }
274
275 while (current_list->first &&
276 !(current_list->first->status & TD_STATUS_ERROR_ACTIVE)) {
277 transfer_descriptor_t *transfer = current_list->first;
278 usb_log_info("Inactive transfer calling callback with status %x.\n",
279 transfer->status);
280 current_list->first = transfer->next_va;
281 transfer_descriptor_dispose(transfer);
282 }
283 if (!current_list->first)
284 current_list->last = current_list->first;
285
286 current_list = current_list->next;
287 }
288 async_usleep(UHCI_CLEANER_TIMEOUT);
289 }
290 return EOK;
291}
292/*---------------------------------------------------------------------------*/
293int uhci_debug_checker(void *arg)
294{
295 uhci_t *instance = (uhci_t*)arg;
296 assert(instance);
297 while (1) {
298 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
299 uint16_t sts = pio_read_16(&instance->registers->usbsts);
300 usb_log_debug("Command register: %X Status register: %X\n", cmd, sts);
301
302 uintptr_t frame_list = pio_read_32(&instance->registers->flbaseadd);
303 if (frame_list != (uintptr_t)addr_to_phys(instance->frame_list)) {
304 usb_log_debug("Framelist address: %p vs. %p.\n",
305 frame_list, addr_to_phys(instance->frame_list));
306 }
307 int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff;
308 usb_log_debug2("Framelist item: %d \n", frnum );
309
310 queue_head_t* qh = instance->transfers_interrupt.queue_head;
311
312 if ((instance->frame_list[frnum] & (~0xf)) != (uintptr_t)addr_to_phys(qh)) {
313 usb_log_debug("Interrupt QH: %p vs. %p.\n",
314 instance->frame_list[frnum] & (~0xf), addr_to_phys(qh));
315 }
316
317 if ((qh->next_queue & (~0xf))
318 != (uintptr_t)addr_to_phys(instance->transfers_control_slow.queue_head)) {
319 usb_log_debug("Control Slow QH: %p vs. %p.\n", qh->next_queue & (~0xf),
320 addr_to_phys(instance->transfers_control_slow.queue_head));
321 }
322 qh = instance->transfers_control_slow.queue_head;
323
324 if ((qh->next_queue & (~0xf))
325 != (uintptr_t)addr_to_phys(instance->transfers_control_full.queue_head)) {
326 usb_log_debug("Control Full QH: %p vs. %p.\n", qh->next_queue & (~0xf),
327 addr_to_phys(instance->transfers_control_full.queue_head));\
328 }
329 qh = instance->transfers_control_full.queue_head;
330
331 if ((qh->next_queue & (~0xf))
332 != (uintptr_t)addr_to_phys(instance->transfers_bulk_full.queue_head)) {
333 usb_log_debug("Bulk QH: %p vs. %p.\n", qh->next_queue & (~0xf),
334 addr_to_phys(instance->transfers_bulk_full.queue_head));
335 }
336/*
337 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
338 cmd |= UHCI_CMD_RUN_STOP;
339 pio_write_16(&instance->registers->usbcmd, cmd);
340*/
341 async_usleep(UHCI_DEBUGER_TIMEOUT);
342 }
343 return 0;
344}
345/*----------------------------------------------------------------------------*/
346bool allowed_usb_packet(
347 bool low_speed, usb_transfer_type_t transfer, size_t size)
348{
349 /* see USB specification chapter 5.5-5.8 for magic numbers used here */
350 switch(transfer) {
351 case USB_TRANSFER_ISOCHRONOUS:
352 return (!low_speed && size < 1024);
353 case USB_TRANSFER_INTERRUPT:
354 return size <= (low_speed ? 8 :64);
355 case USB_TRANSFER_CONTROL: /* device specifies its own max size */
356 return (size <= (low_speed ? 8 : 64));
357 case USB_TRANSFER_BULK: /* device specifies its own max size */
358 return (!low_speed && size <= 64);
359 }
360 return false;
361}
362/**
363 * @}
364 */
Note: See TracBrowser for help on using the repository browser.