source: mainline/uspace/drv/uhci/uhci.c@ 3da5eb9

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

Fixed: add buffer pointer to TD on initialization

Added status info on Inactive transfer debug output

  • Property mode set to 100644
File size: 7.9 KB
RevLine 
[3515533]1#include <errno.h>
2#include <usb/debug.h>
[18e35a7]3#include <usb/usb.h>
[3515533]4
[de0e6b3]5#include "utils/malloc32.h"
[9600516]6
[bf5a3be]7#include "debug.h"
[3515533]8#include "name.h"
9#include "uhci.h"
10
[9ee87f6]11static int uhci_init_transfer_lists(transfer_list_t list[]);
12static int uhci_clean_finished(void *arg);
[0535ee4]13static int uhci_debug_checker(void *arg);
[7977fa1]14
[3515533]15int uhci_init(device_t *device, void *regs)
16{
[9ee87f6]17 assert(device);
18 uhci_print_info("Initializing device at address %p.\n", device);
19
20#define CHECK_RET_FREE_INSTANCE(message...) \
21 if (ret != EOK) { \
22 uhci_print_error(message); \
23 if (instance) { \
24 free(instance); \
25 } \
26 return ret; \
27 } else (void) 0
[3515533]28
29 /* create instance */
[d93ff502]30 uhci_t *instance = malloc(sizeof(uhci_t));
[9ee87f6]31 int ret = instance ? EOK : ENOMEM;
32 CHECK_RET_FREE_INSTANCE("Failed to allocate uhci driver instance.\n");
33
34 bzero(instance, sizeof(uhci_t));
[3515533]35
[18e35a7]36 /* init address keeper(libusb) */
[9ee87f6]37 usb_address_keeping_init(&instance->address_manager, USB11_ADDRESS_MAX);
[d93ff502]38 uhci_print_verbose("Initialized address manager.\n");
[18e35a7]39
[3515533]40 /* allow access to hc control registers */
41 regs_t *io;
[9ee87f6]42 ret = pio_enable(regs, sizeof(regs_t), (void**)&io);
43 CHECK_RET_FREE_INSTANCE("Failed to gain access to registers at %p.\n", io);
[3515533]44 instance->registers = io;
[d93ff502]45 uhci_print_verbose("Device registers accessible.\n");
[3515533]46
[9ee87f6]47 /* init transfer lists */
48 ret = uhci_init_transfer_lists(instance->transfers);
49 CHECK_RET_FREE_INSTANCE("Failed to initialize transfer lists.\n");
[d93ff502]50 uhci_print_verbose("Transfer lists initialized.\n");
[9ee87f6]51
[3515533]52 /* init root hub */
[9600516]53 ret = uhci_root_hub_init(&instance->root_hub, device,
54 (char*)regs + UHCI_ROOT_HUB_PORT_REGISTERS_OFFSET);
[9ee87f6]55 CHECK_RET_FREE_INSTANCE("Failed to initialize root hub driver.\n");
[9600516]56
[d93ff502]57 uhci_print_verbose("Initializing frame list.\n");
[d1984e0]58 instance->frame_list = get_page();
59// memalign32(sizeof(link_pointer_t) * UHCI_FRAME_LIST_COUNT, 4096);
[9600516]60 if (instance->frame_list == NULL) {
61 uhci_print_error("Failed to allocate frame list pointer.\n");
62 uhci_root_hub_fini(&instance->root_hub);
63 free(instance);
64 return ENOMEM;
65 }
66
[9ee87f6]67 /* initialize all frames to point to the first queue head */
68 unsigned i = 0;
69 const uint32_t queue =
70 instance->transfers[USB_TRANSFER_INTERRUPT].queue_head_pa
71 | LINK_POINTER_QUEUE_HEAD_FLAG;
72 for(; i < UHCI_FRAME_LIST_COUNT; ++i) {
73 instance->frame_list[i] = queue;
74 }
75
[9600516]76 const uintptr_t pa = (uintptr_t)addr_to_phys(instance->frame_list);
77
78 pio_write_32(&instance->registers->flbaseadd, (uint32_t)pa);
79
[9ee87f6]80 instance->cleaner = fibril_create(uhci_clean_finished, instance);
81 fibril_add_ready(instance->cleaner);
[3515533]82
[0535ee4]83 instance->debug_checker = fibril_create(uhci_debug_checker, instance);
84 fibril_add_ready(instance->debug_checker);
85
[d1984e0]86 uhci_print_verbose("Starting UHCI HC.\n");
[3da5eb9]87 pio_write_16(&instance->registers->usbcmd, UHCI_CMD_RUN_STOP);
88/*
[d1984e0]89 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
[3da5eb9]90 cmd |= UHCI_CMD_DEBUG;
[d1984e0]91 pio_write_16(&instance->registers->usbcmd, cmd);
[3da5eb9]92*/
[3515533]93 device->driver_data = instance;
94 return EOK;
95}
96/*----------------------------------------------------------------------------*/
[9ee87f6]97int uhci_init_transfer_lists(transfer_list_t transfers[])
[643b983]98{
99 //TODO:refactor
100 transfers[USB_TRANSFER_ISOCHRONOUS].first = NULL;
101 transfers[USB_TRANSFER_ISOCHRONOUS].last = NULL;
[b00163f]102
[643b983]103 int ret;
104 ret = transfer_list_init(&transfers[USB_TRANSFER_BULK], NULL);
105 if (ret != EOK) {
[b00163f]106 uhci_print_error("Failed to initialize bulk queue.\n");
[643b983]107 return ret;
108 }
109
110 ret = transfer_list_init(
111 &transfers[USB_TRANSFER_CONTROL], &transfers[USB_TRANSFER_BULK]);
112 if (ret != EOK) {
[b00163f]113 uhci_print_error("Failed to initialize control queue.\n");
[643b983]114 transfer_list_fini(&transfers[USB_TRANSFER_BULK]);
115 return ret;
116 }
117
118 ret = transfer_list_init(
119 &transfers[USB_TRANSFER_INTERRUPT], &transfers[USB_TRANSFER_CONTROL]);
120 if (ret != EOK) {
[b00163f]121 uhci_print_error("Failed to initialize interrupt queue.\n");
[643b983]122 transfer_list_fini(&transfers[USB_TRANSFER_CONTROL]);
123 transfer_list_fini(&transfers[USB_TRANSFER_BULK]);
124 return ret;
125 }
126
127 return EOK;
128}
[b00163f]129/*----------------------------------------------------------------------------*/
[d93ff502]130int uhci_transfer(
[b00163f]131 device_t *dev,
[d03ade7]132 usb_target_t target,
[b00163f]133 usb_transfer_type_t transfer_type,
[d93ff502]134 bool toggle,
[d03ade7]135 usb_packet_id pid,
[b00163f]136 void *buffer, size_t size,
137 usbhc_iface_transfer_out_callback_t callback_out,
138 usbhc_iface_transfer_in_callback_t callback_in,
[d03ade7]139 void *arg )
[b00163f]140{
141 // TODO: Add support for isochronous transfers
142 if (transfer_type == USB_TRANSFER_ISOCHRONOUS)
143 return ENOTSUP;
144
145 if (size >= 1024)
146 return ENOTSUP;
147
148 transfer_descriptor_t *td = NULL;
149 callback_t *job = NULL;
150 int ret = EOK;
151
152#define CHECK_RET_TRANS_FREE_JOB_TD(message) \
153 if (ret != EOK) { \
154 uhci_print_error(message); \
155 if (job) { \
[45c4f5a]156 callback_dispose(job); \
[b00163f]157 } \
[de0e6b3]158 if (td) { free32(td); } \
[b00163f]159 return ret; \
160 } else (void) 0
161
162
[45c4f5a]163 job = callback_get(dev, buffer, size, callback_in, callback_out, arg);
164 ret = job ? EOK : ENOMEM;
[b00163f]165 CHECK_RET_TRANS_FREE_JOB_TD("Failed to allocate callback structure.\n");
166
[3da5eb9]167 td = transfer_descriptor_get(3, size, false, target, pid, job->new_buffer);
[d03ade7]168 ret = td ? EOK : ENOMEM;
[eb03e92]169 CHECK_RET_TRANS_FREE_JOB_TD("Failed to setup transfer descriptor.\n");
[b00163f]170
171 td->callback = job;
172
173 assert(dev);
174 uhci_t *instance = (uhci_t*)dev->driver_data;
175 assert(instance);
176
177 ret = transfer_list_append(&instance->transfers[transfer_type], td);
178 CHECK_RET_TRANS_FREE_JOB_TD("Failed to append transfer descriptor.\n");
179
180 return EOK;
181}
[3da5eb9]182/*---------------------------------------------------------------------------*/
[9ee87f6]183int uhci_clean_finished(void* arg)
184{
185 uhci_print_verbose("Started cleaning fibril.\n");
186 uhci_t *instance = (uhci_t*)arg;
187 assert(instance);
[579dec2]188
[9ee87f6]189 while(1) {
[d1984e0]190 uhci_print_verbose("Running cleaning fibril on: %p.\n", instance);
[579dec2]191 /* iterate all transfer queues */
[0535ee4]192 int i = 0;
[d1984e0]193 for (; i < TRANSFER_QUEUES; ++i) {
[579dec2]194 /* Remove inactive transfers from the top of the queue
195 * TODO: should I reach queue head or is this enough? */
[0535ee4]196 volatile transfer_descriptor_t * it =
197 instance->transfers[i].first;
198 uhci_print_verbose("Running cleaning fibril on queue: %p (%s).\n",
199 &instance->transfers[i], it ? "SOMETHING" : "EMPTY");
[3da5eb9]200
201 if (it)
202 uhci_print_verbose("First in queue: %p (%x).\n",
203 it, it->status);
204
[579dec2]205 while (instance->transfers[i].first &&
206 !(instance->transfers[i].first->status & TD_STATUS_ERROR_ACTIVE)) {
207 transfer_descriptor_t *transfer = instance->transfers[i].first;
[3da5eb9]208 uhci_print_info("Inactive transfer calling callback with status %x.\n",
209 transfer->status);
[579dec2]210 instance->transfers[i].first = transfer->next_va;
[de0e6b3]211 transfer_descriptor_dispose(transfer);
[579dec2]212 }
213 if (!instance->transfers[i].first)
214 instance->transfers[i].last = instance->transfers[i].first;
215 }
[0535ee4]216 async_usleep(UHCI_CLEANER_TIMEOUT);
[9ee87f6]217 }
218 return EOK;
219}
[0535ee4]220/*---------------------------------------------------------------------------*/
221int uhci_debug_checker(void *arg)
222{
223 uhci_t *instance = (uhci_t*)arg;
224 assert(instance);
225 while (1) {
226 uint16_t reg;
227 reg = pio_read_16(&instance->registers->usbcmd);
228 uhci_print_verbose("Command register: %X\n", reg);
229 reg = pio_read_16(&instance->registers->usbsts);
230 uhci_print_verbose("Status register: %X\n", reg);
[3da5eb9]231/*
[0535ee4]232 uintptr_t frame_list = pio_read_32(&instance->registers->flbaseadd);
233 uhci_print_verbose("Framelist address: %p vs. %p.\n",
234 frame_list, addr_to_phys(instance->frame_list));
235 int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff;
236 uhci_print_verbose("Framelist item: %d \n", frnum );
237
238 queue_head_t* qh = instance->transfers[USB_TRANSFER_INTERRUPT].queue_head;
239 uhci_print_verbose("Interrupt QH: %p vs. %p.\n",
240 instance->frame_list[frnum], addr_to_phys(qh));
241
242 uhci_print_verbose("Control QH: %p vs. %p.\n", qh->next_queue,
243 addr_to_phys(instance->transfers[USB_TRANSFER_CONTROL].queue_head));
244 qh = instance->transfers[USB_TRANSFER_CONTROL].queue_head;
245
246 uhci_print_verbose("Bulk QH: %p vs. %p.\n", qh->next_queue,
247 addr_to_phys(instance->transfers[USB_TRANSFER_BULK].queue_head));
[3da5eb9]248 uint16_t cmd = pio_read_16(&instance->registers->usbcmd);
249 cmd |= UHCI_CMD_RUN_STOP;
250 pio_write_16(&instance->registers->usbcmd, cmd);
251*/
[0535ee4]252
253 async_usleep(UHCI_DEBUGER_TIMEOUT);
254 }
255 return 0;
256}
Note: See TracBrowser for help on using the repository browser.