NDS WiFi Programming with devkitARM — Part 4

Posted in NintendoDS, Uncategorized, sensors on February 25th, 2010 by Chaos Engineer

We've established the important parts of the PC application, the NdsInterfaceHost, and now comes time to actually delve into the DS programming aspects w/ devkitARM and dswifi. We will here lay out the design of the NintendoDS application, the NdsInterfaceClient. Many of the network and socket concepts we covered during Part 3 of this article will carry over into the DS code. I will try not to cover the same ground. Also note that it is entirely possible to use C++ and iostream type code with devkitARM. I chose to use standard C because it is less verbose, and for the DS platform, it just seems more appropriate to use less abstraction.

The NDS application exhibits much code that is ancillary to the main purpose of the project. Being a platform that is rarely developed for, there is only a small community focusing on NintendoDS development. I figure that any code that exploits the features of the DS or establishes a framework that can be extended and used for new purposes is a good contribution to the community. I won't cover this ancillary code, but be sure to download the project and examine the extra bits like the chat server that will let you telnet into your DS.

The basis of the NdsInterfaceClient is simple. On start, it displays some information about the DS it is running on, and then prompts the user to press B to get a list of wireless access points (APs). The code then calls Wifi_ScanMode(), and enters a loop that constantly polls the number of APs, collects information about each, and displays this information on the screen. The Wifi_ScanMode() call actually tells the dswifi library to poll and maintain an internal list of available APs. You can then query the length of this internal list using Wifi_GetNumAp(), and then get specifics using Wifi_GetAPData(...), passing the index of a particular AP.

For the AP list, I reused some code written by Stephen Stair (the author of the dswifi library). I added some ANSI color for readability, and changed the format for AP data so it offered more info, particularly letting the user know if an AP was WAP protected (and thus unusable by dswifi). One of devkitPro's first feats was to implement a console on the DS. The state of this implementation today is quite elite. The console supports ANSI escape sequences, which allow coloring text and arbitrarily relocating the cursor position. One could create an ASCII game using the console and escape sequences alone. The chat server code shows how to use escape sequences to create a "window" in the console.

Once an AP is selected from the list, the code will determine if the user needs to enter an encryption key. If so, a keyboard appears, and allows the entry of an encryption key. The user can enter either a 40b or 104b encryption key. Based on the length of the entry, the code determines if it is 64 or 128-bit WEP encryption, and attempts a connection to the AP. This is one of the least documented basic steps for using dswifi, and on top of that, there are some curiosities worth mentioning.

WAP encryption is unsupported by the NDS and NDS Lite. Support for WAP was added to the DSi, but it isn't likely we'll see it supported by the dswifi library any time soon, if ever. We are limited to using WEP-secured APs. There are two levels of supported WEP encryption, 64-bit and 128-bit. Here is where a curiosity appears. Both 64b and 128b WEP encryption use a 24b Initialization Vector, which is not included in the length of the encryption key you enter. So the actual length of the entered encryption keys for 64b and 128b WEP encryption are 40b and 104b, respectively. It is fine to refer to these encryption levels as either bit length including the IV, or bit length excluding the IV. Strangely, the dswifi library does both, and defines the following two values: WEPMODE_40BIT and WEPMODE_128BIT. It should be obvious which is which.

So, with the keyboard displayed a user can enter the ASCII representation of a 40b or 104b hex key, which is either a 10 or 26 character long string, respectively. We then need to convert this ASCII representation of a hex value into a 5 or 13 byte actual hex value. To accomplish this, we look at each character in the string and convert it from it's ASCII value to it's hex value. We then pack two of these hex values into a single byte (unsigned char), and append each byte onto our encryption key. Once we have 5 or 13 of these bytes, we pass them to the Wifi_ConnectAP(...) function, and wait for the connection to succeed or fail. This is accomplished by polling Wifi_AssocStatus(), waiting for it to return ASSOCSTATUS_ASSOCIATED, or ASSOCSTATUS_CANNOTCONNECT.

Once we successfully connect to a wireless access point, our DS will be assigned an IP address (we assume the use of DHCP). Now connected to the network, we can then start using functions like gethostbyname(...) to look up DNS entries, or connect(...) to make socket connections to remote hosts. Things are finally getting interesting. To provide a user interface to a variety of functions, we print a simple menu, with each DS button bound to a different function, and wait for a keypress:

 
if(status == ASSOCSTATUS_ASSOCIATED) while(1)
{
	u32 ip = Wifi_GetIP();
 
	consoleClear();
 
	iprintf("\n");
	iprintf("ip: [%i.%i.%i.%i]\n", (ip ) & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF);
	iprintf("--------------------------------\n");
	iprintf("Press Y to nslookup host name\n");
	iprintf("Press X to connect to interface\n");
	iprintf("Press A to listen on socket\n");
	iprintf("Press B to break\n\n");
 
	keypressed = 0;
	while(!(keypressed & (KEY_Y | KEY_X | KEY_A | KEY_B | KEY_L)))
	{
		scanKeys();
		keypressed = keysDown();
	}
 
	if(keypressed & KEY_Y)
	{
		//execute gethostbyname(...)
	}
 
	//etc...
}
 

