Changeset a94cbfa in mainline


Ignore:
Timestamp:
2018-01-26T00:55:38Z (6 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
77733a9
Parents:
047fbc8
git-author:
Ondřej Hlavatý <aearsis@…> (2018-01-26 00:15:17)
git-committer:
Ondřej Hlavatý <aearsis@…> (2018-01-26 00:55:38)
Message:

ehci+ohci: proper handling of unfinished transfer

Because transfers can be aborted, the RH may not store a pointer to
them. Also, it's no longer true that the batch automatically locks the
endpoint, the mechanism must be used explicitly.

Location:
uspace/drv/bus/usb
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/ehci/ehci_rh.c

    r047fbc8 ra94cbfa  
    127127
    128128        ehci_rh_hub_desc_init(instance, EHCI_RD(caps->hcsparams));
    129         instance->unfinished_interrupt_transfer = NULL;
     129        instance->status_change_endpoint = NULL;
    130130
    131131        return virthub_base_init(&instance->base, name, &ops, instance,
     
    150150                usb_log_debug("RH(%p): BATCH(%p) adding as unfinished",
    151151                    instance, batch);
    152                 /* This is safe because only status change interrupt transfers
    153                  * return NAK. The assertion holds true because the batch
    154                  * existence prevents communication with that ep */
    155                 assert(instance->unfinished_interrupt_transfer == NULL);
    156                 instance->unfinished_interrupt_transfer = batch;
     152
     153                /* Lock the HC guard */
     154                fibril_mutex_lock(batch->ep->guard);
     155                const int err = endpoint_activate_locked(batch->ep, batch);
     156                if (err) {
     157                        fibril_mutex_unlock(batch->ep->guard);
     158                        return err;
     159                }
     160
     161                /*
     162                 * Asserting that the HC do not run two instances of the status
     163                 * change endpoint - shall be true.
     164                 */
     165                assert(!instance->status_change_endpoint);
     166
     167                endpoint_add_ref(batch->ep);
     168                instance->status_change_endpoint = batch->ep;
     169                fibril_mutex_unlock(batch->ep->guard);
    157170        } else {
    158171                usb_log_debug("RH(%p): BATCH(%p) virtual request complete: %s",
     
    172185int ehci_rh_interrupt(ehci_rh_t *instance)
    173186{
    174         //TODO atomic swap needed
    175         usb_transfer_batch_t *batch = instance->unfinished_interrupt_transfer;
    176         instance->unfinished_interrupt_transfer = NULL;
    177         usb_log_debug2("RH(%p): Interrupt. Processing batch: %p",
    178             instance, batch);
     187        endpoint_t *ep = instance->status_change_endpoint;
     188        if (!ep)
     189                return EOK;
     190
     191        fibril_mutex_lock(ep->guard);
     192        usb_transfer_batch_t * const batch = ep->active_batch;
     193        endpoint_deactivate_locked(ep);
     194        instance->status_change_endpoint = NULL;
     195        fibril_mutex_unlock(ep->guard);
     196
     197        endpoint_del_ref(ep);
     198
    179199        if (batch) {
     200                usb_log_debug2("RH(%p): Interrupt. Processing batch: %p",
     201                    instance, batch);
    180202                batch->error = virthub_base_request(&instance->base, batch->target,
    181203                    batch->dir, (void*) batch->setup.buffer,
  • uspace/drv/bus/usb/ehci/ehci_rh.h

    r047fbc8 ra94cbfa  
    6161                uint8_t rempow[STATUS_BYTES(EHCI_MAX_PORTS) * 2];
    6262        } __attribute__((packed)) hub_descriptor;
    63         /** interrupt transfer waiting for an actual interrupt to occur */
    64         usb_transfer_batch_t *unfinished_interrupt_transfer;
    6563        bool reset_flag[EHCI_MAX_PORTS];
    6664        bool resume_flag[EHCI_MAX_PORTS];
     65
     66        /*
     67         * This is sort of hacky, but better than duplicating functionality.
     68         * We cannot simply store a pointer to a transfer in-progress, in order
     69         * to allow it to be aborted. We can however store a reference to the
     70         * Status Change Endpoint. Note that this is mixing two worlds together
     71         * - otherwise, the RH is "a device" and have no clue about HC, apart
     72         * from accessing its registers.
     73         */
     74        endpoint_t *status_change_endpoint;
     75
    6776} ehci_rh_t;
    6877
  • uspace/drv/bus/usb/ohci/ohci_rh.c

    r047fbc8 ra94cbfa  
    162162
    163163        ohci_rh_hub_desc_init(instance);
    164         instance->unfinished_interrupt_transfer = NULL;
     164        instance->status_change_endpoint = NULL;
    165165        return virthub_base_init(&instance->base, name, &ops, instance,
    166166            NULL, &instance->hub_descriptor.header, HUB_STATUS_CHANGE_PIPE);
     
    182182            batch->buffer, batch->buffer_size, &batch->transferred_size);
    183183        if (batch->error == ENAK) {
    184                 /* This is safe because only status change interrupt transfers
    185                  * return NAK. The assertion holds true because the batch
    186                  * existence prevents communication with that ep */
    187                 assert(instance->unfinished_interrupt_transfer == NULL);
    188                 instance->unfinished_interrupt_transfer = batch;
     184                /* Lock the HC guard */
     185                fibril_mutex_lock(batch->ep->guard);
     186                const int err = endpoint_activate_locked(batch->ep, batch);
     187                if (err) {
     188                        fibril_mutex_unlock(batch->ep->guard);
     189                        return err;
     190                }
     191
     192                /*
     193                 * Asserting that the HC do not run two instances of the status
     194                 * change endpoint - shall be true.
     195                 */
     196                assert(!instance->status_change_endpoint);
     197
     198                endpoint_add_ref(batch->ep);
     199                instance->status_change_endpoint = batch->ep;
     200                fibril_mutex_unlock(batch->ep->guard);
    189201        } else {
    190202                usb_transfer_batch_finish(batch);
     
    202214int ohci_rh_interrupt(ohci_rh_t *instance)
    203215{
    204         //TODO atomic swap needed
    205         usb_transfer_batch_t *batch = instance->unfinished_interrupt_transfer;
    206         instance->unfinished_interrupt_transfer = NULL;
     216        endpoint_t *ep = instance->status_change_endpoint;
     217        if (!ep)
     218                return EOK;
     219
     220        fibril_mutex_lock(ep->guard);
     221        usb_transfer_batch_t * const batch = ep->active_batch;
     222        endpoint_deactivate_locked(ep);
     223        instance->status_change_endpoint = NULL;
     224        fibril_mutex_unlock(ep->guard);
     225
     226        endpoint_del_ref(ep);
     227
    207228        if (batch) {
    208229                batch->error = virthub_base_request(&instance->base, batch->target,
  • uspace/drv/bus/usb/ohci/ohci_rh.h

    r047fbc8 ra94cbfa  
    6161                uint8_t rempow[STATUS_BYTES(OHCI_MAX_PORTS) * 2];
    6262        } __attribute__((packed)) hub_descriptor;
    63         /** interrupt transfer waiting for an actual interrupt to occur */
    64         usb_transfer_batch_t *unfinished_interrupt_transfer;
     63        /** A hacky way to emulate interrupts over polling. See ehci_rh. */
     64        endpoint_t *status_change_endpoint;
    6565} ohci_rh_t;
    6666
Note: See TracChangeset for help on using the changeset viewer.