Changeset 94e9c29 in mainline for uspace/drv/bus/usb/xhci/isoch.c


Ignore:
Timestamp:
2018-01-13T00:54:24Z (6 years ago)
Author:
Salmelu <salmelu@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
230ef1c
Parents:
0e7380f
Message:

xhci: Isoch mfindex epoch counting

Isochronous mfindex is now saved together with wrap epochs.
This allows us to work with endpoints with larger intervals,
that need to schedule over epochs.

File:
1 edited

Legend:

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

    r0e7380f r94e9c29  
    8080
    8181        fibril_timer_clear_locked(isoch->feeding_timer);
    82         isoch->last_mfindex = -1U;
     82        isoch->last_mf = -1U;
    8383        usb_log_info("[isoch] Endpoint" XHCI_EP_FMT ": Data flow reset.", XHCI_EP_ARGS(*ep));
    8484}
     
    195195}
    196196
     197/** The number of bits the MFINDEX is stored in at HW */
     198#define EPOCH_BITS 14
     199/** The delay in usec for the epoch wrap */
     200#define EPOCH_DELAY 500000
     201/** The amount of microframes the epoch is checked for a delay */
     202#define EPOCH_LOW_MFINDEX 8 * 100
     203
     204static inline uint64_t get_system_time()
     205{
     206        struct timeval tv;
     207        getuptime(&tv);
     208        return ((uint64_t) tv.tv_sec) * 1000000 + ((uint64_t) tv.tv_usec);
     209}
     210
     211static inline uint64_t get_current_microframe(const xhci_hc_t *hc)
     212{
     213        const uint32_t reg_mfindex = XHCI_REG_RD(hc->rt_regs, XHCI_RT_MFINDEX);
     214        /*
     215         * If the mfindex is low and the time passed since last mfindex wrap
     216         * is too high, we have entered the new epoch already (and haven't received event yet).
     217         */
     218        uint64_t epoch = hc->wrap_count;
     219        if (reg_mfindex < EPOCH_LOW_MFINDEX && get_system_time() - hc->wrap_time > EPOCH_DELAY) {
     220                ++epoch;
     221        }
     222        return (epoch << EPOCH_BITS) + reg_mfindex;
     223}
     224
    197225static inline void calc_next_mfindex(xhci_endpoint_t *ep, xhci_isoch_transfer_t *it)
    198226{
    199227        xhci_isoch_t * const isoch = ep->isoch;
    200         if (isoch->last_mfindex == -1U) {
     228        if (isoch->last_mf == -1U) {
    201229                const xhci_bus_t *bus = bus_to_xhci_bus(ep->base.device->bus);
    202230                const xhci_hc_t *hc = bus->hc;
    203231
    204                 /* Choose some number, give us a little time to prepare the
    205                  * buffers */
    206                 it->mfindex = XHCI_REG_RD(hc->rt_regs, XHCI_RT_MFINDEX) + 1
    207                        + isoch->buffer_count * ep->interval
    208                        + hc->ist;
     232                /* Delay the first frame by some time to fill the buffer, but at most 10 miliseconds. */
     233                const uint64_t delay = min(isoch->buffer_count * ep->interval, 10 * 8);
     234                it->mfindex = get_current_microframe(hc) + 1 + delay + hc->ist;
    209235
    210236                // Align to ESIT start boundary
     
    212238                it->mfindex &= ~(ep->interval - 1);
    213239        } else {
    214                 it->mfindex = (isoch->last_mfindex + ep->interval) % XHCI_MFINDEX_MAX;
     240                it->mfindex = isoch->last_mf + ep->interval;
    215241        }
    216242}
     
    227253typedef struct {
    228254        window_position_t position;
    229         uint32_t offset;
     255        uint64_t offset;
    230256} window_decision_t;
    231257
     
    236262 * uframes it's off.
    237263 */
    238 static inline void window_decide(window_decision_t *res, xhci_hc_t *hc, uint32_t mfindex)
    239 {
    240         uint32_t current_mfindex = XHCI_REG_RD(hc->rt_regs, XHCI_RT_MFINDEX) + 1;
    241 
    242         /*
    243          * In your mind, rotate the clock so the window is at its beginning.
    244          * The length of the window is always the same, and by rotating the
    245          * mfindex too, we can decide by the value of it easily.
    246          */
    247         mfindex = (mfindex - current_mfindex - hc->ist + XHCI_MFINDEX_MAX) % XHCI_MFINDEX_MAX;
    248         const uint32_t end = END_FRAME_DELAY - hc->ist;
    249         const uint32_t threshold = (XHCI_MFINDEX_MAX + end) / 2;
    250 
    251         if (mfindex <= end) {
     264static inline void window_decide(window_decision_t *res, xhci_hc_t *hc, uint64_t mfindex)
     265{
     266        const uint64_t current_mf = get_current_microframe(hc);
     267        const uint64_t start = current_mf + hc->ist + 1;
     268        const uint64_t end = current_mf + END_FRAME_DELAY;
     269
     270        if (mfindex < start) {
     271                res->position = WINDOW_TOO_LATE;
     272                res->offset = start - mfindex;
     273        } else if (mfindex <= end) {
    252274                res->position = WINDOW_INSIDE;
    253         } else if (mfindex > threshold) {
    254                 res->position = WINDOW_TOO_LATE;
    255                 res->offset = XHCI_MFINDEX_MAX - mfindex;
    256275        } else {
    257276                res->position = WINDOW_TOO_SOON;
    258277                res->offset = mfindex - end;
    259278        }
    260         /*
    261          * TODO: The "size" of the clock is too low. We have to scale it a bit
    262          * to ensure correct scheduling of transfers, that are
    263          * buffer_count * interval away from now.
    264          * Maximum interval is 8 seconds, which means we need a size of
    265          * 16 seconds. The size of MFIINDEX is 2 seconds only.
    266          *
    267          * A plan is to create a thin abstraction at HC, which would return
    268          * a time from 32-bit clock, having its high bits updated by the
    269          * MFINDEX Wrap Event, and low bits from the MFINDEX register. Using
    270          * this 32-bit clock, one can plan 6 days ahead.
    271          */
    272279}
    273280
     
    293300        bool fed = false;
    294301
    295         /*
    296          * There might be a case, where no transfer can't be put on the ring immediately
    297          * (for endpoints with interval >= 500ms). In that case, the transfer buffers could fill
    298          * and the first condition wouldn't be enough to enter the loop.
    299          */
    300         while (isoch->hw_enqueue != isoch->enqueue || isoch->transfers[isoch->hw_enqueue].state == ISOCH_FILLED) {
     302        while (isoch->transfers[isoch->hw_enqueue].state == ISOCH_FILLED) {
    301303                xhci_isoch_transfer_t * const it = &isoch->transfers[isoch->hw_enqueue];
    302304
     
    317319
    318320                case WINDOW_INSIDE:
    319                         usb_log_debug2("[isoch] feeding buffer %lu at 0x%x",
     321                        usb_log_debug2("[isoch] feeding buffer %lu at 0x%llx",
    320322                            it - isoch->transfers, it->mfindex);
    321323                        it->error = schedule_isochronous_trb(ep, it);
     
    332334                case WINDOW_TOO_LATE:
    333335                        /* Missed the opportunity to schedule. Just mark this transfer as skipped. */
    334                         usb_log_debug2("[isoch] missed feeding buffer %lu at 0x%x by %u uframes",
     336                        usb_log_debug2("[isoch] missed feeding buffer %lu at 0x%llx by %llu uframes",
    335337                            it - isoch->transfers, it->mfindex, wd.offset);
    336338                        it->state = ISOCH_COMPLETE;
     
    405407
    406408                case WINDOW_TOO_LATE:
    407                         usb_log_debug2("[isoch] missed feeding buffer %lu at 0x%x by %u uframes",
     409                        usb_log_debug2("[isoch] missed feeding buffer %lu at 0x%llx by %llu uframes",
    408410                            it - isoch->transfers, it->mfindex, wd.offset);
    409411                        /* Missed the opportunity to schedule. Schedule ASAP. */
     
    416418                case WINDOW_INSIDE:
    417419                        isoch->enqueue = (isoch->enqueue + 1) % isoch->buffer_count;
    418                         isoch->last_mfindex = it->mfindex;
    419 
    420                         usb_log_debug2("[isoch] feeding buffer %lu at 0x%x",
     420                        isoch->last_mf = it->mfindex;
     421
     422                        usb_log_debug2("[isoch] feeding buffer %lu at 0x%llx",
    421423                            it - isoch->transfers, it->mfindex);
    422424
     
    503505        /* Calculate when to schedule next transfer */
    504506        calc_next_mfindex(ep, it);
    505         isoch->last_mfindex = it->mfindex;
    506         usb_log_debug2("[isoch] buffer %zu will be on schedule at 0x%x", it - isoch->transfers, it->mfindex);
     507        isoch->last_mf = it->mfindex;
     508        usb_log_debug2("[isoch] buffer %zu will be on schedule at 0x%llx", it - isoch->transfers, it->mfindex);
    507509
    508510        /* Prepare the transfer. */
Note: See TracChangeset for help on using the changeset viewer.