The basic sequence of operations for any PTP or ISO 15470 + * initiator (client) is: acquire the device; wrap it with this + * driver class (or a subclass); issue operations; + * close device. PTP has the notion + * of a (single) session with the device, and until you have an open + * session you may only invoke {@link #getDeviceInfo} and + * {@link #openSession} operations. Moreover, devices may be used + * both for reading images (as from a camera) and writing them + * (as to a digital picture frame), depending on mode support. + * + *
Note that many of the IOExceptions thrown here are actually
+ * going to be This differs from getObject in that the data object is supplied
+ * by the caller. Such objects may specialized behaviors, including in
+ * particular reducing in-memory buffering for large objects.
+ *
+ * @param handle object handle from the current session
+ */
+ public void fillObject (int handle, Data data)
+ throws IOException
+ {
+ Response response = transact1 (Command.GetObject, data, handle);
+
+ switch (response.getCode ()) {
+ case Response.OK:
+ return;
+ default:
+ throw new IOException (response.toString ());
+ }
+ }
+
+ /**
+ * Returns the thumbnail for the image identified by a given handle.
+ * Push-only responders don't need to support this operation.
+ *
+ * @param handle object handle from the current session
+ */
+ public Data getThumb (int handle)
+ throws IOException
+ {
+ Data data = new Data (factory);
+ Response response = transact1 (Command.GetThumb, data, handle);
+
+ switch (response.getCode ()) {
+ case Response.OK:
+ return data;
+ default:
+ throw new IOException (response.toString ());
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////
+
+ // mandatory for push-mode responders
+
+ /**
+ * Sends object metadata, in preparation for sending the object
+ * described by this data.
+ * Pull-only responders don't need to support this operation.
+ *
+ * @param info information about the object; fields in this
+ * dataset are interpreted with respect to the initiator
+ * (for example, initiator's storage id)
+ * @param storage desired responder storage id for the object;
+ * or zero indicating the responder should choose one
+ * @param parent responder object handle for object's parent;
+ * ~0 indicating the root of that object store;
+ * or zero indicating the responder should choose.
+ *
+ * @return response must be examined by caller; getCode() indicates
+ * command status, and if it's "Response.OK" then the positional
+ * parameters hold data used when transferring hierarchies.
+ *
+ * @see #sendObject
+ */
+ public Response sendObjectInfo (ObjectInfo info, int storage, int parent)
+ throws IOException
+ {
+ return transact2 (Command.SendObjectInfo, info, storage, parent);
+ }
+
+ /**
+ * Sends the object identified in a successful preceding
+ * sendObjectInfo call.
+ * Pull-only responders don't need to support this operation.
+ *
+ * @see #sendObjectInfo
+ */
+ public void sendObject (Data obj)
+ throws IOException
+ {
+ Response response = transact0 (Command.SendObject, obj);
+
+ switch (response.getCode ()) {
+ case Response.OK:
+ return;
+ default:
+ throw new IOException (response.toString ());
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Performs a PTP transaction, passing zero command parameters.
+ * @param code the command code
+ * @param data data to be sent or received; or null
+ * @return response; check for code Response.OK before using
+ * any positional response parameters
+ */
+ protected Response transact0 (int code, Data data)
+ throws IOException
+ {
+ synchronized (session) {
+ Command command = new Command (code, session);
+ return transactUnsync (command, data);
+ }
+ }
+
+ /**
+ * Performs a PTP transaction, passing one command parameter.
+ * @param code the command code
+ * @param data data to be sent or received; or null
+ * @param p1 the first positional parameter
+ * @return response; check for code Response.OK before using
+ * any positional response parameters
+ */
+ protected Response transact1 (int code, Data data, int p1)
+ throws IOException
+ {
+ synchronized (session) {
+ Command command = new Command (code, session, p1);
+ return transactUnsync (command, data);
+ }
+ }
+
+ /**
+ * Performs a PTP transaction, passing two command parameters.
+ * @param code the command code
+ * @param data data to be sent or received; or null
+ * @param p1 the first positional parameter
+ * @param p2 the second positional parameter
+ * @return response; check for code Response.OK before using
+ * any positional response parameters
+ */
+ protected Response transact2 (int code, Data data, int p1, int p2)
+ throws IOException
+ {
+ synchronized (session) {
+ Command command = new Command (code, session, p1, p2);
+ return transactUnsync (command, data);
+ }
+ }
+
+ /**
+ * Performs a PTP transaction, passing three command parameters.
+ * @param code the command code
+ * @param data data to be sent or received; or null
+ * @param p1 the first positional parameter
+ * @param p2 the second positional parameter
+ * @param p3 the third positional parameter
+ * @return response; check for code Response.OK before using
+ * any positional response parameters
+ */
+ protected Response transact3 (int code, Data data, int p1, int p2, int p3)
+ throws IOException
+ {
+ synchronized (session) {
+ Command command = new Command (code, session, p1, p2, p3);
+ return transactUnsync (command, data);
+ }
+ }
+
+ /**
+ * Performs a PTP transaction. This is normally used only to ensure
+ * that vendor-specific command names show up nicely in diagnostics,
+ * by providing a subclassed Command (and likely Data as well).
+ *
+ * @param command the command block
+ * @param data data to be sent or received; or null
+ * @return response; check for code Response.OK before using
+ * any positional response parameters
+ */
+ protected Response transact (Command command, Data data)
+ throws IOException
+ {
+ synchronized (session) {
+ return transactUnsync (command, data);
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////
+
+
+ // INVARIANTS:
+ // - caller is synchronized on session
+ // - on return, device is always in idle/"command ready" state
+ // - on return, session was only closed by CloseSession
+ // - on IOException, device (and session!) has been reset
+
+ @SuppressWarnings("finally")
+ private Response transactUnsync (Command command, Data data)
+ throws IOException
+ {
+
+ //if (command.getBlockTypeName (command.getBlockType ()) != "command")
+ // throw new IllegalArgumentException (command.toString ());
+
+ // sanity checking
+ int opcode = command.getCode ();
+ /*
+ if (session.isActive ()) {
+ if (Command.OpenSession == opcode) {
+ throw new IllegalStateException ("session already open");
+ }
+ } else {
+ if (Command.GetDeviceInfo != opcode
+ && Command.OpenSession != opcode
+ ) {
+ throw new IllegalStateException ("no session");
+ }
+ }
+ */
+ // this would be UnsupportedOperationException ...
+ // except that it's not available on jdk 1.1
+ //if (info != null && !info.supportsOperation (opcode))
+ // throw new UnsupportedOpException (command.getCodeName (opcode));
+
+ // ok, then we'll really talk to the device
+ Response response;
+ boolean abort = true;
+
+ try {
+ //OutputStream stream = out.getOutputStream ();
+
+ // issue command
+ // rejected commands will stall both EPs
+ if (trace)
+ System.err.println (command.toString ());
+
+ //stream.write (command.data, 0, command.length);
+ mConnection.bulkTransfer(out, command.data , command.length , 0);
+
+ // may need to terminate request with zero length packet
+ if ((command.length % out.getMaxPacketSize ()) == 0)
+ //stream.write(command.data,offset,length);
+ //stream.write (command.data, 0, 0);
+ mConnection.bulkTransfer(out, command.data , 0 , 0);
+ /*
+ // data exchanged?
+ // errors or cancel (another thread) will stall both EPs
+ if (data != null) {
+
+ // write data?
+ if (!data.isIn ()) {
+ data.offset = 0;
+ data.putHeader (data.getLength (), 2 , opcode,
+ command.getXID ());
+
+ if (trace)
+ System.err.println (data.toString ());
+ /*
+ // Special handling for the read-from-N-mbytes-file case
+ if (data instanceof FileSendData) {
+ FileSendData fd = (FileSendData) data;
+ int len = fd.data.length - fd.offset;
+ int temp;
+
+ // fill up the rest of the first buffer
+ len = fd.read (fd.data, fd.offset, len);
+ if (len < 0)
+ throw new IOException ("eh? " + len);
+ len += fd.offset;
+
+ for (;;) {
+ // write data or terminating packet
+ //stream.write (fd.data, 0, len);
+ //stream.write(command.data,offset,length);
+ mConnection.bulkTransfer(out, fd.data , len , 0);
+
+ if (len != fd.data.length)
+ break;
+
+ len = fd.read (fd.data, 0, fd.data.length);
+ if (len < 0)
+ throw new IOException ("short: " + len);
+ }
+
+ } else {
+ // write data and maybe terminating packet
+ //stream.write (data.data, 0, data.length);
+ //stream.write(command.data,offset,length);
+ mConnection.bulkTransfer(out, data.data , data.length , 0);
+ if ((data.length % out.getMaxPacketSize ()) == 0)
+ //stream.write (data.data, 0, 0);
+ //stream.write(command.data,offset,length);
+ mConnection.bulkTransfer(out, data.data , 0 , 0);
+ }
+
+
+ // read data?
+ } else {
+ byte buf1 [] = new byte [inMaxPS];
+ //int len = in.getInputStream ().read (buf1);
+ int len = inMaxPS;
+ mConnection.bulkTransfer(in, buf1 , inMaxPS , 0);
+
+ // Get the first bulk packet(s), check header for length
+ data.data = buf1;
+ data.length = len;
+ //data.length = data.getLength ();
+ if (trace)
+ System.err.println (data.toString ());
+ if (data.getBlockTypeName (data.getBlockType ()) != "data"
+ || data.getCode () != command.getCode ()
+ || data.getXID () != command.getXID ()) {
+ throw new IOException ("protocol err 1, " + data);
+ }
+
+ // get the rest of it
+ int expected = data.getLength ();
+
+ // Special handling for the write-to-N-mbytes-file case
+ if (data instanceof FileData) {
+ FileData fd = (FileData) data;
+
+ fd.write (buf1, Data.HDR_LEN, len - Data.HDR_LEN);
+
+ if (len == inMaxPS && expected != inMaxPS) {
+ //InputStream is = in.getInputStream ();
+
+ // at max usb data rate, 128K ~= 0.11 seconds
+ // typically it's more time than that
+ buf1 = new byte [128 * 1024];
+
+ do {
+ //len = is.read (buf1);
+ //Needs to be fixed by Ash
+ mConnection.bulkTransfer(in, buf1 , inMaxPS , 0);
+ fd.write (buf1, 0, len);
+ } while (len == buf1.length);
+ }
+
+ } else if (len == inMaxPS && expected != inMaxPS) {
+ buf1 = new byte [expected];
+ System.arraycopy (data.data, 0, buf1, 0, len);
+ data.data = buf1;
+ //data.length += in.getInputStream ().read (buf1, len, expected - len);
+ }
+
+ // if ((expected % inMaxPS) == 0)
+ // ... next packet will be zero length
+
+ // and do whatever parsing needs to be done
+ data.parse ();
+ }
+ }
+
+ // (short) read the response
+ // this won't stall anything
+ byte buf [] = new byte [Response.MAX_LEN];
+ //int len = in.getInputStream ().read (buf);
+ mConnection.bulkTransfer(in, buf , inMaxPS , 0);
+ int len = inMaxPS;
+ //if (len == 0) // ZLP terminated previous data?
+ //len = in.getInputStream ().read (buf);
+
+ response = new Response (buf, len, factory);
+ if (trace)
+ System.err.println (response.toString ());
+
+ abort = false;
+ return response;
+ */
+ } /*catch (Exception e) {
+ if (debug)
+ e.printStackTrace ();
+
+ // PTP devices will stall bulk EPs on error ... recover.
+ if (e.isStalled ()) {
+ int status = -1;
+
+ try {
+ // NOTE: this is the request's response code! It can't
+ // be gotten otherwise; despite current specs, this is a
+ // "control-and-bulk" protocol, NOT "bulk-only"; or more
+ // structurally, the protocol handles certain operations
+ // concurrently.
+ status = getClearStatus ();
+ } catch (USBException x) {
+ if (debug) x.printStackTrace ();
+ }
+
+ // something's very broken
+ if (status == Response.OK || status == -1)
+ throw e;
+
+ // treat status code as the device's response
+ response = new Response (new byte [Response.HDR_LEN],
+ factory);
+ response.putHeader (Response.HDR_LEN, 3 ,
+ Response.OK, command.getXID ());
+ //status, command.getXID ());
+
+ if (trace)
+ System.err.println ("STALLED: " + response.toString ());
+
+ abort = false;
+ return response;
+ }
+ throw e;
+
+ } */
+ finally {
+ /*if (abort) {
+ // not an error we know how to recover;
+ // bye bye session!
+ try {
+ reset ();
+ } catch (IOException e) {
+ if (debug) e.printStackTrace ();
+ }
+ }*/
+ return null;
+ }
+ }
+
+ /**
+ * This is just like
+ * java.lang.UnsupportedOperationException
+ * except that it can be used on JDK 1.1 JVMs, which don't
+ * have that class.
+ */
+ /*
+ public static class UnsupportedOpException extends RuntimeException
+ {
+ UnsupportedOpException (String s) { super (s); }
+ }*/
+}
diff --git a/src/com/ash/beta/Buffer.java b/src/com/ash/beta/Buffer.java
new file mode 100644
index 0000000..09a1ffd
--- /dev/null
+++ b/src/com/ash/beta/Buffer.java
@@ -0,0 +1,318 @@
+package com.ash.beta;
+
+//Copyright 2000 by David Brownell Strings representing times (YYYYMMDDThhmmss[.s]{,Z,{+,-}hhmm})
+ * are not currently transformed to Java-oriented representations.
+ *
+ * @version $Id: Buffer.java,v 1.5 2001/04/12 23:13:00 dbrownell Exp $
+ * @author David Brownell
+ */
+public class Buffer
+{
+ // package private
+ byte data [];
+ int length;
+ int offset;
+
+ // package private
+ Buffer (byte buf [])
+ {
+ this (buf, buf.length);
+ }
+
+ // package private
+ Buffer (byte buf [], int len)
+ {
+ if (buf != null && (len < 0 || len > buf.length))
+ throw new IllegalArgumentException ();
+ if (buf == null && len != 0)
+ throw new IllegalArgumentException ();
+ data = buf;
+ length = len;
+ offset = 0;
+ }
+
+
+ /** Unmarshals a signed 8 bit integer from a fixed buffer offset. */
+ protected final int getS8 (int index)
+ {
+ return data [index];
+ }
+
+ /** Unmarshals an unsigned 8 bit integer from a fixed buffer offset. */
+ protected final int getU8 (int index)
+ {
+ return 0xff & data [index];
+ }
+
+ /** Marshals an 8 bit integer (signed or unsigned) */
+ protected final void put8 (int value)
+ {
+ data [offset++] = (byte) value;
+ }
+
+ /** Unmarshals the next signed 8 bit integer */
+ protected final int nextS8 ()
+ {
+ return data [offset++];
+ }
+
+ /** Unmarshals the next unsigned 8 bit integer */
+ protected final int nextU8 ()
+ {
+ return 0xff & data [offset++];
+ }
+
+ /** Unmarshals an array of signed 8 bit integers */
+ protected final int [] nextS8Array ()
+ {
+ int len = /* unsigned */ nextS32 ();
+ int retval [] = new int [len];
+ for (int i = 0; i < len; i++)
+ retval [i] = nextS8 ();
+ return retval;
+ }
+
+
+ /** Unmarshals an array of 8 bit integers */
+ protected final int [] nextU8Array ()
+ {
+ int len = /* unsigned */ nextS32 ();
+ int retval [] = new int [len];
+ for (int i = 0; i < len; i++)
+ retval [i] = nextU8 ();
+ return retval;
+ }
+
+
+ /** Unmarshals a signed 16 bit integer from a fixed buffer offset. */
+ protected final int getS16 (int index)
+ {
+ int retval;
+
+ retval = 0xff & data [index++];
+ retval |= data [index] << 8;
+ return retval;
+ }
+
+ /** Unmarshals an unsigned 16 bit integer from a fixed buffer offset. */
+ protected final int getU16 (int index)
+ {
+ int retval;
+
+ retval = 0xff & data [index++];
+ retval |= 0xff00 & (data [index] << 8);
+ return retval;
+ }
+
+ /** Marshals a 16 bit integer (signed or unsigned) */
+ protected final void put16 (int value)
+ {
+ data [offset++] = (byte) value;
+ data [offset++] = (byte) (value >> 8);
+ }
+
+ /** Unmarshals the next signed 16 bit integer */
+ protected final int nextS16 ()
+ {
+ int retval = getS16 (offset);
+ offset += 2;
+ return retval;
+ }
+
+ /** Unmarshals the next unsinged 16 bit integer */
+ protected final int nextU16 ()
+ {
+ int retval = getU16 (offset);
+ offset += 2;
+ return retval;
+ }
+
+ /** Unmarshals an array of signed 16 bit integers */
+ protected final int [] nextS16Array ()
+ {
+ int len = /* unsigned */ nextS32 ();
+ int retval [] = new int [len];
+ for (int i = 0; i < len; i++)
+ retval [i] = nextS16 ();
+ return retval;
+ }
+
+ /** Unmarshals an array of unsigned 16 bit integers */
+ protected final int [] nextU16Array ()
+ {
+ int len = /* unsigned */ nextS32 ();
+ int retval [] = new int [len];
+ for (int i = 0; i < len; i++)
+ retval [i] = nextU16 ();
+ return retval;
+ }
+
+
+ /** Unmarshals a signed 32 bit integer from a fixed buffer offset. */
+ protected final int getS32 (int index)
+ {
+ int retval;
+
+ retval = (0xff & data [index++]) ;
+ retval |= (0xff & data [index++]) << 8;
+ retval |= (0xff & data [index++]) << 16;
+ retval |= data [index ] << 24;
+
+ return retval;
+ }
+
+ /** Marshals a 32 bit integer (signed or unsigned) */
+ protected final void put32 (int value)
+ {
+ data [offset++] = (byte) value;
+ data [offset++] = (byte) (value >> 8);
+ data [offset++] = (byte) (value >> 16);
+ data [offset++] = (byte) (value >> 24);
+ }
+
+ /** Unmarshals the next signed 32 bit integer */
+ protected final int nextS32 ()
+ {
+ int retval = getS32 (offset);
+ offset += 4;
+ return retval;
+ }
+
+ /** Unmarshals an array of signed 32 bit integers. */
+ protected final int [] nextS32Array ()
+ {
+ int len = /* unsigned */ nextS32 ();
+ int retval [] = new int [len];
+ for (int i = 0; i < len; i++) {
+ retval [i] = nextS32 ();
+ }
+ return retval;
+ }
+
+
+
+ /** Unmarshals a signed 64 bit integer from a fixed buffer offset */
+ protected final long getS64 (int index)
+ {
+ long retval = 0xffffffff & getS32 (index);
+
+ retval |= (getS32 (index + 4) << 32);
+ return retval;
+ }
+
+ /** Marshals a 64 bit integer (signed or unsigned) */
+ protected final void put64 (long value)
+ {
+ put32 ((int) value);
+ put32 ((int) (value >> 32));
+ }
+
+ /** Unmarshals the next signed 64 bit integer */
+ protected final long nextS64 ()
+ {
+ long retval = getS64 (offset);
+ offset += 8;
+ return retval;
+ }
+
+ /** Unmarshals an array of signed 64 bit integers */
+ protected final long [] nextS64Array ()
+ {
+ int len = /* unsigned */ nextS32 ();
+ long retval [] = new long [len];
+ for (int i = 0; i < len; i++)
+ retval [i] = nextS64 ();
+ return retval;
+ }
+
+ // Java doesn't yet support 128 bit integers,
+ // needed to support primitives like these:
+
+ // getU128
+ // putU128
+ // nextU128
+ // nextU128Array
+
+ // getS128
+ // putS128
+ // nextS128
+ // nextS128Array
+
+
+ /** Unmarshals a string (or null) from a fixed buffer offset. */
+ protected final String getString (int index)
+ {
+ int savedOffset = offset;
+ String retval;
+
+ offset = index;
+ retval = nextString ();
+ offset = savedOffset;
+ return retval;
+ }
+
+ /** Marshals a string, of length at most 254 characters, or null. */
+ protected void putString (String s)
+ {
+ if (s == null) {
+ put8 (0);
+ return;
+ }
+
+ int len = s.length ();
+
+ if (len > 254)
+ throw new IllegalArgumentException ();
+ put8 (len + 1);
+ for (int i = 0; i < len; i++)
+ put16 ((int) s.charAt (i));
+ put16 (0);
+ }
+
+ /** Unmarshals the next string (or null). */
+ protected String nextString ()
+ {
+ int len = nextU8 ();
+ StringBuffer str;
+
+ if (len == 0)
+ return null;
+
+ str = new StringBuffer (len);
+ for (int i = 0; i < len; i++)
+ str.append ((char) nextU16 ());
+ // drop terminal null
+ str.setLength (len - 1);
+ return str.toString ();
+ }
+}
diff --git a/src/com/ash/beta/Command.java b/src/com/ash/beta/Command.java
new file mode 100644
index 0000000..cc5bfc4
--- /dev/null
+++ b/src/com/ash/beta/Command.java
@@ -0,0 +1,212 @@
+package com.ash.beta;
+
+//Copyright 2000 by David Brownell Create these objects in helper routines which package
+ * intelligence about a given Operation. That is, it'll know
+ * the command code, how many command and response parameters
+ * may be used, particularly significant response code, and
+ * whether the transaction has a data phase (and its direction).
+ *
+ * @version $Id: Command.java,v 1.3 2001/04/12 23:13:00 dbrownell Exp $
+ * @author David Brownell
+ */
+public class Command extends ParamVector
+{
+ private Command (int nparams, int code, Session s)
+ {
+ super (new byte [HDR_LEN + (4 * nparams)], s.getFactory ());
+ putHeader (data.length, 1 /*OperationCode*/, code, s.getNextXID ());
+ }
+
+ /**
+ * This creates a zero-parameter command.
+ * @param code as defined in section 10, table 18
+ * @param s session this command is associated with
+ */
+ Command (int code, Session s)
+ {
+ this (0, code, s);
+ }
+
+ /**
+ * This creates a one-parameter command.
+ * @param code as defined in section 10, table 18
+ * @param s session this command is associated with
+ * @param param1 first operation parameter
+ */
+ Command (int code, Session s, int param1)
+ {
+ this (1, code, s);
+ put32 (param1);
+ }
+
+ /**
+ * This creates a two-parameter command.
+ * @param code as defined in section 10, table 18
+ * @param s session this command is associated with
+ * @param param1 first operation parameter
+ * @param param2 second operation parameter
+ */
+ Command (int code, Session s, int param1, int param2)
+ {
+ this (2, code, s);
+ put32 (param1);
+ put32 (param2);
+ }
+
+ /**
+ * This creates a three-parameter command.
+ * @param code as defined in section 10, table 18
+ * @param s session this command is associated with
+ * @param param1 first operation parameter
+ * @param param2 second operation parameter
+ * @param param3 third operation parameter
+ */
+ Command (int code, Session s, int param1, int param2, int param3)
+ {
+ this (3, code, s);
+ put32 (param1);
+ put32 (param2);
+ put32 (param3);
+ }
+
+ // allegedly some commands could have up to five params
+
+
+ /** OperationCode: */
+ public static final int GetDeviceInfo = 0x1001;
+ /** OperationCode: */
+ public static final int OpenSession = 0x1002;
+ /** OperationCode: */
+ public static final int CloseSession = 0x1003;
+
+ /** OperationCode: */
+ public static final int GetStorageIDs = 0x1004;
+ /** OperationCode: */
+ public static final int GetStorageInfo = 0x1005;
+ /** OperationCode: */
+ public static final int GetNumObjects = 0x1006;
+ /** OperationCode: */
+ public static final int GetObjectHandles = 0x1007;
+
+ /** OperationCode: */
+ public static final int GetObjectInfo = 0x1008;
+ /** OperationCode: */
+ public static final int GetObject = 0x1009;
+ /** OperationCode: */
+ public static final int GetThumb = 0x100a;
+ /** OperationCode: */
+ public static final int DeleteObject = 0x100b;
+
+ /** OperationCode: */
+ public static final int SendObjectInfo = 0x100c;
+ /** OperationCode: */
+ public static final int SendObject = 0x100d;
+ /** OperationCode: */
+ public static final int InitiateCapture = 0x100e;
+ /** OperationCode: */
+ public static final int FormatStore = 0x100f;
+
+ /** OperationCode: */
+ public static final int ResetDevice = 0x1010;
+ /** OperationCode: */
+ public static final int SelfTest = 0x1011;
+ /** OperationCode: */
+ public static final int SetObjectProtection = 0x1012;
+ /** OperationCode: */
+ public static final int PowerDown = 0x1013;
+
+ /** OperationCode: */
+ public static final int GetDevicePropDesc = 0x1014;
+ /** OperationCode: */
+ public static final int GetDevicePropValue = 0x1015;
+ /** OperationCode: */
+ public static final int SetDevicePropValue = 0x1016;
+ /** OperationCode: */
+ public static final int ResetDevicePropValue = 0x1017;
+
+ /** OperationCode: */
+ public static final int TerminateOpenCapture = 0x1018;
+ /** OperationCode: */
+ public static final int MoveObject = 0x1019;
+ /** OperationCode: */
+ public static final int CopyObject = 0x101a;
+ /** OperationCode: */
+ public static final int GetPartialObject = 0x101b;
+
+ /** OperationCode: */
+ public static final int InitiateOpenCapture = 0x101c;
+
+
+ public String getCodeName (int code)
+ {
+ return factory.getOpcodeString (code);
+ }
+
+ static String _getOpcodeString (int code)
+ {
+ switch (code) {
+ case GetDeviceInfo: return "GetDeviceInfo";
+ case OpenSession: return "OpenSession";
+ case CloseSession: return "CloseSession";
+
+ case GetStorageIDs: return "GetStorageIDs";
+ case GetStorageInfo: return "GetStorageInfo";
+ case GetNumObjects: return "GetNumObjects";
+ case GetObjectHandles: return "GetObjectHandles";
+
+ case GetObjectInfo: return "GetObjectInfo";
+ case GetObject: return "GetObject";
+ case GetThumb: return "GetThumb";
+ case DeleteObject: return "DeleteObject";
+
+ case SendObjectInfo: return "SendObjectInfo";
+ case SendObject: return "SendObject";
+ case InitiateCapture: return "InitiateCapture";
+ case FormatStore: return "FormatStore";
+
+ case ResetDevice: return "ResetDevice";
+ case SelfTest: return "SelfTest";
+ case SetObjectProtection: return "SetObjectProtection";
+ case PowerDown: return "PowerDown";
+
+ case GetDevicePropDesc: return "GetDevicePropDesc";
+ case GetDevicePropValue: return "GetDevicePropValue";
+ case SetDevicePropValue: return "SetDevicePropValue";
+ case ResetDevicePropValue: return "ResetDevicePropValue";
+
+ case TerminateOpenCapture: return "TerminateOpenCapture";
+ case MoveObject: return "MoveObject";
+ case CopyObject: return "CopyObject";
+ case GetPartialObject: return "GetPartialObject";
+
+ case InitiateOpenCapture: return "InitiateOpenCapture";
+ }
+ return Container.getCodeString (code);
+ }
+}
diff --git a/src/com/ash/beta/Container.java b/src/com/ash/beta/Container.java
new file mode 100644
index 0000000..70a9ac6
--- /dev/null
+++ b/src/com/ash/beta/Container.java
@@ -0,0 +1,211 @@
+package com.ash.beta;
+
+//Copyright 2000 by David Brownell Note that since the string values to which various codes map
+ * have been interned, you may safely rely on "==" and "!=" when you
+ * make comparisons against constant values.
+ *
+ * @version $Id: Container.java,v 1.8 2001/04/12 23:13:00 dbrownell Exp $
+ * @author David Brownell
+ */
+abstract public class Container extends Buffer
+{
+ // package private
+ NameFactory factory;
+
+ // get/put object handles (not 0, ~0) using session context
+ // Session session;
+
+ // fixed header layout, per annex D
+ // NOTE: session id (9.3.2) is implicit: no multisession over USB
+ // @ 0, u32 length
+ // @ 4, u16 buffer type
+ // @ 6, u16 code
+ // @ 8, u32 xid
+ // TOTAL: 12 bytes
+ static final int HDR_LEN = 12;
+
+ Container (byte buf [], NameFactory f)
+ { super (buf, buf.length); factory = f; }
+
+ Container (byte buf [], int len, NameFactory f)
+ { super (buf, len); factory = f; }
+
+ // package private
+ void putHeader (int len, int type, int code, int xid)
+ {
+ if (offset != 0)
+ throw new IllegalStateException ();
+ put32 (len);
+ put16 (type);
+ put16 (code);
+ put32 (xid);
+ }
+
+
+ /**
+ * Provides a printable representation of data from the block
+ * header, including block type, code, and transaction ID.
+ */
+ public String toString ()
+ {
+ StringBuffer temp = new StringBuffer ();
+ String type = getBlockTypeName (getBlockType ());
+ int code = getCode ();
+
+ temp.append ("{ ");
+ temp.append (type);
+ temp.append ("; len ");
+ temp.append (Integer.toString (getLength ()));
+ temp.append ("; ");
+ temp.append (getCodeName (code));
+ temp.append ("; xid ");
+ temp.append (Integer.toString (getXID ()));
+
+ // inelegant, but ...
+ if (this instanceof ParamVector) {
+ ParamVector vec = (ParamVector) this;
+ int nparams = vec.getNumParams ();
+
+ if (nparams > 0) {
+ temp.append ("; ");
+ for (int i = 0; i < nparams; i++) {
+ if (i != 0)
+ temp.append (" ");
+ temp.append ("0x");
+ temp.append (Integer.toHexString (vec.getParam (i)));
+ }
+ }
+ }
+
+ temp.append (" }");
+ return temp.toString ();
+ }
+
+ void dump (PrintStream out)
+ {
+ // out.println (toString ());
+ }
+
+
+ void parse ()
+ {
+ offset = HDR_LEN;
+ }
+
+ /** Returns the overall length of this data block, including header. */
+ public int getLength ()
+ { return /* unsigned */ getS32 (0); }
+
+ /**
+ * Returns the overall type of this data block as a coded integer.
+ */
+ public final int getBlockType ()
+ { return getU16 (4); }
+
+ /**
+ * Returns an interned string, normally "command", "data", "response",
+ * or "event", corresponding to the coded type. Unrecognized or
+ * undefined values are returned as interned Integer.toHexString values.
+ */
+ public static final String getBlockTypeName (int type)
+ {
+ switch (type) {
+ case 1: return "command";
+ case 2: return "data";
+ case 3: return "response";
+ case 4: return "event";
+ default: return Integer.toHexString (type).intern ();
+ }
+ }
+
+ /**
+ * Returns the operation, response, or event code of this block.
+ */
+ public final int getCode ()
+ {
+ return getU16 (6);
+ }
+
+ /**
+ * Returns an interned string identifying the type of code field,
+ * such as "OperationCode", "ResponseCode", "ObjectFormatCode",
+ * "EventCode", or "DevicePropsCode". Unrecognized or undefined
+ * values are returned as interned Integer.toHexString values.
+ */
+ public static final String getCodeType (int code)
+ {
+ switch (code >> 12) {
+ case 1: return "OperationCode";
+ case 2: return "ResponseCode";
+ case 3: return "ObjectFormatCode";
+ case 4: return "EventCode";
+ case 5: return "DevicePropCode";
+ case 8 + 1: return "Vendor-OpCode";
+ case 8 + 2: return "Vendor-ResponseCode";
+ case 8 + 3: return "Vendor-FormatCode";
+ case 8 + 4: return "Vendor-EventCode";
+ case 8 + 5: return "Vendor-PropCode";
+ default: return Integer.toHexString (code >> 12).intern ();
+ }
+ }
+
+ /**
+ * Subclasses override this to map PTP codes to their names; the
+ * results are always interned strings, so that they can be efficiently
+ * compared ("=", "!=") against constants. Such per-instance methods
+ * permit type-specific subclasses (and vendor extensions) to name their
+ * code values, invoking superclass methods to name all other codes.
+ */
+ public String getCodeName (int code)
+ {
+ return getCodeString (code);
+ }
+
+ /**
+ * Returns an interned string with name of this container's code.
+ */
+ public final String getCodeString ()
+ {
+ return getCodeName (getCode ()).intern ();
+ }
+
+ /**
+ * Returns an interned string with the hexadecimal value of
+ * the specified container code.
+ */
+ public static String getCodeString (int code)
+ {
+ return Integer.toHexString (code).intern ();
+ }
+
+ /**
+ * Returns the ID of the transaction this block is associated with.
+ */
+ public final int getXID ()
+ { return getS32 (8); }
+}
diff --git a/src/com/ash/beta/Data.java b/src/com/ash/beta/Data.java
new file mode 100644
index 0000000..ada4c81
--- /dev/null
+++ b/src/com/ash/beta/Data.java
@@ -0,0 +1,70 @@
+package com.ash.beta;
+
+//Copyright 2000 by David Brownell The values exposed are typically Note that in addition to the image formats identified here,
+ * devices could also support proprietary formats except for use
+ * with thumbnail images.
+ *
+ * @version $Id: ObjectInfo.java,v 1.13 2001/04/12 23:13:00 dbrownell Exp $
+ * @author David Brownell
+ */
+public class ObjectInfo extends Data
+{
+ int storageId; // 8.1
+ int objectFormatCode; // 6.2
+ int protectionStatus; // 0 r/w, 1 r/o
+ int objectCompressedSize;
+
+ int thumbFormat; // 6.2
+ int thumbCompressedSize;
+ int thumbPixWidth;
+ int thumbPixHeight;
+
+ int imagePixWidth;
+ int imagePixHeight;
+ int imageBitDepth;
+ int parentObject;
+
+ int associationType; // 6.4
+ int associationDesc; // 6.4
+ int sequenceNumber; // (ordered associations)
+ String filename; // (sans path)
+
+ String captureDate; // DateTime string
+ String modificationDate; // DateTime string
+ String keywords;
+
+ int handle;
+
+ ObjectInfo (int h, NameFactory f) { super (f); handle = h; }
+
+ /**
+ * Construct an ObjectInfo data packet using the object at the other
+ * end of the specified connection.
+ *
+ * @see BaselineInitiator#sendObjectInfo
+ *
+ * @exception IllegalArgumentException if the object uses an image
+ * format the device doesn't support, or if the object's content
+ * type is not recognized.
+ */
+ public ObjectInfo (URLConnection conn, DeviceInfo devInfo, NameFactory f)
+ {
+ super (false, new byte [1024], f);
+
+ String type = conn.getContentType ();
+
+ // mandatory fields:
+ // objectCompressedSize
+ // objectFormatCode
+ // associationType (if it's an association; we aren't)
+
+ objectCompressedSize = conn.getContentLength ();
+
+ // image formats
+ if (type.startsWith ("image/")) {
+ boolean error = false;
+
+ if ("image/jpeg".equals (type)) {
+ if (devInfo.supportsImageFormat (JFIF))
+ objectFormatCode = JFIF;
+ // cheat: JFIF files won't have all EXIF markers ...
+ else if (devInfo.supportsImageFormat (EXIF_JPEG))
+ objectFormatCode = EXIF_JPEG;
+ else
+ error = true;
+ } else if ("image/tiff".equals (type)) {
+ if (devInfo.supportsImageFormat (TIFF))
+ objectFormatCode = TIFF;
+ // as above, these cheat ...
+ else if (devInfo.supportsImageFormat (TIFF_EP))
+ objectFormatCode = TIFF_EP;
+ else if (devInfo.supportsImageFormat (TIFF_IT))
+ objectFormatCode = TIFF_IT;
+ else
+ error = true;
+ } else {
+ if ("image/gif".equals (type))
+ objectFormatCode = GIF;
+ else if ("image/png".equals (type))
+ objectFormatCode = PNG;
+ else if ("image/vnd.fpx".equals (type))
+ objectFormatCode = FlashPix;
+ else if ("image/x-MS-bmp".equals (type))
+ objectFormatCode = BMP;
+ else if ("image/x-photo-cd".equals (type))
+ objectFormatCode = PCD;
+ else
+ objectFormatCode = UnknownImage;
+ }
+ if (error || !devInfo.supportsImageFormat (objectFormatCode))
+ throw new IllegalArgumentException ("device doesn't support " + type);
+ }
+
+ // text formats
+ else if ("text/html".equals (type))
+ objectFormatCode = HTML;
+ else if ("text/plain".equals (type))
+ objectFormatCode = Text;
+
+ // audio formats
+ else if ("audio/mp3".equals (type))
+ objectFormatCode = MP3;
+ else if ("audio/x-aiff".equals (type))
+ objectFormatCode = AIFF;
+ else if ("audio/x-wav".equals (type))
+ objectFormatCode = WAV;
+
+ // video formats
+ else if ("video/mpeg".equals (type))
+ objectFormatCode = MPEG;
+ // avi, ...
+
+// FIXME: QuickTime
+
+ // we don't recognize this object format
+ else
+ objectFormatCode = Undefined;
+
+ // optional fields: everything else!
+
+ // fill body; header is filled by PTP transaction engine
+ marshal ();
+ }
+
+ private void marshal ()
+ {
+ offset = HDR_LEN;
+
+ put32 (storageId);
+ put16 (objectFormatCode);
+ put16 (protectionStatus);
+ put32 (objectCompressedSize);
+
+ put16 (thumbFormat);
+ put32 (thumbCompressedSize);
+ put32 (thumbPixWidth);
+ put32 (thumbPixHeight);
+
+ put32 (imagePixWidth);
+ put32 (imagePixHeight);
+ put32 (imageBitDepth);
+ put32 (parentObject);
+
+ put16 (associationType);
+ put32 (associationDesc);
+ put32 (sequenceNumber);
+ putString (filename);
+
+ putString (captureDate);
+ putString (modificationDate);
+ putString (keywords);
+
+ length = offset;
+ offset = 0;
+
+ byte temp [] = new byte [length];
+
+ System.arraycopy (data, 0, temp, 0, length);
+ data = temp;
+ }
+
+ public int getLength ()
+ {
+ return isIn () ? super.getLength () : data.length;
+ }
+
+ void parse ()
+ {
+ super.parse ();
+
+ storageId = nextS32 ();
+ objectFormatCode = nextU16 ();
+ protectionStatus = nextU16 ();
+ objectCompressedSize = /* unsigned */ nextS32 ();
+
+ thumbFormat = nextU16 ();
+ thumbCompressedSize = /* unsigned */ nextS32 ();
+ thumbPixWidth = /* unsigned */ nextS32 ();
+ thumbPixHeight = /* unsigned */ nextS32 ();
+
+ imagePixWidth = /* unsigned */ nextS32 ();
+ imagePixHeight = /* unsigned */ nextS32 ();
+ imageBitDepth = /* unsigned */ nextS32 ();
+ parentObject = nextS32 ();
+
+ associationType = nextU16 ();
+ associationDesc = nextS32 ();
+ sequenceNumber = /* unsigned */ nextS32 ();
+ filename = nextString ();
+
+ captureDate = nextString ();
+ modificationDate = nextString ();
+ keywords = nextString ();
+ }
+
+ void line (PrintStream out)
+ {
+ if (filename != null) {
+ // out.print ("Name: ");
+ out.print (filename);
+ out.print ("; ");
+ }
+ if (objectFormatCode == Association) {
+ String associationString;
+
+ associationString = associationString (associationType);
+ if (associationString != null)
+ out.print (associationString);
+ } else {
+ out.print (objectCompressedSize);
+ out.print (" bytes, ");
+ out.print (factory.getFormatString (objectFormatCode));
+ if (thumbFormat != 0) {
+ if (imagePixWidth != 0 && imagePixHeight != 0) {
+ out.print (" ");
+ out.print (imagePixWidth);
+ out.print ("x");
+ out.print (imagePixHeight);
+ }
+ if (imageBitDepth != 0) {
+ out.print (", ");
+ out.print (imageBitDepth);
+ out.print (" bits");
+ }
+ }
+ }
+ out.println ();
+ }
+
+ void dump (PrintStream out)
+ {
+ super.dump (out);
+ out.println ("ObjectInfo:");
+
+ if (storageId != 0) {
+ out.print ("StorageID: 0x");
+ out.print (Integer.toHexString (storageId));
+ switch (protectionStatus) {
+ case 0: out.println (", unprotected"); break;
+ case 1: out.println (", read-only"); break;
+ default:
+ out.print (", reserved protectionStatus 0x");
+ out.println (Integer.toHexString (protectionStatus));
+ break;
+ }
+ }
+
+ if (parentObject != 0)
+ out.println ("Parent: 0x" + Integer.toHexString (parentObject));
+ if (filename != null)
+ out.println ("Filename " + filename);
+ if (sequenceNumber != 0) {
+ out.print ("Sequence = ");
+ out.print (sequenceNumber);
+ }
+
+ // images must have thumbnails, except for sendObjectInfo
+ if (thumbFormat != 0) {
+ out.print ("Image format: ");
+ out.print (factory.getFormatString (objectFormatCode));
+ out.print (", size ");
+ out.print (objectCompressedSize);
+ out.print (", width ");
+ out.print (imagePixWidth);
+ out.print (", height ");
+ out.print (imagePixHeight);
+ out.print (", depth ");
+ out.println (imageBitDepth);
+
+ out.print ("Thumbnail format: ");
+ out.print (factory.getFormatString (thumbFormat));
+ out.print (", size ");
+ out.print (thumbCompressedSize);
+ out.print (", width ");
+ out.print (thumbPixWidth);
+ out.print (", height ");
+ out.print (thumbPixHeight);
+ out.print (", depth ");
+ out.println (imageBitDepth);
+ } else {
+ out.print ("Object format: ");
+ out.print (factory.getFormatString (objectFormatCode));
+ out.print (", size ");
+ out.println (objectCompressedSize);
+
+ if (objectFormatCode == Association) {
+ String associationString;
+
+ associationString = associationString (associationType);
+ if (associationString != null) {
+ out.print ("Association type: ");
+ out.print (associationString);
+ if (associationDesc != 0) {
+ // for albums: reserved for future use
+ // for time sequences: playback delta (millisec)
+ // for 2DPanorama: row count
+ out.print (", desc 0x");
+ out.print (Integer.toHexString (associationDesc));
+ }
+ out.println ();
+ }
+ }
+ }
+
+ if (captureDate != null)
+ out.println ("capture date: " + captureDate);
+ if (modificationDate != null)
+ out.println ("modification date: " + modificationDate);
+ if (keywords != null)
+ out.println ("keywords: " + keywords);
+ }
+
+
+ /** ObjectFormatCode: unrecognized non-image format */
+ public static final int Undefined = 0x3000;
+ /** ObjectFormatCode: associations include folders and panoramas */
+ public static final int Association = 0x3001;
+ /** ObjectFormatCode: */
+ public static final int Script = 0x3002;
+ /** ObjectFormatCode: */
+ public static final int Executable = 0x3003;
+
+ /** ObjectFormatCode: */
+ public static final int Text = 0x3004;
+ /** ObjectFormatCode: */
+ public static final int HTML = 0x3005;
+ /** ObjectFormatCode: */
+ public static final int DPOF = 0x3006;
+ /** ObjectFormatCode: */
+ public static final int AIFF = 0x3007;
+
+ /** ObjectFormatCode: */
+ public static final int WAV = 0x3008;
+ /** ObjectFormatCode: */
+ public static final int MP3 = 0x3009;
+ /** ObjectFormatCode: */
+ public static final int AVI = 0x300a;
+ /** ObjectFormatCode: */
+ public static final int MPEG = 0x300b;
+
+ /** ObjectFormatCode: */
+ public static final int ASF = 0x300c;
+
+ /** ObjectFormatCode: QuickTime video */
+ public static final int QuickTime = 0x300d;
+
+ /** ImageFormatCode: unknown image format */
+ public static final int UnknownImage = 0x3800;
+ /**
+ * ImageFormatCode: EXIF/JPEG version 2.1, the preferred format
+ * for thumbnails and for images.
+ */
+ public static final int EXIF_JPEG = 0x3801;
+ /**
+ * ImageFormatCode: Uncompressed TIFF/EP, the alternate format
+ * for thumbnails.
+ */
+ public static final int TIFF_EP = 0x3802;
+ /** ImageFormatCode: FlashPix image format */
+ public static final int FlashPix = 0x3803;
+
+ /** ImageFormatCode: MS-Windows bitmap image format */
+ public static final int BMP = 0x3804;
+ /** ImageFormatCode: Canon camera image file format */
+ public static final int CIFF = 0x3805;
+ // 3806 is reserved
+ /** ImageFormatCode: Graphics Interchange Format (deprecated) */
+ public static final int GIF = 0x3807;
+
+ /** ImageFormatCode: JPEG File Interchange Format */
+ public static final int JFIF = 0x3808;
+ /** ImageFormatCode: PhotoCD Image Pac*/
+ public static final int PCD = 0x3809;
+ /** ImageFormatCode: Quickdraw image format */
+ public static final int PICT = 0x380a;
+ /** ImageFormatCode: Portable Network Graphics */
+ public static final int PNG = 0x380b;
+
+ /** ImageFormatCode: Tag Image File Format */
+ public static final int TIFF = 0x380d;
+ /** ImageFormatCode: TIFF for Information Technology (graphic arts) */
+ public static final int TIFF_IT = 0x380e;
+ /** ImageFormatCode: JPEG 2000 baseline */
+ public static final int JP2 = 0x380f;
+
+ /** ImageFormatCode: JPEG 2000 extended */
+ public static final int JPX = 0x3810;
+
+
+ /**
+ * Returns true for format codes that have the image type bit set.
+ */
+ public boolean isImage ()
+ {
+ return (objectFormatCode & 0xf800) == 0x3800;
+ }
+
+ /**
+ * Returns true for some recognized video format codes.
+ */
+ public boolean isVideo ()
+ {
+ switch (objectFormatCode) {
+ case AVI:
+ case MPEG:
+ case ASF:
+ case QuickTime:
+ return true;
+ }
+ return false;
+ }
+
+
+ public String getCodeName (int code)
+ {
+ return factory.getFormatString (code);
+ }
+
+ static String _getFormatString (int code)
+ {
+ switch (code) {
+ case Undefined: return "UnknownFormat";
+ case Association: return "Association";
+ case Script: return "Script";
+ case Executable: return "Executable";
+ case Text: return "Text";
+ case HTML: return "HTML";
+ case DPOF: return "DPOF";
+ case AIFF: return "AIFF";
+ case WAV: return "WAV";
+ case MP3: return "MP3";
+ case AVI: return "AVI";
+ case MPEG: return "MPEG";
+ case ASF: return "ASF";
+ case QuickTime: return "QuickTime";
+ case UnknownImage: return "UnknownImage";
+ case EXIF_JPEG: return "EXIF/JPEG";
+ case TIFF_EP: return "TIFF/EP";
+ case FlashPix: return "FlashPix";
+ case BMP: return "BMP";
+ case CIFF: return "CIFF";
+ case GIF: return "GIF";
+ case JFIF: return "JFIF";
+ case PCD: return "PCD";
+ case PICT: return "PICT";
+ case PNG: return "PNG";
+ case TIFF: return "TIFF";
+ case TIFF_IT: return "TIFF/IT";
+ case JP2: return "JP2";
+ case JPX: return "JPX";
+ }
+ return Container.getCodeString (code);
+ }
+
+ // vendor subclasses can just handle the mappings they know,
+ // and defer to superclasses
+ String associationString (int associationType)
+ {
+ switch (associationType) {
+ case 0: return null;
+ case 1: return "GenericFolder";
+ case 2: return "Album";
+ case 3: return "TimeSequence";
+ case 4: return "HorizontalPanorama";
+ case 5: return "VerticalPanorama";
+ case 6: return "2DPanorama";
+ case 7: return "AncillaryData";
+ default:
+ StringBuffer retval;
+ if ((associationType & 0x8000) == 0)
+ retval = new StringBuffer ("Reserved-0x");
+ else
+ retval = new StringBuffer ("Vendor-0x");
+ retval.append (Integer.toHexString (associationType));
+ return retval.toString ();
+ }
+ }
+}
+
diff --git a/src/com/ash/beta/ParamVector.java b/src/com/ash/beta/ParamVector.java
new file mode 100644
index 0000000..84777da
--- /dev/null
+++ b/src/com/ash/beta/ParamVector.java
@@ -0,0 +1,72 @@
+package com.ash.beta;
+
+//Copyright 2000 by David Brownell usb.core.USBException
values. That may
+ * help your application level recovery processing. You should
+ * assume that when any IOException is thrown, your current session
+ * has been terminated.
+ *
+ * @see Initiator
+ *
+ * @version $Id: BaselineInitiator.java,v 1.17 2001/05/30 19:33:43 dbrownell Exp $
+ * @author David Brownell
+ */
+public class BaselineInitiator extends NameFactory //implements Runnable
+{
+ // package private
+ final static boolean debug = false;
+ final static boolean trace = false;
+
+ private UsbManager mUsbManager;
+ private UsbDevice dev;
+ private UsbInterface intf;
+ private UsbEndpoint in;
+ private int inMaxPS;
+ private UsbEndpoint out;
+ private UsbEndpoint intr;
+ private DeviceInfo info;
+ private UsbDeviceConnection mConnection;
+ private Session session = new Session ();
+
+
+ // package private
+ NameFactory factory;
+
+
+ /**
+ * This function returns a PTP interface for a device,
+ * or null if the device has none.
+ */
+ public static UsbInterface getPtpInterface(UsbDevice dev) {
+ UsbInterface intf = dev.getInterface(0);
+ return intf;
+ }
+
+ /**
+ * Constructs a class driver object, if the device supports
+ * operations according to Annex D of the PTP specification.
+ *
+ * @param dev the first PTP interface will be used
+ * @exception IllegalArgumentException if the device has no
+ * Digital Still Imaging Class or PTP interfaces
+ */
+ public BaselineInitiator(UsbDevice dev, UsbManager man, TextView tv4)
+ throws IOException {
+ this(getPtpInterface(dev), dev, man, tv4);
+
+ }
+
+ /**
+ * Constructs a class driver from an interface asserted to
+ * conform fully to Annex D of the PTP specification.
+ *
+ * @param intf_arg the PTP interface to be used
+ * @param tv4
+ * @exception IllegalArgumentException if the interface is
+ * null or doesn't provide the necessary endpoints.
+ */
+ public BaselineInitiator (UsbInterface intf_arg,UsbDevice dev, UsbManager man, TextView tv4)
+ throws IOException
+ {
+
+ intf = intf_arg;
+ mUsbManager = man;
+
+ UsbEndpoint epOut = null;
+ UsbEndpoint epIn = null;
+ UsbEndpoint epEv = null;
+ // look for our bulk endpoints
+ for (int i = 0; i < intf.getEndpointCount(); i++) {
+ UsbEndpoint ep = intf.getEndpoint(i);
+ if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
+ if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {
+ epOut = ep;
+ } else {
+ epIn = ep;
+ }
+ }
+ if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_INT){
+ epEv = ep;
+ }
+ }
+ if (epOut == null || epIn == null) {
+ throw new IllegalArgumentException("not all endpoints found");
+ }
+ out = epOut;
+ in = epIn;
+ intr = epEv;
+
+ // we want exclusive access to this interface.
+ mConnection = mUsbManager.openDevice(dev);
+ //mConnection.claimInterface(intf, true); //Apparently this will stall the device
+ tv4.setText("Done!!");
+
+
+ Command command = new Command(Command.GetDeviceInfo, session);
+ mConnection.bulkTransfer(out, command.data , command.length , 1000);
+ tv4.append(" Sent:");
+
+ StringBuffer hexString1 = new StringBuffer();
+ for (int i=0;ijava.lang.Integer
,
+ * java.lang.Long
, "int" or "long" arrays,
+ * or Strings, and must be cast to more specific types.
+ * The 64 bit integral types are supported, but not the 128 bit ones;
+ * unsigned 64 bit values may not print as intended.
+ *
+ * @version $Id: DevicePropDesc.java,v 1.7 2001/04/12 23:13:00 dbrownell Exp $
+ * @author David Brownell
+ */
+public class DevicePropDesc extends Data
+{
+ int propertyCode;
+ int dataType;
+ boolean writable;
+ Object factoryDefault;
+ Object currentValue;
+ int formType;
+ Object constraints;
+
+ DevicePropDesc (NameFactory f) { super (f); }
+
+ void parse ()
+ {
+ super.parse ();
+
+ // per 13.3.3, tables 23, 24, 25
+ propertyCode = nextU16 ();
+ dataType = nextU16 ();
+ writable = nextU8 () != 0;
+
+ // FIXME use factories, as vendor hooks
+ factoryDefault = DevicePropValue.get (dataType, this);
+ currentValue = DevicePropValue.get (dataType, this);
+
+ formType = nextU8 ();
+ switch (formType) {
+ case 0: // no more
+ break;
+ case 1: // range: min, max, step
+ constraints = new Range (dataType, this);
+ break;
+ case 2: // enumeration: n, value1, ... valueN
+ constraints = parseEnumeration ();
+ break;
+ default:
+ System.err.println ("ILLEGAL prop desc form, " + formType);
+ formType = 0;
+ break;
+ }
+ }
+
+ void dump (PrintStream out)
+ {
+ super.dump (out);
+
+ out.print (factory.getPropertyName (propertyCode));
+ out.print (" = ");
+ out.print (currentValue);
+ if (!writable)
+ out.print (", read-only");
+ out.print (", ");
+ out.print (DevicePropValue.getTypeName (dataType));
+ switch (formType) {
+ case 0:
+ break;
+ case 1: {
+ Range r = (Range) constraints;
+ out.print (" from ");
+ out.print (r.getMinimum ());
+ out.print (" to ");
+ out.print (r.getMaximum ());
+ out.print (" by ");
+ out.print (r.getIncrement ());
+ };
+ break;
+ case 2: {
+ Vector v = (Vector) constraints;
+ out.print (" { ");
+ for (int i = 0; i < v.size (); i++) {
+ if (i != 0)
+ out.print (", ");
+ out.print (v.elementAt (i));
+ }
+ out.print (" }");
+ }
+ break;
+ default:
+ out.print (" form ");
+ out.print (formType);
+ out.print (" (error)");
+ }
+
+ out.print (", default ");
+ out.println (factoryDefault);
+ }
+
+ /** Returns true if the property is writable */
+ public boolean isWritable ()
+ { return writable; }
+
+ /** Returns the current value (treat as immutable!) */
+ public Object getValue ()
+ { return currentValue; }
+
+ /** Returns the factory default value (treat as immutable!) */
+ public Object getDefault ()
+ { return factoryDefault; }
+
+
+ // code values, per 13.3.5 table 26
+
+ public static final int BatteryLevel = 0x5001;
+ public static final int FunctionalMode = 0x5002;
+ public static final int ImageSize = 0x5003;
+
+ public static final int CompressionSetting = 0x5004;
+ public static final int WhiteBalance = 0x5005;
+ public static final int RGBGain = 0x5006;
+ public static final int FStop = 0x5007;
+
+ public static final int FocalLength = 0x5008;
+ public static final int FocusDistance = 0x5009;
+ public static final int FocusMode = 0x500a;
+ public static final int ExposureMeteringMode = 0x500b;
+
+ public static final int FlashMode = 0x500c;
+ public static final int ExposureTime = 0x500d;
+ public static final int ExposureProgramMode = 0x500e;
+ public static final int ExposureIndex = 0x500f;
+
+ public static final int ExposureBiasCompensation = 0x5010;
+ public static final int DateTime = 0x5011;
+ public static final int CaptureDelay = 0x5012;
+ public static final int StillCaptureMode = 0x5013;
+
+ public static final int Contrast = 0x5014;
+ public static final int Sharpness = 0x5015;
+ public static final int DigitalZoom = 0x5016;
+ public static final int EffectMode = 0x5017;
+
+ public static final int BurstNumber = 0x5018;
+ public static final int BurstInterval = 0x5019;
+ public static final int TimelapseNumber = 0x501a;
+ public static final int TimelapseInterval = 0x501b;
+
+ public static final int FocusMeteringMode = 0x501c;
+ public static final int UploadURL = 0x501d;
+ public static final int Artist = 0x501e;
+ public static final int CopyrightInfo = 0x501f;
+
+
+ public String getCodeName (int code)
+ {
+ return factory.getPropertyName (code);
+ }
+
+ static class NameMap {
+ int value;
+ String name;
+ NameMap (int v, String n) { value = v; name = n; }
+ }
+
+ static NameMap names [] = {
+ new NameMap (BatteryLevel, "BatteryLevel"),
+ new NameMap (FunctionalMode, "FunctionalMode"),
+ new NameMap (ImageSize, "ImageSize"),
+ new NameMap (CompressionSetting, "CompressionSetting"),
+ new NameMap (WhiteBalance, "WhiteBalance"),
+ new NameMap (RGBGain, "RGBGain"),
+ new NameMap (FStop, "FStop"),
+ new NameMap (FocalLength, "FocalLength"),
+ new NameMap (FocusDistance, "FocusDistance"),
+ new NameMap (FocusMode, "FocusMode"),
+ new NameMap (ExposureMeteringMode, "ExposureMeteringMode"),
+ new NameMap (FlashMode, "FlashMode"),
+ new NameMap (ExposureTime, "ExposureTime"),
+ new NameMap (ExposureProgramMode, "ExposureProgramMode"),
+ new NameMap (ExposureIndex, "ExposureIndex"),
+ new NameMap (ExposureBiasCompensation, "ExposureBiasCompensation"),
+ new NameMap (DateTime, "DateTime"),
+ new NameMap (CaptureDelay, "CaptureDelay"),
+ new NameMap (StillCaptureMode, "StillCaptureMode"),
+ new NameMap (Contrast, "Contrast"),
+ new NameMap (Sharpness, "Sharpness"),
+ new NameMap (DigitalZoom, "DigitalZoom"),
+ new NameMap (EffectMode, "EffectMode"),
+ new NameMap (BurstNumber, "BurstNumber"),
+ new NameMap (BurstInterval, "BurstInterval"),
+ new NameMap (TimelapseNumber, "TimelapseNumber"),
+ new NameMap (TimelapseInterval, "TimelapseInterval"),
+ new NameMap (FocusMeteringMode, "FocusMeteringMode"),
+ new NameMap (UploadURL, "UploadURL"),
+ new NameMap (Artist, "Artist"),
+ new NameMap (CopyrightInfo, "CopyrightInfo"),
+ };
+
+ static String _getPropertyName (int code)
+ {
+ for (int i = 0; i < names.length; i++)
+ if (names [i].value == code)
+ return names [i].name;
+ return Container.getCodeString (code);
+ }
+
+ /**
+ * Maps standard property names to property codes.
+ * Case is ignored in these comparisons.
+ * @param name string identifying that property.
+ * @return device property code, or -1
+ */
+ public static int getPropertyCode (String name)
+ {
+ for (int i = 0; i < names.length; i++)
+ if (names [i].name.equalsIgnoreCase (name))
+ return names [i].value;
+
+ // FIXME: delegate to superclass
+ return Integer.parseInt (name, 16);
+ }
+
+
+ /**
+ * This class describes value ranges by minima, maxima,
+ * and permissible increments.
+ */
+ public static final class Range
+ {
+ private Object min, max, step;
+
+ Range (int dataType, DevicePropDesc desc)
+ {
+ min = DevicePropValue.get (dataType, desc);
+ max = DevicePropValue.get (dataType, desc);
+ step = DevicePropValue.get (dataType, desc);
+ }
+
+ /** Returns the maximum value of this range */
+ public Object getMaximum () { return max; }
+
+ /** Returns the minimum value of this range */
+ public Object getMinimum () { return min; }
+
+ /** Returns the increment of values in this range */
+ public Object getIncrement () { return step; }
+ }
+
+ /** Returns any range constraints for this property's value, or null */
+ public Range getRange ()
+ {
+ if (formType == 1)
+ return (Range) constraints;
+ return null;
+ }
+
+
+ private Vector parseEnumeration ()
+ {
+ int len = nextU16 ();
+ Vector retval = new Vector (len);
+
+ while (len-- > 0)
+ retval.addElement (DevicePropValue.get (dataType, this));
+ return retval;
+ }
+
+
+ /** Returns any enumerated options for this property's value, or null */
+ public Vector getEnumeration ()
+ {
+ if (formType == 2)
+ return (Vector) constraints;
+ return null;
+ }
+}
diff --git a/src/com/ash/beta/DevicePropValue.java b/src/com/ash/beta/DevicePropValue.java
new file mode 100644
index 0000000..6f73003
--- /dev/null
+++ b/src/com/ash/beta/DevicePropValue.java
@@ -0,0 +1,192 @@
+package com.ash.beta;
+
+//Copyright 2000 by David Brownell
+ *
+ */
+ public static void main (String argv [])
+ {
+ if (argv.length == 0)
+ usage (-1);
+
+ try {
+ //Getopt g;
+ int c;
+ c=0;
+ // get any options
+ //g = new Getopt ("jphoto", argv, "c:d:h", longOpts);
+ //while ((c = g.getopt ()) != -1) {
+ switch (c) {
+ case 'c': // camera or device
+ //device = g.getOptarg ();
+ //continue;
+ case 'd': // directory
+ //directory = new File (g.getOptarg ());
+ //continue;
+ case 'h': // help
+ usage (0);
+ System.exit (0);
+ case 's': // storage unit
+ //storageId = Integer.parseInt (g.getOptarg ());
+ if (storageId < 0) {
+ System.err.println ("--storage N ... "
+ + "parameter must be an integer");
+ usage (-1);
+ }
+ //continue;
+ case 'w': // overwrite files
+ overwrite = true;
+ //continue;
+ default:
+ usage (-1);
+ }
+ // }
+
+ // then the command
+ c = 0; // Added by Ash
+ //c = g.getOptind ();
+
+ if ("cameras".equals (argv [c]) || "devices".equals (argv [c]))
+ cameras (argv, c);
+ else if ("capture".equals (argv [c]))
+ capture (argv, c);
+ else if ("devinfo".equals (argv [c]))
+ devinfo (argv, c);
+ else if ("devprops".equals (argv [c]))
+ devprops (argv, c);
+ else if ("format".equals (argv [c]))
+ format (argv, c);
+ else if ("getprop".equals (argv [c]))
+ getprop (argv, c);
+ else if ("help".equals (argv [c]))
+ usage (0);
+ else if ("images".equals (argv [c]))
+ images (argv, c);
+ else if ("put".equals (argv [c]))
+ put (argv, c);
+ else if ("powerdown".equals (argv [c]))
+ powerdown (argv, c);
+ else if ("reset".equals (argv [c]))
+ reset (argv, c);
+ else if ("selftest".equals (argv [c]))
+ selftest (argv, c);
+ else if ("status".equals (argv [c]))
+ status (argv, c);
+ else if ("storage".equals (argv [c]))
+ storage (argv, c);
+ else if ("thumbs".equals (argv [c]))
+ thumbs (argv, c);
+ else if ("tree".equals (argv [c]))
+ tree (argv, c);
+ else
+ usage (-1);
+
+ } catch (IOException e) {
+ System.err.println ("I/O exception: " + e.getMessage ());
+ // e.printStackTrace ();
+ System.exit (1);
+
+ } catch (SecurityException e) {
+ System.err.println (e.getMessage ());
+ System.exit (1);
+
+ }
+ }
+
+ /*--------------------------------------------------------------------*/
+
+ private static void indent (PrintStream out, int depth)
+ {
+ while (depth >= 8) {
+ out.print ("\t");
+ depth -= 8;
+ }
+ while (depth != 0) {
+ out.print (" ");
+ depth--;
+ }
+ }
+
+ private static Vector getCameras ()
+ throws IOException
+ {
+ Vector cameras = new Vector (2, 5);
+ /*
+ Bus bus [] = HostFactory.getHost ().getBusses ();
+
+ for (int busnum = 0; busnum < bus.length; busnum++) {
+ for (int addr = 1; addr < 128; addr++) {
+ Device dev = bus [busnum].getDevice (addr);
+
+ if (Initiator.getPtpInterface (dev) == null)
+ continue;
+
+ cameras.addElement (dev);
+ }
+ }
+ */
+ return cameras;
+ }
+/*
+ private static Device getDefaultCamera ()
+ throws IOException
+ {
+ Vector cameras = getCameras ();
+
+ if (cameras.size () != 1)
+ return null;
+ return (Device) cameras.elementAt (0);
+ }
+
+ private static Device getCamera (String portId)
+ throws IOException
+ {
+ try {
+ return HostFactory.getHost ().getDevice (portId);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+*/
+ private static Initiator startCamera ()
+ throws IOException
+ {
+ return startCamera (true);
+ }
+
+ private static Initiator startCamera (boolean session)
+ throws IOException
+ {
+ //Device dev = null;
+ Initiator retval;
+/*
+ if (device == null) {
+ dev = getDefaultCamera ();
+ if (dev != null)
+ device = new PortIdentifier (dev).toString ();
+ } else
+ dev = getCamera (device);
+
+ if (dev == null) {
+ if (device != null)
+ System.err.println ("Nothing at port: " + device);
+ else if (getCameras ().size () != 0)
+ System.err.println ("Specify a camera's port ID.");
+ else
+ System.err.println ("No PTP cameras are available.");
+ System.exit (1);
+ }
+ retval = new Initiator (dev);
+
+ if (Initiator.debug)
+ // new Thread (retval, "Events/" + device).start ()
+ ;
+
+ if (session)
+ retval.openSession ();
+ System.out.print ("PTP device at ");
+ System.out.println (device);
+*/
+ return retval = null;
+ }
+
+ static void closeSession (Initiator dev)
+ {
+ try {
+ dev.closeSession ();
+ } catch (Exception e) {
+ // ignore everything!
+ }
+ }
+
+ /*--------------------------------------------------------------------*/
+
+ private static void cameras (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ Enumeration cameras = getCameras ().elements ();
+
+ while (cameras.hasMoreElements ()) {
+ /*
+ Device dev = (Device) cameras.nextElement ();
+ DeviceDescriptor desc = dev.getDeviceDescriptor ();
+ String temp;
+
+ System.out.print (dev.getPortIdentifier ().toString ());
+ System.out.print ("\t");
+
+ if ((temp = desc.getProduct (0)) == null) {
+ System.out.print ("0x");
+ System.out.print (Integer.toHexString (desc.getVendorId ()));
+ System.out.print ("/0x");
+ System.out.print (Integer.toHexString (desc.getProductId ()));
+ System.out.println ();
+ } else
+ System.out.println (temp);
+ */
+ }
+ }
+
+
+ private static void capture (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ Initiator dev = startCamera ();
+ int status = dev.initiateCapture (storageId, 0);
+
+ if (status != Response.OK) {
+ // FIXME -- gets replaced with the better scheme
+ NameFactory factory = dev.getFactory ();
+
+ System.out.print ("Can't initiate capture: ");
+ System.out.println (factory.getResponseString (status));
+ }
+ }
+
+
+ private static void devinfo (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ Initiator dev = startCamera (false);
+ DeviceInfo info = dev.getDeviceInfo ();
+
+ info.dump (System.out);
+ // no session to close!
+ }
+
+
+ private static void devprops (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ Initiator dev = startCamera ();
+ DeviceInfo info;
+
+ // FIXME -- gets replaced with the better scheme
+ NameFactory factory = dev.getFactory ();
+
+ try {
+ info = dev.getDeviceInfo ();
+ for (int i = 0; i < info.propertiesSupported.length; i++) {
+ DevicePropDesc desc = new DevicePropDesc (factory);
+ int propcode = info.propertiesSupported [i];
+ int status;
+
+ status = dev.getDevicePropDesc (propcode, desc);
+ if (status == Response.OK)
+ desc.dump (System.out);
+ else {
+ System.out.print ("... can't read ");
+ System.out.print (factory.getPropertyName (propcode));
+ System.out.print (", ");
+ System.out.println (factory.getResponseString (status));
+ }
+ }
+ } finally {
+ closeSession (dev);
+ }
+ }
+
+
+ private static void format (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ Initiator dev = startCamera ();
+
+ try {
+ if (storageId == 0) {
+ int ids [] = dev.getStorageIDs ();
+ if (ids.length != 1)
+ throw new IOException ("need to specify a storage ID");
+ else
+ storageId = ids [0];
+ }
+ if (!dev.hasStore (storageId)) {
+ System.out.println ("Store " + storageId + " is not present");
+ return;
+ }
+
+ System.out.println ("reformatting storage unit " + storageId);
+
+ int status = dev.formatStore (storageId, 0);
+
+ if (status != Response.OK) {
+ // FIXME -- gets replaced with the better scheme
+ NameFactory factory = dev.getFactory ();
+
+ System.out.print ("... can't reformat: ");
+ System.out.println (factory.getResponseString (status));
+ }
+
+ } finally {
+ closeSession (dev);
+ }
+ }
+
+
+ private static void getprop (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 2))
+ usage (-1);
+
+ int propcode;
+ Initiator dev;
+ DeviceInfo info;
+
+ propcode = DevicePropDesc.getPropertyCode (argv [index + 1]);
+ if (propcode < 0) {
+ System.err.println ("unrecognized property name: "
+ + argv [index + 1]);
+ System.err.println ("'jphoto devinfo' lists device properties");
+ System.exit (1);
+ }
+ dev = startCamera ();
+
+ // FIXME -- gets replaced with the better scheme
+ // ... which should handle extension property names
+ NameFactory factory = dev.getFactory ();
+
+ try {
+ info = dev.getDeviceInfo ();
+
+ if (!info.supportsProperty (propcode))
+ System.err.println ("device does not support property: "
+ + factory.getPropertyName (propcode));
+ else {
+ DevicePropDesc desc;
+ int status;
+
+ desc = new DevicePropDesc (factory);
+ status = dev.getDevicePropDesc (propcode, desc);
+ if (status == Response.OK)
+ desc.dump (System.out);
+ else {
+ System.out.print ("... can't read ");
+ System.out.print (factory.getPropertyName (propcode));
+ System.out.print (", ");
+ System.out.println (factory.getResponseString (status));
+ }
+ }
+ } finally {
+ closeSession (dev);
+ }
+ }
+
+
+ private static void maybeSaveObject (
+ Initiator dev,
+ File dir,
+ int handle,
+ ObjectInfo objinfo,
+ int depth
+ ) throws IOException
+ {
+ String fname = objinfo.filename.toLowerCase ();
+ File f = new File (dir, fname);
+
+ indent (System.out, depth);
+ System.out.print (f.toString ());
+
+ if (!overwrite && f.exists ()) {
+ System.out.println (" ... EXISTS, not saved");
+
+ } else {
+ // FIXME -- gets replaced with the better scheme
+ NameFactory factory = dev.getFactory ();
+
+ FileOutputStream fout = new FileOutputStream (f);
+ FileData obj = new FileData (fout, factory);
+
+ try {
+ dev.fillObject (handle, obj);
+
+ System.out.print (" image saved, size ");
+ System.out.println (obj.getLength ());
+ obj = null;
+
+ } finally {
+ fout.close ();
+ if (obj != null && f.delete ())
+ System.out.println (" ... not saved");
+ }
+ }
+ }
+
+ private static void images (
+ Initiator dev,
+ File dir,
+ int storageId,
+ int assoc,
+ int depth
+ ) throws IOException
+ {
+ int objs [] = dev.getObjectHandles (storageId, 0, assoc);
+
+ for (int i = 0; i < objs.length; i++) {
+ ObjectInfo info = dev.getObjectInfo (objs [i]);
+
+ // still images must have thumbnails
+ if (info.thumbFormat != 0 || info.isImage ())
+ maybeSaveObject (dev, dir, objs [i], info, depth);
+
+ // moving images dount too
+ else if (info.isVideo ())
+ maybeSaveObject (dev, dir, objs [i], info, depth);
+
+ else switch (info.objectFormatCode) {
+ case ObjectInfo.Association:
+ indent (System.out, depth);
+ info.line (System.out);
+ images (dev, dir, storageId, objs [i], depth + 4);
+ break;
+
+ /*
+ // Digital Print Order Form
+ case ObjectInfo.DPOF:
+ maybeSaveObject (dev, dir, objs [i], info, depth);
+ break;
+ */
+
+ default:
+ // indent (System.out, depth);
+ // System.out.println (" ... IGNORED " + info.filename);
+ }
+ }
+ }
+
+ private static void
+ imageStore (Initiator dev, File dir, int id, boolean many)
+ throws IOException
+ {
+ if (dev.hasStore (id)) {
+ if (many)
+ System.out.println ("STORE: " + id);
+ images (dev, dir, id, ~0, 0);
+ } else
+ System.out.println ("Store " + id + " is not present");
+ }
+
+ private static void images (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ Initiator dev = startCamera ();
+ File dir = directory;
+
+ if (dir == null)
+ dir = new File ("images");
+ try {
+ if (!dir.exists ())
+ dir.mkdir ();
+ else if (!dir.isDirectory ())
+ throw new IOException ("not a directory: " + dir.toString ());
+
+ // FIXME: add a way to access the "aggregate across device" mode.
+ // FIXME: if it's supported, use the "only show image handles" mode
+
+ if (storageId != 0)
+ imageStore (dev, dir, storageId, false);
+ else {
+ int ids [] = dev.getStorageIDs ();
+ for (int i = 0; i < ids.length; i++)
+ imageStore (dev, dir, ids [i], ids.length != 1);
+ }
+
+ } finally {
+ // users may interrupt ... shutdown nicely
+ closeSession (dev);
+ }
+ }
+
+
+ private static void put (String argv [], int index)
+ throws IOException
+ {
+ if (index == (argv.length - 1))
+ usage (-1);
+
+ Initiator dev = startCamera ();
+ DeviceInfo devInfo = dev.getDeviceInfo ();
+ int folder = 0;
+
+ if (directory != null) {
+ // for now we only support the "camera chooses location"
+ // mode; eventually, do directory lookup
+
+ System.err.println ("ignoring directory spec: " + directory);
+ }
+
+ // FIXME -- gets replaced with the better scheme
+ NameFactory factory = dev.getFactory ();
+
+ try {
+ for (int i = index + 1; i < argv.length; i++) {
+ try {
+ // see if the argument works as a filename
+ File file = new File (argv [i]);
+ String temp;
+
+ if (!file.exists ())
+ throw new Exception ("not a file");
+ if (file.isDirectory ()) {
+ System.err.println ("... can't send directory yet: "
+ + argv [i]);
+ continue;
+ }
+
+ temp = file.getAbsolutePath ();
+ if (File.separatorChar != '/')
+ temp = temp.replace (File.separatorChar, '/');
+ if (!temp.startsWith ("/"))
+ temp = "/" + temp;
+ argv [i] = "file:" + temp;
+
+ } catch (Exception e) {
+ // otherwise it's a URL or an error
+ }
+
+ System.out.print ("put ");
+ System.out.print (argv [i]);
+ System.out.print (" ");
+
+ try {
+ URL url = new URL (argv [i]);
+
+ try {
+ URLConnection conn = url.openConnection ();
+ ObjectInfo info;
+ Data obj;
+ Response resp;
+
+ info = new ObjectInfo (conn, devInfo, factory);
+ obj = new FileSendData (conn, factory);
+
+ System.out.print (factory.getFormatString (
+ info.objectFormatCode));
+ System.out.print (", size ");
+ System.out.print (info.objectCompressedSize);
+
+ resp = dev.sendObjectInfo (info, storageId, folder);
+ if (resp.getCode () == Response.OK) {
+ // dev.sendObject (obj);
+ System.out.println (" ... NOT ENABLED");
+ } else {
+ System.out.println ("\n\t... ERROR, code = "
+ + resp.getCodeString ());
+ }
+ } catch (IOException e) {
+ System.out.println ("\n\t... IOException, "
+ + e.getMessage ());
+ throw e;
+ }
+ } catch (MalformedURLException e) {
+ System.out.println ("\n\t... not a file or URL: "
+ + argv [i]);
+ } catch (IllegalArgumentException e) {
+ System.out.println ("\n\t... can't send, "
+ + e.getMessage ());
+ }
+ }
+
+ } finally {
+ closeSession (dev);
+ }
+ }
+
+
+ private static void powerdown (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ Initiator dev = startCamera ();
+ int status = -1;
+
+ // FIXME -- gets replaced with the better scheme
+ NameFactory factory = dev.getFactory ();
+
+ try {
+ status = dev.powerDown ();
+ System.out.print ("PowerDown --> ");
+ System.out.println (factory.getResponseString (status));
+ } finally {
+ if (status != Response.OK)
+ closeSession (dev);
+ // session implicitly closed on successful powerdown
+ }
+ }
+
+
+ private static void reset (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ //startCamera (false).reset ();
+ System.out.println ("Device was reset.");
+ }
+
+
+ private static void selftest (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ Initiator dev = startCamera ();
+ int status;
+
+ // FIXME -- gets replaced with the better scheme
+ NameFactory factory = dev.getFactory ();
+
+ try {
+ status = dev.selfTest (0);
+
+ System.out.print ("selftest --> ");
+ System.out.println (factory.getResponseString (status));
+ } finally {
+ closeSession (dev);
+ }
+ }
+
+
+ private static void status (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ Initiator dev = startCamera ();
+ DeviceInfo info;
+ int ids [];
+
+ try {
+ info = dev.getDeviceInfo ();
+ ids = dev.getStorageIDs ();
+
+ info.lines (System.out);
+
+ System.out.print ("Operations: ");
+ System.out.println (info.operationsSupported.length);
+
+ System.out.print ("Modes:");
+ if (dev.isPull ())
+ System.out.print (" Pull");
+ if (dev.isPush ())
+ System.out.print (" Push");
+ System.out.println ();
+
+ // one mandatory object counting mode: aggregate across device
+ if (info.supportsOperation (Command.GetNumObjects)) {
+ try {
+ System.out.println ("Object count: "
+ + dev.getNumObjects (~0, 0, 0));
+ } catch (IOException e) {
+ // maybe has a single storage unit, that's not present
+ }
+ }
+
+ for (int i = 0; i < ids.length; i++) {
+ if (!dev.hasStore (ids [i])) {
+ System.out.println ("Store " + ids [i] + " is not present");
+ continue;
+ }
+ StorageInfo sinfo = dev.getStorageInfo (ids [i]);
+
+ if (ids.length != 1) {
+ System.out.print ("Store #" + ids [i] + ": ");
+ // FIXME: how many objects?
+ }
+ sinfo.line (System.out);
+ // FIXME: size in MBytes
+
+ }
+ } finally {
+ closeSession (dev);
+ }
+ }
+
+
+ private static void
+ storageStore (Initiator dev, int id)
+ throws IOException
+ {
+ System.out.println ("STORE: " + id);
+ if (dev.hasStore (id))
+ dev.getStorageInfo (id).dump (System.out);
+ else
+ System.out.println ("... not present");
+ }
+
+ private static void storage (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ Initiator dev = startCamera ();
+
+ try {
+ if (storageId != 0)
+ storageStore (dev, storageId);
+ else {
+ int ids [] = dev.getStorageIDs ();
+ System.out.println ("Stores: " + ids.length);
+ for (int i = 0; i < ids.length; i++)
+ storageStore (dev, ids [i]);
+ }
+ } finally {
+ closeSession (dev);
+ }
+ }
+
+
+ private static void maybeSaveThumb (
+ Initiator dev,
+ File dir,
+ int handle,
+ ObjectInfo info,
+ int depth
+ ) throws IOException
+ {
+ String fname = info.filename.toLowerCase ();
+ File f;
+
+ fname = info.filename.toLowerCase ();
+ if (info.thumbFormat != ObjectInfo.JFIF
+ && info.thumbFormat != ObjectInfo.TIFF_EP
+ && info.thumbFormat != ObjectInfo.EXIF_JPEG) {
+ // FIXME -- gets replaced with the better scheme
+ NameFactory factory = dev.getFactory ();
+
+ throw new IOException (
+ "illegal thumbnail format "
+ + factory.getFormatString (info.thumbFormat)
+ + " for image "
+ + info.filename
+ );
+ }
+ f = new File (dir, fname);
+
+ indent (System.out, depth);
+ System.out.print (f.toString ());
+
+ if (!overwrite && f.exists ()) {
+ System.out.println (" ... EXISTS, not saved");
+ } else {
+ FileOutputStream fout = new FileOutputStream (f);
+ Data obj = null;
+
+ try {
+ obj = dev.getThumb (handle);
+ fout.write (obj.data, obj.offset, obj.length - obj.offset);
+
+ System.out.print (" thumbnail saved, size ");
+ System.out.println (obj.length - obj.offset);
+ } finally {
+ fout.close ();
+ if (obj == null && f.delete ())
+ System.out.println (" ... not saved");
+ }
+ }
+ }
+
+ private static void thumbs (
+ Initiator dev,
+ File dir,
+ int storageId,
+ int assoc,
+ int depth
+ ) throws IOException
+ {
+ int objs [] = dev.getObjectHandles (storageId, 0, assoc);
+
+ for (int i = 0; i < objs.length; i++) {
+ ObjectInfo info = dev.getObjectInfo (objs [i]);
+
+ // all images have thumbnails
+ if (info.thumbFormat != 0)
+ maybeSaveThumb (dev, dir, objs [i], info, depth);
+
+ else switch (info.objectFormatCode) {
+ case ObjectInfo.Association:
+ indent (System.out, depth);
+ info.line (System.out);
+ thumbs (dev, dir, storageId, objs [i], depth + 4);
+ break;
+
+ default:
+ // ignore ancillary data
+ }
+ }
+ }
+
+ private static void
+ thumbStore (Initiator dev, File dir, int id, boolean many)
+ throws IOException
+ {
+ if (dev.hasStore (id)) {
+ if (many)
+ System.out.println ("STORE: " + id);
+ thumbs (dev, dir, id, ~0, 0);
+ } else
+ System.out.println ("Store " + id + " is not present");
+ }
+
+ private static void thumbs (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ Initiator dev = startCamera ();
+ File dir = directory;
+
+ if (dir == null)
+ dir = new File ("thumbs");
+ try {
+ if (!dir.exists ())
+ dir.mkdir ();
+ else if (!dir.isDirectory ())
+ throw new IOException ("not a directory: " + dir.toString ());
+
+ // FIXME: add a way to access the "aggregate across device" mode.
+ // FIXME: if it's supported, use the "only show image handles" mode
+
+ if (storageId != 0)
+ thumbStore (dev, dir, storageId, false);
+ else {
+ int ids [] = dev.getStorageIDs ();
+ for (int i = 0; i < ids.length; i++)
+ thumbStore (dev, dir, ids [i], ids.length != 1);
+ }
+
+ } finally {
+ // users may interrupt ... shutdown nicely
+ closeSession (dev);
+ }
+ }
+
+ private static void tree (
+ Initiator dev,
+ int storageId,
+ int assoc,
+ int depth
+ ) throws IOException
+ {
+ int objs [] = dev.getObjectHandles (storageId, 0, assoc);
+
+ for (int i = 0; i < objs.length; i++) {
+ ObjectInfo info = dev.getObjectInfo (objs [i]);
+
+ indent (System.out, depth);
+ info.line (System.out);
+ if (ObjectInfo.Association == info.objectFormatCode)
+ tree (dev, storageId, objs [i], depth + 4);
+ }
+ }
+
+ private static void treeStore (Initiator dev, int id, boolean many)
+ throws IOException
+ {
+ if (dev.hasStore (id)) {
+ if (many)
+ System.out.println ("STORE: " + id);
+ tree (dev, id, ~0, 0);
+ } else
+ System.out.println ("Store " + id + " is not present");
+ }
+
+ private static void tree (String argv [], int index)
+ throws IOException
+ {
+ if (index != (argv.length - 1))
+ usage (-1);
+
+ Initiator dev = startCamera ();
+
+ try {
+ if (storageId != 0)
+ treeStore (dev, storageId, false);
+ else {
+ int ids [] = dev.getStorageIDs ();
+ for (int i = 0; i < ids.length; i++)
+ treeStore (dev, ids [i], ids.length != 1);
+ }
+ } finally {
+ closeSession (dev);
+ }
+ }
+}
diff --git a/src/com/ash/beta/KodakExtension.java b/src/com/ash/beta/KodakExtension.java
new file mode 100644
index 0000000..b11a6ff
--- /dev/null
+++ b/src/com/ash/beta/KodakExtension.java
@@ -0,0 +1,145 @@
+package com.ash.beta;
+
+//Copyright 2000 by David Brownell
+ *
+ *
+ * Command and Arguments
+ * Description
+ * Options
+ *
+ *
+ *
+ *
+ * cameras
(same as "devices")
+ * none
+ *
+ *
+ *
+ *
+ * capture
starts capturing images or other objects, according
+ * to the current device properties.
+ * --port-id id
+ *
+ * --storage id
+ *
+ *
+ *
+ *
+ * devices
Lists PTP devices with their port identifiers
+ * none
+ *
+ *
+ *
+ *
+ * devinfo
Displays the DeviceInfo for a camera, including
+ * all the operations, events, device properties,
+ * and object formats supported.
+ * --port-id id
+ *
+ *
+ *
+ *
+ * devprops
shows all device properties, with types and values.
+ * --port-id id
+ *
+ *
+ *
+ *
+ * format
Reformats the specified storage unit (zero based).
+ * --port-id id
+ *
+ *
--storage number
+ *
+ *
+ *
+ *
+ * getprop
propname shows named device property, with type and value.
+ * --port-id id
+ *
+ *
+ *
+ *
+ * help
shows command summary
+ * none
+ *
+ *
+ *
+ *
+ * images
Downloads image files to directory
+ * --port-id id
+ *
+ *
--overwrite
+ *
--directory directory (default "images")
+ *
+ *
+ *
+ * powerdown
Causes the device to power down.
+ * --port-id id
+ *
+ *
+ *
+ *
+ * put
file-or-URL [...] Copies images or other objects to device.
+ * --port-id id
+ * --storage id
+ *
+ *
+ *
+ *
+ * reset
Issues a PTP level reset.
+ * --port-id id
+ *
+ *
+ *
+ *
+ * selftest
Runs a basic device self test.
+ * --port-id id
+ *
+ *
+ *
+ *
+ * status
Shows status summary for the device
+ * --port-id id
+ *
+ *
+ *
+ *
+ * storage
Displays the StorageInfo for the device's
+ * storage units, all or just the specified (zero base) store
+ * --port-id id
+ *
+ *
--storage number
+ *
+ *
+ *
+ *
+ * thumbs
Downloads image thumbnails to directory
+ * --port-id id
+ *
+ *
--overwrite
+ *
--directory directory (default "thumbs")
+ *
+ *
+ *
+ * tree
Lists contents of camera storage.
+ * --port-id id
+ *
+ *
+ * This file is part of PTP Android Camera Control (PACC).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see