Serial Protocol

From Nikon Hacker
Jump to: navigation, search

Introduction

This page is dedicated to understanding the synchronous serial protocol used between the Toshiba chip (aka TX19) running A firmware and the Expeed chip (aka FR80) running B firmware.

Message exchange protocol

Message structure

The protocol is based on messages of the form

<message> = <header> <param> <checksum>

The header itself is built as :

<header> = <function_id> <param_len>

<function_id>, <param_len> and <checksum> are one byte each, and <param> is <param_len> bytes long

Synchronous serial dialog

As the dialog is synchronous, one of the chips (A) is the master and generates the clock signal. If no communication is going on, no clock signal is generated. When the master starts emitting a clock signal, bytes have to be exchanged both ways at the same time: at each clock pulse, one bit is sent and one bit is sampled by the master. So it's not a simple question/answer dialog where each speaks in turn: as soon as the A chip starts sending bytes, it also "receives" (samples) the signal available on its Data-In pin.

Consequently, for each message transmitted from A to B, a message has to be transmitted from B to A. If one of the chips has nothing to transmit, it will have to send a "null message" instead: function_id = 0, no payload (length=0) and corresponding checksum (0xFF). So a "null message" is "00 00 FF"

Another consequence is that the same number of bytes has to be sent in each direction. If messages don't have the same length, dummy data (zeroes) have to be sent as "stuffing" after the message by the chip sending the shortest message.

Acknowledge

Once two messages have been exchanged as explained above, each chip validates the received message and sends an <acknowledge> byte, which is either the received message's id if successful (ACK) or 0xFF if there was an error (NACK).

Example

At startup, FR80 wants to send a message with function id 01 (firmware version) while TX19 does not have any message yet, so it will send a null message (the request for a communication to happen involves a protocol using 2 external lines, see end of page). Let's follow that step by step:

1. TX19 writes the header of its message (00 00) at the same time that FR80 sends the header of its own message (01 03):

  • TX19 sends 00 00
  • FR80 sends 01 03

2. Then come the payload and checksum: For FR80, the payload of message 1 is the firmware version: ascii "101" is encoded as 0x31 0x30 0x31, and the checksum is computed as 0x69. For TX19, null message has no payload and checksum is 0xFF.

Thanks to the headers, the TX19 knows that the FR80 still has 4 bytes (3+checksum) to send while it has only 1 byte to send (0+checksum). So 4 more bytes will have to be exchanged for the messages to be transmitted, and the TX19 will generate the clock for 4 more bytes, and will use dummy bytes to complete its own message :

  • TX19 sends 00 00 FF 00 00 00
  • FR80 sends 01 03 31 30 31 69

3. Then both chips check that the message they received is valid (thanks to the checksum), and as it's OK, they acknowledge it by replying with each other's message id:

  • TX19 sends 00 00 FF 00 00 00 01
  • FR80 sends 01 03 31 30 31 69 00

The longer session below is left to decode as an exercise for the reader :-) :

  • TX19 sends 00 00 FF 00 00 00 01 - 02 06 01 01 00 00 00 00 F5 00 00 00 00 00 00 00 0B - 04 1A 00 01 00 00 00 00 00 03 00 00 00 80 00 C0 00 00 80 00 00 00 02 00 00 C0 00 00 5B 00
  • FR80 sends 01 03 31 30 31 69 00 - 0B 0D 00 00 08 00 00 00 00 00 00 00 00 00 00 DF 02 - 00 00 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04

FR Function table

A constant table is stored in the B firmware at 0x00267CF4-0x00268203 and contains 0x51 records of 0x10 bytes. I call it "table10". Here it is :

