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

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

Use properly initialized interrupt code

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