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

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

Use interrupt emulation until real hardware interrupts work

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