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

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

Fixes(only for new API trackers): add support for toggle bit

  • Property mode set to 100644
File size: 9.3 KB
RevLine 
[c56dbe0]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 */
[3515533]34#include <errno.h>
[afcd86e]35
[3515533]36#include <usb/debug.h>
[18e35a7]37#include <usb/usb.h>
[3515533]38
39#include "uhci.h"
40
[881c47b]41static int uhci_init_transfer_lists(uhci_t *instance);
[9ee87f6]42static int uhci_clean_finished(void *arg);
[0535ee4]43static int uhci_debug_checker(void *arg);
[4d73d71]44static bool allowed_usb_packet(
45 bool low_speed, usb_transfer_type_t, size_t size);
[7977fa1]46
[5944244]47int uhci_init(uhci_t *instance, void *regs, size_t reg_size)
[3515533]48{
[5944244]49#define CHECK_RET_RETURN(message...) \
[9ee87f6]50 if (ret != EOK) { \
[afcd86e]51 usb_log_error(message); \
[9ee87f6]52 return ret; \
53 } else (void) 0
[3515533]54
[18e35a7]55 /* init address keeper(libusb) */
[9ee87f6]56 usb_address_keeping_init(&instance->address_manager, USB11_ADDRESS_MAX);
[afcd86e]57 usb_log_debug("Initialized address manager.\n");
[18e35a7]58
[3515533]59 /* allow access to hc control registers */
60 regs_t *io;
[4687fcd4]61 assert(reg_size >= sizeof(regs_t));
[5944244]62 int ret = pio_enable(regs, reg_size, (void**)&io);
63 CHECK_RET_RETURN("Failed to gain access to registers at %p.\n", io);
[3515533]64 instance->registers = io;
[afcd86e]65 usb_log_debug("Device registers accessible.\n");
[3515533]66
[9ee87f6]67 /* init transfer lists */
[881c47b]68 ret = uhci_init_transfer_lists(instance);
[5944244]69 CHECK_RET_RETURN("Failed to initialize transfer lists.\n");
[afcd86e]70 usb_log_debug("Transfer lists initialized.\n");
[9ee87f6]71
[9600516]72
[afcd86e]73 usb_log_debug("Initializing frame list.\n");
[d1984e0]74 instance->frame_list = get_page();
[1256a0a]75 ret = instance ? EOK : ENOMEM;
[5944244]76 CHECK_RET_RETURN("Failed to get frame list page.\n");
[9600516]77
[9ee87f6]78 /* initialize all frames to point to the first queue head */
79 const uint32_t queue =
[881c47b]80 instance->transfers_interrupt.queue_head_pa
[9ee87f6]81 | LINK_POINTER_QUEUE_HEAD_FLAG;
[5944244]82 unsigned i = 0;
[9ee87f6]83 for(; i < UHCI_FRAME_LIST_COUNT; ++i) {
84 instance->frame_list[i] = queue;
85 }
86
[9600516]87 const uintptr_t pa = (uintptr_t)addr_to_phys(instance->frame_list);
88 pio_write_32(&instance->registers->flbaseadd, (uint32_t)pa);
89
[9a818a9]90 list_initialize(&instance->tracker_list);
91
[9ee87f6]92 instance->cleaner = fibril_create(uhci_clean_finished, instance);
93 fibril_add_ready(instance->cleaner);
[3515533]94
[0535ee4]95 instance->debug_checker = fibril_create(uhci_debug_checker, instance);
96 fibril_add_ready(instance->debug_checker);
97
[3f189c5]98 /* Start the hc with large(64B) packet FSBR */
[5944244]99 pio_write_16(&instance->registers->usbcmd,
100 UHCI_CMD_RUN_STOP | UHCI_CMD_MAX_PACKET);
101 usb_log_debug("Started UHCI HC.\n");
[881c47b]102
[d1984e0]103 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
[3da5eb9]104 cmd |= UHCI_CMD_DEBUG;
[d1984e0]105 pio_write_16(&instance->registers->usbcmd, cmd);
[881c47b]106
[3515533]107 return EOK;
108}
109/*----------------------------------------------------------------------------*/
[881c47b]110int uhci_init_transfer_lists(uhci_t *instance)
[643b983]111{
[881c47b]112 assert(instance);
[b00163f]113
[881c47b]114 /* initialize */
[643b983]115 int ret;
[881c47b]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;
[643b983]148
149 return EOK;
150}
[b00163f]151/*----------------------------------------------------------------------------*/
[9a818a9]152int uhci_schedule(uhci_t *instance, tracker_t *tracker)
153{
154 assert(instance);
155 assert(tracker);
156 const int low_speed = (tracker->speed == LOW_SPEED);
157 if (!allowed_usb_packet(
158 low_speed, tracker->transfer_type, tracker->packet_size)) {
159 usb_log_warning("Invalid USB packet specified %s SPEED %d %zu.\n",
160 low_speed ? "LOW" : "FULL" , tracker->transfer_type,
161 tracker->packet_size);
162 return ENOTSUP;
163 }
164 /* TODO: check available bandwith here */
165
166 transfer_list_t *list =
167 instance->transfers[low_speed][tracker->transfer_type];
168 assert(list);
169 transfer_list_add_tracker(list, tracker);
170 list_append(&tracker->link, &instance->tracker_list);
171
172 return EOK;
173}
174/*----------------------------------------------------------------------------*/
[9ee87f6]175int uhci_clean_finished(void* arg)
176{
[afcd86e]177 usb_log_debug("Started cleaning fibril.\n");
[9ee87f6]178 uhci_t *instance = (uhci_t*)arg;
179 assert(instance);
[579dec2]180
[9ee87f6]181 while(1) {
[9a818a9]182 /* tracker iteration */
183 link_t *current = instance->tracker_list.next;
184 while (current != &instance->tracker_list)
185 {
186 link_t *next = current->next;
187 tracker_t *tracker = list_get_instance(current, tracker_t, link);
188 assert(current == &tracker->link);
189 if (!transfer_descriptor_is_active(tracker->td)) {
[fe10e72]190 usb_log_info("Found inactive tracker with status: %x.\n",
[e2172cf8]191 tracker->td->status);
[9a818a9]192 list_remove(current);
193 tracker->next_step(tracker);
194 }
195 current = next;
196 }
[0535ee4]197 async_usleep(UHCI_CLEANER_TIMEOUT);
[9ee87f6]198 }
199 return EOK;
200}
[0535ee4]201/*---------------------------------------------------------------------------*/
202int uhci_debug_checker(void *arg)
203{
204 uhci_t *instance = (uhci_t*)arg;
205 assert(instance);
206 while (1) {
[5286a2c]207 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
208 uint16_t sts = pio_read_16(&instance->registers->usbsts);
[afcd86e]209 usb_log_debug("Command register: %X Status register: %X\n", cmd, sts);
[881c47b]210
[0535ee4]211 uintptr_t frame_list = pio_read_32(&instance->registers->flbaseadd);
[d6f78857]212 if (frame_list != (uintptr_t)addr_to_phys(instance->frame_list)) {
213 usb_log_debug("Framelist address: %p vs. %p.\n",
214 frame_list, addr_to_phys(instance->frame_list));
215 }
[0535ee4]216 int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff;
[d6f78857]217 usb_log_debug2("Framelist item: %d \n", frnum );
[0535ee4]218
[881c47b]219 queue_head_t* qh = instance->transfers_interrupt.queue_head;
220
221 if ((instance->frame_list[frnum] & (~0xf)) != (uintptr_t)addr_to_phys(qh)) {
222 usb_log_debug("Interrupt QH: %p vs. %p.\n",
223 instance->frame_list[frnum] & (~0xf), addr_to_phys(qh));
224 }
225
226 if ((qh->next_queue & (~0xf))
227 != (uintptr_t)addr_to_phys(instance->transfers_control_slow.queue_head)) {
228 usb_log_debug("Control Slow QH: %p vs. %p.\n", qh->next_queue & (~0xf),
229 addr_to_phys(instance->transfers_control_slow.queue_head));
230 }
231 qh = instance->transfers_control_slow.queue_head;
[0535ee4]232
[881c47b]233 if ((qh->next_queue & (~0xf))
234 != (uintptr_t)addr_to_phys(instance->transfers_control_full.queue_head)) {
235 usb_log_debug("Control Full QH: %p vs. %p.\n", qh->next_queue & (~0xf),
236 addr_to_phys(instance->transfers_control_full.queue_head));\
237 }
238 qh = instance->transfers_control_full.queue_head;
[0535ee4]239
[881c47b]240 if ((qh->next_queue & (~0xf))
241 != (uintptr_t)addr_to_phys(instance->transfers_bulk_full.queue_head)) {
242 usb_log_debug("Bulk QH: %p vs. %p.\n", qh->next_queue & (~0xf),
243 addr_to_phys(instance->transfers_bulk_full.queue_head));
244 }
245/*
[3da5eb9]246 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
247 cmd |= UHCI_CMD_RUN_STOP;
248 pio_write_16(&instance->registers->usbcmd, cmd);
249*/
[0535ee4]250 async_usleep(UHCI_DEBUGER_TIMEOUT);
251 }
252 return 0;
253}
[4d73d71]254/*----------------------------------------------------------------------------*/
255bool allowed_usb_packet(
256 bool low_speed, usb_transfer_type_t transfer, size_t size)
257{
258 /* see USB specification chapter 5.5-5.8 for magic numbers used here */
259 switch(transfer) {
260 case USB_TRANSFER_ISOCHRONOUS:
261 return (!low_speed && size < 1024);
262 case USB_TRANSFER_INTERRUPT:
[3f189c5]263 return size <= (low_speed ? 8 : 64);
[4d73d71]264 case USB_TRANSFER_CONTROL: /* device specifies its own max size */
265 return (size <= (low_speed ? 8 : 64));
266 case USB_TRANSFER_BULK: /* device specifies its own max size */
267 return (!low_speed && size <= 64);
268 }
269 return false;
270}
[c56dbe0]271/**
272 * @}
273 */
Note: See TracBrowser for help on using the repository browser.