Changeset 708d8fcd in mainline


Ignore:
Timestamp:
2018-01-10T00:46:29Z (6 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
f92f6b1
Parents:
4793023
git-author:
Ondřej Hlavatý <aearsis@…> (2018-01-09 19:26:55)
git-committer:
Ondřej Hlavatý <aearsis@…> (2018-01-10 00:46:29)
Message:

xhci: rewritten isochronous transfers

There was a fundamental problem with relying on hardware to send
RING_OVERRUN/UNDERRUN events, which QEMU (and possibly others) do not
send. That resulted in not knowing if the transfer is still on schedule,
and having to ring the doorbell every time. That is not feasible,
because then the transfer can be more frequent than it should be.
Furthermore, it ignored the fact that isochronous TRBs are to be
scheduled not too late, but also not too soon (see 4.11.2.5 of the xHCI
spec).

Now, scheduling the TRBs to hardware is called feeding, and can be
delayed by setting a timer. Ring overruns/underruns are detected also at
the end of handling an event.

Location:
uspace
Files:
2 added
12 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/xhci/Makefile

    r4793023 r708d8fcd  
    3939        endpoint.c \
    4040        hc.c \
     41        isoch.c \
    4142        main.c \
    4243        rh.c \
  • uspace/drv/bus/usb/xhci/bus.c

    r4793023 r708d8fcd  
    5858static endpoint_t *endpoint_create(device_t *, const usb_endpoint_descriptors_t *);
    5959
    60 /** Ops receive generic bus_t pointer. */
    61 static inline xhci_bus_t *bus_to_xhci_bus(bus_t *bus_base)
    62 {
    63         assert(bus_base);
    64         return (xhci_bus_t *) bus_base;
    65 }
    66 
    6760/**
    6861 * Assign address and control endpoint to a new XHCI device. Once this function
  • uspace/drv/bus/usb/xhci/bus.h

    r4793023 r708d8fcd  
    5959int xhci_bus_remove_device(xhci_bus_t *, device_t *);
    6060
     61static inline xhci_bus_t *bus_to_xhci_bus(bus_t *bus_base)
     62{
     63        assert(bus_base);
     64        return (xhci_bus_t *) bus_base;
     65}
     66
    6167#endif
    6268/**
  • uspace/drv/bus/usb/xhci/endpoint.c

    r4793023 r708d8fcd  
    8484         */
    8585        if (dev->speed >= USB_SPEED_HIGH || ep->transfer_type != USB_TRANSFER_INTERRUPT) {
    86                 xhci_ep->interval = desc->endpoint.poll_interval;
     86                xhci_ep->interval = 1 << (xhci_ep->interval - 1);
    8787        }
    8888
     
    9292        }
    9393
    94         if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) {
    95                 xhci_ep->isoch->max_size = desc->companion.bytes_per_interval
    96                         ? desc->companion.bytes_per_interval
    97                         : ep->max_transfer_size;
    98                 /* Technically there could be superspeed plus too. */
    99 
    100                 /* Allocate and setup isochronous-specific structures. */
    101                 xhci_ep->isoch->enqueue = 0;
    102                 xhci_ep->isoch->dequeue = 0;
    103                 xhci_ep->isoch->started = false;
    104 
    105                 fibril_mutex_initialize(&xhci_ep->isoch->guard);
    106                 fibril_condvar_initialize(&xhci_ep->isoch->avail);
    107         }
     94        if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS)
     95                isoch_init(xhci_ep, desc);
    10896
    10997        if ((rc = alloc_transfer_ds(xhci_ep)))
     
    272260}
    273261
    274 /** TODO document this
    275  */
    276 static int xhci_isoch_alloc_transfers(xhci_endpoint_t *xhci_ep) {
    277         int i = 0;
    278         int err = EOK;
    279         while (i < XHCI_ISOCH_BUFFER_COUNT) {
    280                 xhci_isoch_transfer_t *transfer = &xhci_ep->isoch->transfers[i];
    281                 if (dma_buffer_alloc(&transfer->data, xhci_ep->isoch->max_size)) {
    282                         err = ENOMEM;
    283                         break;
    284                 }
    285                 transfer->size = 0;
    286                 ++i;
    287         }
    288 
    289         if (err) {
    290                 --i;
    291                 while(i >= 0) {
    292                         dma_buffer_free(&xhci_ep->isoch->transfers[i].data);
    293                         --i;
    294                 }
    295         }
    296 
    297         return err;
    298 }
    299 
    300262/** Allocate transfer data structures for XHCI endpoint.
    301263 * @param[in] xhci_ep XHCI endpoint to allocate data structures for.
     
    316278
    317279        if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) {
    318                 if ((err = xhci_isoch_alloc_transfers(xhci_ep))) {
     280                if ((err = isoch_alloc_transfers(xhci_ep))) {
    319281                        xhci_trb_ring_fini(&xhci_ep->ring);
    320282                        return err;
     
    427389        XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
    428390        XHCI_EP_DCS_SET(*ctx, 1);
    429         XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32 - 1);
     391        XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32);
    430392
    431393        XHCI_EP_MAX_ESIT_PAYLOAD_LO_SET(*ctx, ep->isoch->max_size & 0xFFFF);
     
    446408        XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
    447409        XHCI_EP_DCS_SET(*ctx, 1);
    448         XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32 - 1);
     410        XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32);
    449411        // TODO: max ESIT payload
    450412}
  • uspace/drv/bus/usb/xhci/endpoint.h

    r4793023 r708d8fcd  
    4545#include <ddf/driver.h>
    4646
     47#include "isoch.h"
     48#include "transfers.h"
    4749#include "trb_ring.h"
    48 
    49 #include "transfers.h"
    5050
    5151typedef struct xhci_device xhci_device_t;
     
    9494
    9595        /** This field is a valid pointer for (and only for) isochronous transfers. */
    96         struct {
    97                 /** The maximum size of an isochronous transfer and therefore the size of buffers */
    98                 size_t max_size;
    99 
    100                 /** Isochronous scheduled transfers with respective buffers */
    101                 #define XHCI_ISOCH_BUFFER_COUNT 4
    102                 xhci_isoch_transfer_t transfers[XHCI_ISOCH_BUFFER_COUNT];
    103 
    104                 /** Indices to transfers */
    105                 size_t dequeue, enqueue;
    106 
    107                 /** Are isochronous transfers started? */
    108                 bool started;
    109 
    110                 /** Protects common buffers. */
    111                 fibril_mutex_t guard;
    112 
    113                 /** Signals filled buffer. */
    114                 fibril_condvar_t avail;
    115         } isoch [0];
     96        xhci_isoch_t isoch [0];
    11697} xhci_endpoint_t;
    11798
  • uspace/drv/bus/usb/xhci/hc.c

    r4793023 r708d8fcd  
    192192        hc->ac64 = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_AC64);
    193193        hc->max_slots = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_SLOTS);
     194        unsigned ist = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_IST);
     195        hc->ist = (ist & 0x10 >> 1) * (ist & 0xf);
    194196
    195197        if ((err = hc_parse_ec(hc))) {
     
    611613 * Ring a xHC Doorbell. Implements section 4.7.
    612614 */
    613 int hc_ring_doorbell(xhci_hc_t *hc, unsigned doorbell, unsigned target)
     615void hc_ring_doorbell(xhci_hc_t *hc, unsigned doorbell, unsigned target)
    614616{
    615617        assert(hc);
     
    617619        pio_write_32(&hc->db_arry[doorbell], v);
    618620        usb_log_debug2("Ringing doorbell %d (target: %d)", doorbell, target);
    619         return EOK;
    620621}
    621622
  • uspace/drv/bus/usb/xhci/hc.h

    r4793023 r708d8fcd  
    8484        unsigned max_slots;
    8585        bool ac64;
     86        unsigned ist;                   /**< IST in microframes */
    8687
    8788        /** Port speed mapping */
     
    105106int hc_start(xhci_hc_t *, bool);
    106107void hc_fini(xhci_hc_t *);
    107 int hc_ring_doorbell(xhci_hc_t *, unsigned, unsigned);
     108void hc_ring_doorbell(xhci_hc_t *, unsigned, unsigned);
    108109int hc_enable_slot(xhci_hc_t *, uint32_t *);
    109110int hc_disable_slot(xhci_hc_t *, xhci_device_t *);
  • uspace/drv/bus/usb/xhci/hw_struct/regs.h

    r4793023 r708d8fcd  
    481481} xhci_rt_regs_t;
    482482
    483 #define XHCI_RT_MFINDEX        mfindex, 32, FIELD
     483#define XHCI_RT_MFINDEX        mfindex, 32, RANGE, 13, 0
     484#define XHCI_MFINDEX_MAX        (1 << 14)
    484485
    485486/**
  • uspace/drv/bus/usb/xhci/hw_struct/trb.h

    r4793023 r708d8fcd  
    147147        xhci_dword_set_bits(&(trb).control, val, 17, 16)
    148148
    149 #define TRB_CTRL_SET_TBC(trb, val) \
     149#define TRB_ISOCH_SET_TBC(trb, val) \
    150150        xhci_dword_set_bits(&(trb).control, val, 8, 7)
    151 #define TRB_CTRL_SET_TLBPC(trb, val) \
     151#define TRB_ISOCH_SET_TLBPC(trb, val) \
    152152        xhci_dword_set_bits(&(trb).control, val, 19, 16)
    153 #define TRB_CTRL_SET_SIA(trb, val) \
     153#define TRB_ISOCH_SET_FRAMEID(trb, val) \
     154        xhci_dword_set_bits(&(trb).control, val, 30, 20)
     155#define TRB_ISOCH_SET_SIA(trb, val) \
    154156        xhci_dword_set_bits(&(trb).control, val, 31, 31)
    155157
  • uspace/drv/bus/usb/xhci/transfers.c

    r4793023 r708d8fcd  
    236236}
    237237
    238 static xhci_isoch_transfer_t* isoch_transfer_get_enqueue(xhci_endpoint_t *ep) {
    239         if (((ep->isoch->enqueue + 1) % XHCI_ISOCH_BUFFER_COUNT) == ep->isoch->dequeue) {
    240                 /* None ready */
    241                 return NULL;
    242         }
    243         xhci_isoch_transfer_t *isoch_transfer = &ep->isoch->transfers[ep->isoch->enqueue];
    244         ep->isoch->enqueue = (ep->isoch->enqueue + 1) % XHCI_ISOCH_BUFFER_COUNT;
    245         return isoch_transfer;
    246 }
    247 
    248 static xhci_isoch_transfer_t* isoch_transfer_get_dequeue(xhci_endpoint_t *ep) {
    249         xhci_isoch_transfer_t *isoch_transfer = &ep->isoch->transfers[ep->isoch->dequeue];
    250         ep->isoch->dequeue = (ep->isoch->dequeue + 1) % XHCI_ISOCH_BUFFER_COUNT;
    251         return isoch_transfer;
    252 }
    253 
    254 static int schedule_isochronous_trb(xhci_trb_ring_t *ring, xhci_endpoint_t *ep, xhci_trb_t *trb,
    255         const size_t len, uintptr_t *trb_phys)
    256 {
    257         TRB_CTRL_SET_XFER_LEN(*trb, len);
    258         // FIXME: TD size 4.11.2.4 (there is no next TRB, so 0?)
    259         TRB_CTRL_SET_TD_SIZE(*trb, 0);
    260         TRB_CTRL_SET_IOC(*trb, 1);
    261         TRB_CTRL_SET_TRB_TYPE(*trb, XHCI_TRB_TYPE_ISOCH);
    262 
    263         // see 4.14.1 and 4.11.2.3 for the explanation, how to calculate those
    264         size_t tdpc = len / 1024 + ((len % 1024) ? 1 : 0);
    265         size_t tbc = tdpc / ep->max_burst;
    266         if (!tdpc % ep->max_burst) --tbc;
    267         size_t bsp = tdpc % ep->max_burst;
    268         size_t tlbpc = (bsp ? bsp : ep->max_burst) - 1;
    269 
    270         TRB_CTRL_SET_TBC(*trb, tbc);
    271         TRB_CTRL_SET_TLBPC(*trb, tlbpc);
    272 
    273         // FIXME: do we want this? 6.4.1.3, p 366 (also possibly frame id?)
    274         TRB_CTRL_SET_SIA(*trb, 1);
    275 
    276         return xhci_trb_ring_enqueue(ring, trb, trb_phys);
    277 }
    278 
    279 static int schedule_isochronous_out(xhci_hc_t* hc, xhci_transfer_t* transfer, xhci_endpoint_t *xhci_ep,
    280         xhci_device_t *xhci_dev)
    281 {
    282         xhci_trb_t trb;
    283         xhci_trb_clean(&trb);
    284 
    285         fibril_mutex_lock(&xhci_ep->isoch->guard);
    286         xhci_isoch_transfer_t *isoch_transfer = isoch_transfer_get_enqueue(xhci_ep);
    287         while (!isoch_transfer) {
    288                 fibril_condvar_wait(&xhci_ep->isoch->avail, &xhci_ep->isoch->guard);
    289                 isoch_transfer = isoch_transfer_get_enqueue(xhci_ep);
    290         }
    291 
    292         isoch_transfer->size = transfer->batch.buffer_size;
    293         if (isoch_transfer->size > 0) {
    294                 memcpy(isoch_transfer->data.virt, transfer->batch.buffer, isoch_transfer->size);
    295         }
    296 
    297         trb.parameter = isoch_transfer->data.phys;
    298 
    299         xhci_trb_ring_t *ring = get_ring(hc, transfer);
    300         int err = schedule_isochronous_trb(ring, xhci_ep, &trb, isoch_transfer->size,
    301                 &isoch_transfer->interrupt_trb_phys);
    302         if (err) {
    303                 fibril_mutex_unlock(&xhci_ep->isoch->guard);
    304                 return err;
    305         }
    306 
    307         /* If not yet started, start the isochronous endpoint transfers - after buffer count - 1 writes */
    308         /* The -1 is there because of the enqueue != dequeue check. The buffer must have at least 2 transfers. */
    309         if (((xhci_ep->isoch->enqueue + 1) % XHCI_ISOCH_BUFFER_COUNT) == xhci_ep->isoch->dequeue && !xhci_ep->isoch->started) {
    310                 const uint8_t slot_id = xhci_dev->slot_id;
    311                 const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
    312                 err = hc_ring_doorbell(hc, slot_id, target);
    313                 xhci_ep->isoch->started = true;
    314         }
    315         fibril_mutex_unlock(&xhci_ep->isoch->guard);
    316         if (err) {
    317                 return err;
    318         }
    319 
    320         /* Isochronous transfers don't handle errors, they skip them all. */
    321         transfer->batch.error = EOK;
    322         transfer->batch.transfered_size = transfer->batch.buffer_size;
    323         usb_transfer_batch_finish(&transfer->batch);
    324         return EOK;
    325 }
    326 
    327 static int schedule_isochronous_in_trbs(xhci_endpoint_t *xhci_ep, xhci_trb_ring_t *ring) {
    328         xhci_trb_t trb;
    329         xhci_isoch_transfer_t *isoch_transfer;
    330         while ((isoch_transfer = isoch_transfer_get_enqueue(xhci_ep)) != NULL) {
    331                 xhci_trb_clean(&trb);
    332                 trb.parameter = isoch_transfer->data.phys;
    333                 isoch_transfer->size = xhci_ep->isoch->max_size;
    334 
    335                 int err = schedule_isochronous_trb(ring, xhci_ep, &trb, isoch_transfer->size,
    336                         &isoch_transfer->interrupt_trb_phys);
    337                 if (err)
    338                         return err;
    339         }
    340         return EOK;
    341 }
    342 
    343 static int schedule_isochronous_in(xhci_hc_t* hc, xhci_transfer_t* transfer, xhci_endpoint_t *xhci_ep,
    344         xhci_device_t *xhci_dev)
    345 {
    346         fibril_mutex_lock(&xhci_ep->isoch->guard);
    347         /* If not yet started, start the isochronous endpoint transfers - before first read */
    348         if (!xhci_ep->isoch->started) {
    349                 xhci_trb_ring_t *ring = get_ring(hc, transfer);
    350                 /* Fill the TRB ring. */
    351                 int err = schedule_isochronous_in_trbs(xhci_ep, ring);
    352                 if (err) {
    353                         fibril_mutex_unlock(&xhci_ep->isoch->guard);
    354                         return err;
    355                 }
    356                 /* Ring the doorbell to start it. */
    357                 const uint8_t slot_id = xhci_dev->slot_id;
    358                 const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
    359                 err = hc_ring_doorbell(hc, slot_id, target);
    360                 if (err) {
    361                         fibril_mutex_unlock(&xhci_ep->isoch->guard);
    362                         return err;
    363                 }
    364                 xhci_ep->isoch->started = true;
    365         }
    366 
    367         xhci_isoch_transfer_t *isoch_transfer = isoch_transfer_get_enqueue(xhci_ep);
    368         while(!isoch_transfer) {
    369                 fibril_condvar_wait(&xhci_ep->isoch->avail, &xhci_ep->isoch->guard);
    370                 isoch_transfer = isoch_transfer_get_enqueue(xhci_ep);
    371         }
    372 
    373         isoch_transfer->size = transfer->batch.buffer_size;
    374         if (transfer->batch.buffer_size <= isoch_transfer->size) {
    375                 if (transfer->batch.buffer_size > 0) {
    376                         memcpy(transfer->batch.buffer, isoch_transfer->data.virt, transfer->batch.buffer_size);
    377                 }
    378                 if (transfer->batch.buffer_size < isoch_transfer->size) {
    379                         // FIXME: somehow notify that buffer was too small, probably batch error code
    380                 }
    381                 transfer->batch.transfered_size = transfer->batch.buffer_size;
    382         }
    383         else {
    384                 memcpy(transfer->batch.buffer, isoch_transfer->data.virt, isoch_transfer->size);
    385                 transfer->batch.transfered_size = isoch_transfer->size;
    386         }
    387 
    388         // Clear and requeue the transfer with new TRB
    389         xhci_trb_t trb;
    390         xhci_trb_clean(&trb);
    391 
    392         trb.parameter = isoch_transfer->data.phys;
    393         isoch_transfer->size = xhci_ep->isoch->max_size;
    394 
    395         xhci_trb_ring_t *ring = get_ring(hc, transfer);
    396         int err = schedule_isochronous_trb(ring, xhci_ep, &trb, isoch_transfer->size,
    397                 &isoch_transfer->interrupt_trb_phys);
    398         fibril_mutex_unlock(&xhci_ep->isoch->guard);
    399 
    400         if (err) {
    401                 return err;
    402         }
    403 
    404         /* Isochronous transfers don't handle errors, they skip them all. */
    405         transfer->batch.error = EOK;
    406         usb_transfer_batch_finish(&transfer->batch);
    407         return EOK;
    408 }
    409 
    410 static int schedule_isochronous(xhci_hc_t* hc, xhci_transfer_t* transfer, xhci_endpoint_t *xhci_ep,
    411         xhci_device_t *xhci_dev)
    412 {
    413         if (transfer->batch.buffer_size > xhci_ep->isoch->max_size) {
    414                 usb_log_error("Cannot schedule an oversized isochronous transfer.");
    415                 return EINVAL;
    416         }
    417 
    418         if (xhci_ep->base.direction == USB_DIRECTION_OUT) {
    419                 return schedule_isochronous_out(hc, transfer, xhci_ep, xhci_dev);
    420         }
    421         else {
    422                 return schedule_isochronous_in(hc, transfer, xhci_ep, xhci_dev);
    423         }
    424 }
    425 
    426 static int handle_isochronous_transfer_event(xhci_hc_t *hc, xhci_trb_t *trb, xhci_endpoint_t *ep) {
    427         fibril_mutex_lock(&ep->isoch->guard);
    428 
    429         int err = EOK;
    430 
    431         const xhci_trb_completion_code_t completion_code = TRB_COMPLETION_CODE(*trb);
    432         switch (completion_code) {
    433                 case XHCI_TRBC_RING_OVERRUN:
    434                 case XHCI_TRBC_RING_UNDERRUN:
    435                         /* Rings are unscheduled by xHC now */
    436                         ep->isoch->started = false;
    437                         /* For OUT, there was nothing to process */
    438                         /* For IN, the buffer has overfilled, we empty the buffers and readd TRBs */
    439                         ep->isoch->enqueue = ep->isoch->dequeue = 0;
    440                         err = EIO;
    441                         break;
    442                 case XHCI_TRBC_SHORT_PACKET:
    443                         usb_log_debug("Short transfer.");
    444                         /* fallthrough */
    445                 case XHCI_TRBC_SUCCESS:
    446                         break;
    447                 default:
    448                         usb_log_warning("Transfer not successfull: %u", completion_code);
    449                         err = EIO;
    450         }
    451 
    452         xhci_isoch_transfer_t *isoch_transfer = isoch_transfer_get_dequeue(ep);
    453         if (isoch_transfer->interrupt_trb_phys != trb->parameter) {
    454                 usb_log_error("Non-matching trb to isochronous transfer, skipping.");
    455                 // FIXME: what to do? probably just kill the whole endpoint
    456                 err = ENOENT;
    457         }
    458 
    459         if (ep->base.direction == USB_DIRECTION_IN) {
    460                 // We may have received less data, that's fine
    461                 isoch_transfer->size -= TRB_TRANSFER_LENGTH(*trb);
    462         }
    463 
    464         fibril_condvar_signal(&ep->isoch->avail);
    465         fibril_mutex_unlock(&ep->isoch->guard);
    466         return err;
     238static int schedule_isochronous(xhci_transfer_t* transfer)
     239{
     240        endpoint_t *ep = transfer->batch.ep;
     241
     242        return ep->direction == USB_DIRECTION_OUT
     243                ? isoch_schedule_out(transfer)
     244                : isoch_schedule_in(transfer);
    467245}
    468246
     
    486264                return ENOENT;
    487265        }
     266        // No need to add reference for endpoint, it is held by the transfer batch.
    488267
    489268        /* FIXME: This is racy. Do we care? */
     
    491270
    492271        if (ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) {
    493                 return handle_isochronous_transfer_event(hc, trb, ep);
     272                return isoch_handle_transfer_event(hc, ep, trb);
    494273        }
    495274
     
    558337        // Isochronous transfer needs to be handled differently
    559338        if (batch->ep->transfer_type == USB_TRANSFER_ISOCHRONOUS) {
    560                 return schedule_isochronous(hc, transfer, xhci_ep, xhci_dev);
     339                return schedule_isochronous(transfer);
    561340        }
    562341
     
    591370        const uint8_t slot_id = xhci_dev->slot_id;
    592371        const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
    593         return hc_ring_doorbell(hc, slot_id, target);
    594 }
     372        hc_ring_doorbell(hc, slot_id, target);
     373        return EOK;
     374}
  • uspace/drv/bus/usb/xhci/transfers.h

    r4793023 r708d8fcd  
    5555} xhci_transfer_t;
    5656
    57 typedef struct {
    58         /* Used buffer size */
    59         uint64_t size;
    60         /* Buffer with data */
    61         dma_buffer_t data;
    62         /* Physical address of enqueued TRB */
    63         uintptr_t interrupt_trb_phys;
    64 } xhci_isoch_transfer_t;
    65 
    6657usb_transfer_batch_t* xhci_transfer_create(endpoint_t *);
    6758int xhci_transfer_schedule(xhci_hc_t *, usb_transfer_batch_t *);
  • uspace/lib/drv/include/usbhc_iface.h

    r4793023 r708d8fcd  
    6464 * Callers shall fill it with bare contents of respective descriptors (in usb endianity).
    6565 */
    66 typedef struct {
     66typedef struct usb_endpoint_descriptors {
    6767        struct {
    6868                uint8_t endpoint_address;
Note: See TracChangeset for help on using the changeset viewer.