field0 field1 field2 field3 meaning
id param_addr param_len flags func_addr (where known)
00 00 00 00 00 00 00 00 00 00 00 00 02 00 20 46 5E null message
01 8F 85 15 E2 00 00 00 03 00 00 00 01 00 20 46 5E firmware version
02 8F 85 15 DC 00 00 00 06 00 00 00 00 00 20 46 5E
03 8F 85 15 D1 00 00 00 0B 00 00 00 00 00 20 46 60
04 8F 85 15 B7 00 00 00 1A 00 00 00 00 00 20 46 F0
05 8F 85 15 76 00 00 00 27 00 00 00 01 00 20 47 34
06 8F 85 15 4F 00 00 00 27 00 00 00 00 00 20 47 90
07 8F 85 15 34 00 00 00 1B 00 00 00 01 00 20 48 D0
08 8F 85 15 19 00 00 00 1B 00 00 00 00 00 20 4A 00
09 8F 85 15 13 00 00 00 06 00 00 00 01 00 20 4C 92
0A 8F 85 0C E4 00 00 00 04 00 00 00 00 00 20 4C A0
0B 8F 85 15 06 00 00 00 0D 00 00 00 01 00 20 4C AE
0C 8F 85 15 01 00 00 00 05 00 00 00 00 00 20 4D 00
0D 8F 85 15 00 00 00 00 01 00 00 00 01 00 20 46 5E
0E 8F 85 14 FD 00 00 00 03 00 00 00 00 00 20 4F 50
0F 8F 85 0D 2C 00 00 00 02 00 00 00 00 00 20 50 18
10 8F 85 0D 2A 00 00 00 02 00 00 00 01 00 20 46 5E
11 8F 85 0C D8 00 00 00 0C 00 00 00 00 00 20 50 5E
12 8F 85 14 F0 00 00 00 0D 00 00 00 01 00 20 54 18
13 8F 85 14 DE 00 00 00 12 00 00 00 01 00 20 54 22
14 8F 85 14 CC 00 00 00 12 00 00 00 00 00 20 54 24
15 8F 85 14 B2 00 00 00 1A 00 00 00 00 00 20 57 22
16 8F 85 14 AE 00 00 00 04 00 00 00 00 00 20 57 BC
17 8F 85 14 AD 00 00 00 01 00 00 00 01 00 20 57 C6
18 8F 85 14 AA 00 00 00 03 00 00 00 00 00 20 57 C8
19 8F 85 14 A9 00 00 00 01 00 00 00 00 00 20 58 8E
1A 8F 85 14 97 00 00 00 12 00 00 00 00 00 20 58 FA
1B 8F 85 13 E1 00 00 00 B6 00 00 00 00 00 20 46 5E
1C 8F 85 13 DF 00 00 00 02 00 00 00 00 00 20 46 5E
1D 00 00 00 00 00 00 00 00 00 00 00 00 00 20 59 6C
1E 8F 85 13 DE 00 00 00 01 00 00 00 01 00 20 5A 86
1F 8F 85 12 E2 00 00 00 FC 00 00 00 01 00 20 5A 9C
20 8F 85 11 E8 00 00 00 FA 00 00 00 00 00 20 5A AC
21 8F 85 11 E7 00 00 00 01 00 00 00 01 00 20 5A BC
22 8F 85 10 EB 00 00 00 FC 00 00 00 00 00 20 46 5E
23 8F 85 10 D7 00 00 00 14 00 00 00 00 00 20 5A CC
24 8F 85 10 B3 00 00 00 24 00 00 00 00 00 20 5A CE
25 8F 85 10 86 00 00 00 2D 00 00 00 00 00 20 46 5E
26 8F 85 10 68 00 00 00 1E 00 00 00 00 00 20 5C 42
27 8F 85 10 43 00 00 00 25 00 00 00 00 00 20 5C 8C
28 8F 85 10 41 00 00 00 02 00 00 00 01 00 20 46 5E
29 8F 85 10 3E 00 00 00 03 00 00 00 01 00 20 67 A8
2A 8F 85 10 3B 00 00 00 03 00 00 00 00 00 20 67 B4
2B 8F 85 10 30 00 00 00 0B 00 00 00 01 00 20 5D 70
2C 8F 85 10 25 00 00 00 0B 00 00 00 00 00 20 5D 7E
2D 8F 85 0F E5 00 00 00 40 00 00 00 00 00 20 5D DE
2E 8F 85 0C 18 00 00 00 40 00 00 00 00 00 20 46 5E
2F 8F 85 0C 58 00 00 00 40 00 00 00 00 00 20 46 5E
30 8F 85 0C 98 00 00 00 40 00 00 00 00 00 20 5E 70
31 8F 85 0F E4 00 00 00 01 00 00 00 01 00 20 5E B4
32 8F 85 0F E1 00 00 00 03 00 00 00 00 00 20 5E E4
33 8F 85 0F D4 00 00 00 0D 00 00 00 01 00 20 5F 36
34 8F 85 0F D3 00 00 00 01 00 00 00 00 00 20 5F 38
35 8F 85 0F D2 00 00 00 01 00 00 00 01 00 20 46 5E
36 8F 85 0F D1 00 00 00 01 00 00 00 00 00 20 60 48
37 8F 85 0F D0 00 00 00 01 00 00 00 01 00 20 46 5E
38 8F 85 0D 28 00 00 00 02 00 00 00 00 00 20 46 5E
39 8F 85 0F CF 00 00 00 01 00 00 00 00 00 20 60 56
3A 8F 85 0F CE 00 00 00 01 00 00 00 01 00 20 46 5E
3B 8F 85 0F CC 00 00 00 02 00 00 00 00 00 20 60 C0
3C 8F 85 0F B2 00 00 00 1A 00 00 00 00 00 20 62 A2
3D 8F 85 0F 6C 00 00 00 46 00 00 00 01 00 20 46 5E
3E 8F 85 0B E0 00 00 00 38 00 00 00 00 00 20 62 B0
3F 00 00 00 00 00 00 00 00 00 00 00 00 00 20 46 5E
40 8F 85 0D 26 00 00 00 02 00 00 00 00 00 20 46 5E
41 8F 85 0F 69 00 00 00 03 00 00 00 01 00 20 67 06
42 8F 85 0F 66 00 00 00 03 00 00 00 00 00 20 46 5E
43 8F 85 0F 51 00 00 00 15 00 00 00 01 00 20 46 5E
44 8F 85 0F 4F 00 00 00 02 00 00 00 00 00 20 67 12
45 8F 85 0C F4 00 00 00 32 00 00 00 01 00 20 46 5E
46 8F 85 0F 4C 00 00 00 03 00 00 00 01 00 20 46 5E
47 8F 85 0F 4B 00 00 00 01 00 00 00 01 00 20 46 5E
48 8F 85 0F 44 00 00 00 07 00 00 00 01 00 20 67 7C
49 8F 85 0E C4 00 00 00 80 00 00 00 01 00 20 67 8A
4A 8F 85 0E 3A 00 00 00 8A 00 00 00 01 00 20 67 98
4B 8F 85 0E 39 00 00 00 01 00 00 00 00 00 20 67 A6
4C 8F 85 0E 38 00 00 00 01 00 00 00 01 00 20 46 5E
4D 00 00 00 00 00 00 00 00 00 00 00 01 00 20 46 5E
4E 8F 89 9C 3D 00 00 00 04 00 00 00 01 00 20 46 5E
4F 8F 85 0D 38 00 00 01 00 00 00 00 00 00 20 68 0E
50 8F 85 0D 37 00 00 00 01 00 00 00 00 00 20 68 10

