Package org.jnode.driver.bus.usb.uhci

Source Code of org.jnode.driver.bus.usb.uhci.UHCIPipe

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.driver.bus.usb.uhci;

import java.util.ArrayList;
import org.apache.log4j.Logger;
import org.jnode.driver.bus.usb.USBConstants;
import org.jnode.driver.bus.usb.USBDevice;
import org.jnode.driver.bus.usb.USBEndPoint;
import org.jnode.driver.bus.usb.USBException;
import org.jnode.driver.bus.usb.USBPipe;
import org.jnode.driver.bus.usb.USBPipeListener;
import org.jnode.driver.bus.usb.USBRequest;
import org.jnode.driver.bus.usb.spi.AbstractUSBRequest;
import org.jnode.system.resource.ResourceManager;
import org.jnode.util.NumberUtils;
import org.jnode.util.Queue;

/**
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
public class UHCIPipe implements USBPipe, USBConstants {

    /**
     * My logger
     */
    private static final Logger log = Logger.getLogger(UHCIPipe.class);
    /**
     * The pipe manager
     */
    private final UHCIPipeManager pm;
    /**
     * The resource manager
     */
    private final ResourceManager rm;
    /**
     * The transfer type of this pipe
     */
    private final int transferType;
    /**
     * The device of this pipe
     */
    protected final USBDevice device;
    /**
     * The endpoint number for this pipe
     */
    protected final int endPointNum;
    /**
     * The endpoint for this pipe
     */
    private final USBEndPoint endPoint;
    /**
     * Is this pipe open
     */
    private boolean open;
    /**
     * The skeleton QueueHead where qh will be linked to
     */
    private final QueueHead skelQH;
    /**
     * The QueueHead where my requests will be added to
     */
    private final QueueHead qh;
    /**
     * List of waiting requests
     */
    private final Queue<USBRequest> queue;
    /**
     * The active request
     */
    private UHCIRequest activeRequest;
    /**
     * My listeners
     */
    private ArrayList<USBPipeListener> listeners = new ArrayList<USBPipeListener>();
    /**
     * The maximum packet size, or -1 if not set
     */
    private final int maxPktSize;

    /**
     * Create a new instance
     *
     * @param pm
     * @param rm
     * @param device
     * @param ep
     * @param transferType
     * @param skelQH
     */
    public UHCIPipe(UHCIPipeManager pm, ResourceManager rm, USBDevice device, USBEndPoint ep, int transferType,
                    QueueHead skelQH) {
        this.pm = pm;
        this.rm = rm;
        this.device = device;
        this.endPoint = ep;
        this.transferType = transferType;
        this.endPointNum = (endPoint != null) ? endPoint.getDescriptor().getEndPointNumber() : 0;
        this.qh = new QueueHead(rm);
        this.skelQH = skelQH;
        this.queue = new Queue<USBRequest>();
        this.maxPktSize = (endPoint != null) ? endPoint.getDescriptor().getMaxPacketSize() : -1;
    }

    /**
     * Is this a control pipe.
     */
    public final boolean isControlPipe() {
        return (transferType == USB_ENDPOINT_XFER_CONTROL);
    }

    /**
     * Is this an interrupt pipe.
     */
    public final boolean isInterruptPipe() {
        return (transferType == USB_ENDPOINT_XFER_INT);
    }

    /**
     * Is this a isochronous pipe.
     */
    public boolean isIsochronousPipe() {
        return (transferType == USB_ENDPOINT_XFER_ISOC);
    }

    /**
     * Is this a bulk pipe.
     */
    public boolean isBulkPipe() {
        return (transferType == USB_ENDPOINT_XFER_BULK);
    }

    /**
     * Is this pipe open.
     */
    public final boolean isOpen() {
        return this.open;
    }

    /**
     * Open this pipe.
     *
     * @throws USBException
     */
    public void open() throws USBException {
        if (!this.open) {
            pm.add(this);
            skelQH.insertLink(qh);
            this.open = true;
        }
    }

    /**
     * Close this pipe.
     */
    public void close() {
        if (this.open) {
            skelQH.removeLink(qh);
            pm.remove(this);
            this.open = false;
        }
    }

    /**
     * Submit a given request via this pipe and return immediately.
     *
     * @param request
     */
    public synchronized void asyncSubmit(USBRequest request) throws USBException {
        if (!open) {
            throw new USBException("Not open");
        }
        if (!(request instanceof UHCIRequest)) {
            throw new IllegalArgumentException("Invalid request type (IUHCIRequest)");
        }
        if (!(request instanceof AbstractUSBRequest)) {
            throw new IllegalArgumentException("Invalid request type (AbstractUSBRequest)");
        }
        final UHCIRequest req = (UHCIRequest) request;
        final AbstractUSBRequest usbReq = (AbstractUSBRequest) request;
        usbReq.setCompleted(false);
        usbReq.setActualLength(0);
        usbReq.setStatus(0);
        if (qh.isEmpty()) {
            activateRequest(req);
        } else {
            queue.add(req);
        }
    }

    /**
     * Submit a given request via this pipe and wait for it to complete.
     *
     * @param request
     * @param timeout
     */
    public void syncSubmit(USBRequest request, long timeout) throws USBException {
        asyncSubmit(request);
        request.waitUntilComplete(timeout);
        if (!request.isCompleted()) {
            throw new USBException("Timeout on request");
        }
        final int status = request.getStatus();
        if ((status & USBREQ_ST_ERROR_MASK) != 0) {
            throw new USBException("USB error 0x" + NumberUtils.hex(status));
        }
    }

    /**
     * Add a listener to this pipe.
     *
     * @param listener
     */
    public synchronized void addListener(USBPipeListener listener) {
        listeners.add(listener);
    }

    /**
     * Remove a listener from this pipe.
     *
     * @param listener
     */
    public synchronized void removeListener(USBPipeListener listener) {
        listeners.remove(listener);
    }

    /**
     * Handle an interrupt.
     */
    protected final synchronized void handleInterrupt() {
        if (this.open) {
            final UHCIRequest req = this.activeRequest;
            if (req != null) {
                handleInterrupt(req);
                if (req.isCompleted()) {
                    qh.remove(req);
                    this.activeRequest = null;
                    // If interrupt, then restart the interrupt
                    if (isInterruptPipe() && open) {
                        try {
                            asyncSubmit(req);
                        } catch (USBException ex) {
                            // Ignore
                        }
                    }
                }
            }
            if ((!queue.isEmpty()) && qh.isEmpty()) {
                final UHCIRequest nextReq = (UHCIRequest) queue.get();
                try {
                    activateRequest(nextReq);
                } catch (USBException ex) {
                    final AbstractUSBRequest areq = (AbstractUSBRequest) nextReq;
                    areq.setStatus(USBConstants.USBREQ_ST_BITSTUFF);
                    areq.setCompleted(true);
                    log.error("Unknown errr in activateRequest", ex);
                }
            }
        }
    }

    /**
     * Active the given request
     *
     * @param req
     */
    private final void activateRequest(UHCIRequest req)
        throws USBException {
        TransferDescriptor td = req.getFirstTD();
        if (td == null) {
            //log.debug("create");
            // It is a new request, create the TD's
            req.createTDs(this);
        } else {
            //log.debug("recycle");
            // It is a recycled request, reset the TD's
            while (td != null) {
                td.resetStatus();
                td = td.getNextTD();
            }
        }
        this.activeRequest = req;
        //log.debug("add");
        qh.add(req);
        //log.debug("activateRequest done");
    }

    /**
     * An interrupt has occurred, see if all TD's are finished and if so, notify all waiting
     * threads.
     */
    private final void handleInterrupt(UHCIRequest request) {
        //log.debug("handleInterrupt");
        // Go through all the TD's and look for active ones and error ones.
        TransferDescriptor errorTD = null;
        TransferDescriptor td = request.getFirstTD();
        int actualLength = 0;
        while ((td != null) && (errorTD == null)) {
            if (td.isAnyError()) {
                //log.debug("Found error TD");
                errorTD = td;
            } else if (td.isActive()) {
                //log.debug("Found active TD");
                return;
            } else {
                td = td.getNextTD();
            }
        }
        // Throw the corresponding exception in case of errors.
        int status = 0;
        if (errorTD != null) {
            if (errorTD.isStalled()) {
                status |= USBREQ_ST_STALLED;
            }
            if (errorTD.isDataBufferError()) {
                status |= USBREQ_ST_DATABUFFER;
            }
            if (errorTD.isBabbleDetected()) {
                status |= USBREQ_ST_BABBLE;
            }
            if (errorTD.isNAKReceived()) {
                status |= USBREQ_ST_NAK;
            }
            if (errorTD.isCRCTimeOutError()) {
                status |= USBREQ_ST_TIMEOUT;
            }
            if (errorTD.isBitstuffError()) {
                status |= USBREQ_ST_BITSTUFF;
            }
        } else {
            status |= USBREQ_ST_COMPLETED;
        }
        final AbstractUSBRequest usbReq = (AbstractUSBRequest) request;
        usbReq.setStatus(status);
        usbReq.setActualLength(actualLength);
        usbReq.setCompleted(true);
        firePipeEvent(request);
    }

    /**
     * Fire a pipe event to all my listeners
     *
     * @param request
     */
    protected void firePipeEvent(UHCIRequest request) {
        final int max = listeners.size();
        final int status = request.getStatus();
        final boolean error = ((status & USBREQ_ST_ERROR_MASK) != 0);
        if (error && isInterruptPipe()) {
            if ((status & USBREQ_ST_NAK) != 0) {
                // No interrupt, ignore event
                return;
            }
        }
        for (int i = 0; i < max; i++) {
            USBPipeListener l = (USBPipeListener) listeners.get(i);
            if (error) {
                l.requestFailed(request);
            } else {
                l.requestCompleted(request);
            }
        }
    }

    /**
     * Create a transfer descriptor for use on this pipe.
     *
     * @param packetId
     * @param data0
     * @param dataBuffer
     * @param dataBufferOffset
     * @param bufLength
     * @param ioc
     * @return The new TD
     */
    protected TransferDescriptor createTD(int packetId, boolean data0, byte[] dataBuffer, int dataBufferOffset,
                                          int bufLength, boolean ioc) {
        final int devAddr = device.getUSBDeviceId();
        final boolean iso = isIsochronousPipe();
        final boolean ls = device.isLowSpeed();
        return new TransferDescriptor(rm, devAddr, endPointNum, packetId, data0, dataBuffer, dataBufferOffset,
            bufLength, iso, ls, ioc);
    }

    /**
     * Gets the maximum packet size to use in this pipe.
     */
    protected final int getMaxPacketSize() {
        if (maxPktSize > 0) {
            return maxPktSize;
        } else {
            return device.getMaxPacketSize(endPointNum);
        }
    }

    /**
     * @return Returns the endPoint.
     */
    public USBEndPoint getEndPoint() {
        return this.endPoint;
    }

}
TOP

Related Classes of org.jnode.driver.bus.usb.uhci.UHCIPipe

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.