Changeset d3086873 in mainline


Ignore:
Timestamp:
2017-11-20T17:23:12Z (6 years ago)
Author:
Salmelu <salmelu@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
6b433a8
Parents:
888238e9
git-author:
Salmelu <salmelu@…> (2017-11-20 17:22:34)
git-committer:
Salmelu <salmelu@…> (2017-11-20 17:23:12)
Message:

First isoch transfers - transfers.c part

Location:
uspace/drv/bus/usb/xhci
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/xhci/endpoint.h

    r888238e9 rd3086873  
    8989        /** Maximum number of bursts within an interval that this endpoint supports */
    9090        uint8_t mult;
     91
     92        /** The maximum size of an isochronous transfer and therefore the size of buffers */
     93        size_t isoch_max_size;
     94
     95        /** Isochronous scheduled transfers with respective buffers */
     96        #define XHCI_ISOCH_BUFFER_COUNT 4
     97        xhci_isoch_transfer_t* isoch_transfers[XHCI_ISOCH_BUFFER_COUNT];
     98
     99        /** Indices to transfers */
     100        size_t isoch_dequeue, isoch_enqueue;
     101
     102        /** Are isochronous transfers started? */
     103        bool isoch_started;
    91104} xhci_endpoint_t;
    92105
  • uspace/drv/bus/usb/xhci/hw_struct/trb.h

    r888238e9 rd3086873  
    146146#define TRB_CTRL_SET_TRT(trb, val) \
    147147        xhci_dword_set_bits(&(trb).control, val, 17, 16)
     148
     149#define TRB_CTRL_SET_TBC(trb, val) \
     150        xhci_dword_set_bits(&(trb).control, val, 8, 7)
     151#define TRB_CTRL_SET_TLBPC(trb, val) \
     152        xhci_dword_set_bits(&(trb).control, val, 19, 16)
     153#define TRB_CTRL_SET_SIA(trb, val) \
     154        xhci_dword_set_bits(&(trb).control, val, 31, 31)
    148155
    149156/**
  • uspace/drv/bus/usb/xhci/transfers.c

    r888238e9 rd3086873  
    232232}
    233233
    234 static int schedule_isochronous(xhci_hc_t* hc, xhci_transfer_t* transfer)
    235 {
    236         /* TODO: Implement me. */
    237         usb_log_error("Isochronous transfers are not yet implemented!");
    238         return ENOTSUP;
     234static xhci_isoch_transfer_t* isoch_transfer_get_enqueue(xhci_endpoint_t *ep) {
     235        if ((ep->isoch_enqueue % XHCI_ISOCH_BUFFER_COUNT) == ep->isoch_dequeue) {
     236                /* None ready */
     237                return NULL;
     238        }
     239        xhci_isoch_transfer_t *isoch_transfer = ep->isoch_transfers[ep->isoch_enqueue];
     240        ep->isoch_enqueue = (ep->isoch_enqueue + 1) % XHCI_ISOCH_BUFFER_COUNT;
     241        return isoch_transfer;
     242}
     243
     244static xhci_isoch_transfer_t* isoch_transfer_get_dequeue(xhci_endpoint_t *ep) {
     245        xhci_isoch_transfer_t *isoch_transfer = ep->isoch_transfers[ep->isoch_dequeue];
     246        ep->isoch_dequeue = (ep->isoch_dequeue + 1) % XHCI_ISOCH_BUFFER_COUNT;
     247        return isoch_transfer;
     248}
     249
     250static int schedule_isochronous_trb(xhci_trb_ring_t *ring, xhci_endpoint_t *ep, xhci_trb_t *trb,
     251        const size_t len, uintptr_t *trb_phys)
     252{
     253        TRB_CTRL_SET_XFER_LEN(*trb, len);
     254        // FIXME: TD size 4.11.2.4 (there is no next TRB, so 0?)
     255        TRB_CTRL_SET_TD_SIZE(*trb, 0);
     256        TRB_CTRL_SET_IOC(*trb, 1);
     257        TRB_CTRL_SET_TRB_TYPE(*trb, XHCI_TRB_TYPE_ISOCH);
     258
     259        // see 4.14.1 and 4.11.2.3 for the explanation, how to calculate those
     260        size_t tdpc = len / 1024 + ((len % 1024) ? 1 : 0);
     261        size_t tbc = tdpc / (ep->max_burst + 1);
     262        if(!tdpc % (ep->max_burst + 1)) --tbc;
     263        size_t bsp = tdpc % (ep->max_burst + 1);
     264        size_t tlbpc = (bsp ? bsp - 1 : ep->max_burst);
     265
     266        TRB_CTRL_SET_TBC(*trb, tbc);
     267        TRB_CTRL_SET_TLBPC(*trb, tlbpc);
     268
     269        // FIXME: do we want this? 6.4.1.3, p 366 (also possibly frame id?)
     270        TRB_CTRL_SET_SIA(*trb, 1);
     271
     272        return xhci_trb_ring_enqueue(ring, trb, trb_phys);
     273}
     274
     275static int schedule_isochronous_out(xhci_hc_t* hc, xhci_transfer_t* transfer, xhci_endpoint_t *xhci_ep,
     276        xhci_device_t *xhci_dev)
     277{
     278        xhci_trb_t trb;
     279        xhci_trb_clean(&trb);
     280
     281        // FIXME add some locks
     282        xhci_isoch_transfer_t *isoch_transfer = isoch_transfer_get_enqueue(xhci_ep);
     283        while (!isoch_transfer) {
     284                // FIXME: add condvar sleep;
     285        }
     286
     287        isoch_transfer->size = transfer->batch.buffer_size;
     288        if (isoch_transfer->size > 0) {
     289                memcpy(isoch_transfer->data_virt, transfer->batch.buffer, isoch_transfer->size);
     290        }
     291
     292        trb.parameter = isoch_transfer->data_phys;
     293
     294        xhci_trb_ring_t *ring = get_ring(hc, transfer);
     295        int err = schedule_isochronous_trb(ring, xhci_ep, &trb, isoch_transfer->size,
     296                &isoch_transfer->interrupt_trb_phys);
     297        if (err) {
     298                return err;
     299        }
     300
     301        /* If not yet started, start the isochronous endpoint transfers - after buffer count - 1 writes */
     302        /* The -2 is there because of the enqueue != dequeue check. The buffer must have at least 2 transfers. */
     303        if (xhci_ep->isoch_enqueue == XHCI_ISOCH_BUFFER_COUNT - 2 && !xhci_ep->isoch_started) {
     304                const uint8_t slot_id = xhci_dev->slot_id;
     305                const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
     306                err = hc_ring_doorbell(hc, slot_id, target);
     307                if (err) {
     308                        return err;
     309                }
     310                xhci_ep->isoch_started = true;
     311        }
     312
     313        /* Isochronous transfers don't handle errors, they skip them all. */
     314        transfer->batch.error = EOK;
     315        transfer->batch.transfered_size = transfer->batch.buffer_size;
     316        usb_transfer_batch_finish(&transfer->batch);
     317        return EOK;
     318}
     319
     320static int schedule_isochronous_in(xhci_hc_t* hc, xhci_transfer_t* transfer, xhci_endpoint_t *xhci_ep,
     321        xhci_device_t *xhci_dev)
     322{
     323        /* If not yet started, start the isochronous endpoint transfers - before first read */
     324        if (!xhci_ep->isoch_started) {
     325                const uint8_t slot_id = xhci_dev->slot_id;
     326                const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
     327                int err = hc_ring_doorbell(hc, slot_id, target);
     328                if (err) {
     329                        return err;
     330                }
     331                xhci_ep->isoch_started = true;
     332        }
     333
     334        // FIXME add some locks
     335        xhci_isoch_transfer_t *isoch_transfer = isoch_transfer_get_enqueue(xhci_ep);
     336        while(!isoch_transfer) {
     337                // FIXME: add condvar sleep;
     338        }
     339
     340        isoch_transfer->size = transfer->batch.buffer_size;
     341        if (transfer->batch.buffer_size <= isoch_transfer->size) {
     342                if (transfer->batch.buffer_size > 0) {
     343                        memcpy(transfer->batch.buffer, isoch_transfer->data_virt, transfer->batch.buffer_size);
     344                }
     345                if (transfer->batch.buffer_size < isoch_transfer->size) {
     346                        // FIXME: somehow notify that buffer was too small, probably batch error code
     347                }
     348                transfer->batch.transfered_size = transfer->batch.buffer_size;
     349        }
     350        else {
     351                memcpy(transfer->batch.buffer, isoch_transfer->data_virt, isoch_transfer->size);
     352                transfer->batch.transfered_size = isoch_transfer->size;
     353        }
     354
     355        // Clear and requeue the transfer with new TRB
     356        xhci_trb_t trb;
     357        xhci_trb_clean(&trb);
     358
     359        trb.parameter = isoch_transfer->data_phys;
     360        isoch_transfer->size = xhci_ep->isoch_max_size;
     361
     362        xhci_trb_ring_t *ring = get_ring(hc, transfer);
     363        int err = schedule_isochronous_trb(ring, xhci_ep, &trb, isoch_transfer->size,
     364                &isoch_transfer->interrupt_trb_phys);
     365        if (err) {
     366                return err;
     367        }
     368
     369        /* Isochronous transfers don't handle errors, they skip them all. */
     370        transfer->batch.error = EOK;
     371        usb_transfer_batch_finish(&transfer->batch);
     372        return EOK;
     373}
     374
     375static int schedule_isochronous(xhci_hc_t* hc, xhci_transfer_t* transfer, xhci_endpoint_t *xhci_ep,
     376        xhci_device_t *xhci_dev)
     377{
     378        if (transfer->batch.buffer_size > xhci_ep->isoch_max_size) {
     379                usb_log_error("Cannot schedule an oversized isochronous transfer.");
     380                return EINVAL;
     381        }
     382
     383        if (xhci_ep->base.direction == USB_DIRECTION_OUT) {
     384                return schedule_isochronous_out(hc, transfer, xhci_ep, xhci_dev);
     385        }
     386        else {
     387                return schedule_isochronous_in(hc, transfer, xhci_ep, xhci_dev);
     388        }
     389}
     390
     391static int handle_isochronous_transfer_event(xhci_hc_t *hc, xhci_trb_t *trb, xhci_endpoint_t *ep) {
     392        fibril_mutex_lock(&ep->base.guard);
     393
     394        xhci_isoch_transfer_t *isoch_transfer = isoch_transfer_get_dequeue(ep);
     395        if (isoch_transfer->interrupt_trb_phys != trb->parameter) {
     396                usb_log_error("Non-matching trb to isochronous transfer, skipping.");
     397                // FIXME: what to do? probably just kill the whole endpoint
     398                return ENOENT;
     399        }
     400
     401        if (ep->base.direction == USB_DIRECTION_IN) {
     402                // We may have received less data, that's fine
     403                isoch_transfer->size -= TRB_TRANSFER_LENGTH(*trb);
     404        }
     405
     406        // TODO: Send notify to waiting fibrils in case they are waiting here
     407
     408        fibril_mutex_unlock(&ep->base.guard);
     409        return EOK;
    239410}
    240411
     
    262433        ep->ring.dequeue = addr;
    263434
     435        if (ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) {
     436                return handle_isochronous_transfer_event(hc, trb, ep);
     437        }
     438
    264439        fibril_mutex_lock(&ep->base.guard);
    265440        usb_transfer_batch_t *batch = ep->base.active_batch;
     
    291466static const transfer_handler transfer_handlers[] = {
    292467        [USB_TRANSFER_CONTROL] = schedule_control,
    293         [USB_TRANSFER_ISOCHRONOUS] = schedule_isochronous,
     468        [USB_TRANSFER_ISOCHRONOUS] = NULL,
    294469        [USB_TRANSFER_BULK] = schedule_bulk,
    295470        [USB_TRANSFER_INTERRUPT] = schedule_interrupt,
     
    317492        }
    318493
     494        // Isochronous transfer needs to be handled differently
     495        if(batch->ep->transfer_type == USB_TRANSFER_ISOCHRONOUS) {
     496                return schedule_isochronous(hc, transfer, xhci_ep, xhci_dev);
     497        }
     498
    319499        const usb_transfer_type_t type = batch->ep->transfer_type;
    320500        assert(type >= 0 && type < ARRAY_SIZE(transfer_handlers));
  • uspace/drv/bus/usb/xhci/transfers.h

    r888238e9 rd3086873  
    5555} xhci_transfer_t;
    5656
     57typedef struct {
     58        /* Used buffer size */
     59        uint64_t size;
     60        /* Pointer to data in virt memory */
     61        void *data_virt;
     62        /* Physical address of the buffer */
     63        uintptr_t data_phys;
     64        /* Physical address of enqueued TRB */
     65        uintptr_t interrupt_trb_phys;
     66} xhci_isoch_transfer_t;
     67
    5768xhci_transfer_t* xhci_transfer_create(endpoint_t *);
    5869int xhci_transfer_schedule(xhci_hc_t *, usb_transfer_batch_t *);
Note: See TracChangeset for help on using the changeset viewer.