Changes in uspace/drv/bus/usb/uhci/hc.c [58563585:d930980] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/bus/usb/uhci/hc.c
r58563585 rd930980 26 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 27 */ 28 29 28 /** @addtogroup drvusbuhcihc 30 29 * @{ … … 33 32 * @brief UHCI Host controller driver routines 34 33 */ 35 34 #include <errno.h> 35 #include <str_error.h> 36 36 #include <adt/list.h> 37 #include <assert.h>38 #include <async.h>39 37 #include <ddi.h> 40 #include <device/hw_res_parsed.h>41 #include <fibril.h>42 #include <errno.h>43 #include <macros.h>44 #include <mem.h>45 #include <stdlib.h>46 #include <str_error.h>47 #include <sys/types.h>48 38 49 39 #include <usb/debug.h> 50 40 #include <usb/usb.h> 51 #include <usb/host/utils/malloc32.h> 52 41 42 #include "hc.h" 53 43 #include "uhci_batch.h" 54 #include "hc.h"55 44 56 45 #define UHCI_INTR_ALLOW_INTERRUPTS \ … … 96 85 static int hc_init_mem_structures(hc_t *instance); 97 86 static int hc_init_transfer_lists(hc_t *instance); 98 87 static int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch); 88 89 static int hc_interrupt_emulator(void *arg); 99 90 static int hc_debug_checker(void *arg); 100 91 92 enum { 93 /** Number of PIO ranges used in IRQ code */ 94 hc_irq_pio_range_count = 95 sizeof(uhci_irq_pio_ranges) / sizeof(irq_pio_range_t), 96 97 /* Number of commands used in IRQ code */ 98 hc_irq_cmd_count = 99 sizeof(uhci_irq_commands) / sizeof(irq_cmd_t) 100 }; 101 101 102 102 /** Generate IRQ code. 103 * @param[out] code IRQ code structure. 104 * @param[in] hw_res Device's resources. 103 * @param[out] ranges PIO ranges buffer. 104 * @param[in] ranges_size Size of the ranges buffer (bytes). 105 * @param[out] cmds Commands buffer. 106 * @param[in] cmds_size Size of the commands buffer (bytes). 107 * @param[in] regs Physical address of device's registers. 108 * @param[in] reg_size Size of the register area (bytes). 105 109 * 106 110 * @return Error code. 107 111 */ 108 int uhci_hc_gen_irq_code(irq_code_t *code, const hw_res_list_parsed_t *hw_res) 109 { 110 assert(code); 111 assert(hw_res); 112 113 if (hw_res->irqs.count != 1 || hw_res->io_ranges.count != 1) 114 return EINVAL; 115 const addr_range_t regs = hw_res->io_ranges.ranges[0]; 116 117 if (RNGSZ(regs) < sizeof(uhci_regs_t)) 112 int 113 hc_get_irq_code(irq_pio_range_t ranges[], size_t ranges_size, irq_cmd_t cmds[], 114 size_t cmds_size, uintptr_t regs, size_t reg_size) 115 { 116 if ((ranges_size < sizeof(uhci_irq_pio_ranges)) || 117 (cmds_size < sizeof(uhci_irq_commands)) || 118 (reg_size < sizeof(uhci_regs_t))) 118 119 return EOVERFLOW; 119 120 120 code->ranges = malloc(sizeof(uhci_irq_pio_ranges)); 121 if (code->ranges == NULL) 122 return ENOMEM; 123 124 code->cmds = malloc(sizeof(uhci_irq_commands)); 125 if (code->cmds == NULL) { 126 free(code->ranges); 127 return ENOMEM; 128 } 129 130 code->rangecount = ARRAY_SIZE(uhci_irq_pio_ranges); 131 code->cmdcount = ARRAY_SIZE(uhci_irq_commands); 132 133 memcpy(code->ranges, uhci_irq_pio_ranges, sizeof(uhci_irq_pio_ranges)); 134 code->ranges[0].base = RNGABS(regs); 135 136 memcpy(code->cmds, uhci_irq_commands, sizeof(uhci_irq_commands)); 137 uhci_regs_t *registers = (uhci_regs_t *) RNGABSPTR(regs); 138 code->cmds[0].addr = (void*)®isters->usbsts; 139 code->cmds[3].addr = (void*)®isters->usbsts; 140 141 usb_log_debug("I/O regs at %p (size %zu), IRQ %d.\n", 142 RNGABSPTR(regs), RNGSZ(regs), hw_res->irqs.irqs[0]); 143 144 return hw_res->irqs.irqs[0]; 121 memcpy(ranges, uhci_irq_pio_ranges, sizeof(uhci_irq_pio_ranges)); 122 ranges[0].base = regs; 123 124 memcpy(cmds, uhci_irq_commands, sizeof(uhci_irq_commands)); 125 uhci_regs_t *registers = (uhci_regs_t *) regs; 126 cmds[0].addr = ®isters->usbsts; 127 cmds[3].addr = ®isters->usbsts; 128 129 return EOK; 130 } 131 132 /** Register interrupt handler. 133 * 134 * @param[in] device Host controller DDF device 135 * @param[in] reg_base Register range base 136 * @param[in] reg_size Register range size 137 * @param[in] irq Interrupt number 138 * @paran[in] handler Interrupt handler 139 * 140 * @return EOK on success or negative error code 141 */ 142 int hc_register_irq_handler(ddf_dev_t *device, uintptr_t reg_base, size_t reg_size, 143 int irq, interrupt_handler_t handler) 144 { 145 int rc; 146 irq_pio_range_t irq_ranges[hc_irq_pio_range_count]; 147 irq_cmd_t irq_cmds[hc_irq_cmd_count]; 148 rc = hc_get_irq_code(irq_ranges, sizeof(irq_ranges), irq_cmds, 149 sizeof(irq_cmds), reg_base, reg_size); 150 if (rc != EOK) { 151 usb_log_error("Failed to generate IRQ commands: %s.\n", 152 str_error(rc)); 153 return rc; 154 } 155 156 irq_code_t irq_code = { 157 .rangecount = hc_irq_pio_range_count, 158 .ranges = irq_ranges, 159 .cmdcount = hc_irq_cmd_count, 160 .cmds = irq_cmds 161 }; 162 163 /* Register handler to avoid interrupt lockup */ 164 rc = register_interrupt_handler(device, irq, handler, &irq_code); 165 if (rc != EOK) { 166 usb_log_error("Failed to register interrupt handler: %s.\n", 167 str_error(rc)); 168 return rc; 169 } 170 171 return EOK; 145 172 } 146 173 147 174 /** Take action based on the interrupt cause. 148 175 * 149 * @param[in] hcd HCDstructure to use.176 * @param[in] instance UHCI structure to use. 150 177 * @param[in] status Value of the status register at the time of interrupt. 151 178 * … … 155 182 * - resume from suspend state (not implemented) 156 183 */ 157 void uhci_hc_interrupt(hcd_t *hcd, uint32_t status) 158 { 159 assert(hcd); 160 hc_t *instance = hcd_get_driver_data(hcd); 184 void hc_interrupt(hc_t *instance, uint16_t status) 185 { 161 186 assert(instance); 162 187 /* Lower 2 bits are transaction error and transaction complete */ … … 172 197 &instance->transfers_bulk_full, &done); 173 198 174 list_foreach_safe(done, current, next) { 175 list_remove(current); 199 while (!list_empty(&done)) { 200 link_t *item = list_first(&done); 201 list_remove(item); 176 202 uhci_transfer_batch_t *batch = 177 uhci_transfer_batch_from_link( current);203 uhci_transfer_batch_from_link(item); 178 204 uhci_transfer_batch_finish_dispose(batch); 179 205 } … … 206 232 * 207 233 * @param[in] instance Memory place to initialize. 208 * @param[in] regs Range of device's I/O control registers. 234 * @param[in] regs Address of I/O control registers. 235 * @param[in] reg_size Size of I/O control registers. 209 236 * @param[in] interrupts True if hw interrupts should be used. 210 237 * @return Error code. … … 214 241 * interrupt fibrils. 215 242 */ 216 int hc_init(hc_t *instance, const hw_res_list_parsed_t *hw_res, bool interrupts) 217 { 218 assert(instance); 219 assert(hw_res); 220 if (hw_res->io_ranges.count != 1 || 221 hw_res->io_ranges.ranges[0].size < sizeof(uhci_regs_t)) 222 return EINVAL; 243 int hc_init(hc_t *instance, void *regs, size_t reg_size, bool interrupts) 244 { 245 assert(reg_size >= sizeof(uhci_regs_t)); 246 int rc; 223 247 224 248 instance->hw_interrupts = interrupts; … … 226 250 227 251 /* allow access to hc control registers */ 228 int ret = pio_enable_range(&hw_res->io_ranges.ranges[0], 229 (void **) &instance->registers); 230 if (ret != EOK) { 231 usb_log_error("Failed to gain access to registers: %s.\n", 232 str_error(ret)); 233 return ret; 234 } 235 236 usb_log_debug("Device registers at %" PRIx64 " (%zuB) accessible.\n", 237 hw_res->io_ranges.ranges[0].address.absolute, 238 hw_res->io_ranges.ranges[0].size); 239 240 ret = hc_init_mem_structures(instance); 241 if (ret != EOK) { 242 usb_log_error("Failed to init UHCI memory structures: %s.\n", 243 str_error(ret)); 244 // TODO: we should disable pio here 245 return ret; 246 } 252 uhci_regs_t *io; 253 rc = pio_enable(regs, reg_size, (void **)&io); 254 if (rc != EOK) { 255 usb_log_error("Failed to gain access to registers at %p: %s.\n", 256 io, str_error(rc)); 257 return rc; 258 } 259 260 instance->registers = io; 261 usb_log_debug( 262 "Device registers at %p (%zuB) accessible.\n", io, reg_size); 263 264 rc = hc_init_mem_structures(instance); 265 if (rc != EOK) { 266 usb_log_error("Failed to initialize UHCI memory structures: %s.\n", 267 str_error(rc)); 268 return rc; 269 } 270 271 hcd_init(&instance->generic, USB_SPEED_FULL, 272 BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11); 273 274 instance->generic.private_data = instance; 275 instance->generic.schedule = hc_schedule; 276 instance->generic.ep_add_hook = NULL; 247 277 248 278 hc_init_hw(instance); 279 if (!interrupts) { 280 instance->interrupt_emulator = 281 fibril_create(hc_interrupt_emulator, instance); 282 fibril_add_ready(instance->interrupt_emulator); 283 } 249 284 (void)hc_debug_checker; 250 285 251 uhci_rh_init(&instance->rh, instance->registers->ports, "uhci"); 252 253 return EOK; 254 } 255 256 /** Safely dispose host controller internal structures 257 * 258 * @param[in] instance Host controller structure to use. 259 */ 260 void hc_fini(hc_t *instance) 261 { 262 assert(instance); 263 //TODO Implement 286 return EOK; 264 287 } 265 288 … … 409 432 } 410 433 411 int uhci_hc_status(hcd_t *hcd, uint32_t *status)412 {413 assert(hcd);414 assert(status);415 hc_t *instance = hcd_get_driver_data(hcd);416 assert(instance);417 418 *status = 0;419 if (instance->registers) {420 uint16_t s = pio_read_16(&instance->registers->usbsts);421 pio_write_16(&instance->registers->usbsts, s);422 *status = s;423 }424 return EOK;425 }426 427 434 /** Schedule batch for execution. 428 435 * … … 433 440 * Checks for bandwidth availability and appends the batch to the proper queue. 434 441 */ 435 int uhci_hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)442 int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch) 436 443 { 437 444 assert(hcd); 438 hc_t *instance = hcd _get_driver_data(hcd);445 hc_t *instance = hcd->private_data; 439 446 assert(instance); 440 447 assert(batch); 441 442 if (batch->ep->address == uhci_rh_get_address(&instance->rh))443 return uhci_rh_schedule(&instance->rh, batch);444 445 448 uhci_transfer_batch_t *uhci_batch = uhci_transfer_batch_get(batch); 446 449 if (!uhci_batch) { … … 454 457 transfer_list_add_batch(list, uhci_batch); 455 458 459 return EOK; 460 } 461 462 /** Polling function, emulates interrupts. 463 * 464 * @param[in] arg UHCI hc structure to use. 465 * @return EOK (should never return) 466 */ 467 int hc_interrupt_emulator(void* arg) 468 { 469 usb_log_debug("Started interrupt emulator.\n"); 470 hc_t *instance = arg; 471 assert(instance); 472 473 while (1) { 474 /* Read and clear status register */ 475 uint16_t status = pio_read_16(&instance->registers->usbsts); 476 pio_write_16(&instance->registers->usbsts, status); 477 if (status != 0) 478 usb_log_debug2("UHCI status: %x.\n", status); 479 hc_interrupt(instance, status); 480 async_usleep(UHCI_INT_EMULATOR_TIMEOUT); 481 } 456 482 return EOK; 457 483 }
Note:
See TracChangeset
for help on using the changeset viewer.