Package org.jnode.driver.ps2

Source Code of org.jnode.driver.ps2.PS2Bus

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

import java.security.PrivilegedExceptionAction;

import javax.naming.NameNotFoundException;

import org.apache.log4j.Logger;
import org.jnode.driver.Bus;
import org.jnode.driver.DeviceException;
import org.jnode.driver.DriverException;
import org.jnode.naming.InitialNaming;
import org.jnode.system.resource.IOResource;
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.system.resource.ResourceOwner;
import org.jnode.util.AccessControllerUtils;
import org.jnode.util.NumberUtils;

/**
* Provides common functionality shared by the drivers (read/write data/status)
*
* @author qades
*/
public class PS2Bus extends Bus implements IRQHandler, PS2Constants {

    /** My logger */
    private static final Logger log = Logger.getLogger(PS2Bus.class);
    private IOResource ioResData;
    private IOResource ioResCtrl;
    private int activeCount = 0;
    private final PS2ByteChannel kbChannel = new PS2ByteChannel();
    private final PS2ByteChannel mouseChannel = new PS2ByteChannel();

    /**
     * Create a PS2 object.
     */
    PS2Bus(Bus parent) {
        super(parent);
    }

    /**
     * All necessary resources are claimed in this method.
     *
     * @param owner the driver for which the resources are to be claimed
     * @return the IRQResource for the driver in question
     */
    final synchronized IRQResource claimResources(ResourceOwner owner, int irq)
        throws DriverException {
        try {
            final ResourceManager rm;
            try {
                rm = InitialNaming.lookup(ResourceManager.NAME);
            } catch (NameNotFoundException ex) {
                throw new DriverException("Cannot find ResourceManager: ", ex);
            }
            if (ioResData == null) {
                ioResData = claimPorts(rm, owner, PS2_DATA_PORT, 1);
                ioResCtrl = claimPorts(rm, owner, PS2_CTRL_PORT, 1);
            }
            final IRQResource irqRes = rm.claimIRQ(owner, irq, this, true);
            if (activeCount == 0) {
                flush();
            }
            activeCount++;
            return irqRes;
        } catch (ResourceNotFreeException ex) {
            throw new DriverException("Cannot claim necessairy resources: ", ex);
        }
    }

    /**
     * Release all resource held by this bus, only if all devices that depend on
     * this bus have been stopped.
     */
    final synchronized void releaseResources() {
        activeCount--;
        if (activeCount == 0) {
            ioResData.release();
            ioResCtrl.release();
            ioResData = null;
            ioResCtrl = null;
        }
    }

    /**
     * Handles a PS/2 interrupt
     *
     * @see org.jnode.system.resource.IRQHandler#handleInterrupt(int)
     */
    public final synchronized void handleInterrupt(int irq) {
        processQueues();
    }

    /**
     * Read the queue until it is empty and process the read data.
     */
    private final void processQueues() {
        int status;
        int loops = 0;
        while (((status = readStatus()) & STAT_OBF) != 0) {
            final int data = readData();
            if (++loops > 1000) {
                log.error("A lot of PS2 data, probably wrong, dropping it");
                continue;
            }

            // determine which driver shall handle the scancode
            final PS2ByteChannel channel;
            if ((status & STAT_MOUSE_OBF) != 0) {
                channel = mouseChannel;
            } else {
                channel = kbChannel;
            }
            // if this driver is not registered, merely exit
            if (channel == null) {
                log.debug("Unhandled scancode 0x" + Integer.toHexString(data) + " status=0x" +
                        Integer.toHexString(status));
            } else {
                // let the driver handle the scancode
                channel.handleScancode(data);
            }
        }
    }

    /**
     * Flush the PS2 output buffer.
     */
    final synchronized void flush() {
        while ((readStatus() & STAT_OBF) != 0) {
            readData();
        }
    }

    /**
     * Get the status of the PS/2 port.
     *
     * @return the status byte
     */
    final int readStatus() {
        return ioResCtrl.inPortByte(PS2_STAT_PORT) & 0xff;
    }

    /**
     * Write a byte to the control port
     *
     * @param b
     */
    private void writeController(int b) throws DeviceException {
        waitWrite();
        ioResCtrl.outPortByte(PS2_CTRL_PORT, b);
    }

    /**
     * Write a byte to the data port
     *
     * @param b
     */
    private final void writeData(int b) throws DeviceException {
        waitWrite();
        ioResData.outPortByte(PS2_DATA_PORT, b);
    }

    /**
     * Wait for a data available in outputbuffer.
     */
    private final boolean waitRead() {
        int count = 0;
        while (count < 1000) {
            if ((readStatus() & STAT_OBF) != 0) {
                return true;
            } else {
                count++;
                Thread.yield();
            }
        }
        return false;
    }

    /**
     * Wait for a non-ready inputbuffer.
     */
    private final void waitWrite() throws DeviceException {
        int count = 0;
        while (count < 1000) {
            if ((readStatus() & STAT_IBF) == 0) {
                return;
            } else {
                count++;
                Thread.yield();
            }
        }
        throw new DeviceException("InputBuffer full");
    }

