package lejos.nxt.comm; import lejos.util.Delay; /** * Low-level USB access. * * @author Lawrie Griffiths, extended to support streams by Andy Shaw * */ public class USB extends NXTCommDevice { public static final int RESET = 0x40000000; static final int BUFSZ = 64; static final int USB_STREAM = 1; static final int USB_STATE_MASK = 0xf0000000; static final int USB_STATE_CONNECTED = 0x10000000; static final int USB_CONFIG_MASK = 0xf000000; static final int USB_WRITABLE = 0x100000; static final int USB_READABLE = 0x200000; // Private versions of LCP values. We don't want to pull in all of the // LCP code. private static final byte SYSTEM_COMMAND_REPLY = 0x01; private static final byte REPLY_COMMAND = 0x02; private static final byte GET_FIRMWARE_VERSION = (byte)0x88; private static final byte GET_DEVICE_INFO = (byte)0x9B; private static final byte NXJ_PACKET_MODE = (byte)0xFF; static { loadSettings(); } private USB() { } private static void flushInput(NXTConnection conn) { conn.discardInput(); } private static boolean isConnected(NXTConnection conn, byte [] cmd) { // This method provides support for packet mode connections. // We wait for the PC to tell us that the connection has been established. // While waiting we support a small sub-set of LCP to allow identification // of the device. int len = 3; boolean ret = false; if (conn.available() < 2) return false; // Look for a system command if (conn.read(cmd, cmd.length, false) >= 2 && cmd[0] == SYSTEM_COMMAND_REPLY) { cmd[2] = (byte)0xff; if (cmd[1] == GET_FIRMWARE_VERSION) { cmd[2] = 0; cmd[3] = 2; cmd[4] = 1; cmd[5] = 3; cmd[6] = 1; len = 7; } // GET DEVICE INFO if (cmd[1] == GET_DEVICE_INFO) { cmd[2] = 0; // We only send back the device devName. for(int i=0;i<devName.length();i++) cmd[3+i] = (byte)devName.charAt(i); len = 33; } // Switch to packet mode if (cmd[1] == NXJ_PACKET_MODE) { // Send back special signature to indicate we have accepted packet // mode cmd[1] = (byte)0xfe; cmd[2] = (byte)0xef; ret = true; len = 3; } cmd[0] = REPLY_COMMAND; conn.write(cmd, len, false); } return ret; } /** * Wait for the USB interface to become available and for a PC side program * to attach to it. * @param timeout length of time to wait (in ms), if 0 wait for ever * @param mode The IO mode to be used for the connection. (see NXTConnection) * @return a connection object or null if no connection. */ public static USBConnection waitForConnection(int timeout, int mode) { // Allocate buffer here for use by other methods. Saves repeated // allocations. byte [] buf = new byte [BUFSZ]; USBConnection conn = new USBConnection(NXTConnection.RAW); usbSetName(devName); usbSetSerialNo(devAddress); usbEnable(((mode & RESET) != 0 ? 1 : 0)); mode &= ~RESET; // Discard any left over input flushInput(conn); if (timeout == 0) timeout = 0x7fffffff; while(timeout-- > 0) { int status = usbStatus(); // Check for the interface to be ready and to be in a non control // configuration. if ((status & USB_STATE_MASK) == USB_STATE_CONNECTED && (status & USB_CONFIG_MASK) != 0) { if (mode == NXTConnection.RAW || (mode == NXTConnection.LCP && ((status & (USB_READABLE|USB_WRITABLE)) == (USB_READABLE|USB_WRITABLE))) || (mode == NXTConnection.PACKET && isConnected(conn, buf))) { conn.setIOMode(mode); return conn; } } Delay.msDelay(1); } usbDisable(); return null; } /** * Wait for ever for the USB connection to become available. * @return a connection object or null if no connection. */ public static USBConnection waitForConnection() { return waitForConnection(0, 0); } /** * Wait for the remote side of the connection to close down. * @param conn The connection associated with this device. * @param timeout */ public static void waitForDisconnect(USBConnection conn, int timeout) { while(timeout-- > 0) { flushInput(conn); int status = usbStatus(); // Wait for the interface to be down if ((status & USB_STATE_MASK) != USB_STATE_CONNECTED || (status & USB_CONFIG_MASK) == 0) break; Delay.msDelay(1); } usbDisable(); } public static native void usbEnable(int reset); public static native void usbDisable(); public static native void usbReset(); public static native int usbRead(byte [] buf, int off, int len); public static native int usbWrite(byte [] buf, int off, int len); public static native int usbStatus(); public static native void usbSetSerialNo(String serNo); public static native void usbSetName(String name); /** * Class to provide polymorphic access to the connection methods. * Gets returned as a singleton by getConnector and can be used to create * connections. */ static class Connector extends NXTCommConnector { /** * Open a connection to the specified name/address using the given I/O mode * @param target The name or address of the device/host to connect to. * @param mode The I/O mode to use for this connection * @return A NXTConnection object for the new connection or null if error. */ public NXTConnection connect(String target, int mode) { return null; } /** * Wait for an incomming connection, or for the request to timeout. * @param timeout Time in ms to wait for the connection to be made * @param mode I/O mode to be used for the accpeted connection. * @return A NXTConnection object for the new connection or null if error. */ public NXTConnection waitForConnection(int timeout, int mode) { return USB.waitForConnection(timeout, mode); } } static NXTCommConnector connector = null; /** * Provides access to the singleton connection object. * This object can be used to create new connections. * @return the connector object */ public static NXTCommConnector getConnector() { if (connector == null) connector = new Connector(); return connector; } }