Initialization Sequence
These events must be done somewhat in sequence. There is some flexibility as to how they can be ordered but it's best to follow this order:
Init Sequence:
// Enable power to the wifi system
POWERCNT |= 2;
// unclear if this is necessary, or exactly what it does.
*((volatile u16 *)0x04000206) = 0x30;
// Set Mac address
copy 6 bytes from flash[0x36] to mac address at 0x04800018
// Disable interrupts from wireless sources
W_IE = 0;
Wake up the wireless system:
[u16 0x04800036] = 0
delay 8 ms
[u16 0x04800168] = 0
temp = BB_Read(1);
BB_Write(1,temp&0x7F);
BB_Write(1,temp);
delay 30 ms
// same as "Init the RF system", below.
// this or the other one is probably not necessary.
RF_Init
Init the Mac system:
// Set the following registers (offset from 0x04800000) to the
// specified values:
reg[0x04] = 0
reg[0x08] = 0
reg[0x0A] = 0
reg[0x12] = 0
reg[0x10] = 0xffff
reg[0x254]= 0
reg[0xB4] = 0xffff
reg[0x80] = 0
reg[0x2A] = 0
reg[0x28] = 0
reg[0xE8] = 0
reg[0xEA] = 0
reg[0xEE] = 1
reg[0xEC] = 0x3F03
reg[0x1A2]= 1
reg[0x1A0]= 0
reg[0x110]= 0x0800
reg[0xBC] = 1
reg[0xD4] = 3
reg[0xD8] = 4
reg[0xDA] = 0x0602
reg[0x76] = 0
Init the RF system:
// write 16bit values from flash (starting at offset 0x44) to
// the following list of registers (offset from 0x04800000) -
// the actual assignments are also listed here for reference.
list: 0x146, 0x148, 0x14A, 0x14C, 0x120, 0x122, 0x154, 0x144,
0x130, 0x132, 0x140, 0x142, 0x038, 0x124, 0x128, 0x150
Actual assignments (based on present firmware dumps):
reg[0x146] = 0x0002
reg[0x148] = 0x0017
reg[0x14A] = 0x0026
reg[0x14C] = 0x1818
reg[0x120] = 0x0048
reg[0x122] = 0x4840
reg[0x154] = 0x0058
reg[0x144] = 0x0042
reg[0x130] = 0x0140
reg[0x132] = 0x8064
reg[0x140] = 0xE0E0
reg[0x142] = 0x2443
reg[0x038] = 0x000E
reg[0x124] = 0x0032
reg[0x128] = 0x01F4
reg[0x150] = 0x0101
Note: DO NOT USE THESE VALUES, READ THE CORRECT ONES FROM FIRMWARE
numentries = flash byte at 0x42 (=0x0C == 12)
numbits = flash byte at 0x41
numbytes = (numbits+7)/8;
// specifies number of bits per transfer
reg[0x184] = ((numbits<<1) & 0x0100) | (numbits&0x7F) -- W_RFSIOCNT
if flash byte 0x40 == 3 { -- doesn't in current firmware...
// not going to go over this one, as it's not the case in present firmware.
for(i=0;i<numentries;i++) RF_Write(0x50000 | (i<<8) | flash byte at 0xCE+i);
} else {
for(i=0;i<numentries;i++) RF_Write(numbytes flash bytes, starting at 0xCE + i*numbytes);
}
// For the second case, which is the currently used one,
// the following values are written to RF_Write:
RF_Write 0x00C007
RF_Write 0x129C03
RF_Write 0x141728
RF_Write 0x1AE8BA
RF_Write 0x1D456F
RF_Write 0x23FFFA
RF_Write 0x241D30
RF_Write 0x280001
RF_Write 0x2C0000
RF_Write 0x069C03
RF_Write 0x080022
RF_Write 0x0DFF6F
// really odd values, unclear exactly what they mean but it's likely they're
// initializing a PLL or something with various frequencies or options.
Note: DO NOT USE THESE VALUES, READ THE CORRECT ONES FROM FIRMWARE
Init the BaseBand System: -- and you thought the RF init was bad?
// The baseband init is rather simple, just read in 105 values and
// write them to the baseband. simple, eh? :)
// routine goes like this:
reg(0x160) = 0x0100
for(i=0;i<0x69;i++) BB_Write( i, flash byte 0x64+i );
For your convenience (or torture), all 105 values are listed here:
BB_Write 0, 0x6D BB_Write 1, 0x9E
BB_Write 2, 0x40 BB_Write 3, 0x05
BB_Write 4, 0x1B BB_Write 5, 0x6C
BB_Write 6, 0x48 BB_Write 7, 0x80
BB_Write 8, 0x38 BB_Write 9, 0x00
BB_Write 10, 0x35 BB_Write 11, 0x07
BB_Write 12, 0x00 BB_Write 13, 0x00
BB_Write 14, 0x00 BB_Write 15, 0x00
BB_Write 16, 0x00 BB_Write 17, 0x00
BB_Write 18, 0x00 BB_Write 19, 0x00
BB_Write 20, 0xB0 BB_Write 21, 0x00
BB_Write 22, 0x04 BB_Write 23, 0x01
BB_Write 24, 0xD8 BB_Write 25, 0xFF
BB_Write 26, 0xFF BB_Write 27, 0xC7
BB_Write 28, 0xBB BB_Write 29, 0x01
BB_Write 30, 0xB6 BB_Write 31, 0x7F
BB_Write 32, 0x5A BB_Write 33, 0x01
BB_Write 34, 0x3F BB_Write 35, 0x01
BB_Write 36, 0x3F BB_Write 37, 0x36
BB_Write 38, 0x36 BB_Write 39, 0x00
BB_Write 40, 0x78 BB_Write 41, 0x28
BB_Write 42, 0x55 BB_Write 43, 0x08
BB_Write 44, 0x28 BB_Write 45, 0x16
BB_Write 46, 0x00 BB_Write 47, 0x01
BB_Write 48, 0x0E BB_Write 49, 0x20
BB_Write 50, 0x02 BB_Write 51, 0x98
BB_Write 52, 0x98 BB_Write 53, 0x1F
BB_Write 54, 0x0A BB_Write 55, 0x08
BB_Write 56, 0x04 BB_Write 57, 0x01
BB_Write 58, 0x00 BB_Write 59, 0x00
BB_Write 60, 0x00 BB_Write 61, 0xFF
BB_Write 62, 0xFF BB_Write 63, 0xFE
BB_Write 64, 0xFE BB_Write 65, 0xFE
BB_Write 66, 0xFE BB_Write 67, 0xFC
BB_Write 68, 0xFC BB_Write 69, 0xFA
BB_Write 70, 0xFA BB_Write 71, 0xFA
BB_Write 72, 0xFA BB_Write 73, 0xFA
BB_Write 74, 0xF8 BB_Write 75, 0xF8
BB_Write 76, 0xF6 BB_Write 77, 0xA5
BB_Write 78, 0x12 BB_Write 79, 0x14
BB_Write 80, 0x12 BB_Write 81, 0x41
BB_Write 82, 0x23 BB_Write 83, 0x03
BB_Write 84, 0x04 BB_Write 85, 0x70
BB_Write 86, 0x35 BB_Write 87, 0x0E
BB_Write 86, 0x16 BB_Write 89, 0x16
BB_Write 90, 0x00 BB_Write 91, 0x00
BB_Write 92, 0x06 BB_Write 93, 0x01
BB_Write 94, 0xFF BB_Write 95, 0xFE
BB_Write 98, 0xFF BB_Write 97, 0xFF
BB_Write 98, 0x00 BB_Write 99, 0x0E
BB_Write 100, 0x13 BB_Write 101, 0x00
BB_Write 102, 0x00 BB_Write 103, 0x28
BB_Write 104, 0x1C
Note: DO NOT USE THESE VALUES, READ THE CORRECT ONES FROM FIRMWARE
// Set Mac address
copy 6 bytes from flash[0x36] to mac address at 0x04800018
// now just set some default varibles
W_RETRLIMIT = 7
Set channel (see section on changing channels)
Set Mode 2 -- sets bottom 3 bits of W_MODE_WEP to 2
Set Wep Mode / key -- Wep mode is bits 3..5 of W_MODE_WEP
BB_Write 0x13, 0x00
BB_Write 0x35, 0x1F
// To further init wifi to the point that you can properly send and
// receive data, there are some more variables that need to be set.
reg[0x032] = 0x8000 -- (clear error flag?)
reg[0x134] = 0xFFFF
reg[0x028] = 0 -- clear W_AID value
reg[0x02A] = 0
reg[0x0E8] = 1 -- enable microsecond counter
reg[0x038] = 0
reg[0x020] = 0 -- clear BSSID
reg[0x022] = 0
reg[0x024] = 0
TX prepare:
reg[0x0AE] = 0x000D -- flush all pending transmits
RX prepare:
reg[0x030] = 0x8000
// these are just my default values, you can set basicly whatever you want.
reg[0x050] = 0x4C00 -- RX Fifo begin
reg[0x052] = 0x5F60 -- RX Fifo end (length = 4960 bytes)
reg[0x056] = 0x0600 -- fifo begin latch address, 0xC00/2 = 0x600
reg[0x05A] = 0x0600 -- fifo end, same as begin at start.
reg[0x062] = 0x5F5E -- last element of fifo (unclear if this is necessary)
reg[0x030] = 0x8001 -- latch new fifo values for hardware use
reg[0x030] = 0x8000 -- enable receive
W_IF=0xFFFF -- clear interrupt flags
W_IE=(whatever) -- set enabled interrupts
reg[0x1AE] = 0x1FFF
reg[0x1AA] = 0
// set this to 0x581 when you successfully connect to an
// access point and fill W_BSSID with a mac address for it.
reg[0x0D0] = 0x181 // (W_RXFILTER)
reg[0x0E0] = 0x000B
reg[0x008] = 0
reg[0x00A] = 0
reg[0x004] = 1 -- hardware mode
reg[0x0E8] = 1
reg[0x0EA] = 1
reg[0x048] = 0
reg[0x038] &= ~2
reg[0x048] = 0
reg[0x0AE] = 2
reg[0x03C] |= 2
reg[0x0AC] = 0xFFFF
And, that's it. after all that the DS should be happy to send and receive packets. It's very possible there are unnecessary register sets in here, but this pseudocode is based very closely on my current working codebase, and it should work. I just haven't had the time to weed out all of the unnecessary parts, and test to verify they were indeed unnecessary.
Please keep in mind that this is very preliminary information, if you find bugs or can't make it work, please let me know.
Reading Firmware
Check out Firmware for more information (code at the bottom).
RF / Baseband communications
RF_Write(data)
while(W_RFSIOBUSY&1);
W_RFSIODATA1=(data & 0xFFFF);
W_RFSIODATA2=(data >> 16);
while(W_RFSIOBUSY&1);
BB_Write(address,data)
counter=10000;
while((W_BBSIOBUSY&1) && (--counter));
if(!counter) return -1;
W_BBSIOWRITE=data;
W_BBSIOCNT=address | 0x5000;
counter=10000;
while((W_BBSIOBUSY&1) && (--counter));
return 0;
BB_Read(address)
while(W_BBSIOBUSY&1);
if(!counter) return -1;
W_BBSIOCNT=address | 0x6000;
while(W_BBSIOBUSY&1);
return W_BBSIOREAD;
Wifi Transmit Procedure
To transmit data via wifi (Assuming you've already initialized wifi and changed channels to the channel you want):
- Copy the TX Header followed by the 802.11 packet to send anywhere it will fit in MAC memory (halfword-aligned)
- Take the offset from start of MAC memory that you put the packet, divide it by 2, and or with 0x8000 - store this in one of the
W_TXLOC registers
- Store the bit associated with the
W_TXLOC register you used into W_TXCNT - this will send the packet.
- You can then read the result data in
W_TXSTAT when the TX is over (you can tell either by polling or interrupt) to find out how many retries were used, and if the packet was ACK'd
Of course, this is just the simplest approach, you can be a lot more clever about it.
TX Header
The TX header is a structure needed by the wifi hardware, it contains important information such as the length of the packet being sent and the rate at which to send (possibly other things too). The TX header is always 12 bytes, it immediately precedes the data to be sent, and should be put at the location that will be given to the register activating a transmission.
The TX header takes the following form: (entries are halfwords)
Offset Description
+0 Unknown (used by hardware)
+2 Unknown (used by hardware)
+4 Unknown (used by hardware)
+6 Unknown (used by hardware)
+8 Transmit rate (speed in megabits multiplied by 10 - 1.0Mbit is 0x0A, 2.0Mbit is 0x14)
+10 Transmit length (bytes) transmits the data at +12 through +12+length-1 inclusive.
Important note! TX length includes the length of a 4-byte "FCS" (checksum) for the packet. The hardware generates the FCS for you, but you still must include it in the packet length. Also note that if the 802.11 WEP enabled bit is set in the header, the packet will be automatically encrypted via the wep algorithm - however, the software is responsible for providing the 4-byte IV block with the WEP key ID and the 24bit IV value.
Wifi Receive Procedure
To receive data via wifi, you either need to handle the wifi received data interrupt, or you need to poll W_RXHWWRITECSR - whenever it is != W_RXREADCSR, there is a new packet. When there is a new packet, take the following approach:
- Calculate the length of the new packet (read "received frame length" which is +8 bytes from the start of the packet) - total frame length is (12 + received frame length) padded to a multiple of 4 bytes.
- Read the data out of the RX FIFO area (keep in mind it's a circular buffer and you may have to wrap around the end of the buffer)
- Set the value of
W_RXREADCSR to the location of the next packet (add the length of the packet, and wrap around if necessary)
Keep in mind, W_RXREADCSR and W_RXHWWRITECSR must be multiplied by 2 to get a byte offset from the start of MAC memory
RX Header
The RX header is an informational structure that provides needed information about a received packet. It is written right before the received packet data in the rx circular buffer. The RX header is always 12 bytes.
The RX header takes the following form: (entries are halfwords)
Offset Description
+0 Unknown (used by hardware)
+2 Unknown (used by hardware)
+4 Unknown (used by hardware)
+6 Unknown (used by hardware)
+8 Received frame length (bytes)
+10 bottom 8 bits are RSSI (not 100% sure)
Important Note: Received frame lengths are always multiples of 4 bytes. While the actual header length + received frame length may be less, when incrementing the read cursor you must pad the length to a multiple of 4 bytes
Wifi Change Channels Procedure
You can set the channel to any channel number between 1 and 13 (inclusive) on the DS hardware. here's some pseudocode to do it:
read 3 bytes of flash starting at (0xF2+(channel-1)*6) to a temporary value
call RF_Write with the 3 bytes you just read (as a 0-padded long value)
read 3 bytes of flash starting at (0xF5+(channel-1)*6) to a temporary value
again, call RF_Write with the 3 bytes you just read (as a 0-padded long value)
delay a few milliseconds
call BB_Write with parameters 0x1E, and the byte in flash at 0x146+(channel-1)
Congrats, you are now ready to transmit/receive on whatever channel you picked.