    /**
     * Read a byte from the data port
     *
     * @return
     */
    private final int readData() {
        return ioResData.inPortByte(PS2_DATA_PORT) & 0xff;
    }

    /**
     * Gets the mode register
     *
     * @return int
     */
    final synchronized int getMode() throws DeviceException {
        try {
            writeController(CCMD_READ_MODE);
            if (waitRead()) {
                return readData();
            } else {
                throw new DeviceException("Not return data from READ_MODE");
            }
        } finally {
            processQueues();
        }
    }

    /**
     * Sets the mode register
     */
    final void setMode(int mode) throws DeviceException {
        writeController(CCMD_WRITE_MODE);
        writeData(mode);
    }

    /**
     * Test for the presence of a connected mouse device
     *
     * @return true if a mouse is present, false if not
     */
    final synchronized boolean testMouse() throws DeviceException {
        try {
            writeController(CCMD_TEST_MOUSE);
            if (waitRead()) {
                final int status = readStatus();
                final int rc = readData();
                log.debug("testMouse rc=0x" + NumberUtils.hex(rc, 2) + ", status 0x" +
                        NumberUtils.hex(status, 2));
                return (rc != 0xFF);
            } else {
                log.debug("No return from TEST_MOUSE");
                return false;
            }
        } finally {
            processQueues();
        }
    }

    /**
     * Activate/Deactivate the mouse
     */
    final void setMouseEnabled(boolean enable) throws DeviceException {
        writeController(enable ? CCMD_MOUSE_ENABLE : CCMD_MOUSE_DISABLE);
        int mode = getMode();
        if (enable) {
            mode |= MODE_MOUSE_INT;
        } else {
            mode &= ~MODE_MOUSE_INT;
        }
        setMode(mode);
    }

    /**
     * Activate/Deactivate the keyboard
     */
    final void setKeyboardEnabled(boolean enable) throws DeviceException {
        writeController(enable ? CCMD_KB_ENABLE : CCMD_KB_DISABLE);
        int mode = getMode();
        if (enable) {
            mode |= MODE_INT;
        } else {
            mode &= ~MODE_INT;
        }
        setMode(mode);
    }

    /**
     * Read an ACK and cnt bytes. The bytes (except the ACK) are added to the
     * given channel.
     *
     * @param channel
     * @param cnt
     * @return
     */
    private final boolean readAckAndData(PS2ByteChannel channel, int cnt) {
        if (!waitRead()) {
            return false;
        }
        int data = readData();
        switch (data) {
            case REPLY_ACK:
                break;
            case REPLY_RESEND:
                log.error("Mouse replied with RESEND");
                return false;
            default:
                // Not an ACK, consider it data
                channel.handleScancode(data);
                cnt--;
                break;
        }
        while (cnt > 0) {
            if (!waitRead()) {
                return false;
            }
            data = readData();
            channel.handleScancode(data);
            cnt--;
        }
        return true;
    }

    /**
     * Send a single byte command to the mouse
     *
     * @param cmd
     * @return Success (true / false)
     */
    private final boolean sendMouseCommand(int cmd, int returnCnt) throws DeviceException {
        // Transmit the command
        writeController(CCMD_WRITE_MOUSE);
        writeData(cmd);
        return readAckAndData(mouseChannel, returnCnt);
    }

    /**
     * Write a series of commands to the mouse
     *
     * @param cmd The command
     * @param params The command parameters
     * @return
     */
    final synchronized boolean writeMouseCommands(int cmd, int[] params, int returnCnt)
        throws DeviceException {
        // First clear the mouse channel, otherwise we might read
        // old data back
        mouseChannel.clear();

        if (params == null) {
            return sendMouseCommand(cmd, returnCnt);
        } else {
            if (!sendMouseCommand(cmd, 0)) {
                return false;
            }
            final int cnt = params.length;
            for (int i = 0; i < cnt - 1; i++) {
                if (!sendMouseCommand(params[i], 0)) {
                    return false;
                }
            }
            return sendMouseCommand(params[cnt - 1], returnCnt);
        }
    }

    /**
     * @return
     */
    final PS2ByteChannel getKbChannel() {
        return this.kbChannel;
    }

    /**
     * @return
     */
    final PS2ByteChannel getMouseChannel() {
        return this.mouseChannel;
    }

    private IOResource claimPorts(final ResourceManager rm, final ResourceOwner owner,
            final int low, final int length) throws ResourceNotFreeException, DriverException {
        try {
            return AccessControllerUtils.doPrivileged(new PrivilegedExceptionAction<IOResource>() {
                public IOResource run() throws ResourceNotFreeException {
                    return rm.claimIOResource(owner, low, length);
                }
            });
        } catch (ResourceNotFreeException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new DriverException("Unknown exception", ex);
        }

    }
}
TOP

Related Classes of org.jnode.driver.ps2.PS2Bus

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.