DSWifi
|
DSWifi is a library that runs on the ARM9 and ARM7 simultaneously, and it has a fundamentally asynchronous design. The ARM9 sends commands to the ARM7 using shared RAM, and the ARM7 eventually handles the commands. This design is consistent with how WiFi works: some actions require a long time to complete, sometimes up to a few seconds!
The ARM7 can do several operations without intervention from the ARM9. It can look for access points and connect to them. It can act as an access point (when acting as a multiplayer host) and accept and deny connections. This means that, in practice, the ARM9 sets the desired state of the ARM7 and the ARM7 performs steps in the direction that will reach the desired state of the ARM9.
The library supports three global modes:
It also supports other internal modes:
As an initial explanation: DSWifi has three queues in regular RAM, and two in WiFi MAC RAM.
Both the ARM9 and ARM7 can transmit packets. The ARM7 sends management packets and manages the responses. For example, this is done to connect to APs, and to let other DS devices connect to a DS acting as host. If the ARM9 had to handle the connection handshake it would be much less efficient.
When transmitting packets generated by the ARM7, they are saved to a temporary buffer (at the moment, this buffer can only hold one packet).
When transmitting packets from the ARM9 they are saved in a circular buffer in shared memory between the ARM7 and ARM9. Then, the ARM9 sends a sync FIFO message to the ARM7 to notify it that there are packets in that buffer.
The ARM7 has priority transmitting packets over the ARM9. If there is any packet in the ARM7 queue, that one will be transmitted over the ones waiting to be transmitted from the ARM9.
Regardless of the origin, in the end, the packet is copied to MAC RAM, and the transfer is started. After the transmission is complete, an interrupt is generated, and the ARM7 checks the queues again. Only one packet is ever copied to MAC RAM at any point.
When a transfer is requiested, the WiFi hardware modifies some fields in the packet header (like the duration) and it transfers it.
When receiving packets, they are saved by the hardware in MAC RAM in a circular buffer. When packets are received, an interrupt is triggered, and the ARM7 starts handling them.
Some packets are handled directly by the ARM7 and they are never sent to the ARM9 (authentication and beacon packets, for example). Data packets are saved to a circular buffer in shared RAM between ARM7 and ARM9. Whenever a packet is saved to this buffer, the ARM7 sends a sync FIFO message to the ARM9 to notify it. Eventually, the ARM9 will check the buffer and handle it.
When in Internet mode, data packets are sent to sgIP. When in multiplayer mode, data packets are sent to the packet handlers defined by the developer.
When in multiplayer host mode, it is required to regularly send beacon packets. This packet is generated on the ARM9 and sent to the ARM7 using the regular TX queues, but it isn't sent by the ARM7 as usual. It is saved in a specific part of MAC RAM reserved for beacon packets. The hardware regularly sends this packet without any kind of DSWifi intervention.
CMD packets are handled like regular ARM9 packets, only the hardware transmission step is different.
REPLY packets are different. There are two memory regions in MAC RAM reserved for REPLY packets. They are independent from the TX buffer region, so a multiplayer client can still send regular messages while a REPLY packet is ready to be sent.
Multiplayer packets are handled like regular data packets when they are received. They are sent to the ARM9 using the same queue as regular data packets.
DSWifi can send debug messages from the ARM7 to the ARM9 with different information about the status of the library and potential errors. The debug messages are disabled in release builds of the library, you need to link the debug version of DSWifi if you want to check them.
In the ARM9 you need to setup a libnds console. Then, assign it to the ARM7:
You don't need to modify anything in the ARM7 code. However, in your ARM7 Makefile you need to link with libdswifi7d.a
instead of libdswifi7.a
. Look for LIBS
and replace -ldswifi7
by -ldswifi7d
.
If you have an ARM9-only project, you will need to convert it to a combined ARM7 + ARM9 project. Currently BlocksDS doesn't distribute any default ARM7 binary with debug DSWifi messages.
The WiFi hardware of the DS isn't fully compatible with the IEEE 802.11b standard. It only supports 1 and 2 Mbit/s transfer rates, and it doesn't support 5.5 and 11. However, some routers really don't like this.
When connecting to an AP the device needs to communicate the supported transfer rates. Initially, DSWifi tries to connect to them by being honest. However, it's fairly common that routers reject the connection with status code 18 "Association denied due to requesting station not supporting all data rates in the BSSBasicRateSet parameter, where BSS refers to basic service set".
It retries by lying and saying it supports 5.5 and 11 Mbit/s as well. This works because the AP retries with slower transfer rates if it doesn't get any response after sending a packet with the fast transfer rates.
Note that in multiplayer mode all communications are done at 2 Mbit/s, and there is no possibility of being rejected by the host because DSWifi can accept any client that only supports 1 and 2 Mbit/s.
The WiFI hardware of the DS supports an efficient way to transfer packets while in multiplayer mode. Normally, when sending packets between a device and an AP, it is needed to do a handshake. The transmitter sends a RTS (request to send) packet, the receiver sends a CTS (clear to send) packet, the transmitter sends the actual packet it wants to send, and the receiver sends an ACK (acknowledgement) packet. This handshake is okay when there are only two devices communicating, and DSWifi supports it, but it can really slow things down when multiple clients are connecting to the same underpowered device (like a DS).
In order to solve this issue, the DS supports sending packets using what DSWifi calls CMD/REPLY messages. With this system, the DS acting as host (which is just an AP) starts a contention free period (CFP). It sends a CF-Poll packet (CMD packet), and all clients connected to it send a CF-ACK packets as a response. The clients send the CF-ACK packets in order based on their association IDs (AID), this is done automatically by the hardware. Finally, when the last client has sent its data, the host sends a CF-ACK with no data to finalize the process. This process is more efficient than the 4-way handshake explained before because there are no handshakes between the initial CF-Poll and the last CF-ACK packets.
CMD packets can be sent like regular packets, but they require more setup. DSWifi needs to fill more fields in the header (and the body!) of the packet, and DSWifi needs to caclulate the duration of the transmission periods of host and clients. It also needs to setup a countdown timer that will cancel the process when it reaches 0.
A REPLY packet needs to be saved to WiFi RAM. Then, its address is written to a WiFi register. When the client determines it needs to send a message, it checks this register. If it's disabled, it will send an empty CF-ACK packet with no body. If this register is enabled, it will send the packet it points to.
The format of WiFi packets is defined by the IEEE 802.11 standard. There's no point in documenting this in DSWifi as there are much better references that can be used. The main reference used to develop DSWifi after the creation of BlockDS is "802.11 Wireless Networks: The Definitive Guide" by Matthew Gast. This book has enough low-level information to develop everything that DSWifi needs. Another book that was used as reference is "IEEE 802.11 Handbook: A Designer's Companion" by Bob O'Hara, Al Petrick, but this book doesn't have enough low-level information to be used on its own.