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

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

Add some more error checking

  • Property mode set to 100644
File size: 8.9 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(transfer_list_t list[]);
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->transfers);
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[USB_TRANSFER_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(transfer_list_t transfers[])
108{
109 //TODO:refactor
110 transfers[USB_TRANSFER_ISOCHRONOUS].first = NULL;
111 transfers[USB_TRANSFER_ISOCHRONOUS].last = NULL;
112
113 int ret;
114 ret = transfer_list_init(&transfers[USB_TRANSFER_BULK], NULL);
115 if (ret != EOK) {
116 usb_log_error("Failed to initialize bulk queue.\n");
117 return ret;
118 }
119
120 ret = transfer_list_init(
121 &transfers[USB_TRANSFER_CONTROL], &transfers[USB_TRANSFER_BULK]);
122 if (ret != EOK) {
123 usb_log_error("Failed to initialize control queue.\n");
124 transfer_list_fini(&transfers[USB_TRANSFER_BULK]);
125 return ret;
126 }
127
128 ret = transfer_list_init(
129 &transfers[USB_TRANSFER_INTERRUPT], &transfers[USB_TRANSFER_CONTROL]);
130 if (ret != EOK) {
131 usb_log_error("Failed to initialize interrupt queue.\n");
132 transfer_list_fini(&transfers[USB_TRANSFER_CONTROL]);
133 transfer_list_fini(&transfers[USB_TRANSFER_BULK]);
134 return ret;
135 }
136
137 return EOK;
138}
139/*----------------------------------------------------------------------------*/
140int uhci_transfer(
141 uhci_t *instance,
142 device_t *dev,
143 usb_target_t target,
144 usb_transfer_type_t transfer_type,
145 bool toggle,
146 usb_packet_id pid,
147 void *buffer, size_t size,
148 usbhc_iface_transfer_out_callback_t callback_out,
149 usbhc_iface_transfer_in_callback_t callback_in,
150 void *arg)
151{
152 // TODO: Add support for isochronous transfers
153 if (transfer_type == USB_TRANSFER_ISOCHRONOUS) {
154 usb_log_warning("ISO transfer not supported.\n");
155 return ENOTSUP;
156 }
157
158 if (transfer_type == USB_TRANSFER_INTERRUPT
159 && size >= 64) {
160 usb_log_warning("Interrupt transfer too big %zu.\n", size);
161 return ENOTSUP;
162 }
163
164
165 if (size >= 1024) {
166 usb_log_warning("Transfer too big.\n");
167 return ENOTSUP;
168 }
169
170 transfer_descriptor_t *td = NULL;
171 callback_t *job = NULL;
172 int ret = EOK;
173 assert(dev);
174
175#define CHECK_RET_TRANS_FREE_JOB_TD(message) \
176 if (ret != EOK) { \
177 usb_log_error(message); \
178 if (job) { \
179 callback_dispose(job); \
180 } \
181 if (td) { free32(td); } \
182 return ret; \
183 } else (void) 0
184
185 job = callback_get(dev, buffer, size, callback_in, callback_out, arg);
186 ret = job ? EOK : ENOMEM;
187 CHECK_RET_TRANS_FREE_JOB_TD("Failed to allocate callback structure.\n");
188
189 td = transfer_descriptor_get(3, size, false, target, pid, job->new_buffer);
190 ret = td ? EOK : ENOMEM;
191 CHECK_RET_TRANS_FREE_JOB_TD("Failed to setup transfer descriptor.\n");
192
193 td->callback = job;
194
195 usb_log_debug("Appending a new transfer to queue.\n");
196 ret = transfer_list_append(&instance->transfers[transfer_type], td);
197 CHECK_RET_TRANS_FREE_JOB_TD("Failed to append transfer descriptor.\n");
198
199 return EOK;
200}
201/*---------------------------------------------------------------------------*/
202int uhci_clean_finished(void* arg)
203{
204 usb_log_debug("Started cleaning fibril.\n");
205 uhci_t *instance = (uhci_t*)arg;
206 assert(instance);
207
208 while(1) {
209 usb_log_debug("Running cleaning fibril on: %p.\n", instance);
210 /* iterate all transfer queues */
211 int i = 0;
212 for (; i < TRANSFER_QUEUES; ++i) {
213 /* Remove inactive transfers from the top of the queue
214 * TODO: should I reach queue head or is this enough? */
215 volatile transfer_descriptor_t * it =
216 instance->transfers[i].first;
217 usb_log_debug("Running cleaning fibril on queue: %p (%s).\n",
218 &instance->transfers[i], it ? "SOMETHING" : "EMPTY");
219
220 if (it)
221 usb_log_debug("First in queue: %p (%x).\n",
222 it, it->status);
223
224 while (instance->transfers[i].first &&
225 !(instance->transfers[i].first->status & TD_STATUS_ERROR_ACTIVE)) {
226 transfer_descriptor_t *transfer = instance->transfers[i].first;
227 usb_log_info("Inactive transfer calling callback with status %x.\n",
228 transfer->status);
229 instance->transfers[i].first = transfer->next_va;
230 transfer_descriptor_dispose(transfer);
231 }
232 if (!instance->transfers[i].first)
233 instance->transfers[i].last = instance->transfers[i].first;
234 }
235 async_usleep(UHCI_CLEANER_TIMEOUT);
236 }
237 return EOK;
238}
239/*---------------------------------------------------------------------------*/
240int uhci_debug_checker(void *arg)
241{
242 uhci_t *instance = (uhci_t*)arg;
243 assert(instance);
244 while (1) {
245 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
246 uint16_t sts = pio_read_16(&instance->registers->usbsts);
247 usb_log_debug("Command register: %X Status register: %X\n", cmd, sts);
248/*
249 uintptr_t frame_list = pio_read_32(&instance->registers->flbaseadd);
250 usb_log_debug("Framelist address: %p vs. %p.\n",
251 frame_list, addr_to_phys(instance->frame_list));
252 int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff;
253 usb_log_debug("Framelist item: %d \n", frnum );
254
255 queue_head_t* qh = instance->transfers[USB_TRANSFER_INTERRUPT].queue_head;
256 usb_log_debug("Interrupt QH: %p vs. %p.\n",
257 instance->frame_list[frnum], addr_to_phys(qh));
258
259 usb_log_debug("Control QH: %p vs. %p.\n", qh->next_queue,
260 addr_to_phys(instance->transfers[USB_TRANSFER_CONTROL].queue_head));
261 qh = instance->transfers[USB_TRANSFER_CONTROL].queue_head;
262
263 usb_log_debug("Bulk QH: %p vs. %p.\n", qh->next_queue,
264 addr_to_phys(instance->transfers[USB_TRANSFER_BULK].queue_head));
265 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
266 cmd |= UHCI_CMD_RUN_STOP;
267 pio_write_16(&instance->registers->usbcmd, cmd);
268*/
269
270 async_usleep(UHCI_DEBUGER_TIMEOUT);
271 }
272 return 0;
273}
274/**
275 * @}
276 */
Note: See TracBrowser for help on using the repository browser.