The TI-84 Plus USB Protocol: A Partial Analysis Benjamin Moody March 31, 2006 ====================================================================== 1 Introduction --------------- This represents my current understanding of the protocol used to communicate with the OS and boot code of the TI-84 Plus. It is based mainly on: - Logs of the communication between the calculator and TI-Connect, obtained by Romain Liévin and Dan Englender; - My own analysis of the boot code and OS of the TI-84 Plus SE. I've been investigating the 84 Plus because that's the calculator I'm more familiar with. Nevertheless it looks like a lot of this is applicable to the TI-89 Titanium as well. Since I don't, as of this writing, have one of these calculators to play with, I haven't personally tested any of this! Now, on with the protocol... 2 Raw Packets -------------- All data sent over USB is encoded as part of a "raw packet." These packets have a fixed maximum size specified by the calculator: 255 bytes for the 84 Plus, or 1023 bytes for the Titanium. It seems that a raw packet may or may not be sent as a single USB transfer (but sending more than one raw packet as a single transfer is probably not wise.) Raw packets have a five-byte header followed by an n-byte data section: LL LL LL LL TT DD ... where LLLLLLLL = length of the data section; TT = packet type; DD = data bytes (LLLLLLLL of them.) It should be noted that, unlike the DBUS protocol, the length is transmitted in big endian order (most significant byte first.) In fact, all 16- and 32-bit numbers sent as part of the USB protocol (with the exception of numbers that are actually part of variable data) are transmitted in big-endian order. For example, 0x00000004 0x01 0x00 0x00 0x04 0x00 0x00000002 0x05 0xE0 0x00 0x00000010 0x04 0x00 0x00 0x00 0x0A 0x00 0x01 0x00 0x03 0x00 0x01 0x00 0x00 0x00 0x00 0x07 0xD0 are all valid and complete raw packets. There are five types of raw packets, indicated by the TT field: TT = 1: Buffer Size Request TT = 2: Buffer Size Allocation TT = 3: Virtual Packet Data with Continuation TT = 4: Virtual Packet Data Final TT = 5: Virtual Packet Data Acknowledgement Each of these plays its own crucial role in the protocol, and each will be discussed in the following sections. 3 Buffer Size Negotiation -------------------------- Before any real communication can take place, one crucial question must be settled: how big can a single raw packet be? At the moment the TI-84 Plus limits incoming and outgoing raw packets to 255 bytes, and the 89 Titanium limits them to 1023 bytes, yet that may change in the future. The device wishing to intiate communication, therefore, begins by sending a type 1 (Buffer Size Request) raw packet. The contents of this packet are a 32-bit integer (big endian, remember) which is the largest size of raw packet the device is willing to handle. Note that this length does not include the header; so the TI-84 Plus, which can handle a total raw packet size of 255 bytes, sends the number 0xFA, or 250. The receiving device must then decide whether the chosen buffer size is acceptable, or whether it must be made smaller still. (For example, TI-Connect gives a maximum packet size of one kilobyte, which is too large for the TI-84 Plus to handle.) The receiving device chooses a buffer size acceptable to both parties and sends a type 2 (Buffer Size Allocation) raw packet, whose contents are a 32-bit integer equal to the chosen size. The complete negotiation between TI-Connect and a TI-84 Plus thus goes as follows: TI-Connect sends 9 bytes stating that it can send and receive packets up to one kilobyte: PC->TI: 0x00000004 0x01 0x00000400 The calculator requires a smaller buffer, and replies: TI->PC: 0x00000004 0x02 0x000000FA Now both parties have agreed on a buffer size (0xFA.) Of course, both devices should now take care not to send packets larger than this size. It seems that this negotiation must be done at the start of every "operation;" after the communication is finished, the calculator will forget about the previously established buffer size. 4 Virtual Packets ------------------ 250 bytes is never enough, though. To actually send data back and forth we want to be able to send blocks of arbitrary size. This is where virtual packets come in. A virtual packet is always sent as the payload of one or more raw packets. Specifically, it is sent as the payload of zero or more type 3 (Virtual Packet Data with Continuation) raw packets followed by a single type 4 (Virtual Packet Data Final) raw packet. Only one virtual packet may be sent at a time; each virtual packet thus ends with its own type 4 raw packet. The virtual packet is then obtained by concatenating the contents of the raw packets, in order. So what is the actual structure of a virtual packet? Very much like the structure of a raw packet, yet subtly different. It has a six-byte header instead of five: LL LL LL LL TT TT DD ... where, like a raw packet, LLLLLLLL = length of the data section; TTTT = packet type; DD = data bytes (LLLLLLLL of them.) Thus there can potentially be as many as 65536 different virtual packet types defined. At the moment there are only about 22 of them, yet there is room for expansion. An example is sorely needed at this point. Here is a fairly common packet, the standard "ping," as it would be sent in a single type 4 raw packet: 0x00000010 0x04 0x0000000A 0x0001 0x00 0x03 0x00 0x01 0x00 0x00 0x00 0x00 0x07 0xD0 Note that the length of this virtual packet's data section is 10, or 0x0A. But the header is an additional 6 bytes, which brings the total size of the raw packet's data section to 0x10. The same virtual packet could also be sent (though it generally isn't) as a type 3 followed by a type 4: 0x00000008 0x03 0x0000000A 0x0001 0x00 0x03 0x00000008 0x04 0x00 0x01 0x00 0x00 0x00 0x00 0x07 0xD0 The actual distribution of data between the raw packets is not important; in either case the calculator will "unwrap" the same 16 bytes. Finally, each type 3 or type 4 raw packet must be acknowledged by the receiving device; this acknowledgement takes the form of a type 5 (Virtual Packet Data Acknowledgement) raw packet. The contents of this packet are always the 16-bit number 0xE000. I have no idea why. So the complete transmission of a 10-byte ping, beginning with the buffer size negotiation, looks like this: PC->TI: 0x00000004 0x01 0x00000400 TI->PC: 0x00000004 0x02 0x000000FA PC->TI: 0x00000010 0x04 0x0000000A 0x0001 0x00 0x03 0x00 0x01 0x00 0x00 0x00 0x00 0x07 0xD0 TI->PC: 0x00000002 0x05 0xE000 From now on I will ignore all of these details. Where I write that the calculator "sends a virtual packet with XXXX as its contents" this implies that the appropriate data will be encoded and sent as a series of type 3 and 4 raw packets, and of course the PC will acknowledge each one. 5 Virtual Packet Types ----------------------- The following are the types of virtual packets I have been able to identify thus far. There may be more out there. TTTT Description 0x0001 Ping / Set Mode 0x0002 Begin OS Transfer 0x0003 Acknowledgement of OS Transfer 0x0005 OS Data 0x0006 Acknowledgement of EOT 0x0007 Parameter Request 0x0008 Parameter Data 0x0009 Request Directory Listing 0x000A Variable Header 0x000B Request to Send 0x000C Request Variable 0x000D Variable Contents 0x000E Parameter Set 0x0010 Delete Variable 0x0011 Unknown 0x0012 Acknowledgement of Mode Setting 0xAA00 Acknowledgement of Data 0xBB00 Unknown 0xDD00 End of Transmission 0xEE00 Error Type 0x0001: Ping / Set Mode ---------------------------- The "ping" packet is sent to begin every transmission, immediately following the buffer size negotiation. In fact, it must always be the first packet sent, as it determines the set of commands the calculator will accept. The contents of this packet are: MM MM MM MM MM MM XX XX XX XX MMMMMMMMMMMM = mode ID; XXXXXXXX = unknown. The 84 Plus checks XXXXXXXX to be between 2000 and 131071 (inclusive.) I have no idea what this represents. If it is too small, an 0x001C error is sent; if it is too big, an 0x001D error is sent. Next, MMMMMMMMMMMM is checked. This determines what types of packets will be allowed in the following transmission. The following mode ID's are accepted by the 84 Plus OS: 0x000100010000 Startup mode; only ping is accepted. 0x000200010000 Basic mode; ping, OS transfer, and parameter request are accepted. 0x000300010000 Normal operation mode; ping, parameter request, directory listing, request to send, request variable, parameter set, variable delete, and command 0x0011 are accepted. The 84 Plus boot code only accepts the first two mode ID's. If the mode ID is acceptable, a type 0x0012 (Acknowledgement of Mode Setting) is sent in response. This contains the value of XXXXXXXX. Type 0x0002: Begin OS Transfer ------------------------------ This packet begins an OS transfer (or maybe also a certificate transfer?) It's only accepted in mode 2. An 0x0003 (Acknowledgement of OS Transfer) is sent in response. I don't have any details of this packet, but I'm guessing that it is a single byte which is 0 for an OS and other values for other things. Type 0x0003: Acknowledgement of OS Transfer ------------------------------------------- This packet is sent in response to an 0x0002 packet. Its contents will be something like: 0x00 0x00 0x01 0x04 0x00 0x00 0x00 0x00 The 0x0104 might be the maximum allowed size of an 0x0005 packet. Type 0x0005: OS Data -------------------- This packet contains OS data. It is analogous to a combination of the VAR and XDP packets in the DBUS protocol. Its contents are: AA AA PP FF DD ... AAAA = Z80 logical address to write data (zero for header and signature) PP = page to write data (zero for header and signature) FF = flag (zero for normal data, 0x80 for header or signature) DD = OS data, up to 256 bytes. Type 0x0006: Acknowledgement of EOT ----------------------------------- This packet is sent in response to an 0xdd00 (End of Transmission) packet. It seems it is only sent in Flash transfers. Its first byte is 1 if validation succeeded, or 0 if it failed. Type 0x0007: Parameter Request ------------------------------ This packet requests calculator "parameters." More on that later. Type 0x0008: Parameter Data --------------------------- This packet is sent in response to an 0x0007 (Parameter Request) packet. More on that later. Type 0x0009: Request Directory Listing -------------------------------------- This packet requests a directory listing. Each packet sent in reply will contain both the variable name and certain variable attributes; this packet allows you to specify which attributes you're interested in. Its contents should be: NN NN NN NN ( II II ) ... 0x00 0x01 0x00 0x01 0x00 0x01 0x01 \---------- ?????????? ----------/ NNNNNNNN = number of variable attributes requested; IIII = 16-bit ID of each attribute requested. The calculator will reply with a series of 0x000A packets, each containing a variable name and the requested attributes. Type 0x000A: Variable Header ---------------------------- This packet is sent either (a) as part of a directory listing; or (b) in response to an 0x000C (Request Variable) packet. In addition to the name, it can contain zero or more variable "attributes," which can be any sort of variable metadata (type, size, archived state, perhaps the folder name...) The contents of this packet are: LL LL UU ... 0x00 NN NN ( II II VV [ JJ JJ DD ... ] ) ... \??/ LLLL = length of variable's name; UU = variable name in UTF-8 (LLLL bytes); NNNN = number of variable attributes; for each attribute: IIII = 16-bit attribute ID; VV = 0 if attribute is valid; and if VV = 0: JJJJ = length of attribute data; DD = attribute data (JJJJ bytes.) For example: 0x0001 0x41 (Variable name: "A") 0x00 (???) 0x0006 (6 attributes present) 0x0002 (Attribute number 2 / data type:) 0x00 0x0004 (Attribute is valid, data is 4 bytes) 0xF0070000 (Data type.) 0x0003 (Attribute number 3 / is variable archived?:) 0x00 0x0001 (Attribute is valid, data is 1 byte) 0x00 (Variable is not archived.) 0x0005 (Attribute number 5) 0x01 (Attribute is invalid) 0x0001 (Attribute number 1 / variable data size:) 0x00 0x0004 (Attribute is valid, data is 4 bytes) 0x00000009 (Variable data is 9 bytes.) 0x0041 (Attribute number 41) 0x01 (Attribute is invalid) 0x0042 (Attribute number 42) 0x01 (Attribute is invalid) Known attributes: 0x0001 Variable size (4 bytes) 0x0002 Variable type (4 bytes) 0x0003 Is archived / in Flash? (1 byte) 0x0011 Variable type (used for requesting variables) (4 bytes) A variable type is the standard type ID plus either 0xF0030000, 0xF0070000, or 0xF00B0000. (Perhaps this varies with the device type.) Type 0x000B: Request to Send ---------------------------- This is a variation of the variable header packet. Its contents are: LL LL UU ... 00 00 00 00 09 01 NN NN ( II II JJ JJ DD ... ) ... \--- ??????? ---/ LLLL = length of name; UU = variable name in UTF-8 (LLLL bytes); NNNN = number of attributes; and for each attribute: IIII = 16-bit attribute ID (see above); JJJJ = length of attribute data; DD = attribute data (JJJJ bytes.) Note the lack of VV bytes, as all attributes the PC is sending are presumably valid. Type 0x000C: Variable Request ----------------------------- This is also a variation of the variable header packet. It requests both a variable header and the variable contents, so it includes BOTH a list of attributes the PC provides (data type) and a list of attributes the calculator needs to provide (variable size, is it archived?) Its contents are: /-------- ????????? --------\ LL LL UU ... 0x00 0x01 0xFF 0xFF 0xFF 0xFF NN NN ( II II ) ... MM MM ( II II JJ JJ DD ... ) 0x00 0x00 \- ??? -/ LLLL = length of name; UU = variable name in UTF-8 (LLLL bytes); NNNN = number of attributes requested; for each attribute: IIII = 16-bit attribute ID; MMMM = number of attributes specified; for each attribute: IIII = 16-bit attribute ID; JJJJ = length of attribute data; DD = attribute data (JJJJ bytes.) Note: When you specify the variable type, you must use attribute 0x0011 rather than 0x0002. I don't know why. Type 0x000D: Variable Contents ------------------------------ This packet simply contains the contents of the variable previously agreed upon. All meta-data is sent as part of the 0x000A or 0x000B packet. Type 0x000E: Parameter Set -------------------------- This packet attempts to set a parameter value. More on that later. Type 0x0010: Delete Variable ---------------------------- This is yet another variation of the variable header packet. Its contents are: LL LL UU ... 0x00 NN NN ( II II JJ JJ DD ... ) ... 0x01 0x00 0x00 0x00 0x00 LLLL = length of name; UU = variable name in UTF-8 (LLLL bytes); NNNN = number of attributes; and for each attribute: IIII = 16-bit attribute ID (see above); JJJJ = length of attribute data; DD = attribute data (JJJJ bytes.) If successful, the calculator sends an 0xAA00 (Acknowledgement of Data) packet in response. Type 0x0012: Acknowledgement of Mode Setting -------------------------------------------- This packet is sent in response to a ping. Its contents are four bytes: the same XXXXXXXX value sent in the ping. Type 0xAA00: Acknowledgement of Data ------------------------------------ This packet is sent to acknowledge many different commands. Its contents are a single byte, which always appears to be 1. Type 0xBB00 ----------- This is a strange one. The calculator appears to ignore this packet when it is received. Sometimes it is sent after an 0x0007 packet is received; sometimes it isn't. It usually contains the number 0x0001d4c0 (120000). Type 0xDD00: End of Transmission -------------------------------- This packet is sent when an OS transfer or directory listing is done. Type 0xEE00: Error ------------------ This packet indicates an error; the contents are a 2-byte error code. 6 Variable Transfers --------------------- Transferring variable data is done by the same overall method used in the DBUS protocol. Before sending or requesting a variable, you may want to retrieve a directory listing, which is done as follows: PC->TI: Request Directory Listing (0x0009) TI->PC: Variable Header (0x000A) TI->PC: Variable Header (0x000A) ... TI->PC: End of Transmission (0xDD00) To request a variable to be sent: PC->TI: Request Variable (0x000C) TI->PC: Variable Header (0x000A) TI->PC: Variable Contents (0x000D) To request to send a variable (silently): PC->TI: Request to Send (0x000B) TI->PC: Acknowledge (0xAA00) PC->TI: Variable Contents (0x000D) TI->PC: Acknowledge (0xAA00) PC->TI: End of Transmission (0xDD00) To request that a variable be deleted: PC->TI: Delete Variable (0x0010) TI->PC: Acknowledge (0xAA00) 7 Calculator Parameters ------------------------ A great deal of non-variable data (much more than was ever available through the DBUS protocol) is available in the form of "parameters." These are simply strings of binary data which can be read from (and some written to) the calculator. A parameter is read using the type 0x0007 (Parameter Request) packet. This packet's contents are NN NN ( II II ) ... NNNN = number of parameters requested; IIII = 16-bit ID of each parameter requested. For example, sending a type 0x0007 packet containing 0x0001 0x0002 requests parameter number 2, which happens to be the product name. In response, the calculator sends a type 0x0008 (Parameter Data) packet, whose contents are: NN NN ( II II VV [ LL LL DD ... ] ) ... NNNN = number of parameters requested; for each parameter: IIII = 16-bit parameter ID; VV = 0 if the given parameter ID is valid, 1 if it isn't; and if VV = 0: LLLL = length of the parameter data; DD = parameter data (LLLL bytes.) So for parameter 0x0002, the calculator might reply with a type 0x0008 packet containing: 0x0001 (one parameter requested) 0x0002 (parameter number 2) 0x00 (parameter is valid) 0x0019 (parameter data is 0x19 bytes long) 0x54 0x49 0x2D 0x38 (parameter data: in this case, an 0x34 0x20 0x50 0x6C ASCII string) 0x75 0x73 0x20 0x53 0x69 0x6C 0x76 0x65 0x72 0x20 0x45 0x64 0x69 0x74 0x69 0x6F 0x6E Multiple parameters can be requested at once; for example a type 0x0007 packet could contain: 0x0004 0x0002 0x000E 0x0011 0x0025 if you wanted to retrieve not only the product name, but also the amount of free RAM, free archive space, and the current time from the calculator's clock. You can also remotely assign values to certain parameters. (This is the way to set the clock, for instance.) To set a parameter, send an 0x000E packet containing: II II LL LL DD ... The calculator will either acknowledge with 0xAA00, or return an 0x0022 error if the parameter ID is invalid. Note that only one parameter can be set at once. The following parameters are present on the TI-84 Plus as of OS 2.40: (B = parameter available in boot mode; W = parameter writable.) ID B W Type Description 0001 * Int32 Product number? (0x0000000A for the 84 Plus) 0002 * String Product name, e.g. "TI-84 Plus Silver Edition" 0003 * 5 bytes Main part of calculator ID (in binary) 0004 * Int16 Hardware version 0005 * Unknown Unknown 0006 Byte Language ID 0007 Byte Sub-Language ID 0008 * Int16 DBUS device type? (0x0073 for the 84 Plus) 0009 * 4 bytes Boot Version (16 bit major, 8 bit minor, 8 bit micro) 000A * Byte 0 = no OS loaded (boot mode), 1 = OS loaded 000B 4 bytes OS Version (16 bit major, 8 bit minor, 8 bit micro) 000C * Int64 Amount of RAM physically present 000D * Int64 Maximum amount of RAM available to user 000E Int64 Amount of RAM currently free 000F * Int64 Amount of FlashROM physically present 0010 * Int64 Maximum amount of Flash available to user 0011 Int64 Amount of Flash currently free 0012 Int64 Maximum number of App pages available 0013 Int64 Number of App pages free 0019 Byte Unknown (always 1) 001A Byte Unknown (always 0) 001B Byte Unknown (always 0) 001C Byte Unknown (always 1) 001D Byte Unknown (always 1) 001E Int16 LCD width? 001F Int16 LCD height? 0022 768 bytes LCD contents 0023 Byte Unknown (always 1) 0024 * Byte 0 = clock off, 1 = clock on 0025 * Int32 Clock (seconds since 1997 Jan 1) 0027 * Byte 0 = Y/M/D, 1 = M/D/Y, 2 = D/M/Y style 0028 * Byte 0 = 12 hour, 1 = 24 hour mode 0029 Byte Unknown (always 0) 002D * Byte 0 = battery low, 1 = battery good 0030 * 32 bytes Unknown 0031 Unknown Unknown 0032 * Byte Unknown (0 or 1 according to system flag 5,(iy+51)) 0036 16 bytes Calculator ID, maybe? 0037 Byte 0 = not at homescreen, 1 = at homescreen 0038 Byte Unknown (always 0) 0039 Byte 0 = screen not split, 1 = screen split