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

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

add some basic packet checks

  • Property mode set to 100644
File size: 10.8 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
89 pio_write_32(&instance->registers->flbaseadd, (uint32_t)pa);
90
91 instance->cleaner = fibril_create(uhci_clean_finished, instance);
92 fibril_add_ready(instance->cleaner);
93
94 instance->debug_checker = fibril_create(uhci_debug_checker, instance);
95 fibril_add_ready(instance->debug_checker);
96
97 /* Start the hc with large(64b) packet FSBR */
98 pio_write_16(&instance->registers->usbcmd,
99 UHCI_CMD_RUN_STOP | UHCI_CMD_MAX_PACKET);
100 usb_log_debug("Started UHCI HC.\n");
101
102 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
103 cmd |= UHCI_CMD_DEBUG;
104 pio_write_16(&instance->registers->usbcmd, cmd);
105
106 return EOK;
107}
108/*----------------------------------------------------------------------------*/
109int uhci_init_transfer_lists(uhci_t *instance)
110{
111 assert(instance);
112
113 /* initialize */
114 int ret;
115 ret = transfer_list_init(&instance->transfers_bulk_full, "BULK_FULL");
116 assert(ret == EOK);
117 ret = transfer_list_init(&instance->transfers_control_full, "CONTROL_FULL");
118 assert(ret == EOK);
119 ret = transfer_list_init(&instance->transfers_control_slow, "CONTROL_SLOW");
120 assert(ret == EOK);
121 ret = transfer_list_init(&instance->transfers_interrupt, "INTERRUPT");
122 assert(ret == EOK);
123
124 transfer_list_set_next(&instance->transfers_control_full,
125 &instance->transfers_bulk_full);
126 transfer_list_set_next(&instance->transfers_control_slow,
127 &instance->transfers_control_full);
128 transfer_list_set_next(&instance->transfers_interrupt,
129 &instance->transfers_control_slow);
130
131 /*FSBR*/
132#ifdef FSBR
133 transfer_list_set_next(&instance->transfers_bulk_full,
134 &instance->transfers_control_full);
135#endif
136
137 instance->transfers[0][USB_TRANSFER_INTERRUPT] =
138 &instance->transfers_interrupt;
139 instance->transfers[1][USB_TRANSFER_INTERRUPT] =
140 &instance->transfers_interrupt;
141 instance->transfers[0][USB_TRANSFER_CONTROL] =
142 &instance->transfers_control_full;
143 instance->transfers[1][USB_TRANSFER_CONTROL] =
144 &instance->transfers_control_slow;
145 instance->transfers[0][USB_TRANSFER_CONTROL] =
146 &instance->transfers_control_full;
147
148 return EOK;
149}
150/*----------------------------------------------------------------------------*/
151int uhci_transfer(
152 uhci_t *instance,
153 device_t *dev,
154 usb_target_t target,
155 usb_transfer_type_t transfer_type,
156 bool toggle,
157 usb_packet_id pid,
158 bool low_speed,
159 void *buffer, size_t size,
160 usbhc_iface_transfer_out_callback_t callback_out,
161 usbhc_iface_transfer_in_callback_t callback_in,
162 void *arg)
163{
164 if (!allowed_usb_packet(low_speed, transfer_type, size)) {
165 usb_log_warning("Invalid USB packet specified %s SPEED %d %zu.\n",
166 low_speed ? "LOW" : "FULL" , transfer_type, size);
167 return ENOTSUP;
168 }
169
170 // TODO: Add support for isochronous transfers
171 if (transfer_type == USB_TRANSFER_ISOCHRONOUS) {
172 usb_log_warning("ISO transfer not supported.\n");
173 return ENOTSUP;
174 }
175
176 transfer_list_t *list = instance->transfers[low_speed][transfer_type];
177 assert(list);
178
179 transfer_descriptor_t *td = NULL;
180 callback_t *job = NULL;
181 int ret = EOK;
182 assert(dev);
183
184#define CHECK_RET_TRANS_FREE_JOB_TD(message) \
185 if (ret != EOK) { \
186 usb_log_error(message); \
187 if (job) { \
188 callback_dispose(job); \
189 } \
190 if (td) { free32(td); } \
191 return ret; \
192 } else (void) 0
193
194 job = callback_get(dev, buffer, size, callback_in, callback_out, arg);
195 ret = job ? EOK : ENOMEM;
196 CHECK_RET_TRANS_FREE_JOB_TD("Failed to allocate callback structure.\n");
197
198 td = transfer_descriptor_get(3, size, false, target, pid, job->new_buffer);
199 ret = td ? EOK : ENOMEM;
200 CHECK_RET_TRANS_FREE_JOB_TD("Failed to setup transfer descriptor.\n");
201
202 td->callback = job;
203
204 usb_log_debug("Appending a new transfer to queue %s.\n", list->name);
205
206 ret = transfer_list_append(list, td);
207 CHECK_RET_TRANS_FREE_JOB_TD("Failed to append transfer descriptor.\n");
208
209 return EOK;
210}
211/*---------------------------------------------------------------------------*/
212int uhci_clean_finished(void* arg)
213{
214 usb_log_debug("Started cleaning fibril.\n");
215 uhci_t *instance = (uhci_t*)arg;
216 assert(instance);
217
218 while(1) {
219 usb_log_debug("Running cleaning fibril on: %p.\n", instance);
220 /* iterate all transfer queues */
221 transfer_list_t *current_list = &instance->transfers_interrupt;
222 while (current_list) {
223 /* Remove inactive transfers from the top of the queue
224 * TODO: should I reach queue head or is this enough? */
225 volatile transfer_descriptor_t * it =
226 current_list->first;
227 usb_log_debug("Running cleaning fibril on queue: %s (%s).\n",
228 current_list->name, it ? "SOMETHING" : "EMPTY");
229
230 if (it) {
231 usb_log_debug("First in queue: %p (%x) PA:%x.\n",
232 it, it->status, addr_to_phys((void*)it) );
233 usb_log_debug("First to send: %x\n",
234 (current_list->queue_head->element) );
235 }
236
237 while (current_list->first &&
238 !(current_list->first->status & TD_STATUS_ERROR_ACTIVE)) {
239 transfer_descriptor_t *transfer = current_list->first;
240 usb_log_info("Inactive transfer calling callback with status %x.\n",
241 transfer->status);
242 current_list->first = transfer->next_va;
243 transfer_descriptor_dispose(transfer);
244 }
245 if (!current_list->first)
246 current_list->last = current_list->first;
247
248 current_list = current_list->next;
249 }
250 async_usleep(UHCI_CLEANER_TIMEOUT);
251 }
252 return EOK;
253}
254/*---------------------------------------------------------------------------*/
255int uhci_debug_checker(void *arg)
256{
257 uhci_t *instance = (uhci_t*)arg;
258 assert(instance);
259 while (1) {
260 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
261 uint16_t sts = pio_read_16(&instance->registers->usbsts);
262 usb_log_debug("Command register: %X Status register: %X\n", cmd, sts);
263
264 uintptr_t frame_list = pio_read_32(&instance->registers->flbaseadd);
265 if (frame_list != (uintptr_t)addr_to_phys(instance->frame_list)) {
266 usb_log_debug("Framelist address: %p vs. %p.\n",
267 frame_list, addr_to_phys(instance->frame_list));
268 }
269 int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff;
270 usb_log_debug2("Framelist item: %d \n", frnum );
271
272 queue_head_t* qh = instance->transfers_interrupt.queue_head;
273
274 if ((instance->frame_list[frnum] & (~0xf)) != (uintptr_t)addr_to_phys(qh)) {
275 usb_log_debug("Interrupt QH: %p vs. %p.\n",
276 instance->frame_list[frnum] & (~0xf), addr_to_phys(qh));
277 }
278
279 if ((qh->next_queue & (~0xf))
280 != (uintptr_t)addr_to_phys(instance->transfers_control_slow.queue_head)) {
281 usb_log_debug("Control Slow QH: %p vs. %p.\n", qh->next_queue & (~0xf),
282 addr_to_phys(instance->transfers_control_slow.queue_head));
283 }
284 qh = instance->transfers_control_slow.queue_head;
285
286 if ((qh->next_queue & (~0xf))
287 != (uintptr_t)addr_to_phys(instance->transfers_control_full.queue_head)) {
288 usb_log_debug("Control Full QH: %p vs. %p.\n", qh->next_queue & (~0xf),
289 addr_to_phys(instance->transfers_control_full.queue_head));\
290 }
291 qh = instance->transfers_control_full.queue_head;
292
293 if ((qh->next_queue & (~0xf))
294 != (uintptr_t)addr_to_phys(instance->transfers_bulk_full.queue_head)) {
295 usb_log_debug("Bulk QH: %p vs. %p.\n", qh->next_queue & (~0xf),
296 addr_to_phys(instance->transfers_bulk_full.queue_head));
297 }
298/*
299 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
300 cmd |= UHCI_CMD_RUN_STOP;
301 pio_write_16(&instance->registers->usbcmd, cmd);
302*/
303 async_usleep(UHCI_DEBUGER_TIMEOUT);
304 }
305 return 0;
306}
307/*----------------------------------------------------------------------------*/
308bool allowed_usb_packet(
309 bool low_speed, usb_transfer_type_t transfer, size_t size)
310{
311 /* see USB specification chapter 5.5-5.8 for magic numbers used here */
312 switch(transfer) {
313 case USB_TRANSFER_ISOCHRONOUS:
314 return (!low_speed && size < 1024);
315 case USB_TRANSFER_INTERRUPT:
316 return size <= (low_speed ? 8 :64);
317 case USB_TRANSFER_CONTROL: /* device specifies its own max size */
318 return (size <= (low_speed ? 8 : 64));
319 case USB_TRANSFER_BULK: /* device specifies its own max size */
320 return (!low_speed && size <= 64);
321 }
322 return false;
323}
324/**
325 * @}
326 */
Note: See TracBrowser for help on using the repository browser.