Overview
Palm OS® provides direct support for WiFi wireless networking through a set of ioctl
commands that allow an application to find, connect to, and disconnect from wireless networks. Other commands allow applications to monitor the status of a wireless network, configure wireless security, and perform other standard management tasks necessary when using WiFi networking.
While the operating system includes the necessary user interface to manage WiFi connectivity, these ioctl
commands are available for developers to provide custom solutions.
WiFi Concepts
A WiFi network is identified by an SSID. The SSID is an ASCII string of up to 32 characters.
There are two types of WiFi networks. The typical network, operating in infrastructure mode, is formed by devices connecting wirelessly to an access access point, which is a dedicated device. This device is also sometimes referred to as a base station. Each access point is a Basic Service Set (BSS). An Extended Service Set (ESS) is a entwork of one or more access points that is referred to by a single SSID.
The other type of network, called an ad-hoc network, is created when one or more devices are connected together wirelessly without a dedicated access point.
Locating and Opening a WiFi Interface
Before you can manage WiFi, you need to find and open the WiFi interface. This is done using IOSGetDriverNameByIndex()
and IOSOpen()
. See Listing 16.1 for an example.
Listing 16.1 Finding and opening the WiFi interface
char name[64]; uint16_t nameLen sizeof(name); status_t err; IOSGetDriverNameByIndex(iosDriverClassWifi, 0, (char *) name, &nameLen); int32 wifiDataFD = IOSOpen(name, 0, &err); strcat(name, "_mgmt"); int32 wifiMgmtFD = IOSOpen(name, 0, &err);
To send data over WiFi, you simply open the interface using the name returned by IOSGetDriverNameByIndex()
. If, however, you want to send ioctl commands to the WiFi interface, you need to append the string "_mgmt" to the returned name to access the management interface.
Getting Information About the WiFi Interface
Before using the WiFi interface, your application may need to obtain information about the device's capabilities or status. For example, you may need to determine what forms of encryption it supports, whether or not it's already connected to a network, or what channels and transmission rates it supports.
Determining Supported Encryption Modes
To determine which encryption modes the interface supports, use the WIOCGETSECCAPS
command, as demonstrated in Listing 16.2.
Listing 16.2 Getting supported encryption modes
WifiGetSecCapType modes; status_t err; IOSIoctl(wifiFD, WIOCGETSECCAPS, (int32_t) &modes, &err); if (modes.capabilities & WifiSecOpen) { /* Open System is supported */ } if (modes.capabilities & WifiSecWEP) { / * WEP is supported */ }
Getting the Interface Status
You can obtain information about the current status of the WiFi interface using the WIOCGETSTATUS
ioctl
. This is seen in Listing 16.3.
Listing 16.3 Getting the current status of the WiFi interface
uint32_t status; status_t err; IOSIoctl(wifiFD, WIOCGETSTATUS, (int32_t) &status, &err);
After IOSIoctl()
returns, status
contains a value indicating the current state of the WiFi interface:
-
WifiStatusDisconnected
indicates that the interface is not currently connected to a network. -
WifiStatusConnectedAccessPoint
indicates that the interface is connected to an access point. -
WifiStatusConnectedAdHoc
indicates that the interface is connected to an ad-hoc network. -
WifiStatusOutOfRange
indicates that the interface is currently connected, but that the network is not currently in range. The state will automatically return toWifiStatusConnectedAccessPoint
orWifiStatusConnectedAdHoc
when the network is in range again. -
WifiStatusConnecting
indicates that the interface is in the process of attempting to establish a connection. -
WifiStatusConnectionFailed
indicates that the most recent connection attempt failed. -
WifiStatusUndefined
indicates that for whatever reason, the interface's status could not be determined.
Identifying the Currently Connected Network
If you wish to determine the SSID or BSSID of the network to which the interface is connected, use the WIOCGETSSID
or WIOCGETBSSID
command.
Listing 16.4 Getting the name and BSSID of the access point or ad-hoc network
WifiSSIDType ssid; WifiBSSIDType bssid; status_t err; IOSIoctl(wifiFD, WIOCGETSSID, (int32_t) &ssid, &err); IOSIoctl(wifiFD, WIOCGETBSSID, (int32_t) &bssid, &err);
Determining Supported Channels and Transmission Rates
To determine which channels the WiFi interface supports, use the WIOCGETCHANNEL
command. This also reports the channel the interface is currently using.
Listing 16.5 Determining supported channels
status_t err; WifiChannelType channels; channels.current = 0; channels.supportedMask = 0; IOSIoctl(wifiFD, WIOCGETCHANNEL, (int32_t) &channels, &err);
After this code executes, channels.current
is set to the channel on which the interface is currently communicating, and channels.supportedMask
is a bit mask of all the channels the interface supports. See "Channel Constants" for a list of the channel number flags.
The code in Listing 16.6 determines the rates supported by the interface, which rates are preferred, and what rate is currently in use.
Listing 16.6 Determining supported transmission rates
WifiGetRatesType rates; status_t err; rates.preferred_rates = 0; rates.supported_rates = 0; rates.current_rate = 0; IOSIoctl(wifiFD, WIOCGETRATES, (int32_t) &rates, &err);
On return, rates.current_rate
indicates the transmission rate currently in effect, rates.supported_rates
is a bit mask of all the transmission rates the interface supports, and rates.preferred_rates
is a bit mask of the rates the interface is best suited for. See "Transmission Rate Flags" for the possible values.
NOTE: The preferred rates always default to the complete set of supported rates. You may change them if you wish, using the
WIOCSETRATES
ioctl.
Getting the Signal Strength
There are two ways to keep apprised of the current signal strength. You can manually poll the signal strength using the WIOCGETCURRENTRSSI
command, or you can enable automatic signal strength update notification.
Listing 16.7 Getting the current signal strength
WifiGetRSSIType current; IOSIoctl(wifiFD, WIOCGETCURRENTRSSI, (int32_t) ¤t, &err);
After the code in Listing 16.7 executes, current.signal
contains the current signal strength, as a percentage between 0 and 100.
To receive periodic notification of changes to the signal strength, use the WIOCSETRSSIUPDATE
command. With this command, you can choose to receive notification events whenever any change to signal strength occurs, whenever the signal strength changes by a given amount, or at a specific interval.
Listing 16.8 Enabling automatic signal strength notifications
WifiRSSIUpdateType update; status_t err; /* notify me when signal strength changes by +/- 2% */ update.updateMode = WifiRSSIUpdateOnDelta; update.updateValue = 2; /* notify me every 1000 milliseconds */ update.updateMode = WifiRSSIUpdatePeriodic; update.updateValue = 1000; /* notify me every time the signal strength changes */ update.updateMode = WifiRSSIUpdateAlways; update.updateValue = 0; /* never notify me of signal strength changes */ update.updateMode = WifiRSSIUpdateNever; update.updateValue = 0; IOSIoctl(wifiFD, WIOCSETRSSIUPDATE, (int32_t) &update, &err);
The example in Listing 16.8 shows how to set up the WifiRSSIUpdateType
structure for each of the four notification modes. Once the structure is prepared, call IOSIoctl()
to issue the request.
Finding an Access Point or Ad-hoc Network
To locate an access point or ad-hoc network to which you can connect, you need to use the WIOCSCAN
or WIOCPASSIVESCAN
command.
Active Scanning
If you want to simply perform a one-time scan of the airwaves for available ad-hoc networks and access points, use WIOCSCAN
. See Listing 16.9.
Listing 16.9 Performing a one-shot scan for access points and ad-hoc networks
WifiScanRequestType cmd; status_t err; memset(&cmd, 0, sizeof(WifiScanRequestType)); cmd.channels = WifiChannel_All; cmd.rates = WifiRate_All; cmd.timeout = 2000; cmd.blockTillCompletion = 0; IOSIoctl(wifiFD, WIOCSCAN, (int32_t) &cmd, &err);
The scan is performed asynchronously; the IOSIoctl()
will return immediately. Your application's event loop will receive WiFi events with the scan results. See "Obtaining Scan Results" for details on how to parse the results.
The blockTillCompletion
flag indicates whether or not you want the ioctl to block until the first scan result arrives. This example sets it to 0, indicating that we want to return immediately.
Passive Scanning
If you would prefer to constantly be kept informed of the available access points and ad-hoc networks, as they move in and out of range, or are turned on and off, you can enable passive scanning mode. While in passive scanning mode, your application's event loop will receive scan result events when appropriate. See Listing 16.10 for an example of how to enable passive scanning.
Listing 16.10 Enabling passive scanning
WifiPassiveScanType scan; status_t err; memset(&scan, 0, sizeof(WifiPassiveScanType)); scan.enableScanning = true; scan.channelMask = WifiChannel_All; scan.rateMask = WifiRate_All; scan.interval = 1000; IOSIoctl(wifiFD, WIOCPASSIVESCAN, (int32_t) &scan, &err);
The example above enables scanning for access points or ad-hoc networks operating on any channel and at any transmission rate. Scan results will be delivered to the application every 1,000 milliseconds.
To disable passive scanning, issue the WIOCPASSIVESCAN
command again, with the enableScanning
field set to false
.
Obtaining Scan Results
Normally, your application receives scan results as a wifiScanResults
event in its event loop. The event's WifiEventType
structure describes the detected access point in detail. Each time an access point or ad-hoc network is found, a wifiScanResults
event is delivered.
You can also manually fetch the scan results from the WiFi adapter by using the WIOCGETSCANRESULTS
command. This command can be used in a loop to fetch all the scan results available, as seen in Listing 16.11.
Listing 16.11 Using WIOCGETSCANRESULTS
uint16_t index = 0; uint16_t last = 0; WifiGetScanResultsType scan; status_t err; do { memset(&scan, 0, sizeof(WifiGetScanResultsType)); scan.last = last; scan.index = index; IOSIoctl(m_fd, WIOCGETSCANRESULTS, (int32_t)&scan, &err); if (err == P_OK) { /* results received successfully in scan */ } else { /* error receiving scan results */ } last = scan.last; index = scan.index; index++; } while (index <= last);
Configuring Encryption
WiFi supports the concept of encryption to protect data security. There are two security modes currently supported by Palm OS: open system (unencrypted) and Wired Equivalent Privacy (WEP).
To use WEP encryption, an encryption key needs to be configured prior to connecting to the network. There are three steps required to accomplish this. First, it's necessary to store the key in the adapter. A WiFi adapter can store up to four encryption keys, which can then be selected among depending on which network is being accessed.
Listing 16.12 Setting an encryption key
WifiSetWEPKeyType arg; status_t err; arg.key = 0; /* key number to set */ memset(arg.data, 0, 16); arg.data_len = Ascii2Binary(arg.data, keyString, 16); IOSIoctl(wifiFD, WIOCSETKEY, (int32_t) &arg, &err);
The code in Listing 16.12 sets key 0 to the string specified by keyString
. See Listing 16.17 for the code for the Ascii2Binary()
function.
Once the key has been stored on the adapter, it must be selected using the WIOCSETDEFAULTKEY
command. See Listing 16.13.
Listing 16.13 Selecting the default key
status_t err; IOSIoctl(wifiFD, WIOCSETDEFAULTKEY, (int32_t) 0, &err);
Finally, once the key has been selected, it's possible to put the interface into WEP mode by using the WIOCSETSECMODE
command, as seen in Listing 16.14.
Listing 16.14 Enabling encryption
status_t err; uint32_t mode = WifiSecWEP; IOSIoctl(wifiFD, WIOCSETSECMODE, (int32_t) &mode, &err);
To disable encryption, simply set the mode to WifiSecOpen
.
Connecting To a Network
Once you have found an access point or ad-hoc network to which you wish to connect, you can connect to that network using either the WIOCCONNECT
or the WIOCJOIN
ioctl
.
If you have the SSID of the network or ad-hoc network, you use the WIOCCONNECT
command, as shown in Listing 16.15.
Listing 16.15 Connecting to a wireless network using an SSID
WifiConnectType arg; status_t err; strncpy(arg.ssid, theSSID, 32); arg.timeout = 3000; arg.blockTillCompletion = false; IOSIoctl(wifiFD, WIOCCONNECT, (int32_t) &arg, &err);
In this example, we choose to try for three seconds (3,000 milliseconds) before timing out. In addition, since the blockTillCompletion
flag is set to false, the call will return at once. We must then check the status of the connection periodically to detect when the connection is actually opened (or if the connection fails to open).
Your event loop (using either a Pollbox or IOSPoll()
) will receive notifications as the status of the connection changes: wifiConnectAccessPoint
or wifiConnectAdHoc
when the connection is established, wifiConnecting
while connection is being attempted, wifiOutOfRange
if the access point is out of range but was opened anyway under the assumption that it will be eventually, wifiMediaUnavailable
if the 802.11 hardware is missing, or wifiConnectFailed
if the connection could not be established.
If you have the BSSID (MAC address) and channel number of a network to which you wish to connect, you can use the WIOCJOIN
command instead, as shown in Listing 16.16.
Listing 16.16 Connecting to a wireless network using a BSSID and channel number
WifiJoinType arg; status_t err; Ascii2Binary(arg.bssid, "FF:FF:FF:FF:FF", 6); arg.channel = theChannel; IOSIoctl(wifiFD, WIOCJOIN, (int32_t) &arg, &err);
This code uses a function called Ascii2Binary()
to convert the MAC ID string into the proper format. Ascii2Binary()
is shown in Listing 16.17.
Listing 16.17 Converting a hex string into packed binary format
int Ascii2Binary(uint8_t * buf, const char* p, size_t length) { uint8_t nibble; size_t i = 0; static char map[22] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f'}; while (*p != NULL && i < length) { // Skip over MAC octet separators if (*p == ':') { p++; continue; } nibble = 0; for(int j = 0; j < 22; j++) { if (p[0] == map[j]) { if (j >= 16) nibble = j - 6; else nibble = j; break; } } buf[i] |= nibble << 4; p++; if (*p == NULL) break; nibble = 0; for(int j = 0; j < 22; j++) { if (p[0] == map[j]) { if (j >= 16) nibble = j - 6; else nibble = j; break; } } buf[i] |= nibble; i++; p++; } return i; }
Once a connection has been established, the wireless network can be used just like any other network connection, using the Sockets API or IOS STDIO calls.
Managing a Wireless Connection
Once the connection is established, your application's event loop will receive events on the WiFi management file descriptor, informing you of changes in the status of the connection, as well as results of specific requests you issue. Your event loop needs to either poll the file descriptor, or use a Pollbox. These concepts are covered in "Polling STREAMS File Descriptors".
WiFi events use the WifiEventType
structure to return data to your event handler. The possible events are listed in "Event Type Constants".
Disconnecting From a Network
To disconnect from a WiFi network, use the WIOCDISCONNECT
command. See Listing 16.18.
Listing 16.18 Disconnecting from a WiFi network
status_t err; IOSIoctl(wifiFD, WIOCDISCONNECT, NULL, &err);
Creating an Ad-hoc Network
Palm OS devices can create an ad-hoc network using the WIOCCREATEIBSS
ioctl
.
Listing 16.19 Creating an ad-hoc network
WifiCreateIBSSType ibss; status_t err; memset(&ibss, 0, sizeof(WifiCreateIBSSType)); strncpy(ibss.ssid, "MyAdhocNet", 32); ibss.channel = 8; IOSIoctl(wifiFD, WIOCCREATEIBSS, (int32_t) &ibss, &err);
The example in Listing 16.19 creates a new ad-hoc network named "MyAdhocNet" operating on channel 8. If this is successful, other WiFi-enabled devices can then connect to the new ad-hoc network just like any other network.