Fork us on GitHub Follow us on Facebook Follow us on Twitter

Changeset e67c50a in mainline


Ignore:
Timestamp:
2018-02-01T21:13:23Z (4 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial
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.

Location:
uspace/drv/bus/usb/ohci
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/ohci/hw_struct/endpoint_descriptor.h

    r3e6ff9a re67c50a  
    165165
    166166/**
     167 * Set the HeadP of ED. Do not call unless the ED is Halted.
     168 * @param instance ED
     169 */
     170static inline void ed_set_head_td(ed_t *instance, const td_t *td)
     171{
     172        assert(instance);
     173        const uintptr_t pa = addr_to_phys(td);
     174        OHCI_MEM32_WR(instance->td_head, pa & ED_TDHEAD_PTR_MASK);
     175}
     176
     177/**
    167178 * Set next ED in ED chain.
    168179 * @param instance ED to modify
  • uspace/drv/bus/usb/ohci/hw_struct/transfer_descriptor.c

    r3e6ff9a re67c50a  
    8888        }
    8989
     90        td_set_next(instance, next);
     91}
     92
     93void td_set_next(td_t *instance, const td_t *next)
     94{
    9095        OHCI_MEM32_WR(instance->next, addr_to_phys(next) & TD_NEXT_PTR_MASK);
     96}
    9197
    92 }
    9398/**
    9499 * @}
  • uspace/drv/bus/usb/ohci/hw_struct/transfer_descriptor.h

    r3e6ff9a re67c50a  
    9292} __attribute__((packed)) td_t;
    9393
    94 void td_init(td_t *instance, const td_t *next,
    95     usb_direction_t dir, const void *buffer, size_t size, int toggle);
     94void td_init(td_t *, const td_t *, usb_direction_t, const void *, size_t, int);
     95void td_set_next(td_t *, const td_t *);
    9696
    9797/**
  • 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
  • uspace/drv/bus/usb/ohci/ohci_batch.h

    r3e6ff9a re67c50a  
    3838#include <assert.h>
    3939#include <stdbool.h>
     40#include <usb/dma_buffer.h>
    4041#include <usb/host/usb_transfer_batch.h>
    4142
     
    4748        usb_transfer_batch_t base;
    4849
    49         /** Endpoint descriptor of the target endpoint. */
    50         ed_t *ed;
    51         /** List of TDs needed for the transfer */
    52         td_t **tds;
    5350        /** Number of TDs used by the transfer */
    5451        size_t td_count;
    55         /** Data buffer, must be accessible by the OHCI hw. */
    56         char *device_buffer;
     52
     53        /**
     54         * List of TDs needed for the transfer - together with setup data
     55         * backed by the dma buffer. Note that the TD pointers are pointing to
     56         * the DMA buffer initially, but as the scheduling must use the first TD
     57         * from EP, it is replaced.
     58         */
     59        td_t **tds;
     60        char *setup_buffer;
     61        char *data_buffer;
     62
     63        dma_buffer_t ohci_dma_buffer;
    5764} ohci_transfer_batch_t;
    5865
  • uspace/drv/bus/usb/ohci/ohci_bus.c

    r3e6ff9a re67c50a  
    7575        assert(dev);
    7676
    77         ohci_endpoint_t *ohci_ep = malloc(sizeof(ohci_endpoint_t));
     77        ohci_endpoint_t *ohci_ep = calloc(1, sizeof(ohci_endpoint_t));
    7878        if (ohci_ep == NULL)
    7979                return NULL;
     
    8181        endpoint_init(&ohci_ep->base, dev, desc);
    8282
    83         ohci_ep->ed = malloc32(sizeof(ed_t));
    84         if (ohci_ep->ed == NULL) {
     83        const errno_t err = dma_buffer_alloc(&ohci_ep->dma_buffer, sizeof(ed_t) + 2 * sizeof(td_t));
     84        if (err) {
    8585                free(ohci_ep);
    8686                return NULL;
    8787        }
    8888
    89         ohci_ep->td = malloc32(sizeof(td_t));
    90         if (ohci_ep->td == NULL) {
    91                 free32(ohci_ep->ed);
    92                 free(ohci_ep);
    93                 return NULL;
    94         }
     89        ohci_ep->ed = ohci_ep->dma_buffer.virt;
     90
     91        ohci_ep->tds[0] = (td_t *) ohci_ep->ed + 1;
     92        ohci_ep->tds[1] = ohci_ep->tds[0] + 1;
    9593
    9694        link_initialize(&ohci_ep->eplist_link);
     
    109107        ohci_endpoint_t *instance = ohci_endpoint_get(ep);
    110108
    111         free32(instance->ed);
    112         free32(instance->td);
     109        dma_buffer_free(&instance->dma_buffer);
    113110        free(instance);
    114111}
     
    125122                return err;
    126123
    127         ed_init(ohci_ep->ed, ep, ohci_ep->td);
     124        ed_init(ohci_ep->ed, ep, ohci_ep->tds[0]);
    128125        hc_enqueue_endpoint(bus->hc, ep);
    129126        endpoint_set_online(ep, &bus->hc->guard);
  • uspace/drv/bus/usb/ohci/ohci_bus.h

    r3e6ff9a re67c50a  
    3838#include <assert.h>
    3939#include <adt/list.h>
     40#include <usb/dma_buffer.h>
    4041#include <usb/host/usb2_bus.h>
    4142
     
    4344#include "hw_struct/transfer_descriptor.h"
    4445
    45 /** Connector structure linking ED to to prepared TD. */
     46/**
     47 * Connector structure linking ED to to prepared TD.
     48 *
     49 * OHCI requires new transfers to be appended at the end of a queue. But it has
     50 * a weird semantics of a leftover TD, which serves as a placeholder. This left
     51 * TD is overwritten with first TD of a new transfer, and the spare one is used
     52 * as the next placeholder. Then the two are swapped for the next transaction.
     53 */
    4654typedef struct ohci_endpoint {
    4755        endpoint_t base;
     
    4957        /** OHCI endpoint descriptor */
    5058        ed_t *ed;
    51         /** Currently enqueued transfer descriptor */
    52         td_t *td;
     59        /** TDs to be used at the beginning and end of transaction */
     60        td_t *tds [2];
     61
     62        /** Buffer to back ED + 2 TD */
     63        dma_buffer_t dma_buffer;
     64
    5365        /** Link in endpoint_list*/
    5466        link_t eplist_link;
Note: See TracChangeset for help on using the changeset viewer.