The fields are:

  • func_addr : the address of the function
  • param_addr : the address in RAM where function parameters will be stored
  • param_len : length of the parameters (in bytes)
  • flags : holds 2 booleans as bit1 and bit0

Note : if param_len is 0 (no param), then no param address is needed (e.g. ids 00, 1D, 3F, 4D)

Structures

The queue of messages to be sent are stored in a number of structures and variables

dlists as fifos

Many aspects of the firmware rely on double linked lists, or dlists, which are composed of individual elements of the form

<ptr_prev><ptr_next><value>

Each element holds two pointers, one pointing to the previous element (ptr_prev), one to the next element (ptr_next), and a value, or payload, which is the actual data. dlists can be used as queues (FIFO), stacks (LIFO) or plain lists.

The implementation uses a "sentinel" element to indicate an empty list, which is an element that has both ptr_prev and ptr_next pointing to that element's own address.

In the case of CSIO, dlists are used as stacks, each new element being "pushed" in the first position, and each element to be processed being "popped" from that position. To avoid confusion of such stacks with the program stack, I'll use here the name lifo.

The serial protocol uses two lists containing elements to be processed, and the process works with one or the other, according to a value set by one of the functions that is called via messages (function_id=0x0E).

Elements array

The elements that are inserted in the fifos are built as an array of 0x51 elements of 0xC bytes, stored in RAM at 0x8F89965C-0x8F899A1C. I call it "arrayC"

