/* * $Id$ * * Copyright (C) 2003-2015 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.net.prism2; import static org.jnode.driver.net.prism2.Prism2Constants.Register.CMD; import static org.jnode.driver.net.prism2.Prism2Constants.Register.DATA0; import static org.jnode.driver.net.prism2.Prism2Constants.Register.EVACK; import static org.jnode.driver.net.prism2.Prism2Constants.Register.EVSTAT; import static org.jnode.driver.net.prism2.Prism2Constants.Register.OFFSET0; import static org.jnode.driver.net.prism2.Prism2Constants.Register.PARAM0; import static org.jnode.driver.net.prism2.Prism2Constants.Register.PARAM1; import static org.jnode.driver.net.prism2.Prism2Constants.Register.PARAM2; import static org.jnode.driver.net.prism2.Prism2Constants.Register.SELECT0; import static org.jnode.driver.net.prism2.Prism2Constants.Register.STATUS; import org.apache.log4j.Logger; import org.jnode.driver.DriverException; import org.jnode.system.resource.MemoryResource; import org.jnode.util.LittleEndian; import org.jnode.util.NumberUtils; import org.jnode.util.TimeUtils; import org.jnode.util.TimeoutException; /** * Class responsible for handling the low level I/O to the prism2 device. * * @author Ewout Prangsma (epr@users.sourceforge.net) */ final class Prism2IO implements Prism2Constants { /** * My logger */ private static final Logger log = Logger.getLogger(Prism2IO.class); /** * The memory mapped registers */ private final MemoryResource regs; /** * Initialize this instance. * * @param regs */ public Prism2IO(MemoryResource regs) { this.regs = regs; } /** * Release all resources. */ final void release() { regs.release(); } /** * Gets a register value. * * @param reg */ final int getReg(Register reg) { return regs.getShort(reg.getOffset()) & 0xFFFF; } /** * Gets a register value. * * @param reg */ final void setReg(Register reg, int value) { regs.setShort(reg.getOffset(), (short) value); } /** * Execute a command and wait until it has completed. * * @param cmd * @param parm0 * @param parm1 * @param parm2 * @return the RESULT code. */ final Result executeCommand(Command cmd, int cmdFlags, int parm0, int parm1, int parm2, Prism2CommandResponse response) throws TimeoutException { // Wait for the busy bit to clear waitUntilNotBusy(); // Write command setReg(PARAM0, parm0); setReg(PARAM1, parm1); setReg(PARAM2, parm2); setReg(CMD, cmd.getCode() | cmdFlags); // Wait until command completion waitUntilCommandCompleted(); // Read status and response final int status; if (response != null) { response.initialize(this); status = response.getStatus(); } else { status = getReg(STATUS); } // Acknowledge setReg(EVACK, EVACK_CMD); // Return the result code. return Result.getByCode((status & STATUS_RESULT) >> 8); } /** * Wait until a given event mask is reached, or a timeout occurs. * * @param eventMask * @param eventAck * @param wait * @param timeout */ final int waitForEvent(int eventMask, int eventAck, int wait, int timeout) { for (int counter = 0; counter < timeout; counter++) { final int reg = getReg(EVSTAT); if ((reg & eventMask) != 0) { // Acknowledge setReg(EVACK, reg & (eventMask | eventAck)); return reg; } TimeUtils.sleep(wait); } log.debug("Timeout in waitForEvent"); return 0; } /** * Wait until the device is no longer busy. * * @throws TimeoutException */ private final void waitUntilNotBusy() throws TimeoutException { for (int counter = 0; counter < 10; counter++) { final int cmd = getReg(CMD); if ((cmd & CMD_BUSY) == 0) { return; } else { TimeUtils.sleep(5); } } ; throw new TimeoutException("Prism2 still busy cmd=0x" + NumberUtils.hex(getReg(CMD), 4)); } /** * Wait until the command is completed. * * @throws TimeoutException */ private final void waitUntilCommandCompleted() throws TimeoutException { for (int counter = 0; counter < 200; counter++) { final int reg = getReg(EVSTAT); if ((reg & EVSTAT_CMD) != 0) { return; } else { TimeUtils.sleep(2); } } ; throw new TimeoutException("Prism2 still busy evstat=0x" + NumberUtils.hex(getReg(EVSTAT), 4)); } /** * Wait until the BAP is no longer busy. * * @throws TimeoutException */ private final void waitUntilBapNotBusy() throws TimeoutException { for (int counter = 0; counter < 100; counter++) { final int cmd = getReg(OFFSET0); if ((cmd & OFFSET_BUSY) == 0) { return; } else { TimeUtils.sleep(5); } } ; throw new TimeoutException("Prism2 still busy offset=0x" + NumberUtils.hex(getReg(OFFSET0), 4)); } /** * Throw an exception depending on the given result code. No exception is * thrown if the result is success. * * @param result * @throws DriverException */ final void resultToException(Result result) throws DriverException { switch (result) { case SUCCESS: return; case CARD_FAIL: throw new DriverException("Card failure"); case NO_BUFF: throw new DriverException("No buffer"); case CMD_ERR: throw new DriverException("Command error"); default: throw new DriverException("Unknown result code 0x" + NumberUtils.hex(result.getCode(), 2)); } } /** * Copy from the BAP into the given byte buffer. * * @param id FID or RID, destined for the select register (host order) * @param offset An _even_ offset into the buffer for the given FID/RID * @param dst Destination buffer * @param dstOffset Offset in destination buffer * @param len length of data to transfer in bytes * @throws DriverException */ final void copyFromBAP(int id, int offset, byte[] dst, int dstOffset, int len) throws DriverException { // Prepare the BAP prepareBAP(id, offset); // Read even(len) buf contents from data reg final int maxlen = len & 0xFFFE; for (int i = 0; i < maxlen; i += 2) { final int v = getReg(DATA0); LittleEndian.setInt16(dst, dstOffset + i, v); } // If len odd, handle last byte if ((len % 2) != 0) { final int v = getReg(DATA0); dst[dstOffset + len - 1] = (byte) (v & 0xFF); } } /** * Copy to the BAP from the given byte buffer. * * @param id FID or RID, destined for the select register (host order) * @param offset An _even_ offset into the buffer for the given FID/RID * @param src Source buffer * @param srcOffset Offset in source buffer * @param len length of data to transfer in bytes * @throws DriverException */ final void copyToBAP(int id, int offset, byte[] src, int srcOffset, int len) throws DriverException { // Prepare the BAP prepareBAP(id, offset); // Write even(len) buf contents to data reg final int maxlen = len & 0xFFFE; for (int i = 0; i < maxlen; i += 2) { final int v = LittleEndian.getInt16(src, srcOffset + i); setReg(DATA0, v); } // If len odd, handle last byte if ((len % 2) != 0) { int v = getReg(DATA0); prepareBAP(id, offset + maxlen); v = (v & 0xFF00) | (src[srcOffset + len - 1] & 0xFF); setReg(DATA0, v); } } /** * Prepare the BAP registers for a transfer. * * @param id FID or RID, destined for the select register (host order) * @param offset An _even_ offset into the buffer for the given FID/RID * @throws DriverException * @throws IllegalArgumentException For an invalid offset. */ private final void prepareBAP(int id, int offset) throws DriverException { if ((offset > BAP_OFFSET_MAX) || ((offset % 2) != 0)) { throw new IllegalArgumentException("Invalid offset " + offset); } // Write fid/rid and offset setReg(SELECT0, id); TimeUtils.sleep(1); setReg(OFFSET0, offset); // Wait for the bap to settle. try { waitUntilBapNotBusy(); } catch (TimeoutException ex) { throw new DriverException("Cannot setup BAP in time", ex); } // Test for errors if ((getReg(OFFSET0) & OFFSET_ERR) != 0) { throw new DriverException("Error in offset"); } } }