Now the code governing the interaction with the Host interface is very similar to the code in the NdsHostInterface application. We synchronously send a register signal, receive the signal port (and data port) back with the register acknowledgement signal from the Host interface. We then use this signal port to construct a listener, and this data port to pipe input data to the Host. This is the main loop of the NdsInterfaceClient:

 
while(1)
{
	tvTimeout.tv_sec = 0;
	tvTimeout.tv_usec = 1000; // This will throttle the data xfer rate just a tad...
	// I've found that anything less than 1000 microseconds will make select return immediately
	// when called on the DS platform
 
	FD_ZERO(&fdsRead);
	FD_SET(sktServer, &fdsRead);
 
	retval = select(1, &fdsRead, NULL, NULL, &tvTimeout);
 
	if (retval == -1)
		iprintf("select() error\n");
	else if (retval)
	{
 
		if((sktClient = accept(sktServer, (struct sockaddr *)&adrSignalClient, &iClientSize)) < 0)
		{
			iprintf("Failed to accept client connection.\n");
			continue;
		}
		iprintf("Client connected: %s\n", inet_ntoa(adrSignalClient.sin_addr));
		InterfaceSignalHandler(sktClient);
	}
 
	// If there are no signals from the host, and pen is down, send input data on UDP port.
	if(keysHeld() & KEY_TOUCH)
	{
		touchRead(&touchXY);
		pos = 0x00000000;
		pos = (touchXY.px | ((int)(touchXY.py) << 16));
		iprintf("Sending 0x%x", pos);
		sendto(sktData, &pos, 4, 0, &adrData, sizeof(adrData));
	}
 
	scanKeys();
	if(keysDown() & KEY_B)
		break;
}
 
// Broke out of main loop, so unregister from Host and shutdown...
 

That is really the jist of the NdsInterfaceClient. Obviously check out the source code to see how to appropriately construct sockets. In my last article in this series, I'll provide links to the complete source code for both applications. I might take the time to make the NdsInterfaceHost compile on Windows as well as Linux and OSX, but no promises. I'd rather leave the standard Berkeley sockets implementation alone, and let you port it to winsock yourself... or even better let you install a REAL operating system, and run it there. =)

NDS WiFi programming with devkitARM

Posted in HID, NintendoDS, sensors, sockets on June 6th, 2009 by Chaos Engineer

I have a grand plan to make a beowulf cluster dedicated to computing, interfacing and manipulating chaotic systems. I have a protocol devised that would allow multiple applications running on an arbitrary number of computers in a network pool hardware resources for this task. Each application (node) would have it's own capabilities, and could be used to collect sensory data, provide a human interface, or just to crunch numbers. I could easily have a management node that would maintain a list of other nodes and their abilities, and query them as needed. I recently had a great idea to use a NintendoDS as both an interface (via the touchscreen), and a sensory device (collecting audio samples with the buit-in microphone).

So I did a bunch of NDS programming a few years back using the devkitPro toolchain, specifically devkitARM. They were at release r20 then, and dswifi had just made it on the scene. For those who don't know, dswifi is a library written by Stephen Stair that allows NintendoDS homebrew developers using devkitARM to use the wifi features of the NDS. You can connect to wireless access points, and from there you have network access and a standard Berkley sockets interface, so you can pretty much accomplish anything you set your mind to. I just downloaded the latest version of devkitARM, which is now r26. I was quite pleased with the progress the toolchain had made during my absence, and especially pleased to see dswifi so well integrated. Things pretty much went downhill from there.

NOTE: 09.06.16 - The rest of this post is for historical information only. After I isolated the bug in dswifi that was the root of my problems, I notified the maintainers of devkitPro who committed a fix. There are now new releases of libnds, dswifi and default arm 7 available that correctly handle the DS' MAC address. Please see the addendum at the end of this article for more information. Do not downgrade your devkitPro install if you are having problems. Instead get the very latest versions of all libraries, and you should be good.

There were no examples provided with the nds-examples package that showed how to connect to WEP secured Access Point (AP). The only examples provided were for either using the WFC information stored in the NDS firmware, or for connecting to an unsecured AP. They did have code to enumerate APs and to provide a keyboard interface that I happily integrated. Prior to even testing the included examples I poured a couple hours into writing a code base using the most involved methods possible (swapping out all the simple 0 argument init functions with the 8 argument equivalents) to establish a console and collect and display a list of APs. Selecting an AP from this list that was secured would then provide a prompt to specify either 40-bit or 104-bit encryption. The user could then type in the 10 or 26 character string using a tiny little keyboard on the DS touchscreen. This string was then encoded to a byte array and passed to the Wifi_ConnectAP(...) function provided by dswifi.

