Here is another frequently requested Pocket PC programming trick. With the wide adoption of WiFi / 802.11x wireless for PDAs, Pocket PC developers very frequently want to programatically turn the wireless LAN adapter on their device on or off. There are various reasons for this, not least of which is the desire to exercise greater control over the power consumption of the wireless radio.
The trick to doing this is our friend the DeviceIoControl API. Basically what you need to do is this:
To power off the WLAN radio: Unbind the wireless adapter, then power it off.
To power on the WLAN radio: Power on the adapter, then bind it.
Here is the code:
// wlanutils.cpp : Defines the entry point for the DLL application.
//
#include
#include
#include
#include
#include
#include "wlanutils.h"
///
//Global definitions
//g_pszAdapterName --> Used for the multisz null terminated adapter string name for the
// IOCTL_NDIS_BIND_ADAPTER and IOCTL_NDIS_UNBIND_ADAPTER ioctls
// By the time this variable is needed, it is in the proper format.
TCHAR* g_pszAdapterName = NULL;
DWORD g_dwAdapterNameLen = 0;
//g_pszDriverName --> Contains the name of the active WLAN driver.
//This value corresponds to the SanDisk SDIO WiFi card.
//The driver name for your device may be different. Look in
//the device registry under HKLM/Drivers for yours.
TCHAR g_pszDriverName[] = TEXT("SDN1:");
//
//Public exported functions
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
int GetRadioState()
{
HANDLE hDriver = NULL;
DWORD dwBytesOut = 0;
CEDEVICE_POWER_STATE ps;
int nPowerState = GET_POWER_STATE_ERROR;
hDriver = ::CreateFile(g_pszDriverName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE != hDriver)
{
BOOL bRes = ::DeviceIoControl(hDriver, IOCTL_POWER_GET, NULL, 0, &ps, sizeof(ps), &dwBytesOut, NULL);
if (bRes) {
nPowerState = ((ps == D4)?GET_POWER_STATE_OFF:GET_POWER_STATE_ON); //Note: This treats all states but off as on, i.e, stanby is considered on
}
::CloseHandle(hDriver);
}
return (nPowerState);
}
//bPowerState paramter is TRUE to turn radio on, FALSE to turn off
BOOL PowerRadio(BOOL bPowerState)
{
HANDLE hNdis = NULL;
HANDLE hDriver = NULL;
BOOL bRes = FALSE;
DWORD dwBytesOut = 0;
DWORD dwError = 0;
CEDEVICE_POWER_STATE ps;
GetAdapterName();
ps = (bPowerState?D0:D4);
if (!bPowerState)
{ //Power off the radio
hNdis = ::CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE != hNdis)
{
bRes = ::DeviceIoControl(hNdis, IOCTL_NDIS_UNBIND_ADAPTER, g_pszAdapterName, g_dwAdapterNameLen, NULL, 0, &dwBytesOut, NULL);
if (bRes)
{
hDriver = ::CreateFile(g_pszDriverName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE != hDriver)
{
bRes = ::DeviceIoControl(hDriver, IOCTL_POWER_SET, NULL, 0, &ps, sizeof(ps), &dwBytesOut, NULL);
}
else bRes = FALSE;
::CloseHandle(hDriver);
}
::CloseHandle(hNdis);
}
}
else
{ //Power on the radio
hDriver = ::CreateFile(g_pszDriverName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE != hDriver)
{
bRes = ::DeviceIoControl(hDriver, IOCTL_POWER_SET, NULL, 0, &ps, sizeof(ps), &dwBytesOut, NULL);
if (bRes)
{
hNdis = ::CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE != hNdis)
{
bRes = ::DeviceIoControl(hNdis, IOCTL_NDIS_BIND_ADAPTER, g_pszAdapterName, g_dwAdapterNameLen, NULL, 0, &dwBytesOut, NULL);
::CloseHandle(hNdis);
}
else bRes = FALSE;
}
::CloseHandle(hDriver);
}
}
return (bRes);
}
///
//Internal functions
void GetAdapterName()
{
DWORD dwError = 0;
DWORD dwSize = 0;
::GetAdaptersInfo(NULL, &dwSize);
IP_ADAPTER_INFO* pInfo = (IP_ADAPTER_INFO*)new char[dwSize];
TCHAR pszAdapterName[MAX_ADAPTER_NAME_LENGTH+4];
if ((dwError = ::GetAdaptersInfo(pInfo, &dwSize)) == NO_ERROR)
{
IP_ADAPTER_INFO* pCurrentAdapter = pInfo;
while (pCurrentAdapter)
{
if (pCurrentAdapter->Type == MIB_IF_TYPE_ETHERNET)
{
MultiByteToWideChar( CP_ACP, 0, pCurrentAdapter->AdapterName, -1, pszAdapterName, sizeof(pszAdapterName) / sizeof(pszAdapterName[0]));
//We assume there is only one ethernet adapter per device
break;
}
pCurrentAdapter = pCurrentAdapter->Next;
}
}
//Add the terminating zeros required by the IOCTL_NDIS_BIND_ADAPTER and IOCTL_NDIS_UNBIND_ADAPTER ioctls
DWORD dwLen = _tcslen(pszAdapterName)*sizeof(TCHAR);
g_dwAdapterNameLen = (_tcslen(pszAdapterName)+3)*sizeof(TCHAR);
//Free previous use of the adapter name string
if (g_pszAdapterName != NULL) {
::LocalFree(g_pszAdapterName);
g_pszAdapterName = NULL;
}
g_pszAdapterName = (TCHAR*)::LocalAlloc(LPTR, g_dwAdapterNameLen);
memmove(g_pszAdapterName, pszAdapterName, dwLen);
delete [] pInfo;
}
The trick to doing this is our friend the DeviceIoControl API. Basically what you need to do is this:
To power off the WLAN radio: Unbind the wireless adapter, then power it off.
To power on the WLAN radio: Power on the adapter, then bind it.
Here is the code:
// wlanutils.cpp : Defines the entry point for the DLL application.
//
#include
#include
#include
#include
#include
#include "wlanutils.h"
///
//Global definitions
//g_pszAdapterName --> Used for the multisz null terminated adapter string name for the
// IOCTL_NDIS_BIND_ADAPTER and IOCTL_NDIS_UNBIND_ADAPTER ioctls
// By the time this variable is needed, it is in the proper format.
TCHAR* g_pszAdapterName = NULL;
DWORD g_dwAdapterNameLen = 0;
//g_pszDriverName --> Contains the name of the active WLAN driver.
//This value corresponds to the SanDisk SDIO WiFi card.
//The driver name for your device may be different. Look in
//the device registry under HKLM/Drivers for yours.
TCHAR g_pszDriverName[] = TEXT("SDN1:");
//
//Public exported functions
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
int GetRadioState()
{
HANDLE hDriver = NULL;
DWORD dwBytesOut = 0;
CEDEVICE_POWER_STATE ps;
int nPowerState = GET_POWER_STATE_ERROR;
hDriver = ::CreateFile(g_pszDriverName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE != hDriver)
{
BOOL bRes = ::DeviceIoControl(hDriver, IOCTL_POWER_GET, NULL, 0, &ps, sizeof(ps), &dwBytesOut, NULL);
if (bRes) {
nPowerState = ((ps == D4)?GET_POWER_STATE_OFF:GET_POWER_STATE_ON); //Note: This treats all states but off as on, i.e, stanby is considered on
}
::CloseHandle(hDriver);
}
return (nPowerState);
}
//bPowerState paramter is TRUE to turn radio on, FALSE to turn off
BOOL PowerRadio(BOOL bPowerState)
{
HANDLE hNdis = NULL;
HANDLE hDriver = NULL;
BOOL bRes = FALSE;
DWORD dwBytesOut = 0;
DWORD dwError = 0;
CEDEVICE_POWER_STATE ps;
GetAdapterName();
ps = (bPowerState?D0:D4);
if (!bPowerState)
{ //Power off the radio
hNdis = ::CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE != hNdis)
{
bRes = ::DeviceIoControl(hNdis, IOCTL_NDIS_UNBIND_ADAPTER, g_pszAdapterName, g_dwAdapterNameLen, NULL, 0, &dwBytesOut, NULL);
if (bRes)
{
hDriver = ::CreateFile(g_pszDriverName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE != hDriver)
{
bRes = ::DeviceIoControl(hDriver, IOCTL_POWER_SET, NULL, 0, &ps, sizeof(ps), &dwBytesOut, NULL);
}
else bRes = FALSE;
::CloseHandle(hDriver);
}
::CloseHandle(hNdis);
}
}
else
{ //Power on the radio
hDriver = ::CreateFile(g_pszDriverName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE != hDriver)
{
bRes = ::DeviceIoControl(hDriver, IOCTL_POWER_SET, NULL, 0, &ps, sizeof(ps), &dwBytesOut, NULL);
if (bRes)
{
hNdis = ::CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
if (INVALID_HANDLE_VALUE != hNdis)
{
bRes = ::DeviceIoControl(hNdis, IOCTL_NDIS_BIND_ADAPTER, g_pszAdapterName, g_dwAdapterNameLen, NULL, 0, &dwBytesOut, NULL);
::CloseHandle(hNdis);
}
else bRes = FALSE;
}
::CloseHandle(hDriver);
}
}
return (bRes);
}
///
//Internal functions
void GetAdapterName()
{
DWORD dwError = 0;
DWORD dwSize = 0;
::GetAdaptersInfo(NULL, &dwSize);
IP_ADAPTER_INFO* pInfo = (IP_ADAPTER_INFO*)new char[dwSize];
TCHAR pszAdapterName[MAX_ADAPTER_NAME_LENGTH+4];
if ((dwError = ::GetAdaptersInfo(pInfo, &dwSize)) == NO_ERROR)
{
IP_ADAPTER_INFO* pCurrentAdapter = pInfo;
while (pCurrentAdapter)
{
if (pCurrentAdapter->Type == MIB_IF_TYPE_ETHERNET)
{
MultiByteToWideChar( CP_ACP, 0, pCurrentAdapter->AdapterName, -1, pszAdapterName, sizeof(pszAdapterName) / sizeof(pszAdapterName[0]));
//We assume there is only one ethernet adapter per device
break;
}
pCurrentAdapter = pCurrentAdapter->Next;
}
}
//Add the terminating zeros required by the IOCTL_NDIS_BIND_ADAPTER and IOCTL_NDIS_UNBIND_ADAPTER ioctls
DWORD dwLen = _tcslen(pszAdapterName)*sizeof(TCHAR);
g_dwAdapterNameLen = (_tcslen(pszAdapterName)+3)*sizeof(TCHAR);
//Free previous use of the adapter name string
if (g_pszAdapterName != NULL) {
::LocalFree(g_pszAdapterName);
g_pszAdapterName = NULL;
}
g_pszAdapterName = (TCHAR*)::LocalAlloc(LPTR, g_dwAdapterNameLen);
memmove(g_pszAdapterName, pszAdapterName, dwLen);
delete [] pInfo;
}