Changeset 32fb6bce in mainline for uspace/lib/usbhost/src/hcd.c
- Timestamp:
- 2017-12-18T22:50:21Z (6 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 7f70d1c
- Parents:
- 1ea0bbf
- git-author:
- Ondřej Hlavatý <aearsis@…> (2017-12-18 22:04:50)
- git-committer:
- Ondřej Hlavatý <aearsis@…> (2017-12-18 22:50:21)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/usbhost/src/hcd.c
r1ea0bbf r32fb6bce 36 36 #include <assert.h> 37 37 #include <async.h> 38 #include <ddf/interrupt.h> 38 39 #include <errno.h> 39 40 #include <macros.h> … … 45 46 46 47 #include "bus.h" 48 #include "ddf_helpers.h" 47 49 #include "endpoint.h" 48 50 #include "usb_transfer_batch.h" … … 50 52 #include "hcd.h" 51 53 52 53 /** Initialize hcd_t structure. 54 * Initializes device and endpoint managers. Sets data and hook pointer to NULL. 55 * 56 * @param hcd hcd_t structure to initialize, non-null. 57 * @param max_speed Maximum supported USB speed (full, high). 58 * @param bandwidth Available bandwidth, passed to endpoint manager. 59 * @param bw_count Bandwidth compute function, passed to endpoint manager. 60 */ 61 void hcd_init(hcd_t *hcd) { 54 int hc_dev_add(ddf_dev_t *); 55 int hc_dev_remove(ddf_dev_t *); 56 int hc_dev_gone(ddf_dev_t *); 57 int hc_fun_online(ddf_fun_t *); 58 int hc_fun_offline(ddf_fun_t *); 59 60 static driver_ops_t hc_driver_ops = { 61 .dev_add = hc_dev_add, 62 .dev_remove = hc_dev_remove, 63 .dev_gone = hc_dev_gone, 64 .fun_online = hc_fun_offline, 65 .fun_offline = hc_fun_offline, 66 }; 67 68 static const hc_driver_t *hc_driver; 69 70 int hc_driver_main(const hc_driver_t *driver) 71 { 72 driver_t ddf_driver = { 73 .name = driver->name, 74 .driver_ops = &hc_driver_ops, 75 }; 76 77 /* Remember ops to call. */ 78 hc_driver = driver; 79 80 return ddf_driver_main(&ddf_driver); 81 } 82 83 /** IRQ handling callback, forward status from call to diver structure. 84 * 85 * @param[in] dev DDF instance of the device to use. 86 * @param[in] iid (Unused). 87 * @param[in] call Pointer to the call from kernel. 88 */ 89 static void irq_handler(ipc_callid_t iid, ipc_call_t *call, ddf_dev_t *dev) 90 { 91 assert(dev); 92 hc_device_t *hcd = dev_to_hcd(dev); 93 94 const bus_ops_t *ops = BUS_OPS_LOOKUP(hcd->bus->ops, interrupt); 95 assert(ops); 96 97 const uint32_t status = IPC_GET_ARG1(*call); 98 ops->interrupt(hcd->bus, status); 99 } 100 101 /** Worker for the HW interrupt replacement fibril. 102 */ 103 static int interrupt_polling(void *arg) 104 { 105 hc_device_t *hcd = arg; 62 106 assert(hcd); 63 64 hcd_set_implementation(hcd, NULL, NULL, NULL); 107 bus_t *bus = hcd->bus; 108 109 const bus_ops_t *interrupt_ops = BUS_OPS_LOOKUP(bus->ops, interrupt); 110 const bus_ops_t *status_ops = BUS_OPS_LOOKUP(bus->ops, status); 111 if (!interrupt_ops || !status_ops) 112 return ENOTSUP; 113 114 uint32_t status = 0; 115 while (status_ops->status(bus, &status) == EOK) { 116 interrupt_ops->interrupt(bus, status); 117 status = 0; 118 /* We should wait 1 frame - 1ms here, but this polling is a 119 * lame crutch anyway so don't hog the system. 10ms is still 120 * good enough for emergency mode */ 121 async_usleep(10000); 122 } 123 return EOK; 124 } 125 126 static inline void irq_code_clean(irq_code_t *code) 127 { 128 if (code) { 129 free(code->ranges); 130 free(code->cmds); 131 code->ranges = NULL; 132 code->cmds = NULL; 133 code->rangecount = 0; 134 code->cmdcount = 0; 135 } 136 } 137 138 /** Register interrupt handler 139 * 140 * @param[in] device Host controller DDF device 141 * @param[in] regs Register range 142 * @param[in] irq Interrupt number 143 * @paran[in] handler Interrupt handler 144 * @param[in] gen_irq_code IRQ code generator. 145 * 146 * @return IRQ capability handle on success. 147 * @return Negative error code. 148 */ 149 static int hcd_ddf_setup_interrupts(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res) 150 { 151 assert(hcd); 152 irq_code_t irq_code = {0}; 153 154 if (!hc_driver->irq_code_gen) 155 return ENOTSUP; 156 157 const int irq = hc_driver->irq_code_gen(&irq_code, hcd, hw_res); 158 if (irq < 0) { 159 usb_log_error("Failed to generate IRQ code: %s.\n", 160 str_error(irq)); 161 return irq; 162 } 163 164 /* Register handler to avoid interrupt lockup */ 165 const int irq_cap = register_interrupt_handler(hcd->ddf_dev, irq, irq_handler, &irq_code); 166 irq_code_clean(&irq_code); 167 if (irq_cap < 0) { 168 usb_log_error("Failed to register interrupt handler: %s.\n", 169 str_error(irq_cap)); 170 return irq_cap; 171 } 172 173 /* Enable interrupts */ 174 int ret = hcd_ddf_enable_interrupt(hcd, irq); 175 if (ret != EOK) { 176 usb_log_error("Failed to enable interrupts: %s.\n", 177 str_error(ret)); 178 unregister_interrupt_handler(hcd->ddf_dev, irq_cap); 179 return ret; 180 } 181 return irq_cap; 182 } 183 184 /** Initialize HC in memory of the driver. 185 * 186 * @param device DDF instance of the device to use 187 * @return Error code 188 * 189 * This function does all the preparatory work for hc and rh drivers: 190 * - gets device's hw resources 191 * - attempts to enable interrupts 192 * - registers interrupt handler 193 * - calls driver specific initialization 194 * - registers root hub 195 */ 196 int hc_dev_add(ddf_dev_t *device) 197 { 198 int ret = EOK; 199 assert(device); 200 201 if (!hc_driver->hc_add) { 202 usb_log_error("Driver '%s' does not support adding devices.", hc_driver->name); 203 return ENOTSUP; 204 } 205 206 ret = hcd_ddf_setup_hc(device, hc_driver->hc_device_size); 207 if (ret != EOK) { 208 usb_log_error("Failed to setup HC device.\n"); 209 return ret; 210 } 211 212 hc_device_t *hcd = dev_to_hcd(device); 213 214 hw_res_list_parsed_t hw_res; 215 ret = hcd_ddf_get_registers(hcd, &hw_res); 216 if (ret != EOK) { 217 usb_log_error("Failed to get register memory addresses " 218 "for `%s': %s.\n", ddf_dev_get_name(device), 219 str_error(ret)); 220 goto err_hcd; 221 } 222 223 ret = hc_driver->hc_add(hcd, &hw_res); 224 if (ret != EOK) { 225 usb_log_error("Failed to init HCD.\n"); 226 goto err_hw_res; 227 } 228 229 assert(hcd->bus); 230 231 /* Setup interrupts */ 232 hcd->irq_cap = hcd_ddf_setup_interrupts(hcd, &hw_res); 233 if (hcd->irq_cap >= 0) { 234 usb_log_debug("Hw interrupts enabled.\n"); 235 } 236 237 /* Claim the device from BIOS */ 238 if (hc_driver->claim) 239 ret = hc_driver->claim(hcd); 240 if (ret != EOK) { 241 usb_log_error("Failed to claim `%s' for `%s': %s", 242 ddf_dev_get_name(device), hc_driver->name, str_error(ret)); 243 goto err_irq; 244 } 245 246 /* Start hw */ 247 if (hc_driver->start) 248 ret = hc_driver->start(hcd); 249 if (ret != EOK) { 250 usb_log_error("Failed to start HCD: %s.\n", str_error(ret)); 251 goto err_irq; 252 } 253 254 const bus_ops_t *ops = BUS_OPS_LOOKUP(hcd->bus->ops, status); 255 256 /* Need working irq replacement to setup root hub */ 257 if (hcd->irq_cap < 0 && ops) { 258 hcd->polling_fibril = fibril_create(interrupt_polling, hcd->bus); 259 if (!hcd->polling_fibril) { 260 usb_log_error("Failed to create polling fibril\n"); 261 ret = ENOMEM; 262 goto err_started; 263 } 264 fibril_add_ready(hcd->polling_fibril); 265 usb_log_warning("Failed to enable interrupts: %s." 266 " Falling back to polling.\n", str_error(hcd->irq_cap)); 267 } 268 269 /* 270 * Creating root hub registers a new USB device so HC 271 * needs to be ready at this time. 272 */ 273 if (hc_driver->setup_root_hub) 274 ret = hc_driver->setup_root_hub(hcd); 275 if (ret != EOK) { 276 usb_log_error("Failed to setup HC root hub: %s.\n", 277 str_error(ret)); 278 goto err_polling; 279 } 280 281 usb_log_info("Controlling new `%s' device `%s'.\n", 282 hc_driver->name, ddf_dev_get_name(device)); 283 return EOK; 284 285 err_polling: 286 // TODO: Stop the polling fibril (refactor the interrupt_polling func) 287 // 288 err_started: 289 if (hc_driver->stop) 290 hc_driver->stop(hcd); 291 err_irq: 292 unregister_interrupt_handler(device, hcd->irq_cap); 293 if (hc_driver->hc_remove) 294 hc_driver->hc_remove(hcd); 295 err_hw_res: 296 hw_res_list_parsed_clean(&hw_res); 297 err_hcd: 298 hcd_ddf_clean_hc(hcd); 299 return ret; 300 } 301 302 int hc_dev_remove(ddf_dev_t *dev) 303 { 304 int err; 305 hc_device_t *hcd = dev_to_hcd(dev); 306 307 if (hc_driver->stop) 308 if ((err = hc_driver->stop(hcd))) 309 return err; 310 311 unregister_interrupt_handler(dev, hcd->irq_cap); 312 313 if (hc_driver->hc_remove) 314 if ((err = hc_driver->hc_remove(hcd))) 315 return err; 316 317 hcd_ddf_clean_hc(hcd); 318 319 // TODO probably not complete 320 321 return EOK; 322 } 323 324 int hc_dev_gone(ddf_dev_t *dev) 325 { 326 int err = ENOTSUP; 327 hc_device_t *hcd = dev_to_hcd(dev); 328 329 if (hc_driver->hc_gone) 330 err = hc_driver->hc_gone(hcd); 331 332 hcd_ddf_clean_hc(hcd); 333 334 return err; 335 } 336 337 int hc_fun_online(ddf_fun_t *fun) 338 { 339 assert(fun); 340 341 device_t *dev = ddf_fun_data_get(fun); 342 assert(dev); 343 344 usb_log_info("Device(%d): Requested to be brought online.", dev->address); 345 return bus_device_online(dev); 346 } 347 348 int hc_fun_offline(ddf_fun_t *fun) 349 { 350 assert(fun); 351 352 device_t *dev = ddf_fun_data_get(fun); 353 assert(dev); 354 355 usb_log_info("Device(%d): Requested to be taken offline.", dev->address); 356 return bus_device_offline(dev); 65 357 } 66 358 … … 72 364 * @return Max packet size for EP 0 73 365 */ 74 int hcd_get_ep0_max_packet_size(uint16_t *mps, hcd_t *hcd, device_t *dev)366 int hcd_get_ep0_max_packet_size(uint16_t *mps, bus_t *bus, device_t *dev) 75 367 { 76 368 assert(mps); … … 97 389 98 390 usb_log_debug("Requesting first 8B of device descriptor to determine MPS."); 99 ssize_t got = hcd_send_batch_sync(hcd,dev, control_ep, USB_DIRECTION_IN,391 ssize_t got = bus_device_send_batch_sync(dev, control_ep, USB_DIRECTION_IN, 100 392 (char *) &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8, 101 393 "read first 8 bytes of dev descriptor"); … … 156 448 * 157 449 */ 158 statictoggle_reset_mode_t hcd_get_request_toggle_reset_mode(450 toggle_reset_mode_t hcd_get_request_toggle_reset_mode( 159 451 const usb_device_request_setup_packet_t *request) 160 452 { … … 186 478 } 187 479 188 /** Prepare generic usb_transfer_batch and schedule it.189 * @param hcd Host controller driver.190 * @param target address and endpoint number.191 * @param setup_data Data to use in setup stage (Control communication type)192 * @param in Callback for device to host communication.193 * @param out Callback for host to device communication.194 * @param arg Callback parameter.195 * @param name Communication identifier (for nicer output).196 * @return Error code.197 */198 int hcd_send_batch(hcd_t *hcd, device_t *device, usb_target_t target,199 usb_direction_t direction, char *data, size_t size, uint64_t setup_data,200 usbhc_iface_transfer_callback_t on_complete, void *arg, const char *name)201 {202 assert(hcd);203 assert(device->address == target.address);204 205 if (!hcd->ops.schedule) {206 usb_log_error("HCD does not implement scheduler.\n");207 return ENOTSUP;208 }209 210 endpoint_t *ep = bus_find_endpoint(device, target, direction);211 if (ep == NULL) {212 usb_log_error("Endpoint(%d:%d) not registered for %s.\n",213 device->address, target.endpoint, name);214 return ENOENT;215 }216 217 // TODO cut here aka provide helper to call with instance of endpoint_t in hand218 assert(ep->device == device);219 220 usb_log_debug2("%s %d:%d %zu(%zu).\n",221 name, target.address, target.endpoint, size, ep->max_packet_size);222 223 const size_t bw = endpoint_count_bw(ep, size);224 /* Check if we have enough bandwidth reserved */225 if (ep->bandwidth < bw) {226 usb_log_error("Endpoint(%d:%d) %s needs %zu bw "227 "but only %zu is reserved.\n",228 device->address, ep->endpoint, name, bw, ep->bandwidth);229 return ENOSPC;230 }231 232 usb_transfer_batch_t *batch = usb_transfer_batch_create(ep);233 if (!batch) {234 usb_log_error("Failed to create transfer batch.\n");235 return ENOMEM;236 }237 238 batch->target = target;239 batch->buffer = data;240 batch->buffer_size = size;241 batch->setup.packed = setup_data;242 batch->dir = direction;243 batch->on_complete = on_complete;244 batch->on_complete_data = arg;245 246 /* Check for commands that reset toggle bit */247 if (ep->transfer_type == USB_TRANSFER_CONTROL)248 batch->toggle_reset_mode249 = hcd_get_request_toggle_reset_mode(&batch->setup.packet);250 251 const int ret = hcd->ops.schedule(hcd, batch);252 if (ret != EOK) {253 usb_log_warning("Batch %p failed to schedule: %s", batch, str_error(ret));254 usb_transfer_batch_destroy(batch);255 }256 257 /* Drop our own reference to ep. */258 endpoint_del_ref(ep);259 260 return ret;261 }262 263 typedef struct {264 fibril_mutex_t done_mtx;265 fibril_condvar_t done_cv;266 unsigned done;267 268 size_t transfered_size;269 int error;270 } sync_data_t;271 272 static int sync_transfer_complete(void *arg, int error, size_t transfered_size)273 {274 sync_data_t *d = arg;275 assert(d);276 d->transfered_size = transfered_size;277 d->error = error;278 fibril_mutex_lock(&d->done_mtx);279 d->done = 1;280 fibril_condvar_broadcast(&d->done_cv);281 fibril_mutex_unlock(&d->done_mtx);282 return EOK;283 }284 285 ssize_t hcd_send_batch_sync(hcd_t *hcd, device_t *device, usb_target_t target,286 usb_direction_t direction, char *data, size_t size, uint64_t setup_data,287 const char *name)288 {289 assert(hcd);290 sync_data_t sd = { .done = 0 };291 fibril_mutex_initialize(&sd.done_mtx);292 fibril_condvar_initialize(&sd.done_cv);293 294 const int ret = hcd_send_batch(hcd, device, target, direction,295 data, size, setup_data,296 sync_transfer_complete, &sd, name);297 if (ret != EOK)298 return ret;299 300 fibril_mutex_lock(&sd.done_mtx);301 while (!sd.done)302 fibril_condvar_wait(&sd.done_cv, &sd.done_mtx);303 fibril_mutex_unlock(&sd.done_mtx);304 305 return (sd.error == EOK)306 ? (ssize_t) sd.transfered_size307 : (ssize_t) sd.error;308 }309 310 480 311 481 /**
Note:
See TracChangeset
for help on using the changeset viewer.