Each element, to be part of a dlist, has the structure <ptr_prev><ptr_next><value>. In this case, the <value> consists of 4 bytes : <flags><function_id><null><null>, where:

  • <flags> is a bitmap. Bits 7 and 6 are used
  • <function_id> is initialized with the index of the record in arrayC (0x00-0x50)
  • <null> is an unused byte

Variables

The following variables are used:

address size label description
0x8F850CEC word lifo1 points to the start of the first dlist
0x8F899654 word lifo2 points to the start of the second dlist
0x8F899C39 byte index_in function index of message received
0x8F899C38 byte index_in_copy copy of index_in (if it was valid, otherwise 0), sent as ACK. If CRC check fails, NACK (-1) is sent
0x8F899C35 byte length_in length of param received in message
0x8F899C33 byte checksum_in computed checksum of received message
0x8F899A2C word ptr_rec_in ptr to element table10[index_in]
0x8F899A32-0x8F899B31 0x100bytes param_in area for param received by serial
0x8F899C3A byte index_out function index of message to be sent (value fetched from lifo head)
0x8F899C37 byte index_out_copy copy of index_out of message to be sent
0x8F899C36 byte length_out length of param to be sent, taken from field1 of records in table10, or from <length_out_1F> for function index 1F
0x8F899C32 byte checksum_out computed checksum of message to be sent
0x8F899A28 word ptr_rec_out ptr to element table10[index_out]
0x8F899B32-0x8F899C31 0x100 bytes param_out area for param to be sent by serial
0x8F899C34 byte length_min min(<length_in>,<length_out>)
0x8F899C3B byte int_16_mode index [0-3] of the function to be called by int 0x16
0x8F899C3C byte int_1B_mode index [0-4] of the function to be called by int 0x1B
0x8F850D34 byte length_out_1F holds the (variable) length of the parameter for function 1F
0x8F8515E5 byte index_in_copy2 if valid, used by target 4/4 of int 16
0x8F8515E6 boolean must_use_lifo1 switched by 00204F50
0x8F899A30 boolean ? only ever used in int_16_body
0x8F899A31 boolean send_ack_error only ever used in Tsk19part4_process_param_in_1A8D90, seems to have no effect...
0x8F80CA18 word ? filled with the value of 0x138 for function 0x3B

EventFlags

Event flag 12:

bit 0 : not relevant

bit 1 : set to 0 upon init when enabling int_16, and waiting for it to become 1 meaning data has been received and is ready. Set by int_16 mode 1/2/3

bit 2 : set by int_16 mode 2

bit 3 : set to 0 before reading head of lifo, waiting for it to become 1 before trying again if empty. Set to 1 when an item is pushed into a lifo

bit 4 : int_1B occurred, success

bit 5 : int_1B occurred, failure

bit 6 : set before starting timer, and waiting the timer interrupt to be triggered

bit 7 : set when fifo and array init is done

Communication Request

Two special addresses are used in the FR code controlling the exchanges, that are really registers corresponding to I/O ports. They are:

0x50000100, bit 5:

This is pin 5 of FR Port 0 and is physically connected to pin 3 of port 5 of the TX CPU (INTF).

It is set by FR when serial interrupt occurs in modes 1 or 2, or timer interrupt occurs in mode 2, to request serial transmission. Reset after transmitting param part 1 or 2, or (N)ACK.

0x50000107, bit 6:

This is pin 6 of FR port 7 and is physically connected to pin 3 of port C of the TX CPU (INT16).

It is tested by FR during init of external interrupt 6 to see if a transmission is requested by the TX CPU.