Changeset 5fe3f954 in mainline


Ignore:
Timestamp:
2018-02-12T10:28:14Z (6 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
ee820ff
Parents:
1d758fc
git-author:
Ondřej Hlavatý <aearsis@…> (2018-02-12 10:23:51)
git-committer:
Ondřej Hlavatý <aearsis@…> (2018-02-12 10:28:14)
Message:

xhci: refactor splitting transfers

Allow nonzero offsets (not yet used bc. of other drivers), usage of
bigger chunks than PAGE_SIZE. The performance is now at 1.3 GB/s, when
using 16MB transfers (the current maximum) and without debug logs.

Location:
uspace
Files:
2 edited

Legend:

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

    r1d758fc r5fe3f954  
    4848} stage_dir_flag_t;
    4949
    50 #define REQUEST_TYPE_DTD (0x80)
    51 #define REQUEST_TYPE_IS_DEVICE_TO_HOST(rq) ((rq) & REQUEST_TYPE_DTD)
    52 
    53 
    5450/** Get direction flag of data stage.
    5551 *  See Table 7 of xHCI specification.
     
    5955{
    6056        /* See Table 7 of xHCI specification */
    61         return REQUEST_TYPE_IS_DEVICE_TO_HOST(bmRequestType) && (wLength > 0)
     57        return SETUP_REQUEST_TYPE_IS_DEVICE_TO_HOST(bmRequestType) && (wLength > 0)
    6258                ? STAGE_OUT
    6359                : STAGE_IN;
     
    8076
    8177        /* See Table 7 of xHCI specification */
    82         return REQUEST_TYPE_IS_DEVICE_TO_HOST(bmRequestType)
     78        return SETUP_REQUEST_TYPE_IS_DEVICE_TO_HOST(bmRequestType)
    8379                ? DATA_STAGE_IN
    8480                : DATA_STAGE_NO;
     
    124120}
    125121
    126 static int calculate_trb_count(xhci_transfer_t *transfer)
    127 {
    128         const size_t size = transfer->batch.size;
    129         return (size + PAGE_SIZE - 1 )/ PAGE_SIZE;
    130 }
    131 
    132 static void trb_set_buffer(xhci_transfer_t *transfer, xhci_trb_t *trb,
    133         size_t i, size_t total, size_t *remaining)
    134 {
    135         const uintptr_t ptr = dma_buffer_phys(&transfer->batch.dma_buffer,
    136                 transfer->batch.dma_buffer.virt + i * PAGE_SIZE);
    137 
    138         trb->parameter = host2xhci(64, ptr);
    139         TRB_CTRL_SET_TD_SIZE(*trb, max(31, total - i - 1));
    140         if (*remaining > PAGE_SIZE) {
    141                 TRB_CTRL_SET_XFER_LEN(*trb, PAGE_SIZE);
    142                 *remaining -= PAGE_SIZE;
    143         }
    144         else {
    145                 TRB_CTRL_SET_XFER_LEN(*trb, *remaining);
    146                 *remaining = 0;
    147         }
     122#define MAX_CHUNK_SIZE (1 << 16)
     123
     124typedef struct {
     125        /* Input parameters */
     126        dma_buffer_t buf;
     127        size_t chunk_size, packet_count, mps, max_trb_count;
     128
     129        /* Changing at runtime */
     130        size_t transferred, remaining;
     131        void *pos;
     132} trb_splitter_t;
     133
     134static void trb_splitter_init(trb_splitter_t *ts, xhci_transfer_t *transfer)
     135{
     136        ts->buf = transfer->batch.dma_buffer;
     137
     138        const size_t chunk_mask = dma_policy_chunk_mask(ts->buf.policy);
     139        ts->chunk_size = (chunk_mask > MAX_CHUNK_SIZE + 1)
     140                ? MAX_CHUNK_SIZE : (chunk_mask + 1);
     141
     142        ts->remaining = transfer->batch.size;
     143        ts->max_trb_count = (ts->remaining + ts->chunk_size - 1) / ts->chunk_size + 1;
     144        ts->mps = transfer->batch.ep->max_packet_size;
     145        ts->packet_count = (ts->remaining + ts->mps - 1) / ts->mps;
     146
     147        ts->transferred = 0;
     148        ts->pos = ts->buf.virt + transfer->batch.offset;
     149}
     150
     151static void trb_split_next(xhci_trb_t *trb, trb_splitter_t *ts)
     152{
     153        xhci_trb_clean(trb);
     154
     155        size_t size = min(ts->remaining, ts->chunk_size);
     156
     157        /* First TRB might be misaligned */
     158        if (ts->transferred == 0) {
     159                const size_t offset = (ts->pos - ts->buf.virt) % ts->chunk_size;
     160                size = min(size, ts->chunk_size - offset);
     161        }
     162
     163        ts->transferred += size;
     164        ts->remaining -= size;
     165
     166        const size_t tx_packets = (ts->transferred + ts->mps - 1) / ts->mps;
     167        const unsigned td_size = min(31, ts->packet_count - tx_packets);
     168
     169        /* Last TRB must have TD Size = 0 */
     170        assert(ts->remaining > 0 || td_size == 0);
     171
     172        uintptr_t phys = dma_buffer_phys(&ts->buf, ts->pos);
     173
     174        trb->parameter = host2xhci(64, phys);
     175        TRB_CTRL_SET_TD_SIZE(*trb, td_size);
     176        TRB_CTRL_SET_XFER_LEN(*trb, size);
     177        TRB_CTRL_SET_TRB_TYPE(*trb, XHCI_TRB_TYPE_NORMAL);
     178
     179        if (ts->remaining)
     180                TRB_CTRL_SET_CHAIN(*trb, 1);
     181
     182        ts->pos += size;
    148183}
    149184
     
    155190        usb_device_request_setup_packet_t* setup = &batch->setup.packet;
    156191
    157         size_t buffer_count = 0;
    158         if (setup->length > 0) {
    159                 buffer_count = calculate_trb_count(transfer);
    160         }
    161 
    162         xhci_trb_t trbs[buffer_count + 2];
    163 
    164         xhci_trb_t *trb_setup = trbs;
     192        trb_splitter_t splitter;
     193        trb_splitter_init(&splitter, transfer);
     194
     195        xhci_trb_t trbs[splitter.max_trb_count + 2];
     196        size_t trbs_used = 0;
     197
     198        xhci_trb_t *trb_setup = &trbs[trbs_used++];
    165199        xhci_trb_clean(trb_setup);
    166200
    167         TRB_CTRL_SET_SETUP_WVALUE(*trb_setup, setup->value);
    168         TRB_CTRL_SET_SETUP_WLENGTH(*trb_setup, setup->length);
    169         TRB_CTRL_SET_SETUP_WINDEX(*trb_setup, setup->index);
    170         TRB_CTRL_SET_SETUP_BREQ(*trb_setup, setup->request);
    171         TRB_CTRL_SET_SETUP_BMREQTYPE(*trb_setup, setup->request_type);
     201        trb_setup->parameter = batch->setup.packed;
    172202
    173203        /* Size of the setup packet is always 8 */
     
    180210            get_transfer_type(trb_setup, setup->request_type, setup->length));
    181211
    182         /* Data stage */
    183         if (setup->length > 0) {
    184                 int stage_dir = REQUEST_TYPE_IS_DEVICE_TO_HOST(setup->request_type)
    185                                         ? STAGE_IN : STAGE_OUT;
    186                 size_t remaining = transfer->batch.size;
    187 
    188                 for (size_t i = 0; i < buffer_count; ++i) {
    189                         xhci_trb_clean(&trbs[i + 1]);
    190                         trb_set_buffer(transfer, &trbs[i + 1], i, buffer_count, &remaining);
    191 
    192                         TRB_CTRL_SET_DIR(trbs[i + 1], stage_dir);
    193                         TRB_CTRL_SET_TRB_TYPE(trbs[i + 1], XHCI_TRB_TYPE_DATA_STAGE);
    194 
    195                         if (i == buffer_count - 1) break;
    196 
    197                         /* Set the chain bit as this is not the last TRB */
    198                         TRB_CTRL_SET_CHAIN(trbs[i], 1);
    199                 }
    200         }
     212        stage_dir_flag_t stage_dir = (transfer->batch.dir == USB_DIRECTION_IN)
     213                                ? STAGE_IN : STAGE_OUT;
     214
     215        /* Data stage - first TRB is special */
     216        if (splitter.remaining > 0) {
     217                xhci_trb_t *trb = &trbs[trbs_used++];
     218                trb_split_next(trb, &splitter);
     219                TRB_CTRL_SET_TRB_TYPE(*trb, XHCI_TRB_TYPE_DATA_STAGE);
     220                TRB_CTRL_SET_DIR(*trb, stage_dir);
     221        }
     222        while (splitter.remaining > 0)
     223                trb_split_next(&trbs[trbs_used++], &splitter);
    201224
    202225        /* Status stage */
    203         xhci_trb_t *trb_status = trbs + buffer_count + 1;
     226        xhci_trb_t *trb_status = &trbs[trbs_used++];
    204227        xhci_trb_clean(trb_status);
    205228
     
    217240
    218241        return xhci_trb_ring_enqueue_multiple(get_ring(transfer), trbs,
    219             buffer_count + 2, &transfer->interrupt_trb_phys);
    220 }
    221 
    222 static errno_t schedule_bulk(xhci_hc_t* hc, xhci_transfer_t *transfer)
    223 {
     242            trbs_used, &transfer->interrupt_trb_phys);
     243}
     244
     245static errno_t schedule_bulk_intr(xhci_hc_t* hc, xhci_transfer_t *transfer)
     246{
     247        xhci_trb_ring_t * const ring = get_ring(transfer);
     248        if (!ring)
     249                return EINVAL;
     250
    224251        /* The stream-enabled endpoints need to chain ED trb */
    225252        xhci_endpoint_t *ep = xhci_endpoint_get(transfer->batch.ep);
    226         if (!ep->primary_stream_data_size) {
    227                 const size_t buffer_count = calculate_trb_count(transfer);
    228                 xhci_trb_t trbs[buffer_count];
    229                 size_t remaining = transfer->batch.size;
    230 
    231                 for (size_t i = 0; i < buffer_count; ++i) {
    232                         xhci_trb_clean(&trbs[i]);
    233                         trb_set_buffer(transfer, &trbs[i], i, buffer_count, &remaining);
    234                         TRB_CTRL_SET_TRB_TYPE(trbs[i], XHCI_TRB_TYPE_NORMAL);
    235 
    236                         if (i == buffer_count - 1) break;
    237 
    238                         /* Set the chain bit as this is not the last TRB */
    239                         TRB_CTRL_SET_CHAIN(trbs[i], 1);
    240                 }
     253        const bool use_streams = !!ep->primary_stream_data_size;
     254
     255        trb_splitter_t splitter;
     256        trb_splitter_init(&splitter, transfer);
     257
     258        const size_t trb_count = splitter.max_trb_count + use_streams;
     259        xhci_trb_t trbs[trb_count];
     260        size_t trbs_used = 0;
     261
     262        while (splitter.remaining > 0)
     263                trb_split_next(&trbs[trbs_used++], &splitter);
     264
     265        if (!use_streams) {
    241266                /* Set the interrupt bit for last TRB */
    242                 TRB_CTRL_SET_IOC(trbs[buffer_count - 1], 1);
    243 
    244                 xhci_trb_ring_t* ring = get_ring(transfer);
    245                 return xhci_trb_ring_enqueue_multiple(ring, &trbs[0], buffer_count,
    246                         &transfer->interrupt_trb_phys);
     267                TRB_CTRL_SET_IOC(trbs[trbs_used - 1], 1);
    247268        }
    248269        else {
    249                 xhci_trb_ring_t* ring = get_ring(transfer);
    250                 if (!ring) {
    251                         return EINVAL;
    252                 }
    253 
    254                 const size_t buffer_count = calculate_trb_count(transfer);
    255                 xhci_trb_t trbs[buffer_count + 1];
    256                 size_t remaining = transfer->batch.size;
    257 
    258                 for (size_t i = 0; i < buffer_count; ++i) {
    259                         xhci_trb_clean(&trbs[i]);
    260                         trb_set_buffer(transfer, &trbs[i], i, buffer_count + 1, &remaining);
    261                         TRB_CTRL_SET_TRB_TYPE(trbs[i], XHCI_TRB_TYPE_NORMAL);
    262                         TRB_CTRL_SET_CHAIN(trbs[i], 1);
    263                 }
    264                 TRB_CTRL_SET_ENT(trbs[buffer_count - 1], 1);
    265 
    266                 xhci_trb_clean(&trbs[buffer_count]);
    267                 trbs[buffer_count].parameter = host2xhci(64, (uintptr_t) transfer);
    268                 TRB_CTRL_SET_TRB_TYPE(trbs[buffer_count], XHCI_TRB_TYPE_EVENT_DATA);
    269                 TRB_CTRL_SET_IOC(trbs[buffer_count], 1);
    270 
    271                 return xhci_trb_ring_enqueue_multiple(ring, &trbs[0], buffer_count + 1,
    272                         &transfer->interrupt_trb_phys);
    273         }
    274 }
    275 
    276 static errno_t schedule_interrupt(xhci_hc_t* hc, xhci_transfer_t* transfer)
    277 {
    278         const size_t buffer_count = calculate_trb_count(transfer);
    279         xhci_trb_t trbs[buffer_count];
    280         size_t remaining = transfer->batch.size;
    281 
    282         for (size_t i = 0; i < buffer_count; ++i) {
    283                 xhci_trb_clean(&trbs[i]);
    284                 trb_set_buffer(transfer, &trbs[i], i, buffer_count, &remaining);
    285                 TRB_CTRL_SET_TRB_TYPE(trbs[i], XHCI_TRB_TYPE_NORMAL);
    286 
    287                 if (i == buffer_count - 1) break;
    288 
    289                 /* Set the chain bit as this is not the last TRB */
    290                 TRB_CTRL_SET_CHAIN(trbs[i], 1);
    291         }
    292         /* Set the interrupt bit for last TRB */
    293         TRB_CTRL_SET_IOC(trbs[buffer_count - 1], 1);
    294 
    295         xhci_trb_ring_t* ring = get_ring(transfer);
    296         return xhci_trb_ring_enqueue_multiple(ring, &trbs[0], buffer_count,
     270                /* Clear the chain bit on the last TRB */
     271                TRB_CTRL_SET_CHAIN(trbs[trbs_used - 1], 1);
     272                TRB_CTRL_SET_ENT(trbs[trbs_used - 1], 1);
     273
     274                xhci_trb_t *ed = &trbs[trbs_used++];
     275                xhci_trb_clean(ed);
     276                ed->parameter = host2xhci(64, (uintptr_t) transfer);
     277                TRB_CTRL_SET_TRB_TYPE(*ed, XHCI_TRB_TYPE_EVENT_DATA);
     278                TRB_CTRL_SET_IOC(*ed, 1);
     279        }
     280
     281        return xhci_trb_ring_enqueue_multiple(ring, trbs, trbs_used,
    297282                &transfer->interrupt_trb_phys);
    298283}
     
    429414        [USB_TRANSFER_CONTROL] = schedule_control,
    430415        [USB_TRANSFER_ISOCHRONOUS] = NULL,
    431         [USB_TRANSFER_BULK] = schedule_bulk,
    432         [USB_TRANSFER_INTERRUPT] = schedule_interrupt,
     416        [USB_TRANSFER_BULK] = schedule_bulk_intr,
     417        [USB_TRANSFER_INTERRUPT] = schedule_bulk_intr,
    433418};
    434419
  • uspace/lib/usb/include/usb/request.h

    r1d758fc r5fe3f954  
    8585#define SETUP_REQUEST_TYPE_DEVICE_TO_HOST (1 << 7)
    8686#define SETUP_REQUEST_TYPE_HOST_TO_DEVICE (0 << 7)
     87#define SETUP_REQUEST_TYPE_IS_DEVICE_TO_HOST(rt) ((rt) & (1 << 7))
    8788#define SETUP_REQUEST_TYPE_GET_TYPE(rt) ((rt >> 5) & 0x3)
    8889#define SETUP_REQUEST_TYPE_GET_RECIPIENT(rec) (rec & 0x1f)
Note: See TracChangeset for help on using the changeset viewer.