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

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

Add QH element pointer to debug output

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