Changeset 72af8da in mainline for uspace/drv/uhci-hcd/uhci.c
- Timestamp:
- 2011-03-16T18:50:17Z (13 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 42a3a57
- Parents:
- 3e7b7cd (diff), fcf07e6 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/uhci-hcd/uhci.c
r3e7b7cd r72af8da 26 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 27 */ 28 /** @addtogroup usb 28 29 /** @addtogroup drvusbuhci 29 30 * @{ 30 31 */ … … 34 35 #include <errno.h> 35 36 #include <str_error.h> 36 #include < adt/list.h>37 #include < libarch/ddi.h>38 37 #include <ddf/interrupt.h> 38 #include <usb_iface.h> 39 #include <usb/ddfiface.h> 39 40 #include <usb/debug.h> 40 #include <usb/usb.h>41 #include <usb/ddfiface.h>42 #include <usb_iface.h>43 41 44 42 #include "uhci.h" 45 43 #include "iface.h" 46 47 static irq_cmd_t uhci_cmds[] = { 48 { 49 .cmd = CMD_PIO_READ_16, 50 .addr = NULL, /* patched for every instance */ 51 .dstarg = 1 52 }, 53 { 54 .cmd = CMD_PIO_WRITE_16, 55 .addr = NULL, /* pathed for every instance */ 56 .value = 0x1f 57 }, 58 { 59 .cmd = CMD_ACCEPT 60 } 61 }; 62 63 static int usb_iface_get_address(ddf_fun_t *fun, devman_handle_t handle, 64 usb_address_t *address) 44 #include "pci.h" 45 46 47 /** IRQ handling callback, identifies device 48 * 49 * @param[in] dev DDF instance of the device to use. 50 * @param[in] iid (Unused). 51 * @param[in] call Pointer to the call that represents interrupt. 52 */ 53 static void irq_handler(ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *call) 54 { 55 assert(dev); 56 uhci_hc_t *hc = &((uhci_t*)dev->driver_data)->hc; 57 uint16_t status = IPC_GET_ARG1(*call); 58 assert(hc); 59 uhci_hc_interrupt(hc, status); 60 } 61 /*----------------------------------------------------------------------------*/ 62 /** Get address of the device identified by handle. 63 * 64 * @param[in] dev DDF instance of the device to use. 65 * @param[in] iid (Unused). 66 * @param[in] call Pointer to the call that represents interrupt. 67 */ 68 static int usb_iface_get_address( 69 ddf_fun_t *fun, devman_handle_t handle, usb_address_t *address) 65 70 { 66 71 assert(fun); 67 uhci_t *hc = fun_to_uhci(fun); 68 assert(hc); 69 70 usb_address_t addr = device_keeper_find(&hc->device_manager, 71 handle); 72 device_keeper_t *manager = &((uhci_t*)fun->dev->driver_data)->hc.device_manager; 73 74 usb_address_t addr = device_keeper_find(manager, handle); 72 75 if (addr < 0) { 73 76 return addr; … … 81 84 } 82 85 /*----------------------------------------------------------------------------*/ 83 static usb_iface_t hc_usb_iface = { 84 .get_hc_handle = usb_iface_get_hc_handle_hc_impl, 86 /** Gets handle of the respective hc (this or parent device). 87 * 88 * @param[in] root_hub_fun Root hub function seeking hc handle. 89 * @param[out] handle Place to write the handle. 90 * @return Error code. 91 */ 92 static int usb_iface_get_hc_handle( 93 ddf_fun_t *fun, devman_handle_t *handle) 94 { 95 assert(handle); 96 ddf_fun_t *hc_fun = ((uhci_t*)fun->dev->driver_data)->hc_fun; 97 assert(hc_fun != NULL); 98 99 *handle = hc_fun->handle; 100 return EOK; 101 } 102 /*----------------------------------------------------------------------------*/ 103 /** This iface is generic for both RH and HC. */ 104 static usb_iface_t usb_iface = { 105 .get_hc_handle = usb_iface_get_hc_handle, 85 106 .get_address = usb_iface_get_address 86 107 }; 87 108 /*----------------------------------------------------------------------------*/ 88 static ddf_dev_ops_t uhci_ops = { 89 .interfaces[USB_DEV_IFACE] = &hc_usb_iface, 90 .interfaces[USBHC_DEV_IFACE] = &uhci_iface, 91 }; 92 /*----------------------------------------------------------------------------*/ 93 static int uhci_init_transfer_lists(uhci_t *instance); 94 static int uhci_init_mem_structures(uhci_t *instance); 95 static void uhci_init_hw(uhci_t *instance); 96 97 static int uhci_interrupt_emulator(void *arg); 98 static int uhci_debug_checker(void *arg); 99 100 static bool allowed_usb_packet( 101 bool low_speed, usb_transfer_type_t, size_t size); 102 103 104 int uhci_init(uhci_t *instance, ddf_dev_t *dev, void *regs, size_t reg_size) 105 { 106 assert(reg_size >= sizeof(regs_t)); 107 int ret; 108 109 static ddf_dev_ops_t uhci_hc_ops = { 110 .interfaces[USB_DEV_IFACE] = &usb_iface, 111 .interfaces[USBHC_DEV_IFACE] = &uhci_hc_iface, /* see iface.h/c */ 112 }; 113 /*----------------------------------------------------------------------------*/ 114 /** Get root hub hw resources (I/O registers). 115 * 116 * @param[in] fun Root hub function. 117 * @return Pointer to the resource list used by the root hub. 118 */ 119 static hw_resource_list_t *get_resource_list(ddf_fun_t *fun) 120 { 121 assert(fun); 122 return &((uhci_rh_t*)fun->driver_data)->resource_list; 123 } 124 /*----------------------------------------------------------------------------*/ 125 static hw_res_ops_t hw_res_iface = { 126 .get_resource_list = get_resource_list, 127 .enable_interrupt = NULL 128 }; 129 /*----------------------------------------------------------------------------*/ 130 static ddf_dev_ops_t uhci_rh_ops = { 131 .interfaces[USB_DEV_IFACE] = &usb_iface, 132 .interfaces[HW_RES_DEV_IFACE] = &hw_res_iface 133 }; 134 /*----------------------------------------------------------------------------*/ 135 /** Initialize hc and rh ddf structures and their respective drivers. 136 * 137 * @param[in] instance UHCI structure to use. 138 * @param[in] device DDF instance of the device to use. 139 * 140 * This function does all the preparatory work for hc and rh drivers: 141 * - gets device hw resources 142 * - disables UHCI legacy support 143 * - asks for interrupt 144 * - registers interrupt handler 145 */ 146 int uhci_init(uhci_t *instance, ddf_dev_t *device) 147 { 148 assert(instance); 149 instance->hc_fun = NULL; 150 instance->rh_fun = NULL; 109 151 #define CHECK_RET_DEST_FUN_RETURN(ret, message...) \ 110 if (ret != EOK) { \ 111 usb_log_error(message); \ 112 if (instance->ddf_instance) \ 113 ddf_fun_destroy(instance->ddf_instance); \ 114 return ret; \ 115 } else (void) 0 116 117 /* Create UHCI function. */ 118 instance->ddf_instance = ddf_fun_create(dev, fun_exposed, "uhci"); 119 ret = (instance->ddf_instance == NULL) ? ENOMEM : EOK; 120 CHECK_RET_DEST_FUN_RETURN(ret, 121 "Failed to create UHCI device function.\n"); 122 123 instance->ddf_instance->ops = &uhci_ops; 124 instance->ddf_instance->driver_data = instance; 125 126 ret = ddf_fun_bind(instance->ddf_instance); 152 if (ret != EOK) { \ 153 usb_log_error(message); \ 154 if (instance->hc_fun) \ 155 ddf_fun_destroy(instance->hc_fun); \ 156 if (instance->rh_fun) \ 157 ddf_fun_destroy(instance->rh_fun); \ 158 return ret; \ 159 } 160 161 uintptr_t io_reg_base = 0; 162 size_t io_reg_size = 0; 163 int irq = 0; 164 165 int ret = 166 pci_get_my_registers(device, &io_reg_base, &io_reg_size, &irq); 167 CHECK_RET_DEST_FUN_RETURN(ret, 168 "Failed(%d) to get I/O addresses:.\n", ret, device->handle); 169 usb_log_info("I/O regs at 0x%X (size %zu), IRQ %d.\n", 170 io_reg_base, io_reg_size, irq); 171 172 ret = pci_disable_legacy(device); 173 CHECK_RET_DEST_FUN_RETURN(ret, 174 "Failed(%d) to disable legacy USB: %s.\n", ret, str_error(ret)); 175 176 bool interrupts = false; 177 ret = pci_enable_interrupts(device); 178 if (ret != EOK) { 179 usb_log_warning( 180 "Failed(%d) to enable interrupts, fall back to polling.\n", 181 ret); 182 } else { 183 usb_log_debug("Hw interrupts enabled.\n"); 184 interrupts = true; 185 } 186 187 instance->hc_fun = ddf_fun_create(device, fun_exposed, "uhci-hc"); 188 ret = (instance->hc_fun == NULL) ? ENOMEM : EOK; 189 CHECK_RET_DEST_FUN_RETURN(ret, 190 "Failed(%d) to create HC function.\n", ret); 191 192 ret = uhci_hc_init(&instance->hc, instance->hc_fun, 193 (void*)io_reg_base, io_reg_size, interrupts); 194 CHECK_RET_DEST_FUN_RETURN(ret, "Failed(%d) to init uhci-hcd.\n", ret); 195 instance->hc_fun->ops = &uhci_hc_ops; 196 instance->hc_fun->driver_data = &instance->hc; 197 ret = ddf_fun_bind(instance->hc_fun); 127 198 CHECK_RET_DEST_FUN_RETURN(ret, 128 199 "Failed(%d) to bind UHCI device function: %s.\n", 129 200 ret, str_error(ret)); 130 131 /* allow access to hc control registers */ 132 regs_t *io; 133 ret = pio_enable(regs, reg_size, (void**)&io); 134 CHECK_RET_DEST_FUN_RETURN(ret, 135 "Failed(%d) to gain access to registers at %p: %s.\n", 136 ret, str_error(ret), io); 137 instance->registers = io; 138 usb_log_debug("Device registers at %p(%u) accessible.\n", 139 io, reg_size); 140 141 ret = uhci_init_mem_structures(instance); 142 CHECK_RET_DEST_FUN_RETURN(ret, 143 "Failed to initialize UHCI memory structures.\n"); 144 145 uhci_init_hw(instance); 146 instance->cleaner = 147 fibril_create(uhci_interrupt_emulator, instance); 148 fibril_add_ready(instance->cleaner); 149 150 instance->debug_checker = fibril_create(uhci_debug_checker, instance); 151 fibril_add_ready(instance->debug_checker); 152 153 usb_log_info("Started UHCI driver.\n"); 201 #undef CHECK_RET_HC_RETURN 202 203 #define CHECK_RET_FINI_RETURN(ret, message...) \ 204 if (ret != EOK) { \ 205 usb_log_error(message); \ 206 if (instance->hc_fun) \ 207 ddf_fun_destroy(instance->hc_fun); \ 208 if (instance->rh_fun) \ 209 ddf_fun_destroy(instance->rh_fun); \ 210 uhci_hc_fini(&instance->hc); \ 211 return ret; \ 212 } 213 214 /* It does no harm if we register this on polling */ 215 ret = register_interrupt_handler(device, irq, irq_handler, 216 &instance->hc.interrupt_code); 217 CHECK_RET_FINI_RETURN(ret, 218 "Failed(%d) to register interrupt handler.\n", ret); 219 220 instance->rh_fun = ddf_fun_create(device, fun_inner, "uhci-rh"); 221 ret = (instance->rh_fun == NULL) ? ENOMEM : EOK; 222 CHECK_RET_FINI_RETURN(ret, 223 "Failed(%d) to create root hub function.\n", ret); 224 225 ret = uhci_rh_init(&instance->rh, instance->rh_fun, 226 (uintptr_t)instance->hc.registers + 0x10, 4); 227 CHECK_RET_FINI_RETURN(ret, 228 "Failed(%d) to setup UHCI root hub.\n", ret); 229 230 instance->rh_fun->ops = &uhci_rh_ops; 231 instance->rh_fun->driver_data = &instance->rh; 232 ret = ddf_fun_bind(instance->rh_fun); 233 CHECK_RET_FINI_RETURN(ret, 234 "Failed(%d) to register UHCI root hub.\n", ret); 235 154 236 return EOK; 155 #undef CHECK_RET_DEST_FUN_RETURN 156 } 157 /*----------------------------------------------------------------------------*/ 158 void uhci_init_hw(uhci_t *instance) 159 { 160 assert(instance); 161 162 /* reset everything, who knows what touched it before us */ 163 pio_write_16(&instance->registers->usbcmd, UHCI_CMD_GLOBAL_RESET); 164 async_usleep(10000); /* 10ms according to USB spec */ 165 pio_write_16(&instance->registers->usbcmd, 0); 166 167 /* reset hc, all states and counters */ 168 pio_write_16(&instance->registers->usbcmd, UHCI_CMD_HCRESET); 169 while ((pio_read_16(&instance->registers->usbcmd) & UHCI_CMD_HCRESET) != 0) 170 { async_usleep(10); } 171 172 /* set framelist pointer */ 173 const uint32_t pa = addr_to_phys(instance->frame_list); 174 pio_write_32(&instance->registers->flbaseadd, pa); 175 176 /* enable all interrupts, but resume interrupt */ 177 pio_write_16(&instance->registers->usbintr, 178 UHCI_INTR_CRC | UHCI_INTR_COMPLETE | UHCI_INTR_SHORT_PACKET); 179 180 /* Start the hc with large(64B) packet FSBR */ 181 pio_write_16(&instance->registers->usbcmd, 182 UHCI_CMD_RUN_STOP | UHCI_CMD_MAX_PACKET | UHCI_CMD_CONFIGURE); 183 } 184 /*----------------------------------------------------------------------------*/ 185 int uhci_init_mem_structures(uhci_t *instance) 186 { 187 assert(instance); 188 #define CHECK_RET_DEST_CMDS_RETURN(ret, message...) \ 189 if (ret != EOK) { \ 190 usb_log_error(message); \ 191 if (instance->interrupt_code.cmds != NULL) \ 192 free(instance->interrupt_code.cmds); \ 193 return ret; \ 194 } else (void) 0 195 196 /* init interrupt code */ 197 instance->interrupt_code.cmds = malloc(sizeof(uhci_cmds)); 198 int ret = (instance->interrupt_code.cmds == NULL) ? ENOMEM : EOK; 199 CHECK_RET_DEST_CMDS_RETURN(ret, "Failed to allocate interrupt cmds space.\n"); 200 201 { 202 irq_cmd_t *interrupt_commands = instance->interrupt_code.cmds; 203 memcpy(interrupt_commands, uhci_cmds, sizeof(uhci_cmds)); 204 interrupt_commands[0].addr = (void*)&instance->registers->usbsts; 205 interrupt_commands[1].addr = (void*)&instance->registers->usbsts; 206 instance->interrupt_code.cmdcount = 207 sizeof(uhci_cmds) / sizeof(irq_cmd_t); 208 } 209 210 /* init transfer lists */ 211 ret = uhci_init_transfer_lists(instance); 212 CHECK_RET_DEST_CMDS_RETURN(ret, "Failed to initialize transfer lists.\n"); 213 usb_log_debug("Initialized transfer lists.\n"); 214 215 /* frame list initialization */ 216 instance->frame_list = get_page(); 217 ret = instance ? EOK : ENOMEM; 218 CHECK_RET_DEST_CMDS_RETURN(ret, "Failed to get frame list page.\n"); 219 usb_log_debug("Initialized frame list.\n"); 220 221 /* initialize all frames to point to the first queue head */ 222 const uint32_t queue = 223 instance->transfers_interrupt.queue_head_pa 224 | LINK_POINTER_QUEUE_HEAD_FLAG; 225 226 unsigned i = 0; 227 for(; i < UHCI_FRAME_LIST_COUNT; ++i) { 228 instance->frame_list[i] = queue; 229 } 230 231 /* init address keeper(libusb) */ 232 device_keeper_init(&instance->device_manager); 233 usb_log_debug("Initialized device manager.\n"); 234 235 return EOK; 236 #undef CHECK_RET_DEST_CMDS_RETURN 237 } 238 /*----------------------------------------------------------------------------*/ 239 int uhci_init_transfer_lists(uhci_t *instance) 240 { 241 assert(instance); 242 #define CHECK_RET_CLEAR_RETURN(ret, message...) \ 243 if (ret != EOK) { \ 244 usb_log_error(message); \ 245 transfer_list_fini(&instance->transfers_bulk_full); \ 246 transfer_list_fini(&instance->transfers_control_full); \ 247 transfer_list_fini(&instance->transfers_control_slow); \ 248 transfer_list_fini(&instance->transfers_interrupt); \ 249 return ret; \ 250 } else (void) 0 251 252 /* initialize TODO: check errors */ 253 int ret; 254 ret = transfer_list_init(&instance->transfers_bulk_full, "BULK_FULL"); 255 CHECK_RET_CLEAR_RETURN(ret, "Failed to init BULK list."); 256 257 ret = transfer_list_init(&instance->transfers_control_full, "CONTROL_FULL"); 258 CHECK_RET_CLEAR_RETURN(ret, "Failed to init CONTROL FULL list."); 259 260 ret = transfer_list_init(&instance->transfers_control_slow, "CONTROL_SLOW"); 261 CHECK_RET_CLEAR_RETURN(ret, "Failed to init CONTROL SLOW list."); 262 263 ret = transfer_list_init(&instance->transfers_interrupt, "INTERRUPT"); 264 CHECK_RET_CLEAR_RETURN(ret, "Failed to init INTERRUPT list."); 265 266 transfer_list_set_next(&instance->transfers_control_full, 267 &instance->transfers_bulk_full); 268 transfer_list_set_next(&instance->transfers_control_slow, 269 &instance->transfers_control_full); 270 transfer_list_set_next(&instance->transfers_interrupt, 271 &instance->transfers_control_slow); 272 273 /*FSBR*/ 274 #ifdef FSBR 275 transfer_list_set_next(&instance->transfers_bulk_full, 276 &instance->transfers_control_full); 277 #endif 278 279 instance->transfers[0][USB_TRANSFER_INTERRUPT] = 280 &instance->transfers_interrupt; 281 instance->transfers[1][USB_TRANSFER_INTERRUPT] = 282 &instance->transfers_interrupt; 283 instance->transfers[0][USB_TRANSFER_CONTROL] = 284 &instance->transfers_control_full; 285 instance->transfers[1][USB_TRANSFER_CONTROL] = 286 &instance->transfers_control_slow; 287 instance->transfers[0][USB_TRANSFER_BULK] = 288 &instance->transfers_bulk_full; 289 290 return EOK; 291 #undef CHECK_RET_CLEAR_RETURN 292 } 293 /*----------------------------------------------------------------------------*/ 294 int uhci_schedule(uhci_t *instance, batch_t *batch) 295 { 296 assert(instance); 297 assert(batch); 298 const int low_speed = (batch->speed == USB_SPEED_LOW); 299 if (!allowed_usb_packet( 300 low_speed, batch->transfer_type, batch->max_packet_size)) { 301 usb_log_warning("Invalid USB packet specified %s SPEED %d %zu.\n", 302 low_speed ? "LOW" : "FULL" , batch->transfer_type, 303 batch->max_packet_size); 304 return ENOTSUP; 305 } 306 /* TODO: check available bandwith here */ 307 308 transfer_list_t *list = 309 instance->transfers[low_speed][batch->transfer_type]; 310 assert(list); 311 transfer_list_add_batch(list, batch); 312 313 return EOK; 314 } 315 /*----------------------------------------------------------------------------*/ 316 void uhci_interrupt(uhci_t *instance, uint16_t status) 317 { 318 assert(instance); 319 transfer_list_remove_finished(&instance->transfers_interrupt); 320 transfer_list_remove_finished(&instance->transfers_control_slow); 321 transfer_list_remove_finished(&instance->transfers_control_full); 322 transfer_list_remove_finished(&instance->transfers_bulk_full); 323 } 324 /*----------------------------------------------------------------------------*/ 325 int uhci_interrupt_emulator(void* arg) 326 { 327 usb_log_debug("Started interrupt emulator.\n"); 328 uhci_t *instance = (uhci_t*)arg; 329 assert(instance); 330 331 while (1) { 332 uint16_t status = pio_read_16(&instance->registers->usbsts); 333 if (status != 0) 334 usb_log_debug2("UHCI status: %x.\n", status); 335 status |= 1; 336 uhci_interrupt(instance, status); 337 pio_write_16(&instance->registers->usbsts, 0x1f); 338 async_usleep(UHCI_CLEANER_TIMEOUT * 5); 339 } 340 return EOK; 341 } 342 /*---------------------------------------------------------------------------*/ 343 int uhci_debug_checker(void *arg) 344 { 345 uhci_t *instance = (uhci_t*)arg; 346 assert(instance); 347 348 #define QH(queue) \ 349 instance->transfers_##queue.queue_head 350 351 while (1) { 352 const uint16_t cmd = pio_read_16(&instance->registers->usbcmd); 353 const uint16_t sts = pio_read_16(&instance->registers->usbsts); 354 const uint16_t intr = 355 pio_read_16(&instance->registers->usbintr); 356 357 if (((cmd & UHCI_CMD_RUN_STOP) != 1) || (sts != 0)) { 358 usb_log_debug2("Command: %X Status: %X Intr: %x\n", 359 cmd, sts, intr); 360 } 361 362 uintptr_t frame_list = 363 pio_read_32(&instance->registers->flbaseadd) & ~0xfff; 364 if (frame_list != addr_to_phys(instance->frame_list)) { 365 usb_log_debug("Framelist address: %p vs. %p.\n", 366 frame_list, addr_to_phys(instance->frame_list)); 367 } 368 369 int frnum = pio_read_16(&instance->registers->frnum) & 0x3ff; 370 usb_log_debug2("Framelist item: %d \n", frnum ); 371 372 uintptr_t expected_pa = instance->frame_list[frnum] & (~0xf); 373 uintptr_t real_pa = addr_to_phys(QH(interrupt)); 374 if (expected_pa != real_pa) { 375 usb_log_debug("Interrupt QH: %p vs. %p.\n", 376 expected_pa, real_pa); 377 } 378 379 expected_pa = QH(interrupt)->next_queue & (~0xf); 380 real_pa = addr_to_phys(QH(control_slow)); 381 if (expected_pa != real_pa) { 382 usb_log_debug("Control Slow QH: %p vs. %p.\n", 383 expected_pa, real_pa); 384 } 385 386 expected_pa = QH(control_slow)->next_queue & (~0xf); 387 real_pa = addr_to_phys(QH(control_full)); 388 if (expected_pa != real_pa) { 389 usb_log_debug("Control Full QH: %p vs. %p.\n", 390 expected_pa, real_pa); 391 } 392 393 expected_pa = QH(control_full)->next_queue & (~0xf); 394 real_pa = addr_to_phys(QH(bulk_full)); 395 if (expected_pa != real_pa ) { 396 usb_log_debug("Bulk QH: %p vs. %p.\n", 397 expected_pa, real_pa); 398 } 399 async_usleep(UHCI_DEBUGER_TIMEOUT); 400 } 401 return 0; 402 #undef QH 403 } 404 /*----------------------------------------------------------------------------*/ 405 bool allowed_usb_packet( 406 bool low_speed, usb_transfer_type_t transfer, size_t size) 407 { 408 /* see USB specification chapter 5.5-5.8 for magic numbers used here */ 409 switch(transfer) 410 { 411 case USB_TRANSFER_ISOCHRONOUS: 412 return (!low_speed && size < 1024); 413 case USB_TRANSFER_INTERRUPT: 414 return size <= (low_speed ? 8 : 64); 415 case USB_TRANSFER_CONTROL: /* device specifies its own max size */ 416 return (size <= (low_speed ? 8 : 64)); 417 case USB_TRANSFER_BULK: /* device specifies its own max size */ 418 return (!low_speed && size <= 64); 419 } 420 return false; 237 #undef CHECK_RET_FINI_RETURN 421 238 } 422 239 /**
Note:
See TracChangeset
for help on using the changeset viewer.