Disposable Digital Camera

Low-Level USB Interface

[camera pic from pcworld.com article]


Heritage

The Dakota's USB interface is very similar to cameras that share a similar chipset -- specifically, the cameras that use the SPCA504x chip, have FLASH storage of pictures, and revision 2 of the firmware. The gPhoto project has reverse-engineered and released compatible drivers for four of these similar cameras: the "Aiptek 1.3 mega PocketCam," the $90 "Aiptek Smart Megacam," the "Maxell Max Pocket," and the "Benq DC1300."

Much of the information on this page comes from the libgphoto source code.  Commands that I think are common to many revision 2 firmware cameras are in white, while commands that I think are unique (or nonstandard) to this camera are highlighted in yellow.

Request
Value
Index
Bytes
 Description
R 0x00
xxxx
ADDR
1+
Read from external memory.  Addresses 0x2100-0x21FE are mapped to 0x1200; all others are left alone. More than one byte is readable (I've verified 512).
W 0x00
xx##
ADDR
0
Write a byte (in value_low) to external memory
W 0x00
xxxx
ADDR
1+
Write multiple bytes to memory? (just a  guess)
W 0x10
xxxx
0000

Upload code. Use with care; will probably destroy your camera.
W 0x10
xxxx
0001

Upload EEPROM data.
W 0x10
used
else

??
R 0x20
0000
0000
5
Get firmware revision. Returns a constant 02 00 00 xx 02, where xx is data at register 0x20FF, and was 05 when I tried. This might be a hardware revision level.
R 0x20
0000
0001
4
unknown. Returns a constant 01 26 30 00
R 0x21
0000
0000
1
Get ready status (ext. memory 0x0001). The upper nibble seems to be the operation mode, while the lowest bit is set if data is ready for transfer from the Bulk Pipe.
R 0x21
0000
0001
1
Get some sort of status (ext. memory 0x000b)
R 0x21 0000
0002
1
Get some sort of status (ext. memory 0x000c). While reading TOC, least significant bit set seems to mean that the USB transfer was triggered. LS nibble is then cleared when done. (May be called "Page Status")
R 0x21
0000
0003
7
Get all statuses at once: Returns (0x000a) (index_low) (index_high) (0x0001) (0x000b) (0x000c) (bRequest)
R 0x22
xxxx
xxxx
4
Returns 4 bytes from external memory: (0x0005) (0x0004) (0x0003) (0x0002)
R 0x23

used
varies
??
R 0x24
xxxx
0000
1
Does some stuff, sets 0x0bb9 to 0,1,2, or 3, then returns (0bb9).  0bb9 seems to be some sort of return-byte-count. - meaning unknown.
R 0x24
xxxx
0008
1
Returns (0x0ed1) - meaning unknown.
R 0x25

used
1
??
R 0x26
xxxx
xxxx
1
Returns (0x0264) - meaning unknown.
R 0x27
xxxx
xxxx
1
Returns (0x0f36) - meaning unknown.
R 0x28
0000
0000
1
Check to see if SDRAM-capable. Returns 00 (not capable)
R 0x28
0000
0001
1
Check to see if FLASH-capable. Returns 03, which I suspect is some size indicator.
R 0x28
0000
0002
-
Check to see if CARD-capable. Does not return data (but there is a function to parse for this value!)
R 0x29
xxxx
xxxx
2
Return (0x0e9b) (0x0e5a) - meaning unknown. May be "Data Time Request"
W 0x29
xxxx
xxxx
xx
Ignored. Is the time-of-day clock setting, but is not used in this camera.
R 0x2a
xxxx
0000
1
Return (0x0e5e) - meaning unknown.
R 0x2a
xxxx
0010
2
Return (0x25) (0x24) from internal memory - meaning unknown.
R 0x2d
xxxx
0000
3
Returns the first 3 digits of the camera's serial number. This usually starts with 2 or 3 letters.
R 0x2d
xxxx
0001
8
Returns the last 8 digits of the camera's serial number. These are usually digits.
R 0x2d
xxxx
0002
1
Read the initialization status. 0 means initialized, 1 means response was incorrect, 2 means that the response has not been checked yet (write the most significant byte last to check it). This status is stored in external memory location 0x0ee7* and can be modified directly by a "W 0x00" command. (* will probably change in future revisions of firmware)
R 0x2d
xxxx
0003
1
Read the maximum number of pictures this camera can take (25).
R 0x2d
xxxx
0004
4
Returns data - meaning unknown.
W 0x2d
xxxx
0000
1
Write least significant byte of response. Set initialization status to 2 (in-progress)
W 0x2d
xxxx
0001
1
Write next-to-least significant byte of response. Set initialization status to 2 (in-progress)
W 0x2d
xxxx
0002
1
Write next-to-most significant byte of response. Set initialization status to 2 (in-progress)
W 0x2d
xxxx
0003
1
Write most significant byte of initialization response, calculates the correct response, and compare the two. If correct, set initialization status to 0 (initialized), otherwise set to 1 (wrong). You don't need to know the correct initialization response or algorithm; you can read the correct response from memory at 0x0ee1* (MSB) – 0x0ee4 (LSB).  (* will probably change in future revisions of firmware)
W 0x51
count
0000
0
Take a picture.  Value contains the number of pictures to take in succession (at a rate of about 1 every three seconds), minus one. Since USB-only power alone doesn't seem to be enough to charge the photo flash, these pictures are taken without it. Not in gphoto, but may work for other similar cameras cameras. Other values of index may do other things, but it seems that only 0000 works.
W 0x52
used
used
0
Delete picture(s). Index and value parameters are used, but not quite known exactly how. gphoto sets both to 0000, and that seems to delete all pictures (but I haven't verified that).
R 0x54
0000
0000
2
Return number of pictures stored (n_toc_entries)
R 0x54
used
0001
varies
Get table of contents (value = n_toc_entries, must not be zero). Won't return anything if the initialization hasn't been done.
R 0x54
used
0002
varies
Read a file. The value must be the number in the file name, not the index number (as used in other SPCA504-based cameras). For example, to load file "DSC_0003.JPG", value should be 3. Won't return anything if the initialization hasn't been done.
R 0x54
xxxx
0003
1
Returns some value (unknown, may be a counter, may be related to video capture)
R 0x54
xxxx
else
0
Resets counter returned in index = 0003. (not sure of function, may be a reset)
R 0x54
used
0003
1?
Return (0x0bce)  (value must not be zero)
R 0x54
used
else
0?
Sets (0x0bce) to 0. Value must not be zero, index must not be 1, 2, or 3.
(many more commands exist and have not yet been added to this table)


Initialization Procedure Unique to This Camera

These commands are necessary for interoperability, and I believe that they are unique to the Dakota Digital.  Without this initialization sequence, no bulk data from any "bRequest=0x54" commands will be returned, including the file table-of-contents and the actual file download. The protocol is poorly implemented and is a perfect example of "bolt-on security" rather than "designed-in security".  Not only is the code inefficient, it provides little security. In fact, before finding this initialization sequence, I found two other simpler methods to retrieve my pictures. Of all the methods, though, this looks most likely to be preserved in future versions of the camera's firmware.

The general initialization procedure is to read the last 8 digits of the serial number (R 0x2d, index=1), hash them into a 32-bit number, and then upload this value back to the camera with a series of 4 commands (one byte at a time).

The actual hash formula used by the camera is:
hash  = (s[7] + 0xd0) & 0x0f
hash |= ((s[6] + 0xd0) & 0x0f) << 4
hash |= ((s[5] + 0xd0) & 0x0f) << 8
hash |= ((s[4] + 0xd0) & 0x0f) << 12
hash |= ((s[3] + 0xd0) & 0x0f) << 16
hash |= ((s[2] + 0xd0) & 0x0f) << 20
hash |= ((s[1] + 0xd0) & 0x0f) << 24
hash |= ((s[0] + 0xd0) & 0x0f) << 28
hash = (~hash) * 4

         Notes: "~" is a one's complement. s[0] is the most significant digit of the last 8 digits of the serial number.

It's horribly written code, but it works. The "add 0xd0" is probably the compiler's implementation of "minus 0x30", which would convert an ASCII digit (as the serial number is stored) into a binary coded digit. But, that's totally unnecessary if you're going to strip out the high nibble with the "and 0x0f"... do one or the other, but don't do both. The "and 0x0f" alone is better because it will always return the same value as the camera's code. The shift routines for this camera take a long time (proportional to the number of bits shifted), and could have been avoided: as implemented, this routine requires 112 shifts, when this could be easily done with 28 or no shifts. The 32-bit multiply by 4 is also expensive compared to a simple shift by 2.

A quicker hash formula used in my code is:
hash  = s[7] & 0x0f
hash |= (s[6] & 0x0f) << 4
hash |= (s[5] & 0x0f) << 8
hash |= (s[4] & 0x0f) << 12
hash |= (s[3] & 0x0f) << 16
hash |= (s[2] & 0x0f) << 20
hash |= (s[1] & 0x0f) << 24
hash |= (s[0] & 0x0f) << 28
hash = (~hash) << 2

I kept the expensive shift functions because I'm implementing my interface code on a beefy 32-bit computer, not a little 8-bit microcontroller.


Initialization Workarounds
There are three workarounds that allow you to read pictures without using the initialization sequence. Numbers 1 and 2 are the most obvious, and were what I found first
  1. Read the flash using raw commands (very slow, but thorough)
  2. Set the initialization status to tell the camera that it is initialized. Use W 0x00, value = 0000, index = 0ee7. This may change in future version of the firmware.
  3. Write a random byte to the most significant byte of the (W 0x2d, index = 0003) to force the camera to calculate the correct hash value, and then read that back from memory (stored at 0x0ee1 MSB – 0x0ee4 LSB).  This will probably change in future revisions of firmware

Resetting the Camera

(to be documented). See the source code for an example. Not sure if needed, but done by gphoto.

Reading the Table of Contents
Reading the table of contents is a two step process - first, you must get the file count, and then request the actual table. There are no subdirectories, and directory entries are in FAT12 format. I'm not sure if there are deleted files included in the count. This procedure is exactly the same as done by gphoto for other cameras.

To get the number of entries, issue a Read of 2 bytes with bRequest=0x54, value=0000, and index=0000. You'll get a 16-bit count back, least significant byte first.

Next, calculate the length of the table of contents. Each entries is 32 bytes, so multiply by the count to find the size. Round up to an even multiple of 512 bytes.

Then issue the read-table-of-contents command: Read 0 bytes with bRequest = 0x54, value = the number of entries, and index = 0001.  This will tell the camera to start the transfer, but it takes a little time before you can actually begin reading data out of the pipe. Repeatedly use the read-ready-status command (bRequest = 0x21, value=0000, index=0000) until you get a 1 back in the least significant bit, and then transfer the rounded-up bytes from the bulk read pipe.

Each entry will follow the FAT12 format, except since the camera doesn't have an on-board clock, don't expect the date and time to be correct. You don't need the cluster value, either (I haven't really investigated the value you get back to see if it's valid or useful). I've reproducted the FAT32 data structure here; it's a superset of FAT12:

Data Type
Descripton
char[11]
Filename. The first 8 characters are the base, and the last 3 are the extension.
UInt8
Attribute (bit coded, they'll all be files)
UInt8
reserved
UInt8 create time, tenths of a second.
UInt16 create time
UInt16 create date
UInt16 Last access date
UInt16 First cluster, upper 16 bits (used only with FAT32)
UInt16 write time
UInt16 write date
UInt16 First cluster, lower 16 bits
UInt32 File size


Read File Command

To read the picture data, you must first know the file name and size – these are available from the table of contents. (In fact, you probably must read the table of contents - this also seems to set up some internal stuff in the camera that allows the read file command to work).

Then, issue a read command with bRequest = 0x54, index = 0003, length = 0 bytes, and a value set to the number found in the file name (example: "DSC_0003.JPG", value should be 3). Again, use the read-ready-status command (bRequest = 0x21, value=0000, index=0000) until you get a 1 back in the least significant bit.  Round the file size up to the next size divisible by 0x4000, and then read this number of bytes from the bulk input pipe (not the control pipe).

Erasing Pictures
(to be documented). See the source code for an example. Procedure done by gphoto will erase all photos, but it looks like it will be possible to erase specific pictures.




back to my dakota digital page
my homepage