Wifi AP list and a tiny keyboard

Wifi AP list and a tiny keyboard

I then spent three times as long troubleshooting. It took a while for me to remember that I have MAC address filtering enabled on my router (*bonk self*). I loaded up a wifi enabled game, pulled the MAC address of my DS from Nintendo's WFC interface, and put it on my router's Access Control List. I rubbed my hands in greedy anticipation, and loaded up my homebrew app once again. No joy ensued. I still could not connect to my AP.

At this point, I decided to try something simple just to verify the functionality of dswifi (yes, I'm backasswards when it comes to programming), so I loaded up one of the basic dswifi examples. Strangely, it did not work either. I knew it wasn't my DS or router anymore, because I would connect with a proper DS game that was wifi enabled. I didn't see anyone else complaining in the devkitPro forums, but r26 was *just* released and the forums over there aren't exactly teeming with DS programmers. I thought maybe the devkitPro toolchain for x64 Linux might have been the culprit, so I tried in Windows XP as well. Still no joy.

Operating under the assumption that dswifi worked at SOME point in devkitPro, I decided it was time to start digging backwards through the releases, and testing the functionality as I went with the most simple example possible... one that loaded the AP information from the WFC info stored in the DS and automatically tried to connect. This simple example did indeed work back in devkitPro r25 (2009.02.20). I then increased my resolution to try and find *exactly* where dswifi broke in devkitPro.

<SNIP>
Several paragraphs removed at the request of the devkitPro maintainers.

This section basically highlighted my procedures to somewhat subvert the available devkitPro library distributions to create a tool chain that pre-dated issues with the DS MAC address handling. As noted above, the bug I isolated has been fixed in the latest devkitPro library distributions, so this information is no longer pertinent. I found out that in my setup, the simple WFC autoconnect example worked fine when built with dswifi 0.3.7, but refused to work in dswifi 0.3.8. So I configured a motley devkitPro toolchain with different versions of specific libraries in order to get dswifi working for me.
</SNIP>

Telnetting into my NintendoDS Echo Server.

Telnetting into my NintendoDS Echo Server.

I now have code running on my DS that allows me to connect to my WEP secured AP, and from there get a socket connection to my workstation. Its pretty blasted awesome to be able to telnet into your DS and see what you type into your computer appear on the screen. Its several magnitudes more awesome to use the DS touchscreen and keys to remotely manipulate a 3D chaotic system in realtime. Add the ability to pipe recorded audio data from the DS microphone over the network and into that system where the fourier-tranformed signal is then used to modulate said system, and you might as well be dividing by zero.

UPDATE 2009.06.07:

After butting heads with the maintainers of devkitPro on the forum (they deleted a few of my posts because they don't like people telling users to downgrade or linking to obsolete libraries -- support headaches) who said the examples worked fine in r26, I pulled the source code for dswifi 0.3.7 and 0.3.8 than ran diff against the versions, excluding the svn nonsense:

diff -urNp -x '*svn*'  dswifi-src-0.3.7 dswifi-src-0.3.8 > dswifi.patch

Thanks to diff and a little persistence reading through the patch file, I found exactly where my problem was originating.

Remember how I said I had MAC address filtering enabled on my router? Well recent changes to dswifi did indeed break the example, for me at least... the MAC address bytes were being read from the firmware but written to a main data structure incorrectly:

 
for(i=0;i<3;i++)
	WifiData->MacAddr[i]= ReadFlashByte(0x36+i) + (ReadFlashByte(0x36+i+1)<<8);
 

This loop doesn't write the data to MacAddr as intended. After the loop, MacAddr is filled with the bytes from the following firmware addresses : 0x36, 0x37, 0x37, 0x38, 0x38, 0x39. The intention is obviously to write the bytes from 0x36, 0x37, 0x38, 0x39, 0x40, 0x41. Simple mistake, simple solution. I notified the maintainers, so when you download devkitPro r27, you can thank me when connecting to APs with MAC address filtering. =)

ADDENDUM 2009.06.16:

A fix for the MAC address bug in dswifi was submitted to the devkitPro svn on June 11th. The maintainers built new distributions of three libraries the same day. So if you get the following versions of libnds, dswifi and default arm7, your DS' MAC will be properly presented to access points:

libnds 1.3.5
dswifi 0.3.9
default arm7 0.5.4

Way to go, devkitPro.

You can see the full discourse on the forums here.

With this issue now past tense, I'll work on posting a PART 2 of this article with some actually useful programming information.

Tags: , , , ,