Changeset 5fd9c30 in mainline for uspace/drv/bus/usb/xhci/transfers.c


Ignore:
Timestamp:
2017-10-21T20:52:56Z (7 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
766043c
Parents:
74b852b
Message:

usbhost refactoring: let transfer_batch be initialized by bus

Currently makes older HCs fail, work in progress.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/xhci/transfers.c

    r74b852b r5fd9c30  
    108108}
    109109
    110 xhci_transfer_t* xhci_transfer_alloc(usb_transfer_batch_t* batch) {
    111         xhci_transfer_t* transfer = malloc(sizeof(xhci_transfer_t));
     110xhci_transfer_t* xhci_transfer_create(endpoint_t* ep) {
     111        xhci_transfer_t* transfer = calloc(1, sizeof(xhci_transfer_t));
    112112        if (!transfer)
    113113                return NULL;
    114114
    115         memset(transfer, 0, sizeof(xhci_transfer_t));
    116         transfer->batch = batch;
     115        usb_transfer_batch_init(&transfer->batch, ep);
     116
    117117        link_initialize(&transfer->link);
    118         transfer->hc_buffer = batch->buffer_size > 0 ? malloc32(batch->buffer_size) : NULL;
    119118
    120119        return transfer;
    121120}
    122121
    123 void xhci_transfer_fini(xhci_transfer_t* transfer) {
    124         if (transfer) {
    125                 if (transfer->batch->buffer_size > 0)
    126                         free32(transfer->hc_buffer);
    127 
    128                 usb_transfer_batch_destroy(transfer->batch);
    129 
    130                 free(transfer);
    131         }
    132 }
    133 
    134 int xhci_schedule_control_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch)
    135 {
    136         if (!batch->setup_size) {
    137                 usb_log_error("Missing setup packet for the control transfer.");
    138                 return EINVAL;
    139         }
    140         if (batch->ep->transfer_type != USB_TRANSFER_CONTROL) {
    141                 /* This method only works for control transfers. */
    142                 usb_log_error("Attempted to schedule a control transfer to non control endpoint.");
    143                 return EINVAL;
    144         }
    145 
    146         usb_device_request_setup_packet_t* setup =
    147                 (usb_device_request_setup_packet_t*) batch->setup_buffer;
    148 
    149         /* For the TRB formats, see xHCI specification 6.4.1.2 */
    150         xhci_transfer_t *transfer = xhci_transfer_alloc(batch);
    151 
    152         if (!transfer->direction) {
    153                 // Sending stuff from host to device, we need to copy the actual data.
    154                 memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
    155         }
    156 
    157         xhci_trb_t trbs[3];
    158         int trbs_used = 0;
     122void xhci_transfer_destroy(xhci_transfer_t* transfer)
     123{
     124        assert(transfer);
     125
     126        if (transfer->hc_buffer)
     127                free32(transfer->hc_buffer);
     128
     129        free(transfer);
     130}
     131
     132static xhci_trb_ring_t *get_ring(xhci_hc_t *hc, xhci_transfer_t *transfer)
     133{
     134        xhci_endpoint_t *xhci_ep = xhci_endpoint_get(transfer->batch.ep);
     135        uint8_t slot_id = xhci_ep->device->slot_id;
     136
     137        xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[transfer->batch.ep->target.endpoint];
     138        assert(ring);
     139        return ring;
     140}
     141
     142static int schedule_control(xhci_hc_t* hc, xhci_transfer_t* transfer)
     143{
     144        usb_transfer_batch_t *batch = &transfer->batch;
     145        xhci_trb_ring_t *ring = get_ring(hc, transfer);
     146        xhci_endpoint_t *xhci_ep = xhci_endpoint_get(transfer->batch.ep);
     147
     148        usb_device_request_setup_packet_t* setup = &batch->setup.packet;
     149
     150        xhci_trb_t trbs[3];
     151        int trbs_used = 0;
    159152
    160153        xhci_trb_t *trb_setup = trbs + trbs_used++;
     
    178171        xhci_trb_t *trb_data = NULL;
    179172        if (setup->length > 0) {
    180                 trb_data = trbs + trbs_used++;
    181                 xhci_trb_clean(trb_data);
     173                trb_data = trbs + trbs_used++;
     174                xhci_trb_clean(trb_data);
    182175
    183176                trb_data->parameter = addr_to_phys(transfer->hc_buffer);
     
    191184                TRB_CTRL_SET_TRB_TYPE(*trb_data, XHCI_TRB_TYPE_DATA_STAGE);
    192185
    193                 transfer->direction = REQUEST_TYPE_IS_DEVICE_TO_HOST(setup->request_type)
     186                int stage_dir = REQUEST_TYPE_IS_DEVICE_TO_HOST(setup->request_type)
    194187                                        ? STAGE_IN : STAGE_OUT;
    195                 TRB_CTRL_SET_DIR(*trb_data, transfer->direction);
     188                TRB_CTRL_SET_DIR(*trb_data, stage_dir);
    196189        }
    197190
     
    207200        TRB_CTRL_SET_DIR(*trb_status, get_status_direction_flag(trb_setup, setup->request_type, setup->length));
    208201
    209         xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
    210         uint8_t slot_id = xhci_ep->device->slot_id;
    211         xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[batch->ep->target.endpoint];
    212 
    213         int err = xhci_trb_ring_enqueue_multiple(ring, trbs, trbs_used, &transfer->interrupt_trb_phys);
    214         if (err != EOK)
    215                 return err;
    216 
    217         list_append(&transfer->link, &hc->transfers);
    218 
    219202        // Issue a Configure Endpoint command, if needed.
    220203        if (configure_endpoint_needed(setup)) {
    221                 // TODO: figure out the best time to issue this command
    222                 // FIXME: on fail, we need to "cancel" in-flight TRBs and remove transfer from the list
    223                 err = xhci_device_configure(xhci_ep->device, hc);
    224                 if (err != EOK)
    225                         return err;
    226         }
    227 
    228         const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbels start at 1 */
    229         return hc_ring_doorbell(hc, slot_id, target);
    230 }
    231 
    232 int xhci_schedule_bulk_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch)
    233 {
    234         if (batch->setup_size) {
    235                 usb_log_warning("Setup packet present for a bulk transfer. Ignored.");
    236         }
    237         if (batch->ep->transfer_type != USB_TRANSFER_BULK) {
    238                 /* This method only works for bulk transfers. */
    239                 usb_log_error("Attempted to schedule a bulk transfer to non bulk endpoint.");
    240                 return EINVAL;
    241         }
    242 
    243         xhci_transfer_t *transfer = xhci_transfer_alloc(batch);
    244         if (!transfer->direction) {
    245                 // Sending stuff from host to device, we need to copy the actual data.
    246                 memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
    247         }
    248 
     204                const int err = xhci_device_configure(xhci_ep->device, hc);
     205                if (err)
     206                        return err;
     207        }
     208
     209        return xhci_trb_ring_enqueue_multiple(ring, trbs, trbs_used, &transfer->interrupt_trb_phys);
     210}
     211
     212static int schedule_bulk(xhci_hc_t* hc, xhci_transfer_t *transfer)
     213{
    249214        xhci_trb_t trb;
    250215        xhci_trb_clean(&trb);
     
    252217
    253218        // data size (sent for OUT, or buffer size)
    254         TRB_CTRL_SET_XFER_LEN(trb, batch->buffer_size);
     219        TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_size);
    255220        // FIXME: TD size 4.11.2.4
    256221        TRB_CTRL_SET_TD_SIZE(trb, 1);
     
    261226        TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
    262227
    263         xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
     228        xhci_endpoint_t *xhci_ep = xhci_endpoint_get(transfer->batch.ep);
    264229        uint8_t slot_id = xhci_ep->device->slot_id;
    265         xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[batch->ep->target.endpoint];
    266 
    267         xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
    268         list_append(&transfer->link, &hc->transfers);
    269 
    270         // TODO: target = endpoint | stream_id << 16
    271         const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
    272         return hc_ring_doorbell(hc, slot_id, target);
    273 }
    274 
    275 int xhci_schedule_interrupt_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch)
    276 {
    277         if (batch->setup_size) {
    278                 usb_log_warning("Setup packet present for a interrupt transfer. Ignored.");
    279         }
    280         if (batch->ep->transfer_type != USB_TRANSFER_INTERRUPT) {
    281                 /* This method only works for interrupt transfers. */
    282                 usb_log_error("Attempted to schedule a interrupt transfer to non interrupt endpoint.");
    283                 return EINVAL;
    284         }
    285 
    286         xhci_transfer_t *transfer = xhci_transfer_alloc(batch);
    287         if (!transfer->direction) {
    288                 // Sending stuff from host to device, we need to copy the actual data.
    289                 memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
    290         }
    291 
     230        xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[transfer->batch.ep->target.endpoint];
     231
     232        return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
     233}
     234
     235static int schedule_interrupt(xhci_hc_t* hc, xhci_transfer_t* transfer)
     236{
    292237        xhci_trb_t trb;
    293238        xhci_trb_clean(&trb);
     
    295240
    296241        // data size (sent for OUT, or buffer size)
    297         TRB_CTRL_SET_XFER_LEN(trb, batch->buffer_size);
     242        TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_size);
    298243        // FIXME: TD size 4.11.2.4
    299244        TRB_CTRL_SET_TD_SIZE(trb, 1);
     
    304249        TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
    305250
    306         xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
     251        xhci_endpoint_t *xhci_ep = xhci_endpoint_get(transfer->batch.ep);
    307252        uint8_t slot_id = xhci_ep->device->slot_id;
    308         xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[batch->ep->target.endpoint];
    309 
    310         xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
    311         list_append(&transfer->link, &hc->transfers);
    312 
    313         const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
    314         return hc_ring_doorbell(hc, slot_id, target);
    315 }
    316 
    317 int xhci_schedule_isochronous_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch)
    318 {
    319         if (batch->setup_size) {
    320                 usb_log_warning("Setup packet present for a isochronous transfer. Ignored.");
    321         }
    322         if (batch->ep->transfer_type != USB_TRANSFER_ISOCHRONOUS) {
    323                 /* This method only works for isochronous transfers. */
    324                 usb_log_error("Attempted to schedule a isochronous transfer to non isochronous endpoint.");
    325                 return EINVAL;
    326         }
    327 
    328         /* TODO: Implement me. */
    329         usb_log_error("Isochronous transfers are not yet implemented!");
    330         return ENOTSUP;
     253        xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[transfer->batch.ep->target.endpoint];
     254
     255        return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
     256}
     257
     258static int schedule_isochronous(xhci_hc_t* hc, xhci_transfer_t* transfer)
     259{
     260        /* TODO: Implement me. */
     261        usb_log_error("Isochronous transfers are not yet implemented!");
     262        return ENOTSUP;
    331263}
    332264
     
    352284
    353285        list_remove(transfer_link);
    354         usb_transfer_batch_t *batch = transfer->batch;
    355 
    356         const int err = (TRB_COMPLETION_CODE(*trb) == XHCI_TRBC_SUCCESS) ? EOK : ENAK;
    357         const size_t size = batch->buffer_size - TRB_TRANSFER_LENGTH(*trb);
    358         usb_transfer_batch_finish_error(batch, transfer->hc_buffer, size, err);
    359         xhci_transfer_fini(transfer);
     286        usb_transfer_batch_t *batch = &transfer->batch;
     287
     288        batch->error = (TRB_COMPLETION_CODE(*trb) == XHCI_TRBC_SUCCESS) ? EOK : ENAK;
     289        batch->transfered_size = batch->buffer_size - TRB_TRANSFER_LENGTH(*trb);
     290
     291        if (batch->dir == USB_DIRECTION_IN) {
     292                assert(batch->buffer);
     293                assert(batch->transfered_size <= batch->buffer_size);
     294                memcpy(batch->buffer, transfer->hc_buffer, batch->transfered_size);
     295        }
     296
     297        usb_transfer_batch_finish(batch);
    360298        return EOK;
    361299}
     300
     301typedef int (*transfer_handler)(xhci_hc_t *, xhci_transfer_t *);
     302
     303static const transfer_handler transfer_handlers[] = {
     304        [USB_TRANSFER_CONTROL] = schedule_control,
     305        [USB_TRANSFER_ISOCHRONOUS] = schedule_isochronous,
     306        [USB_TRANSFER_BULK] = schedule_bulk,
     307        [USB_TRANSFER_INTERRUPT] = schedule_interrupt,
     308};
     309
     310int xhci_transfer_schedule(xhci_hc_t *hc, usb_transfer_batch_t *batch)
     311{
     312        assert(hc);
     313
     314        xhci_transfer_t *transfer = xhci_transfer_from_batch(batch);
     315        xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
     316        uint8_t slot_id = xhci_ep->device->slot_id;
     317
     318        assert(xhci_ep);
     319        assert(slot_id);
     320
     321        const usb_transfer_type_t type = batch->ep->transfer_type;
     322        assert(type >= 0 && type < ARRAY_SIZE(transfer_handlers));
     323        assert(transfer_handlers[type]);
     324
     325        if (batch->buffer_size > 0) {
     326                transfer->hc_buffer = malloc32(batch->buffer_size);
     327        }
     328
     329        if (batch->dir != USB_DIRECTION_IN) {
     330                // Sending stuff from host to device, we need to copy the actual data.
     331                memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
     332        }
     333
     334        const int err = transfer_handlers[batch->ep->transfer_type](hc, transfer);
     335        if (err)
     336                return err;
     337
     338        list_append(&transfer->link, &hc->transfers);
     339
     340        const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
     341        return hc_ring_doorbell(hc, slot_id, target);
     342}
Note: See TracChangeset for help on using the changeset viewer.