Package org.jnode.driver.bus.ide

Source Code of org.jnode.driver.bus.ide.IDEBus

/*
* $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.ide;

import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import org.apache.log4j.Logger;
import org.jnode.driver.Bus;
import org.jnode.driver.Device;
import org.jnode.driver.DriverException;
import org.jnode.driver.bus.ide.command.IDEIdCommand;
import org.jnode.naming.InitialNaming;
import org.jnode.system.resource.IRQHandler;
import org.jnode.system.resource.IRQResource;
import org.jnode.system.resource.ResourceManager;
import org.jnode.system.resource.ResourceNotFreeException;
import org.jnode.util.NumberUtils;
import org.jnode.util.Queue;
import org.jnode.util.QueueProcessor;
import org.jnode.util.QueueProcessorThread;
import org.jnode.util.TimeUtils;
import org.jnode.util.TimeoutException;

/**
* @author epr
*/
public class IDEBus extends Bus implements IDEConstants, IRQHandler,
    QueueProcessor<IDECommand> {

    /**
     * My logger
     */
    private static final Logger log = Logger.getLogger(IDEBus.class);

    private final IRQResource irqRes;

    /**
     * The command queue for IDE commands
     */
    private final Queue<IDECommand> commandQueue = new Queue<IDECommand>();

    /**
     * The command that is currently being processed
     */
    private IDECommand currentCommand;

    /**
     * The worker thread for the command queue
     */
    private final QueueProcessorThread<IDECommand> queueProcessor;

    /**
     * IO Accessor
     */
    private final IDEIO io;

    /**
     * Is the the primary (true) or secondary (false) channel
     */
    private final boolean primary;

    /**
     * Name of this bus
     */
    private final String name;

    /**
     * Create a new instance
     */
    protected IDEBus(Device parent, boolean primary)
        throws IllegalArgumentException, DriverException, ResourceNotFreeException {
        super(parent);
        this.name = parent.getId() + ':' + (primary ? "primary" : "secondary");
        this.primary = primary;
        IDEDeviceFactory factory;
        try {
            factory = IDEDriverUtils.getIDEDeviceFactory();
        } catch (NamingException ex) {
            throw new DriverException(ex);
        }
        this.io = factory.createIDEIO(parent, primary);

        // Register the irq handler
        final ResourceManager rm;
        try {
            rm = InitialNaming.lookup(ResourceManager.NAME);
        } catch (NameNotFoundException ex) {
            throw new ResourceNotFreeException("Cannot find ResourceManager",
                ex);
        }
        irqRes = rm.claimIRQ(parent, io.getIrq(), this, true);
        // Reset the controller
        softwareReset();
        // Create and start the queue processor
        queueProcessor = new QueueProcessorThread<IDECommand>(name, commandQueue, this);
        queueProcessor.start();

    }

    /**
     * Add the given command to the queue of commands to be executed.
     *
     * @param command
     */
    public void execute(IDECommand command) {
        commandQueue.add(command);
    }

    /**
     * Add the given command to the queue of commands to be executed and wait
     * for the command to finish.
     *
     * @param command
     * @param timeout Maximum time to wait
     * @throws InterruptedException
     * @throws TimeoutException
     */
    public void executeAndWait(IDECommand command, long timeout)
        throws InterruptedException, TimeoutException {
        execute(command);
        command.waitUntilFinished(timeout);
    }

    /**
     * Stop this processor.
     */
    public void stop() {
        queueProcessor.stopProcessor();
        irqRes.release();
        io.release();
    }

    /**
     * Handle the IDE interrupt.
     *
     * @param irq
     */
    public void handleInterrupt(int irq) {
        final IDECommand cmd = currentCommand;
        //log.debug("IDE IRQ " + irq + " cmd=" + cmd);
        if (cmd != null) {
            try {
                cmd.handleIRQ(this, io);
                if (cmd.isFinished()) {
                    this.currentCommand = null;
                }
            } catch (TimeoutException ex) {
                log.error("Timeout in handleIRQ of " + cmd);
                this.currentCommand = null;
                cmd.setError(ERR_ABORT);
            }
        } else if (log.isDebugEnabled()) {
            log.debug("Unknown IDE IRQ " + irq + " status 0x" + NumberUtils.hex(io.getAltStatusReg(), 2));
        }
    }

    /**
     * Probe for the existence of a given IDE device.
     *
     * @param master
     * @return the found IDEDriveDescriptor
     * @throws InterruptedException
     */
    public IDEDriveDescriptor probe(boolean master) throws InterruptedException {

        final String ideId = name + "." + (master ? "master" : "slave");
        log.debug("Probing " + ideId);
        if (!selectDrive(master)) {
            // Cannot select, so we give up
            log.debug("Cannot select drive " + ideId);
            return null;
        }

        // Interrupts enabled
        io.setControlReg(CTR_BLANK);

        // First try a normal IDE Identify command
        IDEIdCommand cmd = new IDEIdCommand(primary, master, false);
        try {
            executeAndWait(cmd, IDE_TIMEOUT);
        } catch (TimeoutException ex) {
            // Do nothing, handled later
        }
        if (cmd.isFinished()) {
            final IDEDriveDescriptor result = cmd.getResult();
            if (result != null) {
                return result;
            } else {
                if (!cmd.isPacketResponse()) {
                    // Not an ATAPI response, try it anyway.
                    log.debug("NON-Packet response from ID command");
                } else {
                    log.debug("Packet response from ID command");
                }
            }
        } else {
            // Finish the command
            cmd.setError(ERR_ABORT);
            // Force a reset of any IRQ
            final int state = io.getStatusReg();
            // Timeout
            log.debug("Timeout of " + ideId + " state=" + NumberUtils.hex(state));
            return null;
        }

        // Clear any interrupts
        io.getStatusReg();
        // Interrupts enabled
        io.setControlReg(CTR_BLANK);

        // IDE Identify failed, do an ATAPI Identify
        cmd = new IDEIdCommand(primary, master, true);
        try {
            executeAndWait(cmd, IDE_TIMEOUT);
        } catch (TimeoutException ex1) {
            // Do nothing, handled later
        }
        // Force a reset of any IRQ
        io.getStatusReg();
        // Good result?
        if (cmd.isFinished()) {
            return cmd.getResult();
        } else {
            // Finish the command
            cmd.setError(ERR_ABORT);
            log.debug("Probe of " + ideId + " done, return null");
            // No device found
            return null;
        }
    }

    protected void softwareReset() {
        // Set reset
        io.setControlReg(CTR_SRST);
        TimeUtils.sleep(5);
        // Reset reset
        io.setControlReg(CTR_BLANK);
        // Wait until not busy
        int loops = 5;
        while (loops-- > 0) {
            if ((io.getStatusReg() & ST_BUSY) == 0) {
                // Device is ready
                this.currentCommand = null;
                return;
            } else {
                TimeUtils.sleep(5);
            }
        }
        log.info("Software reset of ide " + (primary ? "pri" : "sec") + " failed, still busy");
    }

    /**
     * Select the master of slave drive
     *
     * @param master
     * @return True if the controller accepted the setting, false otherwise.
     */
    protected final boolean selectDrive(boolean master) {
        final int select = SEL_BLANK
            | (master ? SEL_DRIVE_MASTER : SEL_DRIVE_SLAVE);
        io.setSelectReg(select);
        // Wait a while
        TimeUtils.sleep(200);
        return (io.getSelectReg() == select);
    }

    /**
     * Is the current register state a packet response.
     *
     * @return if the current register state is a packet response
     */
    protected boolean isPacketResponse() {
        final int sectCount = io.getSectorCountReg();
        final int lbaLow = io.getLbaLowReg();
        final int lbaMid = io.getLbaMidReg();
        final int lbaHigh = io.getLbaHighReg();
        io.getSelectReg();
        return ((sectCount == 0x01) && (lbaLow == 0x01) && (lbaMid == 0x14) && (lbaHigh == 0xEB));
    }

    /**
     * Write data towards the device.
     * If the length is longer then the byte-array, 0 bytes are padded
     * after the byte-array.
     *
     * @param src
     * @param ofs
     * @param length
     */
    public final void writeData(byte[] src, int ofs, int length) {
        final int srcLen = src.length - ofs;
        int len = Math.min(length, srcLen);
        //waitUntilNotBusy();
        for (; len > 0; len -= 2, length -= 2) {
            final int v0 = src[ofs++] & 0xFF;
            final int v1 = src[ofs++] & 0xFF;
            io.setDataReg(v0 | (v1 << 8));
        }
        // Send padding
        for (; length > 0; length -= 2) {
            io.setDataReg(0);
        }
    }

    /**
     * Read data from the device.
     *
     * @param dst
     * @param ofs
     * @param length
     */
    public final void readData(byte[] dst, int ofs, int length) {
        final int srcLen = dst.length - ofs;
        int len = Math.min(length, srcLen);
        //waitUntilNotBusy();
        for (; len > 0; len -= 2, length -= 2) {
            final int v = io.getDataReg();
            dst[ofs++] = (byte) (v & 0xFF);
            dst[ofs++] = (byte) ((v >> 8) & 0xFF);
        }
        // Receive padding
        for (; length > 0; length -= 2) {
            io.getDataReg();
        }
    }

    /**
     * Process an IDE command from our queue.
     *
     * @see org.jnode.util.QueueProcessor#process(java.lang.Object)
     */
    public void process(IDECommand cmd) /*throws Exception*/ {
        // Wait until the controller is not busy anymore
        if (io.isBusy()) {
            try {
                io.waitUntilStatus(ST_BUSY, 0, IDE_DATA_XFER_TIMEOUT, null);
            } catch (TimeoutException ex) {
                log.debug("Controller still busy");
            }
        }

        // If there is a current command active, wait for it.
        final IDECommand current = this.currentCommand;
        if (current != null) {
            if (!current.isFinished()) {
                log.debug("process: wait for current.isFinished");
                try {
                    current.waitUntilFinished(IDE_DATA_XFER_TIMEOUT);
                } catch (InterruptedException ex) {
                    // Ignore
                } catch (TimeoutException ex) {
                    // Ignore
                }
            }
            if (!current.isFinished()) {
                log.error("Last command not finished in time: " + current + " error " + current.hasError());
                current.setError(ERR_ABORT);
                softwareReset();
            }
        }

        this.currentCommand = cmd;
        try {
            io.getStatusReg(); // Flush any pending IRQ
            cmd.setup(IDEBus.this, io);
        } catch (TimeoutException ex) {
            log.error("Timeout in setup of " + cmd + ": " + ex.getMessage());
            if ((io.getAltStatusReg() & ST_ERROR) != 0) {
                cmd.setError(io.getErrorReg());
            } else {
                cmd.setError(ERR_ABORT);
            }
            this.currentCommand = null;
        }
    }
}
TOP

Related Classes of org.jnode.driver.bus.ide.IDEBus

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.