Changeset e67c50a in mainline for uspace/drv/bus/usb/ohci/ohci_batch.c


Ignore:
Timestamp:
2018-02-01T21:13:23Z (7 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
64ce0c1
Parents:
3e6ff9a
git-author:
Ondřej Hlavatý <aearsis@…> (2018-02-01 21:13:22)
git-committer:
Ondřej Hlavatý <aearsis@…> (2018-02-01 21:13:23)
Message:

ohci: use dma memory responsibly

Instead of leaving arbitrary TD behind to be used by next transfer,
prepare two of them in endpoint and use them in a cyclic manner. This
reduces the number of pages allocated per transfer to one.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/ohci/ohci_batch.c

    r3e6ff9a re67c50a  
    5656{
    5757        assert(ohci_batch);
    58         if (ohci_batch->tds) {
    59                 const ohci_endpoint_t *ohci_ep =
    60                     ohci_endpoint_get(ohci_batch->base.ep);
    61                 assert(ohci_ep);
    62                 for (unsigned i = 0; i < ohci_batch->td_count; ++i) {
    63                         if (ohci_batch->tds[i] != ohci_ep->td)
    64                                 free32(ohci_batch->tds[i]);
    65                 }
    66                 free(ohci_batch->tds);
    67         }
    68         free32(ohci_batch->device_buffer);
     58        dma_buffer_free(&ohci_batch->ohci_dma_buffer);
    6959        free(ohci_batch);
    7060}
     
    10191{
    10292        assert(ohci_batch);
    103 
    104         const size_t setup_size = (ohci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
    105                 ? USB_SETUP_PACKET_SIZE
    106                 : 0;
    107 
    10893        usb_transfer_batch_t *usb_batch = &ohci_batch->base;
     94
     95        if (!batch_setup[usb_batch->ep->transfer_type])
     96                return ENOTSUP;
    10997
    11098        ohci_batch->td_count = (usb_batch->buffer_size + OHCI_TD_MAX_TRANSFER - 1)
     
    115103        }
    116104
    117         /* We need an extra place for TD that was left at ED */
    118         ohci_batch->tds = calloc(ohci_batch->td_count + 1, sizeof(td_t*));
    119         if (!ohci_batch->tds) {
    120                 usb_log_error("Failed to allocate OHCI transfer descriptors.");
     105        /* Alloc one more to NULL terminate */
     106        ohci_batch->tds = calloc(ohci_batch->td_count + 1, sizeof(td_t *));
     107        if (!ohci_batch->tds)
    121108                return ENOMEM;
    122         }
    123 
    124         /* Add TD left over by the previous transfer */
    125         ohci_batch->ed = ohci_endpoint_get(usb_batch->ep)->ed;
    126         ohci_batch->tds[0] = ohci_endpoint_get(usb_batch->ep)->td;
    127 
    128         for (unsigned i = 1; i <= ohci_batch->td_count; ++i) {
    129                 ohci_batch->tds[i] = malloc32(sizeof(td_t));
    130                 if (!ohci_batch->tds[i]) {
    131                         usb_log_error("Failed to allocate TD %d.", i);
    132                         return ENOMEM;
    133                 }
    134         }
    135 
    136         /* NOTE: OHCI is capable of handling buffer that crosses page boundaries
    137          * it is, however, not capable of handling buffer that occupies more
    138          * than two pages (the first page is computed using start pointer, the
    139          * other using the end pointer) */
    140         if (setup_size + usb_batch->buffer_size > 0) {
    141                 /* Use one buffer for setup and data stage */
    142                 ohci_batch->device_buffer =
    143                     malloc32(setup_size + usb_batch->buffer_size);
    144                 if (!ohci_batch->device_buffer) {
    145                         usb_log_error("Failed to allocate device buffer");
    146                         return ENOMEM;
    147                 }
    148                 /* Copy setup data */
    149                 memcpy(ohci_batch->device_buffer, usb_batch->setup.buffer, setup_size);
    150                 /* Copy generic data */
    151                 if (usb_batch->dir == USB_DIRECTION_OUT)
    152                         memcpy(
    153                             ohci_batch->device_buffer + setup_size,
    154                             usb_batch->buffer, usb_batch->buffer_size);
    155         }
    156 
    157         assert(batch_setup[usb_batch->ep->transfer_type]);
     109
     110        const size_t td_size = ohci_batch->td_count * sizeof(td_t);
     111        const size_t setup_size = (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL)
     112                ? USB_SETUP_PACKET_SIZE
     113                : 0;
     114
     115        if (dma_buffer_alloc(&ohci_batch->ohci_dma_buffer, td_size + setup_size + usb_batch->buffer_size)) {
     116                usb_log_error("Failed to allocate OHCI DMA buffer.");
     117                return ENOMEM;
     118        }
     119
     120        td_t *tds = ohci_batch->ohci_dma_buffer.virt;
     121
     122        for (size_t i = 0; i < ohci_batch->td_count; i++)
     123                ohci_batch->tds[i] = &tds[i];
     124
     125        /* Presence of this terminator makes TD initialization easier */
     126        ohci_batch->tds[ohci_batch->td_count] = NULL;
     127
     128        ohci_batch->setup_buffer = (void *) (&tds[ohci_batch->td_count]);
     129        memcpy(ohci_batch->setup_buffer, usb_batch->setup.buffer, setup_size);
     130
     131        ohci_batch->data_buffer = ohci_batch->setup_buffer + setup_size;
     132        if (usb_batch->dir == USB_DIRECTION_OUT)
     133                memcpy(ohci_batch->data_buffer, usb_batch->buffer, usb_batch->buffer_size);
     134
    158135        batch_setup[usb_batch->ep->transfer_type](ohci_batch);
    159136
     
    174151        assert(ohci_batch);
    175152
     153        ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ohci_batch->base.ep);
     154        assert(ohci_ep);
     155
    176156        usb_log_debug("Batch %p checking %zu td(s) for completion.",
    177157            &ohci_batch->base, ohci_batch->td_count);
    178158        usb_log_debug2("ED: %08x:%08x:%08x:%08x.",
    179             ohci_batch->ed->status, ohci_batch->ed->td_head,
    180             ohci_batch->ed->td_tail, ohci_batch->ed->next);
    181 
    182         if (!ed_inactive(ohci_batch->ed) && ed_transfer_pending(ohci_batch->ed))
     159            ohci_ep->ed->status, ohci_ep->ed->td_head,
     160            ohci_ep->ed->td_tail, ohci_ep->ed->next);
     161
     162        if (!ed_inactive(ohci_ep->ed) && ed_transfer_pending(ohci_ep->ed))
    183163                return false;
    184164
     
    188168        /* Assume all data got through */
    189169        ohci_batch->base.transferred_size = ohci_batch->base.buffer_size;
    190 
    191         /* Assume we will leave the last(unused) TD behind */
    192         unsigned leave_td = ohci_batch->td_count;
    193170
    194171        /* Check all TDs */
     
    217194                } else {
    218195                        usb_log_debug("Batch %p found error TD(%zu):%08x.",
    219                             &ohci_batch->base, i,
    220                             ohci_batch->tds[i]->status);
     196                            &ohci_batch->base, i, ohci_batch->tds[i]->status);
    221197
    222198                        /* ED should be stopped because of errors */
    223                         assert((ohci_batch->ed->td_head & ED_TDHEAD_HALTED_FLAG) != 0);
    224 
    225                         /* Now we have a problem: we don't know what TD
    226                          * the head pointer points to, the retiring rules
    227                          * described in specs say it should be the one after
    228                          * the failed one so set the tail pointer accordingly.
    229                          * It will be the one TD we leave behind.
     199                        assert((ohci_ep->ed->td_head & ED_TDHEAD_HALTED_FLAG) != 0);
     200
     201                        /* We don't care where the processing stopped, we just
     202                         * need to make sure it's not using any of the TDs owned
     203                         * by the transfer.
     204                         *
     205                         * As the chain is terminated by a TD in ownership of
     206                         * the EP, set it.
    230207                         */
    231                         leave_td = i + 1;
    232 
    233                         /* Check TD assumption */
    234                         assert(ed_head_td(ohci_batch->ed) ==
    235                             addr_to_phys(ohci_batch->tds[leave_td]));
    236 
    237                         /* Set tail to the same TD */
    238                         ed_set_tail_td(ohci_batch->ed,
    239                             ohci_batch->tds[leave_td]);
    240 
    241                         /* Clear possible ED HALT */
    242                         ed_clear_halt(ohci_batch->ed);
     208                        ed_set_head_td(ohci_ep->ed, ohci_ep->tds[0]);
     209
     210                        /* Clear the halted condition for the next transfer */
     211                        ed_clear_halt(ohci_ep->ed);
    243212                        break;
    244213                }
     
    247216            ohci_batch->base.buffer_size);
    248217
    249         const size_t setup_size = (ohci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
    250                 ? USB_SETUP_PACKET_SIZE
    251                 : 0;
    252 
    253218        if (ohci_batch->base.dir == USB_DIRECTION_IN)
    254219                memcpy(ohci_batch->base.buffer,
    255                     ohci_batch->device_buffer + setup_size,
     220                    ohci_batch->data_buffer,
    256221                    ohci_batch->base.transferred_size);
    257222
    258         /* Store the remaining TD */
     223        /* Make sure that we are leaving the right TD behind */
     224        assert(addr_to_phys(ohci_ep->tds[0]) == ed_tail_td(ohci_ep->ed));
     225        assert(ed_tail_td(ohci_ep->ed) == ed_head_td(ohci_ep->ed));
     226
     227        return true;
     228}
     229
     230/** Starts execution of the TD list
     231 *
     232 * @param[in] ohci_batch Batch structure to use
     233 */
     234void ohci_transfer_batch_commit(const ohci_transfer_batch_t *ohci_batch)
     235{
     236        assert(ohci_batch);
     237
    259238        ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ohci_batch->base.ep);
    260         assert(ohci_ep);
    261         ohci_ep->td = ohci_batch->tds[leave_td];
    262 
    263         /* Make sure that we are leaving the right TD behind */
    264         assert(addr_to_phys(ohci_ep->td) == ed_head_td(ohci_batch->ed));
    265         assert(addr_to_phys(ohci_ep->td) == ed_tail_td(ohci_batch->ed));
    266 
    267         return true;
    268 }
    269 
    270 /** Starts execution of the TD list
    271  *
    272  * @param[in] ohci_batch Batch structure to use
    273  */
    274 void ohci_transfer_batch_commit(const ohci_transfer_batch_t *ohci_batch)
    275 {
    276         assert(ohci_batch);
    277         ed_set_tail_td(ohci_batch->ed, ohci_batch->tds[ohci_batch->td_count]);
     239
     240        usb_log_debug("Using ED(%p): %08x:%08x:%08x:%08x.", ohci_ep->ed,
     241            ohci_ep->ed->status, ohci_ep->ed->td_tail,
     242            ohci_ep->ed->td_head, ohci_ep->ed->next);
     243
     244        /*
     245         * According to spec, we need to copy the first TD to the currently
     246         * enqueued one.
     247         */
     248        memcpy(ohci_ep->tds[0], ohci_batch->tds[0], sizeof(td_t));
     249        ohci_batch->tds[0] = ohci_ep->tds[0];
     250
     251        td_t *last = ohci_batch->tds[ohci_batch->td_count - 1];
     252        td_set_next(last, ohci_ep->tds[1]);
     253
     254        ed_set_tail_td(ohci_ep->ed, ohci_ep->tds[1]);
     255
     256        /* Swap the EP TDs for the next transfer */
     257        td_t *tmp = ohci_ep->tds[0];
     258        ohci_ep->tds[0] = ohci_ep->tds[1];
     259        ohci_ep->tds[1] = tmp;
    278260}
    279261
     
    294276        assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
    295277
    296         usb_log_debug("Using ED(%p): %08x:%08x:%08x:%08x.", ohci_batch->ed,
    297             ohci_batch->ed->status, ohci_batch->ed->td_tail,
    298             ohci_batch->ed->td_head, ohci_batch->ed->next);
    299278        static const usb_direction_t reverse_dir[] = {
    300279                [USB_DIRECTION_IN]  = USB_DIRECTION_OUT,
     
    303282
    304283        int toggle = 0;
    305         const char* buffer = ohci_batch->device_buffer;
    306284        const usb_direction_t data_dir = dir;
    307285        const usb_direction_t status_dir = reverse_dir[dir];
     
    310288        td_init(
    311289            ohci_batch->tds[0], ohci_batch->tds[1], USB_DIRECTION_BOTH,
    312             buffer, USB_SETUP_PACKET_SIZE, toggle);
     290            ohci_batch->setup_buffer, USB_SETUP_PACKET_SIZE, toggle);
    313291        usb_log_debug("Created CONTROL SETUP TD: %08x:%08x:%08x:%08x.",
    314292            ohci_batch->tds[0]->status, ohci_batch->tds[0]->cbp,
    315293            ohci_batch->tds[0]->next, ohci_batch->tds[0]->be);
    316         buffer += USB_SETUP_PACKET_SIZE;
    317294
    318295        /* Data stage */
    319296        size_t td_current = 1;
     297        const char* buffer = ohci_batch->data_buffer;
    320298        size_t remain_size = ohci_batch->base.buffer_size;
    321299        while (remain_size > 0) {
     
    370348        usb_direction_t dir = ohci_batch->base.dir;
    371349        assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
    372         usb_log_debug("Using ED(%p): %08x:%08x:%08x:%08x.", ohci_batch->ed,
    373             ohci_batch->ed->status, ohci_batch->ed->td_tail,
    374             ohci_batch->ed->td_head, ohci_batch->ed->next);
    375350
    376351        size_t td_current = 0;
    377352        size_t remain_size = ohci_batch->base.buffer_size;
    378         char *buffer = ohci_batch->device_buffer;
     353        char *buffer = ohci_batch->data_buffer;
    379354        while (remain_size > 0) {
    380355                const size_t transfer_size = remain_size > OHCI_TD_MAX_TRANSFER
Note: See TracChangeset for help on using the changeset viewer.