diff --git a/src/add-ons/kernel/drivers/network/Jamfile b/src/add-ons/kernel/drivers/network/Jamfile index e2b8658381..55339dce90 100644 --- a/src/add-ons/kernel/drivers/network/Jamfile +++ b/src/add-ons/kernel/drivers/network/Jamfile @@ -7,6 +7,7 @@ SubInclude HAIKU_TOP src add-ons kernel drivers network wb840 ; SubInclude HAIKU_TOP src add-ons kernel drivers network via-rhine ; SubInclude HAIKU_TOP src add-ons kernel drivers network rtl8169 ; SubInclude HAIKU_TOP src add-ons kernel drivers network ipro1000 ; +SubInclude HAIKU_TOP src add-ons kernel drivers network ipw2100 ; SubInclude HAIKU_TOP src add-ons kernel drivers network etherpci ; SubIncludeGPL HAIKU_TOP src add-ons kernel drivers network bcm440x ; diff --git a/src/add-ons/kernel/drivers/network/ipw2100/Jamfile b/src/add-ons/kernel/drivers/network/ipw2100/Jamfile new file mode 100644 index 0000000000..4ee52e9143 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/ipw2100/Jamfile @@ -0,0 +1,17 @@ +SubDir HAIKU_TOP src add-ons kernel drivers network ipw2100 ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +KernelAddon ipw2100 : + driver.cpp + ipw2100.cpp + kernel_cpp.c + ; + +Package haiku-ipw2100-cvs : + ipw2100 : + boot home config add-ons kernel drivers bin ; +Package haiku-ipw2100-cvs : + ipw2100 : + boot home config settings kernel drivers sample ; +PackageDriverSymLink haiku-ipw2100-cvs : net ipw2100 ; diff --git a/src/add-ons/kernel/drivers/network/ipw2100/driver.cpp b/src/add-ons/kernel/drivers/network/ipw2100/driver.cpp new file mode 100644 index 0000000000..ab55985add --- /dev/null +++ b/src/add-ons/kernel/drivers/network/ipw2100/driver.cpp @@ -0,0 +1,269 @@ +/* + Driver for Intel(R) PRO/Wireless 2100 devices. + Copyright (C) 2006 Michael Lotz + Released under the terms of the MIT license. +*/ + +#include +#include +#include +#include +#include + +#include "driver.h" +#include "ipw2100.h" +#include "kernel_cpp.h" + + +status_t ipw2100_open(const char *name, uint32 flags, void **cookie); +status_t ipw2100_close(void *cookie); +status_t ipw2100_free(void *cookie); +status_t ipw2100_control(void *cookie, uint32 op, void *args, size_t length); +status_t ipw2100_read(void *cookie, off_t position, void *buffer, size_t *numBytes); +status_t ipw2100_write(void *cookie, off_t position, const void *buffer, size_t *numBytes); + + +int32 api_version = B_CUR_DRIVER_API_VERSION; + + +pci_module_info *gPCIModule; +char *gDeviceNameList[MAX_INSTANCES + 1]; +pci_info *gDeviceList[MAX_INSTANCES]; +int32 gOpenMask = 0; + +device_hooks gDeviceHooks = { + ipw2100_open, + ipw2100_close, + ipw2100_free, + ipw2100_control, + ipw2100_read, + ipw2100_write +}; + + +const char * +identify_device(const pci_info *info) +{ + if (info->vendor_id != VENDOR_ID_INTEL) + return NULL; + + switch (info->device_id) { + case 0x1043: return "Intel(R) PRO/Wireless 2100"; + } + + return NULL; +} + + +status_t +init_hardware() +{ +#ifdef TRACE_IPW2100 + set_dprintf_enabled(true); +#endif + + //TRACE((DRIVER_NAME": init hardware\n")); + + pci_module_info *module; + status_t result = get_module(B_PCI_MODULE_NAME, (module_info **)&module); + if (result < B_OK) + return result; + + int32 index = 0; + result = B_ERROR; + pci_info info; + while (module->get_nth_pci_info(index++, &info) == B_OK) { + const char *deviceName = identify_device(&info); + if (deviceName) { + TRACE((DRIVER_NAME": found device \"%s\"\n", deviceName)); + result = B_OK; + break; + } + } + + put_module(B_PCI_MODULE_NAME); + return result; +} + + +status_t +init_driver() +{ + TRACE((DRIVER_NAME": init driver\n")); + + for (int32 i = 0; i < MAX_INSTANCES; i++) + gDeviceList[i] = NULL; + for (int32 i = 0; i < MAX_INSTANCES + 1; i++) + gDeviceNameList[i] = NULL; + + pci_info *info = new pci_info; + if (!info) + return B_NO_MEMORY; + + status_t result = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCIModule); + if (result < B_OK) { + delete info; + return result; + } + + int32 index = 0; + int32 count = 0; + while (gPCIModule->get_nth_pci_info(index++, info) == B_OK + && count < MAX_INSTANCES) { + const char *deviceName = identify_device(info); + if (!deviceName) + continue; + + char publishName[64]; + sprintf(publishName, "net/ipw2100/%ld", count); + + gDeviceList[count] = info; + gDeviceNameList[count] = strdup(publishName); + + info = new pci_info; + if (!info) + goto error_out_of_memory; + + dprintf(DRIVER_NAME": will publish an \"%s\" as device %ld to /dev/%s\n", deviceName, count, publishName); + count++; + } + + delete info; + return B_OK; + +error_out_of_memory: + for (int32 i = 0; i < MAX_INSTANCES; i++) { + free(gDeviceNameList[i]); + delete gDeviceList[i]; + gDeviceNameList[i] = NULL; + gDeviceList[i] = NULL; + } + + put_module(B_PCI_MODULE_NAME); + return B_ERROR; +} + + +void +uninit_driver() +{ + TRACE((DRIVER_NAME": uninit driver\n")); + for (int32 i = 0; i < MAX_INSTANCES; i++) { + free(gDeviceNameList[i]); + delete gDeviceList[i]; + gDeviceNameList[i] = NULL; + gDeviceList[i] = NULL; + } + + put_module(B_PCI_MODULE_NAME); +} + + +const char ** +publish_devices(void) +{ + //TRACE((DRIVER_NAME": publish devices\n")); + return (const char **)gDeviceNameList; +} + + +device_hooks * +find_device(const char *name) +{ + //TRACE((DRIVER_NAME": find device \"%s\"\n", name)); + + for (int32 i = 0; i < MAX_INSTANCES; i++) { + if (strcmp(gDeviceNameList[i], name) == 0) + return &gDeviceHooks; + } + + TRACE_ALWAYS((DRIVER_NAME": couldn't find device \"%s\"\n", name)); + return NULL; +} + + +//#pragma mark - + + +status_t +ipw2100_open(const char *name, uint32 flags, void **cookie) +{ + //TRACE((DRIVER_NAME": open device\n")); + int32 deviceID = -1; + for (int32 i = 0; i < MAX_INSTANCES && gDeviceNameList[i]; i++) { + if (strcmp(gDeviceNameList[i], name) == 0) { + deviceID = i; + break; + } + } + + if (deviceID < 0) + return B_ERROR; + + // allow only one concurrent access + int32 mask = 1 << deviceID; + if (atomic_or(&gOpenMask, mask) & mask) + return B_BUSY; + + IPW2100 *device = new IPW2100(deviceID, gDeviceList[deviceID], gPCIModule); + status_t result = device->InitCheck(); + if (device->InitCheck() < B_OK) { + delete device; + return result; + } + + *cookie = (void *)device; + return device->Open(flags); +} + + +status_t +ipw2100_close(void *cookie) +{ + //TRACE((DRIVER_NAME": close device\n")); + IPW2100 *device = (IPW2100 *)cookie; + return device->Close(); +} + + +status_t +ipw2100_free(void *cookie) +{ + //TRACE((DRIVER_NAME": free device\n")); + IPW2100 *device = (IPW2100 *)cookie; + + int32 mask = 1 << device->DeviceID(); + + device->Free(); + delete device; + + atomic_and(&gOpenMask, ~mask); + return B_OK; +} + + +status_t +ipw2100_control(void *cookie, uint32 op, void *args, size_t length) +{ + //TRACE((DRIVER_NAME": control device\n")); + IPW2100 *device = (IPW2100 *)cookie; + return device->Control(op, args, length); +} + + +status_t +ipw2100_read(void *cookie, off_t position, void *buffer, size_t *numBytes) +{ + //TRACE((DRIVER_NAME": read device\n")); + IPW2100 *device = (IPW2100 *)cookie; + return device->Read(position, buffer, numBytes); +} + + +status_t +ipw2100_write(void *cookie, off_t position, const void *buffer, size_t *numBytes) +{ + //TRACE((DRIVER_NAME": write device\n")); + IPW2100 *device = (IPW2100 *)cookie; + return device->Write(position, buffer, numBytes); +} diff --git a/src/add-ons/kernel/drivers/network/ipw2100/driver.h b/src/add-ons/kernel/drivers/network/ipw2100/driver.h new file mode 100644 index 0000000000..eeac4bd62b --- /dev/null +++ b/src/add-ons/kernel/drivers/network/ipw2100/driver.h @@ -0,0 +1,29 @@ +/* + Driver for Intel(R) PRO/Wireless 2100 devices. + Copyright (C) 2006 Michael Lotz + Released under the terms of the MIT license. +*/ + +#ifndef _DRIVER_H_ +#define _DRIVER_H_ + +#include +#include + +#define DRIVER_NAME "ipw2100" +#define DRIVER_VERSION 1.0.0 +#define DRIVER_DESCRIPTION "Intel(R) PRO/Wireless 2100 Driver" + +#define MAX_INSTANCES 3 +#define VENDOR_ID_INTEL 0x8086 + +//#define TRACE_IPW2100 +#ifdef TRACE_IPW2100 +#define TRACE(x) dprintf x +#define TRACE_ALWAYS(x) dprintf x +#else +#define TRACE(x) /* nothing */ +#define TRACE_ALWAYS(x) dprintf x +#endif + +#endif // _DRIVER_H_ diff --git a/src/add-ons/kernel/drivers/network/ipw2100/ethernet.h b/src/add-ons/kernel/drivers/network/ipw2100/ethernet.h new file mode 100644 index 0000000000..369b35d2b0 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/ipw2100/ethernet.h @@ -0,0 +1,59 @@ +#ifndef _ETHERNET_H_ +#define _ETHERNET_H_ + +#include + +enum { + ETHER_GETADDR = B_DEVICE_OP_CODES_END, /* get ethernet address */ + ETHER_INIT, /* set irq and port */ + ETHER_NONBLOCK, /* set/unset nonblocking mode */ + ETHER_ADDMULTI, /* add multicast addr */ + ETHER_REMMULTI, /* rem multicast addr */ + ETHER_SETPROMISC, /* set promiscuous */ + ETHER_GETFRAMESIZE, /* get frame size */ + ETHER_ADDTIMESTAMP, /* (try to) add timestamps to packets (BONE ext) */ + ETHER_HASIOVECS, /* does the driver implement writev ? (BONE ext) (bool *) */ + ETHER_GETIFTYPE, /* get the IFT_ type of the interface (int *) */ + ETHER_GETLINKSTATE /* get line speed, quality, duplex mode, etc. */ +}; + +// ethernet data +#define ETHER_ADDRESS_SIZE 6 + +struct ethernet_header { + uint8 dest_address[ETHER_ADDRESS_SIZE]; + uint8 source_address[ETHER_ADDRESS_SIZE]; + uint16 ether_type; + uint8 data[0]; +} _PACKED; + + +struct llc_header { + uint8 dsap; + uint8 ssap; + uint8 control; + uint8 org_code[3]; + uint16 ether_type; +} _PACKED; + +#define LLC_LSAP_SNAP 0xaa +#define LLC_CONTROL_UI 0x03 + + +struct ieee80211_header { + uint8 frame_control[2]; + uint8 duration[2]; + uint8 address1[ETHER_ADDRESS_SIZE]; + uint8 address2[ETHER_ADDRESS_SIZE]; + uint8 address3[ETHER_ADDRESS_SIZE]; + uint8 sequence[2]; + uint8 data[0]; +} _PACKED; + +// flags for frame_control[0] +#define IEEE80211_DATA_FRAME 0x08 + +// flags for frame_control[1] +#define IEEE80211_WEP_ENCRYPTED 0x40 + +#endif // _ETHERNET_H_ diff --git a/src/add-ons/kernel/drivers/network/ipw2100/ipw2100 b/src/add-ons/kernel/drivers/network/ipw2100/ipw2100 new file mode 100644 index 0000000000..ada84ca32f --- /dev/null +++ b/src/add-ons/kernel/drivers/network/ipw2100/ipw2100 @@ -0,0 +1,158 @@ +# +# This is the configuration file for the ipw2100 wireless network driver. +# It should be located at "/boot/home/config/settings/kernel/drivers" and named "ipw2100". +# + +# ----- +# Network setup +# ----- + +# +# mode +# ----- +# This setting specifies the operation mode of the driver and hardware: +# +# 0=IBSS In IBSS mode network devices communicate directly with eachother. No +# access point required. This mode is also called ad-hoc or peer-to-peer mode. +# 1=BSS The BSS mode (also called infrastructure mode) requires each network device +# to connect to a central access point. All traffic (also between two wireless +# devices) is routed through the access point. +# 2=Monitor Monitoring is only really useful when you're debugging a network (if you need +# to check for other, possibly overlapping, wireless networks for example). In +# monitoring mode you are not activly scanning or connecting to a network, you +# rather passivly capture every packet that is sent through the air. Note that +# for this to be useful you should enable packet dumping (below) too. +# +# Valid modes range from 0 to 2, the default is 1 (BSS). +# +mode 1 + +# +# channel +# ----- +# Selects the channel to operate on. The channel should be the same as set on the other wireless +# devices (access point in BSS mode or other computers in IBSS mode). +# +# Valid channels range from 1 to 14, the default is 1. +# +channel 1 + +# +# essid +# ----- +# Specifies which network to connect to ("the network name"). If you don't specify the ESSID the next +# best network will be used to connect. +# +# Valid are strings of up to 32 characters, the default is "unset". +# +essid unset + +# +# privacy +# ----- +# Selects the encryption to be used for the network: +# +# 0=None Don't use encryption at all. +# 1=WEP64 Use WEP with 64 bit strength (40 bit keys + 16 bit initialisation vector) +# 2=WEP128 Use WEP with 128 bit strength (104 bit keys + 16 bit initialisation vector) +# +# Valid privacy settings range from 0 to 2, the default is 0. +# +privacy 0 + +# +# authmode +# ----- +# Selects the authentication mode to be used for joining a network: +# +# 0=Open Use Open System Authentication. +# 1=Shared Use Shared Key Authentication. +# +# Valid authmode settings range from 0 to 1, the default is 0. +# +authmode 0 + +# +# keyindex +# ----- +# Selects the key to be used for encryption out of the four keys specified below. +# +# Valid index settings range from 0 to 3, default is 0. +# +keyindex 0 + +# +# keyX +# ----- +# These four entries are used to specify the WEP keys. Only one of them will be used at any one time +# though (selected by the keyindex setting above). They need to be at the same index that is +# configured at the access point or on other wireless devices. +# The keys can be either specified in ASCII format, where strings of 5 or 13 characters are valid, or in +# hex format, where hex strings of 10 or 26 digits are valid. Note that you need to enclose ASCII +# strings in quotes (") when they contain spaces. Unused keys can be left blank or commented out by +# adding a # at the beginning of the line. +# +# Example of a ASCII key for WEP with 64 bits (5 characters required): +# key0 SOMEK +# +# Example of a hex key for WEP with 128 bits (26 digits required): +# key1 a8d9fec82387d7912fdaa86820 +# +key0 00000000000000000000000000 +key1 00000000000000000000000000 +key2 00000000000000000000000000 +key3 00000000000000000000000000 + + +# ----- +# Device settings +# ----- + +# +# interrupt +# ----- +# If you have configured your network settings and tried to enable the interface but didn't get +# connected this can have a few causes. One of them beeing that the interrupt line reported to the +# driver is not the interrupt line where interrupts actually arrive. If you enable syslog debug output in +# the kernel settings and then examine the syslog in "/var/log" you might see a lot of messages like +# this: +# +# KERN 'idle thread 1'[1]: disable irq 5 +# KERN 'idle thread 1'[1]: irq 5 reenabled (handled by 0x600e4f60) +# +# This indicates that the interrupts were sent to the interrupt line 5, while there was no handler for +# them. In this case you can use the following setting to force the driver into using a specific interrupt +# line. Change the default value of -1 to the desired interrupt line. +# +# Note though that there are other factors that may cause the driver / hardware not to function +# properly. One of which might be the RF kill switch. This switch that needs to be present on all +# wireless equipment provides the function of explicitly disabling the radio wave emitting device. +# You should see a line like the following in the syslog if an RF kill switch is enabled: +# +# KERN 'ifconfig'[2000]: IPW2100: rf kill switch enabled +# +# In this case you should try to toggle the switch / key that is present somewhere on your device. +# If the problem persists, it means that you need a special driver to handle your RF kill switch. This +# is very unfortunate, because there is no way for this driver to override this. You will need to find +# a driver for the specific switch. +# +# Valid settings are any valid interrupt line number, the default is -1 (use the pci provided value). +# +interrupt -1 + +# +# dump_x +# ----- +# Enables packet dumping. The dumptx setting sets whether outgoing packets should be dumped +# before they are sent out, the dumprx setting specifies if incoming packets should be dumped +# before being handed over to the network stack. Packets will be written to a temporary folder +# called "ipwlog" under "/var/tmp". The files are prefixed with either "ipwtx-" or "ipwrx-" depending on +# their direction. They are also numbered continiously. Note that when the driver resets, the counter +# will start at 0 again, possibly overwriting a packet of a previous session. Also note that the +# temporary folders are emptied on startup. You should therefore copy out any data that is of +# interest to you. +# +# Valid settings are 0 (disabled) and 1 (enabled), the default is 0. +# +dumptx 0 +dumprx 0 diff --git a/src/add-ons/kernel/drivers/network/ipw2100/ipw2100.cpp b/src/add-ons/kernel/drivers/network/ipw2100/ipw2100.cpp new file mode 100644 index 0000000000..1859fcfe23 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/ipw2100/ipw2100.cpp @@ -0,0 +1,1560 @@ +/* + Driver for Intel(R) PRO/Wireless 2100 devices. + Copyright (C) 2006 Michael Lotz + Released under the terms of the MIT license. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kernel_cpp.h" +#include "ipw2100.h" +#include "ipw2100_hw.h" +#include "driver.h" +#include "ethernet.h" + + +IPW2100::IPW2100(int32 deviceID, pci_info *info, pci_module_info *module) + : fStatus(B_NO_INIT), + fDeviceID(deviceID), + fPCIInfo(info), + fPCIModule(module), + fRegisterBase(0), + fRegisters(NULL), + fRegistersArea(-1), + fTXRingArea(-1), + fTXRingLog(NULL), + fTXRingPhy(0), + fTXPacketsArea(-1), + fTXPacketsLog(NULL), + fTXPacketsPhy(0), + fRXRingArea(-1), + fRXRingLog(NULL), + fRXRingPhy(0), + fRXPacketsArea(-1), + fRXPacketsLog(NULL), + fRXPacketsPhy(0), + fStatusRingArea(-1), + fStatusRingLog(NULL), + fStatusRingPhy(0), + fCommandArea(-1), + fCommandLog(NULL), + fCommandPhy(0), + fInterruptMask(0), + fTXPosition(0), + fRXPosition(IPW_RX_BUFFER_COUNT - 1), + fAssociated(false), + fOrdinalTable1Base(0), + fOrdinalTable2Base(0), + fOrdinalTable1Length(0), + fOrdinalTable2Length(0), + + // settings + fInterruptLine(-1), + fMode(IPW_MODE_BSS), + fChannel(1), + fTXRates(IPW_TX_RATE_ALL), + fPowerMode(IPW_POWER_MODE_CAM), + fRTSThreshold(IPW_RTS_THRESHOLD_DEFAULT), + fScanOptions(IPW_SCAN_MIXED_CELL), + fAuthMode(IPW_AUTH_MODE_OPEN), + fCiphers(IPW_CIPHER_NONE), + fESSID(NULL), + fWEPKeyIndex(0), + fWEPFlags(0), + fDumpTXPackets(false), + fDumpRXPackets(false) +{ + const char *parameter; + void *settings = load_driver_settings("ipw2100"); + + parameter = get_driver_parameter(settings, "interrupt", NULL, NULL); + if (parameter) + fInterruptLine = atoi(parameter); + + parameter = get_driver_parameter(settings, "mode", NULL, NULL); + if (parameter) { + int mode = atoi(parameter); + switch (mode) { + case 0: fMode = IPW_MODE_IBSS; break; + case 1: fMode = IPW_MODE_BSS; break; + case 2: fMode = IPW_MODE_MONITOR; break; + } + } + + parameter = get_driver_parameter(settings, "channel", NULL, NULL); + if (parameter) { + int channel = atoi(parameter); + if (channel >= 1 && channel <= 14) + fChannel = channel; + } + + parameter = get_driver_parameter(settings, "essid", NULL, NULL); + if (parameter) + fESSID = strdup(parameter); + + uint32 keyLength = 0; + parameter = get_driver_parameter(settings, "privacy", NULL, NULL); + if (parameter) { + int privacy = atoi(parameter); + switch (privacy) { + case 0: { + fCiphers = IPW_CIPHER_NONE; + keyLength = 0; + break; + } + case 1: { + fCiphers = IPW_CIPHER_WEP40; + fWEPFlags = IPW_WEP_FLAGS_HW_ENCRYPT | IPW_WEP_FLAGS_HW_DECRYPT; + keyLength = 5; + break; + } + case 2: { + fCiphers = IPW_CIPHER_WEP104; + fWEPFlags = IPW_WEP_FLAGS_HW_ENCRYPT | IPW_WEP_FLAGS_HW_DECRYPT; + keyLength = 13; + break; + } + } + } + + parameter = get_driver_parameter(settings, "authmode", NULL, NULL); + if (parameter) { + int auth = atoi(parameter); + switch (auth) { + case 0: fAuthMode = IPW_AUTH_MODE_OPEN; break; + case 1: fAuthMode = IPW_AUTH_MODE_SHARED; break; + } + } + + parameter = get_driver_parameter(settings, "keyindex", NULL, NULL); + if (parameter) { + int index = atoi(parameter); + if (index >= 0 && index <= 3) + fWEPKeyIndex = index; + } + + for (uint8 i = 0; i < 4; i++) { + fWEPKeys[i].index = i; + fWEPKeys[i].length = keyLength; + + char keyName[10]; + sprintf(keyName, "key%d", i); + parameter = get_driver_parameter(settings, keyName, NULL, NULL); + if (parameter) { + int32 keyFormat = -1; + switch (strlen(parameter)) { + case 5: + if (keyLength == 5) + keyFormat = 0; + break; + case 13: + if (keyLength == 13) + keyFormat = 0; + break; + case 10: + if (keyLength == 5) + keyFormat = 1; + break; + case 26: + if (keyLength == 13) + keyFormat = 1; + break; + } + + if (keyFormat == 0) + strncpy((char *)fWEPKeys[i].key, parameter, keyLength); + else if (keyFormat == 1) { + uint8 buffer[30]; + for (uint8 j = 0; j < keyLength * 2; j++) { + if (parameter[j] >= '0' && parameter[j] <= '9') + buffer[j] = parameter[j] - '0'; + else if (parameter[j] >= 'a' && parameter[j] <= 'f') + buffer[j] = parameter[j] - 'a' + 10; + else if (parameter[j] >= 'A' && parameter[j] <= 'F') + buffer[j] = parameter[j] - 'A' + 10; + else + break; + } + + for (uint8 j = 0; j < keyLength; j++) + fWEPKeys[i].key[j] = (buffer[j * 2] << 4) + buffer[j * 2 + 1]; + } else { + TRACE_ALWAYS(("IPW2100: the wep key specified in %s is invalid, it will not be used\n", keyName)); + } + } + } + + parameter = get_driver_parameter(settings, "dumptx", NULL, NULL); + if (parameter) { + int value = atoi(parameter); + if (value == 1) + fDumpTXPackets = true; + } + + parameter = get_driver_parameter(settings, "dumprx", NULL, NULL); + if (parameter) { + int value = atoi(parameter); + if (value == 1) + fDumpRXPackets = true; + } + + unload_driver_settings(settings); + + if (fDumpRXPackets || fDumpTXPackets) + mkdir("/var/tmp/ipwlog", 0x0755); + + fRegisterBase = fPCIInfo->u.h0.base_registers[0]; + TRACE(("IPW2100: register base: 0x%08lx\n", fRegisterBase)); + + // enable addressing + uint16 command = fPCIModule->read_pci_config(fPCIInfo->bus, + fPCIInfo->device, fPCIInfo->function, PCI_command, 2); + command |= PCI_command_io | PCI_command_master | PCI_command_memory; + fPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device, + fPCIInfo->function, PCI_command, 2, command); + + // map physical register memory + fRegistersArea = MapPhysicalMemory("ipw2100 registers", + fRegisterBase, (void **)&fRegisters, 128 * 1024); + if (!fRegisters) { + TRACE_ALWAYS(("IPW2100: couldn't map registers\n")); + return; + } + + // disable interrupts + SetInterrupts(0); + + // synchronisation semaphores + fFWInitDoneSem = create_sem(0, "IPW2100 init done sem"); + fAdapterDisabledSem = create_sem(0, "IPW2100 adapter disabled sem"); + fTXTransferSem = create_sem(0, "IPW2100 tx transfer sem"); + fRXTransferSem = create_sem(0, "IPW2100 rx transfer sem"); + fCommandDoneSem = create_sem(0, "IPW2100 command done sem"); + + if (fInterruptLine < 0) + fInterruptLine = fPCIInfo->u.h0.interrupt_line; + + // install the interrupt handler + TRACE_ALWAYS(("IPW2100: installing interrupt handler on interrupt line %ld\n", fInterruptLine)); + install_io_interrupt_handler(fInterruptLine, InterruptHandler, (void *)this, 0); + + TRACE(("IPW2100: device class created (device: %ld)\n", fDeviceID)); + fStatus = B_OK; +} + + +IPW2100::~IPW2100() +{ + fStatus = B_NO_INIT; + + remove_io_interrupt_handler(fInterruptLine, InterruptHandler, (void *)this); + + fRegisters = NULL; + fTXRingLog = NULL; + fRXPacketsLog = NULL; + fRXRingLog = NULL; + fRXPacketsLog = NULL; + fStatusRingLog = NULL; + fCommandLog = NULL; + + delete_area(fRegistersArea); + delete_area(fTXRingArea); + delete_area(fTXPacketsArea); + delete_area(fRXRingArea); + delete_area(fRXPacketsArea); + delete_area(fStatusRingArea); + delete_area(fCommandArea); + + delete_sem(fFWInitDoneSem); + delete_sem(fAdapterDisabledSem); + delete_sem(fTXTransferSem); + delete_sem(fRXTransferSem); + delete_sem(fCommandDoneSem); + + free(fESSID); +} + + +status_t +IPW2100::InitCheck() +{ + return fStatus; +} + + +status_t +IPW2100::Open(uint32 flags) +{ + TRACE(("IPW2100: open\n")); + fClosing = false; + + status_t status = CheckAdapterAccess(); + if (status < B_OK) { + TRACE_ALWAYS(("IPW2100: adapter not accessible via mapped registers\n")); + return status; + } + + status = SoftResetAdapter(); + if (status < B_OK) { + TRACE_ALWAYS(("IPW2100: couldn't softreset the adapter\n")); + return status; + } + + status = LoadMicrocodeAndFirmware(); + if (status < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to load microcode and firmware\n")); + return status; + } + + status = ClearSharedMemory(); + if (status < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to clear shared memory\n")); + return status; + } + + status = SetupBuffers(); + if (status < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to setup buffers\n")); + return status; + } + + status = StartFirmware(); + if (status < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to start firmware\n")); + return status; + } + + status = ReadMACAddress(); + if (status < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to get MAC address\n")); + return status; + } + + // enable all interrupts + SetInterrupts(IPW_INTERRUPT_TX_TRANSFER | IPW_INTERRUPT_RX_TRANSFER + | IPW_INTERRUPT_STATUS_CHANGE | IPW_INTERRUPT_COMMAND_DONE + | IPW_INTERRUPT_FW_INIT_DONE | IPW_INTERRUPT_FATAL_ERROR + | IPW_INTERRUPT_PARITY_ERROR); + + TRACE(("IPW2100: open was successful\n")); + return B_OK; +} + + +status_t +IPW2100::Close() +{ + TRACE(("IPW2100: close\n")); + fClosing = true; + + DisableRadio(); + PowerDown(); + + SetInterrupts(0); + release_sem(fRXTransferSem); + release_sem(fTXTransferSem); + return B_OK; +} + + +status_t +IPW2100::Free() +{ + TRACE(("IPW2100: free\n")); + return B_OK; +} + + +status_t +IPW2100::Read(off_t position, void *buffer, size_t *numBytes) +{ + TRACE(("IPW2100: read at most %ld bytes\n", *numBytes)); + status_t result = acquire_sem(fRXTransferSem); + if (result < B_OK || fClosing) { + TRACE_ALWAYS(("IPW2100: read failed: %s\n", fClosing ? "closing" : "error")); + *numBytes = 0; + return (fClosing ? B_INTERRUPTED : result); + } + + uint32 index = fReadIndex; + for (int32 i = 0; i < IPW_RX_BUFFER_COUNT; i++) { + if (fReadQueue[index]) { + ipw_rx *packet = &fRXPacketsLog[index]; + ipw_status *status = &fStatusRingLog[index]; + + if (fDumpRXPackets) { + char outName[100]; + static uint32 packetIndex = 0; + sprintf(outName, "/var/tmp/ipwlog/ipwrx-%06ld", packetIndex++); + int fd = open(outName, O_CREAT | O_WRONLY | O_TRUNC); + write(fd, packet->data, status->length); + close(fd); + } + + ieee80211_header *frame = (ieee80211_header *)packet->data; + if (fMode == IPW_MODE_MONITOR + || (frame->frame_control[0] & IEEE80211_DATA_FRAME) == 0) { + // just wanted to log this, but it's not interesting + fReadQueue[index] = false; + fReadIndex = index; + return B_INTERRUPTED; + } + + size_t payloadLength = status->length - sizeof(ieee80211_header); + ethernet_header *ether = (ethernet_header *)buffer; + memcpy(ether->dest_address, frame->address1, ETHER_ADDRESS_SIZE); + memcpy(ether->source_address, frame->address3, ETHER_ADDRESS_SIZE); + llc_header *llc = (llc_header *)frame->data; + ether->ether_type = llc->ether_type; + + payloadLength -= sizeof(llc_header); + memcpy(ether->data, frame->data + sizeof(llc_header), payloadLength); + + *numBytes = sizeof(ethernet_header) + payloadLength; + fReadQueue[index] = false; + fReadIndex = index; + return B_OK; + } + + index = (index + 1) % IPW_RX_BUFFER_COUNT; + } + + return B_INTERRUPTED; +} + + +status_t +IPW2100::Write(off_t position, const void *buffer, size_t *numBytes) +{ + TRACE(("IPW2100: write %ld bytes\n", *numBytes)); + if (*numBytes > sizeof(ipw_tx)) { + TRACE_ALWAYS(("IPW2100: packet too big, discarding\n")); + *numBytes = 0; + return B_ERROR; + } + + if (fMode == IPW_MODE_MONITOR) { + *numBytes = 0; + return B_ERROR; + } + + // wait for association + uint32 tries = 1000; + while (!fAssociated) { + snooze(5000); + if (--tries == 0) { + TRACE_ALWAYS(("IPW2100: writing failed due to not beeing associated\n")); + return B_INTERRUPTED; + } + } + + ipw_bd *descriptor = &fTXRingLog[fTXPosition]; + descriptor->physical_address = fTXPacketsPhy + fTXPosition * sizeof(ipw_tx); + descriptor->length = sizeof(ipw_data); + descriptor->flags = IPW_BD_FLAG_TYPE_802_3 | IPW_BD_FLAG_NOT_LAST_FRAGMENT; + descriptor->fragment_count = 2; + + ipw_data *data = (ipw_data *)&(fTXPacketsLog[fTXPosition].data); + memset(data, 0, sizeof(ipw_data)); + data->command = IPW_COMMAND_SEND_DATA; + if (fCiphers != IPW_CIPHER_NONE) { + data->needs_encryption = 1; + data->key_index = fWEPKeyIndex + 1; + } + + ethernet_header *ether = (ethernet_header *)buffer; + memcpy(data->dest_address, ether->dest_address, ETHER_ADDRESS_SIZE); + memcpy(data->source_address, ether->source_address, ETHER_ADDRESS_SIZE); + + fTXPosition = (fTXPosition + 1) % IPW_TX_BUFFER_COUNT; + descriptor = &fTXRingLog[fTXPosition]; + descriptor->physical_address = fTXPacketsPhy + fTXPosition * sizeof(ipw_tx); + descriptor->length = *numBytes - sizeof(ethernet_header) + sizeof(llc_header); + descriptor->flags = IPW_BD_FLAG_TYPE_802_3 | IPW_BD_FLAG_INTERRUPT; + descriptor->fragment_count = 0; + + llc_header llc; + llc.dsap = llc.ssap = LLC_LSAP_SNAP; + llc.control = LLC_CONTROL_UI; + llc.org_code[0] = llc.org_code[1] = llc.org_code[2] = 0; + llc.ether_type = ether->ether_type; + + memcpy(fTXPacketsLog[fTXPosition].data, &llc, sizeof(llc_header)); + memcpy(fTXPacketsLog[fTXPosition].data + sizeof(llc_header), ether->data, *numBytes - sizeof(ethernet_header)); + + if (fDumpTXPackets) { + char outName[100]; + static uint32 packetIndex = 0; + sprintf(outName, "/var/tmp/ipwlog/ipwtx-%06ld", packetIndex++); + int fd = open(outName, O_CREAT | O_WRONLY | O_TRUNC); + write(fd, fTXPacketsLog[fTXPosition].data, descriptor->length); + close(fd); + } + + // tell the firmware that there is data to process + fTXPosition = (fTXPosition + 1) % IPW_TX_BUFFER_COUNT; + WriteReg(IPW_REG_TX_WRITE, fTXPosition); + + status_t result = acquire_sem_etc(fTXTransferSem, 1, B_RELATIVE_TIMEOUT, 200000); + if (result < B_OK || fClosing) { + TRACE_ALWAYS(("IPW2100: error waiting for tx transfer completion: %s\n", fClosing ? "closing" : "timeout")); + *numBytes = 0; + return (fClosing ? B_INTERRUPTED : result); + } + + return B_OK; +} + + +status_t +IPW2100::Control(uint32 op, void *buffer, size_t length) +{ + if (fStatus < B_OK) + return fStatus; + + switch (op) { + case ETHER_INIT: { + TRACE(("IPW2100: ETHER_INIT\n")); + return EtherInit(); + } + + case ETHER_GETADDR: { + TRACE(("IPW2100: ETHER_GETADDR\n")); + memcpy(buffer, &fMACAddress, sizeof(fMACAddress)); + return B_OK; + } + + case ETHER_GETFRAMESIZE: { + TRACE(("IPW2100: ETHER_GETFRAMESIZE\n")); + *(uint32 *)buffer = 2000; + return B_OK; + } + + default: + TRACE_ALWAYS(("IPW2100: unsupported ioctl 0x%08lx\n", op)); + } + + return B_DEV_INVALID_IOCTL; +} + + +//#pragma mark - + + +int32 +IPW2100::InterruptHandler(void *data) +{ + return ((IPW2100 *)data)->Interrupt(); +} + + +int32 +IPW2100::Interrupt() +{ + spinlock lock = 0; + acquire_spinlock(&lock); + + uint32 status = ReadReg(IPW_REG_INTERRUPT); + status &= fInterruptMask; + if (status == 0) { + release_spinlock(&lock); + return B_UNHANDLED_INTERRUPT; + } + + uint32 acknowledge = 0; + int32 result = B_HANDLED_INTERRUPT; + + if (status & IPW_INTERRUPT_TX_TRANSFER) { + acknowledge |= IPW_INTERRUPT_TX_TRANSFER; + result = B_INVOKE_SCHEDULER; + } + + if (status & IPW_INTERRUPT_RX_TRANSFER) { + acknowledge |= IPW_INTERRUPT_RX_TRANSFER; + result = B_INVOKE_SCHEDULER; + } + + if (status & IPW_INTERRUPT_FW_INIT_DONE) { + acknowledge |= IPW_INTERRUPT_FW_INIT_DONE; + result = B_INVOKE_SCHEDULER; + } + + if (status & IPW_INTERRUPT_FATAL_ERROR) { + TRACE_ALWAYS(("IPW2100: interrupt fatal error\n")); + acknowledge |= IPW_INTERRUPT_FATAL_ERROR; + // ToDo: do something... + } + + if (status & IPW_INTERRUPT_PARITY_ERROR) { + TRACE_ALWAYS(("IPW2100: interrupt parity error\n")); + acknowledge |= IPW_INTERRUPT_PARITY_ERROR; + // ToDo: do something... + } + + if (acknowledge) + WriteReg(IPW_REG_INTERRUPT, acknowledge); + + release_spinlock(&lock); + + if (status & IPW_INTERRUPT_TX_TRANSFER) + HandleTXInterrupt(); + if (status & IPW_INTERRUPT_RX_TRANSFER) + HandleRXInterrupt(); + if (status & IPW_INTERRUPT_FW_INIT_DONE) + release_sem_etc(fFWInitDoneSem, 1, B_DO_NOT_RESCHEDULE); + + return result; +} + + +void +IPW2100::HandleTXInterrupt() +{ + release_sem_etc(fTXTransferSem, 1, B_DO_NOT_RESCHEDULE); +} + + +void +IPW2100::HandleRXInterrupt() +{ + uint32 readIndex = ReadReg(IPW_REG_RX_READ); + uint32 writeIndex = (fRXPosition + 1) % IPW_RX_BUFFER_COUNT; + + while (writeIndex != readIndex) { + ipw_status *status = &fStatusRingLog[writeIndex]; + ipw_rx *packet = &fRXPacketsLog[writeIndex]; + + switch (status->code & IPW_STATUS_CODE_MASK) { + case IPW_STATUS_CODE_COMMAND: { +#ifdef TRACE_IPW2100 + ipw_command *command = (ipw_command *)packet->data; + TRACE(("IPW2100: command done received for command %ld\n", command->command)); +#endif + release_sem_etc(fCommandDoneSem, 1, B_DO_NOT_RESCHEDULE); + break; + } + + case IPW_STATUS_CODE_STATUS: { + TRACE(("IPW2100: status change received\n")); + uint32 statusCode = *(uint32 *)packet->data; + if (statusCode & IPW_STATE_INITIALIZED) + TRACE((" initialized\n")); + if (statusCode & IPW_STATE_COUNTRY_FOUND) + TRACE((" country found\n")); + if (statusCode & IPW_STATE_ASSOCIATED) { + TRACE((" associated\n")); + TRACE_ALWAYS(("IPW2100: associated\n")); + fAssociated = true; + } + if (statusCode & IPW_STATE_ASSOCIATION_LOST) { + TRACE((" association lost\n")); + TRACE_ALWAYS(("IPW2100: association lost\n")); + fAssociated = false; + } + if (statusCode & IPW_STATE_ASSOCIATION_CHANGED) + TRACE((" association changed\n")); + if (statusCode & IPW_STATE_SCAN_COMPLETE) + TRACE((" scan complete\n")); + if (statusCode & IPW_STATE_PSP_ENTERED) + TRACE((" psp entered\n")); + if (statusCode & IPW_STATE_PSP_LEFT) + TRACE((" psp left\n")); + if (statusCode & IPW_STATE_RF_KILL) { + TRACE((" rf kill\n")); + TRACE_ALWAYS(("IPW2100: rf kill switch enabled\n")); + } + if (statusCode & IPW_STATE_DISABLED) { + TRACE((" disabled\n")); + release_sem_etc(fAdapterDisabledSem, 1, B_DO_NOT_RESCHEDULE); + } + if (statusCode & IPW_STATE_POWER_DOWN) + TRACE((" power down\n")); + if (statusCode & IPW_STATE_SCANNING) + TRACE((" scanning\n")); + break; + } + + case IPW_STATUS_CODE_DATA_802_11: + case IPW_STATUS_CODE_DATA_802_3: { + TRACE(("IPW2100: %s data received at index %ld with %ld bytes\n", (status->code & IPW_STATUS_CODE_MASK) == IPW_STATUS_CODE_DATA_802_11 ? "802.11" : "802.3", writeIndex, status->length)); + fReadQueue[writeIndex] = true; + release_sem_etc(fRXTransferSem, 1, B_DO_NOT_RESCHEDULE); + break; + } + + case IPW_STATUS_CODE_NOTIFICATION: { + TRACE(("IPW2100: notification received\n")); + break; + } + + default: { + TRACE_ALWAYS(("IPW2100: unknown status code received 0x%04x\n", status->code)); + break; + } + } + + writeIndex = (writeIndex + 1) % IPW_RX_BUFFER_COUNT; + fRXPosition = (writeIndex > 0 ? writeIndex - 1 : IPW_RX_BUFFER_COUNT - 1); + WriteReg(IPW_REG_RX_WRITE, fRXPosition); + TRACE(("IPW2100: new rx write index %ld\n", fRXPosition)); + } +} + + +inline uint32 +IPW2100::ReadReg(uint32 offset) +{ + return *(uint32 *)(fRegisters + offset); +} + + +inline void +IPW2100::WriteReg(uint32 offset, uint32 value) +{ + *(uint32 *)(fRegisters + offset) = value; +} + + +inline uint8 +IPW2100::ReadMem8(uint32 address) +{ + WriteReg(IPW_REG_INDIRECT_ADDRESS, address & IPW_INDIRECT_ADDRESS_MASK); + return *(uint8 *)(fRegisters + IPW_REG_INDIRECT_DATA + (address & ~IPW_INDIRECT_ADDRESS_MASK)); +} + + +inline uint16 +IPW2100::ReadMem16(uint32 address) +{ + WriteReg(IPW_REG_INDIRECT_ADDRESS, address & IPW_INDIRECT_ADDRESS_MASK); + return *(uint16 *)(fRegisters + IPW_REG_INDIRECT_DATA + (address & ~IPW_INDIRECT_ADDRESS_MASK)); +} + + +inline uint32 +IPW2100::ReadMem32(uint32 address) +{ + WriteReg(IPW_REG_INDIRECT_ADDRESS, address & IPW_INDIRECT_ADDRESS_MASK); + return *(uint32 *)(fRegisters + IPW_REG_INDIRECT_DATA); +} + + +inline void +IPW2100::WriteMem8(uint32 address, uint8 value) +{ + WriteReg(IPW_REG_INDIRECT_ADDRESS, address & IPW_INDIRECT_ADDRESS_MASK); + *(uint8 *)(fRegisters + IPW_REG_INDIRECT_DATA + (address & ~IPW_INDIRECT_ADDRESS_MASK)) = value; +} + + +inline void +IPW2100::WriteMem16(uint32 address, uint16 value) +{ + WriteReg(IPW_REG_INDIRECT_ADDRESS, address & IPW_INDIRECT_ADDRESS_MASK); + *(uint16 *)(fRegisters + IPW_REG_INDIRECT_DATA + (address & ~IPW_INDIRECT_ADDRESS_MASK)) = value; +} + + +inline void +IPW2100::WriteMem32(uint32 address, uint32 value) +{ + WriteReg(IPW_REG_INDIRECT_ADDRESS, address & IPW_INDIRECT_ADDRESS_MASK); + *(uint32 *)(fRegisters + IPW_REG_INDIRECT_DATA) = value; +} + + +status_t +IPW2100::ReadOrdinal(uint32 index, uint8 *buffer, size_t *size) +{ + if (index >= IPW_ORDINAL_TABLE_1_START + && index < IPW_ORDINAL_TABLE_1_START + fOrdinalTable1Length) { + TRACE(("IPW2100: reading ordinal from table 1 at index %ld\n", index)); + if (*size < sizeof(uint32)) { + TRACE_ALWAYS(("IPW2100: not enough space to read ordinal\n")); + return B_BAD_VALUE; + } + + uint32 address = ReadMem32(fOrdinalTable1Base + index * 4); + *(uint32 *)buffer = ReadMem32(address); + *size = sizeof(uint32); + return B_OK; + } + + if (index >= IPW_ORDINAL_TABLE_2_START + && index < IPW_ORDINAL_TABLE_2_START + fOrdinalTable2Length) { + TRACE(("IPW2100: reading ordinal from table 2 at index %ld\n", index)); + + index -= IPW_ORDINAL_TABLE_2_START; + uint32 address = ReadMem32(fOrdinalTable2Base + index * 8); + uint16 fieldLength = ReadMem16(fOrdinalTable2Base + index * 8 + sizeof(address)); + uint16 fieldCount = ReadMem16(fOrdinalTable2Base + index * 8 + sizeof(address) + sizeof(fieldLength)); + TRACE(("IPW2100: reading field at 0x%08lx with length %d and count %d\n", address, fieldLength, fieldCount)); + uint32 length = fieldLength * fieldCount; + + if (*size < length) { + TRACE_ALWAYS(("IPW2100: not enough memory to read ordinal (length is %ld)\n", length)); + return B_BAD_VALUE; + } + + for (uint32 i = 0; i < length; i++) + buffer[i] = ReadMem8(address + i); + + *size = length; + return B_OK; + } + + TRACE_ALWAYS(("IPW2100: invalid ordinal index (%ld)\n", index)); + return B_BAD_INDEX; +} + + +status_t +IPW2100::WriteOrdinal(uint32 index, const uint8 *buffer, size_t *size) +{ + if (index >= IPW_ORDINAL_TABLE_1_START + && index < IPW_ORDINAL_TABLE_1_START + fOrdinalTable1Length) { + TRACE(("IPW2100: writing ordinal in table 1 at index %ld\n", index)); + if (*size < sizeof(uint32)) { + TRACE_ALWAYS(("IPW2100: not enough data to write ordinal\n")); + return B_BAD_VALUE; + } + + uint32 address = ReadMem32(fOrdinalTable1Base + index * 4); + WriteMem32(address, *(uint32 *)buffer); + *size = sizeof(uint32); + return B_OK; + } + + TRACE_ALWAYS(("IPW2100: only ordinals in table 1 can be written\n")); + return B_BAD_INDEX; +} + + +void +IPW2100::SetInterrupts(uint32 interrupts) +{ + fInterruptMask = interrupts; + WriteReg(IPW_REG_INTERRUPT_MASK, fInterruptMask); +} + + +uint32 +IPW2100::Interrupts() +{ + return fInterruptMask; +} + + +status_t +IPW2100::SendCommand(uint32 commandId, const uint8 *buffer, size_t length) +{ + ipw_command *command = fCommandLog; + memset(command, 0, sizeof(ipw_command)); + command->command = commandId; + command->length = length; + if (buffer && length > 0) + memcpy(command->data, buffer, length); + + ipw_bd *descriptor = &fTXRingLog[fTXPosition]; + descriptor->physical_address = fCommandPhy; + descriptor->length = sizeof(ipw_command); + descriptor->flags = IPW_BD_FLAG_COMMAND; + descriptor->fragment_count = 1; + + // tell the firmware that there is data to process + fTXPosition = (fTXPosition + 1) % IPW_TX_BUFFER_COUNT; + WriteReg(IPW_REG_TX_WRITE, fTXPosition); + + status_t result = acquire_sem_etc(fCommandDoneSem, 1, B_RELATIVE_TIMEOUT, 200000); + if (result < B_OK) { + TRACE_ALWAYS(("IPW2100: timeout at waiting for command completion\n")); + return result; + } + + // wait some, the firmware needs this + snooze(10000); + return B_OK; +} + + +//#pragma mark - + + +area_id +IPW2100::MapPhysicalMemory(const char *name, uint32 physicalAddress, + void **logicalAddress, size_t size) +{ + TRACE(("IPW2100: mapping physical address 0x%08lx with size %ld\n", physicalAddress, size)); + uint32 offset = physicalAddress & (B_PAGE_SIZE - 1); + physicalAddress = physicalAddress - offset; + size = (size + offset + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); + + void *virtualAddress; + area_id area = map_physical_memory(name, (void *)physicalAddress, size, + B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA, &virtualAddress); + if (area < B_OK) { + TRACE_ALWAYS(("IPW2100: mapping physical address failed\n")); + return area; + } + + virtualAddress = (uint8 *)virtualAddress + offset; + if (logicalAddress) + *logicalAddress = virtualAddress; + + TRACE(("IPW2100: mapped physical address 0x%08lx to 0x%08lx\n", physicalAddress, (uint32)virtualAddress)); + TRACE(("IPW2100: with offset 0x%08lx and size %ld\n", offset, size)); + return area; +} + + +area_id +IPW2100::AllocateContiguous(const char *name, void **logicalAddress, + uint32 *physicalAddress, size_t size) +{ + size = (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); + void *virtualAddress = NULL; + area_id area = create_area(name, &virtualAddress, B_ANY_KERNEL_ADDRESS, + size, B_FULL_LOCK | B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA); + if (area < B_OK) { + TRACE_ALWAYS(("IPW2100: allocating contiguous area failed\n")); + return area; + } + + if (logicalAddress) + *logicalAddress = virtualAddress; + + if (physicalAddress) { + physical_entry physicalEntry; + get_memory_map(virtualAddress, size, &physicalEntry, 1); + *physicalAddress = (uint32)physicalEntry.address; + } + + return area; +} + + +//#pragma mark - + + +status_t +IPW2100::CheckAdapterAccess() +{ + TRACE(("IPW2100: debug register 0x%08lx\n", ReadReg(IPW_REG_DEBUG))); + return (ReadReg(IPW_REG_DEBUG) == IPW_DEBUG_DATA ? B_OK : B_ERROR); +} + + +status_t +IPW2100::SoftResetAdapter() +{ + WriteReg(IPW_REG_RESET, IPW_RESET_SW_RESET); + + // wait for reset + int32 tries; + for (tries = 1000; tries > 0; tries--) { + if (ReadReg(IPW_REG_RESET) & IPW_RESET_PRINCETON_RESET) + break; + + snooze(10); + } + + if (tries == 0) { + TRACE_ALWAYS(("IPW2100: reset wasn't asserted\n")); + return B_ERROR; + } + + TRACE(("IPW2100: reset asserted with %ld tries left\n", tries)); + WriteReg(IPW_REG_CONTROL, IPW_CONTROL_INIT_COMPLETE); + + // wait for clock stabilisation + for (tries = 1000; tries > 0; tries--) { + if (ReadReg(IPW_REG_CONTROL) & IPW_CONTROL_CLOCK_READY) + break; + + snooze(200); + } + + if (tries == 0) { + TRACE_ALWAYS(("IPW2100: clock didn't stabilise\n")); + return B_ERROR; + } + + TRACE(("IPW2100: clock ready with %ld tries left\n", tries)); + + // allow standby + WriteReg(IPW_REG_CONTROL, ReadReg(IPW_REG_CONTROL) + | IPW_CONTROL_ALLOW_STANDBY); + + return B_OK; +} + + +status_t +IPW2100::ResetAdapter() +{ + status_t status = StopMaster(); + if (status < B_OK) { + TRACE_ALWAYS(("IPW2100: master stop failed\n")); + return status; + } + + uint32 reg = ReadReg(IPW_REG_CONTROL); + WriteReg(IPW_REG_CONTROL, reg | IPW_CONTROL_INIT_COMPLETE); + + // wait for clock stabilisation + int32 tries; + for (tries = 1000; tries > 0; tries--) { + if (ReadReg(IPW_REG_CONTROL) & IPW_CONTROL_CLOCK_READY) + break; + + snooze(200); + } + + if (tries == 0) { + TRACE_ALWAYS(("IPW2100: clock didn't stabilise\n")); + return B_ERROR; + } + + TRACE(("IPW2100: clock ready with %ld tries left\n", tries)); + + reg = ReadReg(IPW_REG_RESET); + WriteReg(IPW_REG_RESET, reg | IPW_RESET_SW_RESET); + snooze(10); + + reg = ReadReg(IPW_REG_CONTROL); + WriteReg(IPW_REG_RESET, reg | IPW_CONTROL_INIT_COMPLETE); + return B_OK; +} + + +status_t +IPW2100::StopMaster() +{ + // disable interrupts + SetInterrupts(0); + + uint32 tries; + WriteReg(IPW_REG_RESET, IPW_RESET_STOP_MASTER); + for (tries = 50; tries > 0; tries--) { + if (ReadReg(IPW_REG_RESET) & IPW_RESET_MASTER_DISABLED) + break; + + snooze(10); + } + + if (tries == 0) { + TRACE_ALWAYS(("IPW2100: timeout waiting for master to stop\n")); + return B_ERROR; + } + + TRACE(("IPW2100: master stopped with %ld tries left\n", tries)); + uint32 reg = ReadReg(IPW_REG_RESET); + WriteReg(IPW_REG_RESET, reg | IPW_RESET_PRINCETON_RESET); + return B_OK; +} + + +status_t +IPW2100::SetupBuffers() +{ + fTXRingArea = AllocateContiguous("ipw2100 tx ring", (void **)&fTXRingLog, + &fTXRingPhy, IPW_TX_BUFFER_SIZE); + if (fTXRingArea < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to create tx ring\n")); + return fTXRingArea; + } + + fRXRingArea = AllocateContiguous("ipw2100 rx ring", (void **)&fRXRingLog, + &fRXRingPhy, IPW_RX_BUFFER_SIZE); + if (fRXRingArea < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to create rx ring\n")); + return fRXRingArea; + } + + fStatusRingArea = AllocateContiguous("ipw2100 status ring", + (void **)&fStatusRingLog, &fStatusRingPhy, IPW_STATUS_BUFFER_SIZE); + if (fStatusRingArea < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to create status ring\n")); + return fStatusRingArea; + } + + fCommandArea = AllocateContiguous("ipw2100 command", (void **)&fCommandLog, + &fCommandPhy, sizeof(ipw_command)); + if (fCommandArea < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to create command area\n")); + return fCommandArea; + } + + // these will be filled later + memset(fTXRingLog, 0, IPW_TX_BUFFER_SIZE); + memset(fRXRingLog, 0, IPW_TX_BUFFER_SIZE); + memset(fStatusRingLog, 0, IPW_STATUS_BUFFER_SIZE); + memset(fCommandLog, 0, sizeof(ipw_command)); + + // allocate and fill tx packet infos + fTXPacketsArea = AllocateContiguous("ipw2100 tx packets", + (void **)&fTXPacketsLog, &fTXPacketsPhy, IPW_TX_PACKET_SIZE); + if (fTXPacketsArea < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to create tx packet area\n")); + return fTXPacketsArea; + } + + // allocate and fill rx packet infos + fRXPacketsArea = AllocateContiguous("ipw2100 rx packets", + (void **)&fRXPacketsLog, &fRXPacketsPhy, IPW_RX_PACKET_SIZE); + if (fRXPacketsArea < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to create rx packet area\n")); + return fRXPacketsArea; + } + + for (uint32 i = 0; i < IPW_RX_BUFFER_COUNT; i++) { + fRXRingLog[i].physical_address = fRXPacketsPhy + (i * sizeof(ipw_rx)); + fRXRingLog[i].length = sizeof(ipw_rx); + } + + fTXPosition = 0; + fRXPosition = IPW_RX_BUFFER_COUNT - 1; + + fReadQueue = (bool *)malloc(IPW_RX_BUFFER_COUNT * sizeof(bool)); + for (uint32 i = 0; i < IPW_RX_BUFFER_COUNT; i++) + fReadQueue[i] = false; + fReadIndex = fRXPosition; + + WriteReg(IPW_REG_TX_BASE, (uint32)fTXRingPhy); + WriteReg(IPW_REG_TX_SIZE, IPW_TX_BUFFER_COUNT); + WriteReg(IPW_REG_TX_READ, 0); + WriteReg(IPW_REG_TX_WRITE, fTXPosition); + + WriteReg(IPW_REG_RX_BASE, (uint32)fRXRingPhy); + WriteReg(IPW_REG_RX_SIZE, IPW_RX_BUFFER_COUNT); + WriteReg(IPW_REG_RX_READ, 0); + WriteReg(IPW_REG_RX_WRITE, fRXPosition); + + WriteReg(IPW_REG_STATUS_BASE, (uint32)fStatusRingPhy); + return B_OK; +} + + +status_t +IPW2100::ReadMACAddress() +{ + size_t size = sizeof(fMACAddress); + status_t result = ReadOrdinal(1001, fMACAddress, &size); + if (result < B_OK || size != sizeof(fMACAddress)) { + TRACE_ALWAYS(("IPW2100: failed to read mac address\n")); + return result; + } + return B_OK; +} + + +status_t +IPW2100::LoadMicrocodeAndFirmware() +{ + const char *firmware; + switch (fMode) { + case IPW_MODE_BSS: firmware = "/etc/firmware/ipw2100/ipw2100-1.3.fw"; break; + case IPW_MODE_IBSS: firmware = "/etc/firmware/ipw2100/ipw2100-1.3-i.fw"; break; + case IPW_MODE_MONITOR: firmware = "/etc/firmware/ipw2100/ipw2100-1.3-p.fw"; break; + default: return B_BAD_VALUE; + } + + TRACE_ALWAYS(("IPW2100: loading firmware %s\n", firmware)); + int fd = open(firmware, B_READ_ONLY); + int32 fileSize = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + + uint8 *buffer = (uint8 *)malloc(fileSize); + ssize_t readCount = read(fd, buffer, fileSize); + TRACE(("IPW2100: read %ld bytes\n", readCount)); + close(fd); + + if (readCount < (ssize_t)sizeof(ipw_firmware_header)) { + TRACE_ALWAYS(("IPW2100: firmware too short for header\n")); + free(buffer); + return B_ERROR; + } + + ipw_firmware_header *header = (ipw_firmware_header *)buffer; + if ((uint32)readCount < sizeof(ipw_firmware_header) + header->main_size + + header->ucode_size) { + TRACE_ALWAYS(("IPW2100: firmware too short for data\n")); + free(buffer); + return B_ERROR; + } + + uint8 *code = buffer + sizeof(ipw_firmware_header) + header->main_size; + status_t status = LoadMicrocode(code, header->ucode_size); + if (status < B_OK) { + TRACE_ALWAYS(("IPW2100: loading microcode failed\n")); + free(buffer); + return status; + } + + status = SoftResetAdapter(); + if (status < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to reset before firmware upload\n")); + free(buffer); + return status; + } + + code = buffer + sizeof(ipw_firmware_header); + status = LoadFirmware(code, header->main_size); + if (status < B_OK) { + TRACE_ALWAYS(("IPW2100: loading firmware failed\n")); + free(buffer); + return status; + } + + free(buffer); + return B_OK; +} + + +status_t +IPW2100::LoadMicrocode(const uint8 *buffer, uint32 size) +{ + TRACE(("IPW2100: loading microcode (%ld bytes)\n", size)); + WriteMem32(0x003000e0, 0x80000000); + WriteReg(IPW_REG_RESET, 0); + + WriteMem16(0x00220000, 0x0703); + WriteMem16(0x00220000, 0x0707); + + WriteMem8(0x00210014, 0x72); + WriteMem8(0x00210014, 0x72); + + WriteMem8(0x00210000, 0x40); + WriteMem8(0x00210000, 0x00); + WriteMem8(0x00210000, 0x40); + + uint32 dataLeft = size; + while (dataLeft > 0) { + WriteMem8(0x00210010, *buffer++); + WriteMem8(0x00210010, *buffer++); + dataLeft -= 2; + } + + WriteMem8(0x00210000, 0x00); + WriteMem8(0x00210000, 0x00); + WriteMem8(0x00210000, 0x80); + + WriteMem16(0x00220000, 0x0703); + WriteMem16(0x00220000, 0x0707); + + WriteMem8(0x00210014, 0x72); + WriteMem8(0x00210014, 0x72); + + WriteMem8(0x00210000, 0x00); + WriteMem8(0x00210000, 0x80); + + uint32 tries; + for (tries = 10; tries > 0; tries--) { + if (ReadMem8(0x00210000) & 1) + break; + snooze(10); + } + + if (tries == 0) { + TRACE_ALWAYS(("IPW2100: timeout waiting for microcode to initialize\n")); + return B_ERROR; + } + + symbol_alive_response response; + for (tries = 30; tries > 0; tries--) { + for (uint32 i = 0; i < sizeof(symbol_alive_response) / 2; i++) + ((uint16 *)&response)[i] = ReadMem16(0x00210004); + + TRACE(("IPW2100: symbol alive response:\n")); + TRACE((" command_id: 0x%02x\n", response.command_id)); + TRACE((" sequence_number: 0x%02x\n", response.sequence_number)); + TRACE((" microcode_revision: 0x%02x\n", response.microcode_revision)); + TRACE((" eeprom_valid: 0x%02x\n", response.eeprom_valid)); + TRACE((" valid_flags: 0x%04x\n", response.valid_flags)); + TRACE((" ieee_address: %02x:%02x:%02x:%02x:%02x:%02x\n", response.ieee_address[0], response.ieee_address[1], response.ieee_address[2], response.ieee_address[3], response.ieee_address[4], response.ieee_address[5])); + TRACE((" flags: 0x%04x\n", response.flags)); + TRACE((" pcb_revision: 0x%04x\n", response.pcb_revision)); + TRACE((" clock_settle_time: %d\n", response.clock_settle_time)); + TRACE((" powerup_settle_time: %d\n", response.powerup_settle_time)); + TRACE((" hop_settle_time: %d\n", response.hop_settle_time)); + TRACE((" date: %d-%d-%d\n", response.date[2], response.date[0], response.date[1])); + TRACE((" time: %d:%d\n", response.time[0], response.time[1])); + TRACE((" microcode_valid: 0x%02x\n", response.microcode_valid)); + if (response.command_id == 0x01 && response.microcode_valid == 0x01) + break; + + snooze(10); + } + + if (tries == 0) { + TRACE_ALWAYS(("IPW2100: microcode did not validate itself\n")); + return B_ERROR; + } + + TRACE(("IPW2100: microcode loaded and initialized with %ld tries left\n", tries)); + WriteMem32(0x003000e0, 0); + return B_OK; +} + + +status_t +IPW2100::LoadFirmware(const uint8 *buffer, uint32 size) +{ + TRACE(("IPW2100: loading firmware (%ld bytes)\n", size)); + for (uint32 i = 0; i < size;) { + uint32 address = buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24; + uint16 length = buffer[4] | buffer[5] << 8; + i += 6 + length; + buffer += 6; + + while (length > 0) { + WriteMem32(address, *(uint32 *)buffer); + length -= 4; + address += 4; + buffer += 4; + } + } + + TRACE(("IPW2100: firmware loaded\n")); + return B_OK; +} + + +status_t +IPW2100::ClearSharedMemory() +{ + for (uint32 i = 0; i < IPW_SHARED_MEMORY_LENGTH_0; i++) + WriteMem8(IPW_SHARED_MEMORY_BASE_0 + i, 0); + for (uint32 i = 0; i < IPW_SHARED_MEMORY_LENGTH_1; i++) + WriteMem8(IPW_SHARED_MEMORY_BASE_1 + i, 0); + for (uint32 i = 0; i < IPW_SHARED_MEMORY_LENGTH_2; i++) + WriteMem8(IPW_SHARED_MEMORY_BASE_2 + i, 0); + for (uint32 i = 0; i < IPW_SHARED_MEMORY_LENGTH_3; i++) + WriteMem8(IPW_SHARED_MEMORY_BASE_3 + i, 0); + for (uint32 i = 0; i < IPW_INTERRUPT_MEMORY_LENGTH; i++) + WriteMem8(IPW_INTERRUPT_MEMORY_BASE + i, 0); + return B_OK; +} + + +status_t +IPW2100::StartFirmware() +{ + TRACE(("IPW2100: initializing firmware\n")); + // grant firmware access + WriteReg(IPW_REG_IO, IPW_IO_GPIO1_ENABLE | IPW_IO_GPIO3_MASK + | IPW_IO_LED_OFF); + + SetInterrupts(IPW_INTERRUPT_FW_INIT_DONE); + + // start the firmware + WriteReg(IPW_REG_RESET, 0); + + // wait for the fw init done interrupt + status_t result = acquire_sem_etc(fFWInitDoneSem, 1, B_RELATIVE_TIMEOUT, 1000000); + if (result < B_OK) { + TRACE_ALWAYS(("IPW2100: firmware initialization timed out\n")); + return result; + } + + // discard fatal errors while init + uint32 interrupts = ReadReg(IPW_REG_INTERRUPT); + if (interrupts & (IPW_INTERRUPT_FATAL_ERROR | IPW_INTERRUPT_PARITY_ERROR)) { + TRACE(("IPW2100: discarding error interrupt while firmware initialization\n")); + WriteReg(IPW_REG_INTERRUPT, IPW_INTERRUPT_FATAL_ERROR + | IPW_INTERRUPT_PARITY_ERROR); + } + + TRACE(("IPW2100: firmware initialized\n")); + + WriteReg(IPW_REG_IO, ReadReg(IPW_REG_IO) | IPW_IO_GPIO1_MASK + | IPW_IO_GPIO3_MASK); + + // initialize ordinal table values + fOrdinalTable1Base = ReadReg(IPW_ORDINAL_TABLE_1_ADDRESS); + fOrdinalTable2Base = ReadReg(IPW_ORDINAL_TABLE_2_ADDRESS); + fOrdinalTable1Length = ReadMem32(fOrdinalTable1Base); + fOrdinalTable2Length = ReadMem32(fOrdinalTable2Base) & 0x0000ffff; + + TRACE(("IPW2100: ordinal tables initialized\n")); + TRACE((" table 1 base: 0x%08lx\n", fOrdinalTable1Base)); + TRACE((" table 1 length: %ld\n", fOrdinalTable1Length)); + TRACE((" table 2 base: 0x%08lx\n", fOrdinalTable2Base)); + TRACE((" table 2 length: %ld\n", fOrdinalTable2Length)); + + uint32 value = IPW_FIRMWARE_LOCK_NONE; + size_t length = sizeof(value); + result = WriteOrdinal(IPW_ORD_FIRMWARE_DB_LOCK, (uint8 *)&value, &length); + if (result < B_OK || length != sizeof(value)) { + TRACE_ALWAYS(("IPW2100: unable to set ordinal lock\n")); + return result; + } + + return B_OK; +} + + +status_t +IPW2100::EtherInit() +{ + if (SendCommand(IPW_COMMAND_SET_MODE, (uint8 *)&fMode, sizeof(fMode)) < B_OK) { + TRACE_ALWAYS(("IPW2100: failed to set the desired operation mode\n")); + return B_ERROR; + } + + if (fMode == IPW_MODE_MONITOR) { + // only initialize the bare minimum in monitor mode + SendCommand(IPW_COMMAND_SET_CHANNEL, (uint8 *)&fChannel, sizeof(fChannel)); + EnableAdapter(); + return B_OK; + } + + uint32 beacon = IPW_DEFAULT_BEACON_INTERVAL; + uint32 power = IPW_DEFAULT_TX_POWER; + + ipw_configuration configuration; + configuration.flags = IPW_CONFIG_802_1X_ENABLE | IPW_CONFIG_BSS_MASK + | IPW_CONFIG_IBSS_MASK; + if (fMode == IPW_MODE_IBSS) + configuration.flags |= IPW_CONFIG_IBSS_AUTO_START; + configuration.bss_channel_mask = IPW_BSS_CHANNEL_MASK; + configuration.ibss_channel_mask = IPW_IBSS_CHANNEL_MASK; + + ipw_security security; + security.ciphers = fCiphers; + security.auth_mode = fAuthMode; + + if (SendCommand(IPW_COMMAND_SET_MAC_ADDRESS, fMACAddress, sizeof(fMACAddress)) < B_OK + || (fMode == IPW_MODE_IBSS + && (SendCommand(IPW_COMMAND_SET_CHANNEL, (uint8 *)&fChannel, sizeof(fChannel)) < B_OK + || SendCommand(IPW_COMMAND_SET_BEACON_INTERVAL, (uint8 *)&beacon, sizeof(beacon)) < B_OK + || SendCommand(IPW_COMMAND_SET_TX_POWER_INDEX, (uint8 *)&power, sizeof(power)) < B_OK)) + || SendCommand(IPW_COMMAND_SET_CONFIGURATION, (uint8 *)&configuration, sizeof(configuration)) < B_OK + || SendCommand(IPW_COMMAND_SET_BASIC_TX_RATES, (uint8 *)&fTXRates, sizeof(fTXRates)) < B_OK + || SendCommand(IPW_COMMAND_SET_TX_RATES, (uint8 *)&fTXRates, sizeof(fTXRates)) < B_OK + || SendCommand(IPW_COMMAND_SET_MSDU_TX_RATES, (uint8 *)&fTXRates, sizeof(fTXRates)) < B_OK + || SendCommand(IPW_COMMAND_SET_POWER_MODE, (uint8 *)&fPowerMode, sizeof(fPowerMode)) < B_OK + || SendCommand(IPW_COMMAND_SET_RTS_THRESHOLD, (uint8 *)&fRTSThreshold, sizeof(fRTSThreshold)) < B_OK + || SendCommand(IPW_COMMAND_SET_MANDATORY_BSSID, NULL, 0) < B_OK + || SendCommand(IPW_COMMAND_SET_ESSID, (uint8 *)fESSID, (fESSID ? strlen(fESSID) + 1 : 0)) < B_OK + || SendCommand(IPW_COMMAND_SET_SECURITY_INFO, (uint8 *)&security, sizeof(ipw_security)) < B_OK + || (fCiphers != IPW_CIPHER_NONE + && (SendCommand(IPW_COMMAND_SET_WEP_KEY, (uint8 *)&fWEPKeys[0], sizeof(ipw_wep_key)) < B_OK + || SendCommand(IPW_COMMAND_SET_WEP_KEY, (uint8 *)&fWEPKeys[1], sizeof(ipw_wep_key)) < B_OK + || SendCommand(IPW_COMMAND_SET_WEP_KEY, (uint8 *)&fWEPKeys[2], sizeof(ipw_wep_key)) < B_OK + || SendCommand(IPW_COMMAND_SET_WEP_KEY, (uint8 *)&fWEPKeys[3], sizeof(ipw_wep_key)) < B_OK + || SendCommand(IPW_COMMAND_SET_WEP_KEY_INDEX, (uint8 *)&fWEPKeyIndex, sizeof(fWEPKeyIndex)) < B_OK)) + || SendCommand(IPW_COMMAND_SET_WEP_FLAGS, (uint8 *)&fWEPFlags, sizeof(fWEPFlags)) < B_OK) { + TRACE_ALWAYS(("IPW2100: setting up the adapter configuration failed\n")); + return B_ERROR; + } + + status_t result = EnableAdapter(); + if (result < B_OK) { + TRACE_ALWAYS(("IPW2100: unable to enable the adapter\n")); + return result; + } + + result = ScanForNetworks(); + if (result < B_OK) { + TRACE_ALWAYS(("IPW2100: scan for networks failed\n")); + return result; + } + + return B_OK; +} + + +status_t +IPW2100::DisableAdapter() +{ + status_t result = SendCommand(IPW_COMMAND_DISABLE, NULL, 0); + if (result < B_OK) { + TRACE_ALWAYS(("IPW2100: the disable command failed\n")); + return result; + } + + // wait for the disabled state change + result = acquire_sem_etc(fAdapterDisabledSem, 1, B_RELATIVE_TIMEOUT, 1000000); + if (result < B_OK) { + TRACE_ALWAYS(("IPW2100: timeout when waiting for adapter to become disabled\n")); + return result; + } + + TRACE(("IPW2100: adapter disabled\n")); + return B_OK; +} + + +status_t +IPW2100::EnableAdapter() +{ + status_t result = SendCommand(IPW_COMMAND_ENABLE, NULL, 0); + if (result < B_OK) { + TRACE_ALWAYS(("IPW2100: the enable command failed\n")); + return result; + } + + TRACE(("IPW2100: adapter enabled\n")); + return B_OK; +} + + +status_t +IPW2100::ScanForNetworks() +{ + TRACE(("IPW2100: scanning for networks\n")); + + ipw_scan_options options; + options.flags = fScanOptions; + options.channels = fChannel; + SendCommand(IPW_COMMAND_SET_SCAN_OPTIONS, (uint8 *)&options, sizeof(options)); + + uint32 parameters = 0; + SendCommand(IPW_COMMAND_BROADCAST_SCAN, (uint8 *)¶meters, sizeof(parameters)); + return B_OK; +} + + +status_t +IPW2100::DisableRadio() +{ + SendCommand(IPW_COMMAND_DISABLE_PHY, NULL, 0); + + uint32 tries; + for (tries = 1000; tries > 0; tries--) { + if ((ReadMem32(0x00220000) & 0x00000008) > 0 + && (ReadMem32(0x00300004) & 0x00000001) > 0) { + TRACE(("IPW2100: radio disabled\n")); + break; + } + + snooze(10000); + } + + if (tries == 0) { + TRACE_ALWAYS(("IPW2100: timeout waiting for radio to get disabled\n")); + return B_TIMED_OUT; + } + + return B_OK; +} + + +status_t +IPW2100::PowerDown() +{ + SendCommand(IPW_COMMAND_PREPARE_POWER_DOWN, NULL, 0); + WriteReg(IPW_REG_IO, IPW_IO_GPIO1_ENABLE | IPW_IO_GPIO3_MASK + | IPW_IO_LED_OFF); + StopMaster(); + WriteReg(IPW_REG_RESET, IPW_RESET_SW_RESET); + return B_OK; +} diff --git a/src/add-ons/kernel/drivers/network/ipw2100/ipw2100.h b/src/add-ons/kernel/drivers/network/ipw2100/ipw2100.h new file mode 100644 index 0000000000..f7ca5ee13f --- /dev/null +++ b/src/add-ons/kernel/drivers/network/ipw2100/ipw2100.h @@ -0,0 +1,159 @@ +/* + Driver for Intel(R) PRO/Wireless 2100 devices. + Copyright (C) 2006 Michael Lotz + Released under the terms of the MIT license. +*/ + +#ifndef _IPW2100_H_ +#define _IPW2100_H_ + +#include "ipw2100_hw.h" + +class IPW2100 { +public: + IPW2100(int32 deviceID, pci_info *info, + pci_module_info *module); + ~IPW2100(); + + status_t InitCheck(); + + int32 DeviceID() { return fDeviceID; }; + + status_t Open(uint32 flags); + status_t Close(); + status_t Free(); + + status_t Read(off_t position, void *buffer, + size_t *numBytes); + status_t Write(off_t position, const void *buffer, + size_t *numBytes); + status_t Control(uint32 op, void *args, size_t length); + +private: +static int32 InterruptHandler(void *data); + int32 Interrupt(); + + void HandleTXInterrupt(); + void HandleRXInterrupt(); + + uint32 ReadReg(uint32 offset); + void WriteReg(uint32 offset, uint32 value); + + uint8 ReadMem8(uint32 address); + uint16 ReadMem16(uint32 address); + uint32 ReadMem32(uint32 address); + + void WriteMem8(uint32 address, uint8 value); + void WriteMem16(uint32 address, uint16 value); + void WriteMem32(uint32 address, uint32 value); + + status_t ReadOrdinal(uint32 index, uint8 *buffer, + size_t *size); + status_t WriteOrdinal(uint32 index, const uint8 *buffer, + size_t *size); + + status_t SendCommand(uint32 commandId, const uint8 *buffer, + size_t length); + + area_id MapPhysicalMemory(const char *name, + uint32 physicalAddress, void **logicalAddress, + size_t size); + area_id AllocateContiguous(const char *name, + void **logicalAddress, uint32 *physicalAddress, + size_t size); + + void SetInterrupts(uint32 interrupts); + uint32 Interrupts(); + + status_t CheckAdapterAccess(); + status_t SoftResetAdapter(); + status_t ResetAdapter(); + status_t StopMaster(); + status_t SetupBuffers(); + status_t ReadMACAddress(); + status_t LoadMicrocodeAndFirmware(); + status_t LoadMicrocode(const uint8 *buffer, uint32 size); + status_t LoadFirmware(const uint8 *buffer, uint32 size); + status_t ClearSharedMemory(); + status_t StartFirmware(); + status_t EtherInit(); + status_t DisableAdapter(); + status_t EnableAdapter(); + status_t ScanForNetworks(); + status_t DisableRadio(); + status_t PowerDown(); + + status_t fStatus; + bool fClosing; + + sem_id fFWInitDoneSem; + sem_id fAdapterDisabledSem; + sem_id fTXTransferSem; + sem_id fRXTransferSem; + sem_id fCommandDoneSem; + + int32 fDeviceID; + pci_info *fPCIInfo; + pci_module_info *fPCIModule; + + uint32 fRegisterBase; + uint8 *fRegisters; + area_id fRegistersArea; + + area_id fTXRingArea; + ipw_bd *fTXRingLog; + uint32 fTXRingPhy; + + area_id fTXPacketsArea; + ipw_tx *fTXPacketsLog; + uint32 fTXPacketsPhy; + + area_id fRXRingArea; + ipw_bd *fRXRingLog; + uint32 fRXRingPhy; + + area_id fRXPacketsArea; + ipw_rx *fRXPacketsLog; + uint32 fRXPacketsPhy; + + area_id fStatusRingArea; + ipw_status *fStatusRingLog; + uint32 fStatusRingPhy; + + area_id fCommandArea; + ipw_command *fCommandLog; + uint32 fCommandPhy; + + uint32 fInterruptMask; + uint32 fTXPosition; + uint32 fRXPosition; + bool fAssociated; + + uint32 fReadIndex; + bool *fReadQueue; + + uint32 fOrdinalTable1Base; + uint32 fOrdinalTable2Base; + uint32 fOrdinalTable1Length; + uint32 fOrdinalTable2Length; + + // settings + int32 fInterruptLine; + uint8 fMACAddress[6]; + uint32 fMode; + uint32 fChannel; + uint32 fTXRates; + uint32 fPowerMode; + uint32 fRTSThreshold; + uint32 fScanOptions; + uint8 fAuthMode; + uint32 fCiphers; + char *fESSID; + ipw_wep_key fWEPKeys[4]; + uint32 fWEPKeyIndex; + uint32 fWEPFlags; + bool fDumpTXPackets; + bool fDumpRXPackets; +}; + +#endif // _IPW2100_H_ diff --git a/src/add-ons/kernel/drivers/network/ipw2100/ipw2100_hw.h b/src/add-ons/kernel/drivers/network/ipw2100/ipw2100_hw.h new file mode 100644 index 0000000000..231457b241 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/ipw2100/ipw2100_hw.h @@ -0,0 +1,343 @@ +/* + Driver for Intel(R) PRO/Wireless 2100 devices. + Copyright (C) 2006 Michael Lotz + Released under the terms of the MIT license. +*/ + +#ifndef _IPW2100_HW_H_ +#define _IPW2100_HW_H_ + +// buffer descriptor +struct ipw_bd { + uint32 physical_address; + uint32 length; + uint8 flags; + uint8 fragment_count; + uint8 reserved[6]; +} _PACKED; + +#define IPW_BD_FLAG_TYPE_802_3 0x00 +#define IPW_BD_FLAG_NOT_LAST_FRAGMENT 0x01 +#define IPW_BD_FLAG_COMMAND 0x02 +#define IPW_BD_FLAG_TYPE_802_11 0x04 +#define IPW_BD_FLAG_INTERRUPT 0x08 + +// status descriptor +struct ipw_status { + uint32 length; + uint16 code; + uint8 flags; + uint8 rssi; // received signal strength indicator +} _PACKED; + +#define IPW_STATUS_CODE_COMMAND 0 +#define IPW_STATUS_CODE_STATUS 1 +#define IPW_STATUS_CODE_DATA_802_11 2 +#define IPW_STATUS_CODE_DATA_802_3 3 +#define IPW_STATUS_CODE_NOTIFICATION 4 +#define IPW_STATUS_CODE_MASK 0x0f + +#define IPW_STATUS_FLAG_DECRYPTED 0x01 +#define IPW_STATUS_FLAG_WEP_ENCRYPTED 0x02 + +// adapter states +#define IPW_STATE_INITIALIZED 0x00000001 +#define IPW_STATE_COUNTRY_FOUND 0x00000002 +#define IPW_STATE_ASSOCIATED 0x00000004 +#define IPW_STATE_ASSOCIATION_LOST 0x00000008 +#define IPW_STATE_ASSOCIATION_CHANGED 0x00000010 +#define IPW_STATE_SCAN_COMPLETE 0x00000020 +#define IPW_STATE_PSP_ENTERED 0x00000040 +#define IPW_STATE_PSP_LEFT 0x00000080 +#define IPW_STATE_RF_KILL 0x00000100 +#define IPW_STATE_DISABLED 0x00000200 +#define IPW_STATE_POWER_DOWN 0x00000400 +#define IPW_STATE_SCANNING 0x00000800 + +// data descriptor +struct ipw_data { + uint32 command; + uint32 unused; + uint8 encrypted; + uint8 needs_encryption; + uint8 key_index; + uint8 key_size; + uint8 key[16]; + uint8 reserved[10]; + uint8 source_address[6]; + uint8 dest_address[6]; + uint16 fragment_size; +} _PACKED; + +// data holders +struct ipw_tx { + uint8 data[2500]; +} _PACKED; + +struct ipw_rx { + uint8 data[2500]; +} _PACKED; + +// command descriptor +struct ipw_command { + uint32 command; + uint64 unused; + uint32 length; + uint8 data[400]; + uint32 status; + uint8 reserved[68]; +} _PACKED; + +#define IPW_COMMAND_ENABLE 2 +#define IPW_COMMAND_SET_CONFIGURATION 6 +#define IPW_COMMAND_SET_ESSID 8 +#define IPW_COMMAND_SET_MANDATORY_BSSID 9 +#define IPW_COMMAND_SET_MAC_ADDRESS 11 +#define IPW_COMMAND_SET_MODE 12 +#define IPW_COMMAND_SET_CHANNEL 14 +#define IPW_COMMAND_SET_RTS_THRESHOLD 15 +#define IPW_COMMAND_SET_FRAG_THRESHOLD 16 +#define IPW_COMMAND_SET_POWER_MODE 17 +#define IPW_COMMAND_SET_TX_RATES 18 +#define IPW_COMMAND_SET_BASIC_TX_RATES 19 +#define IPW_COMMAND_SET_WEP_KEY 20 +#define IPW_COMMAND_SET_WEP_KEY_INDEX 25 +#define IPW_COMMAND_SET_WEP_FLAGS 26 +#define IPW_COMMAND_ADD_MULTICAST 27 +#define IPW_COMMAND_SET_BEACON_INTERVAL 29 +#define IPW_COMMAND_SEND_DATA 33 +#define IPW_COMMAND_SET_TX_POWER_INDEX 36 +#define IPW_COMMAND_BROADCAST_SCAN 43 +#define IPW_COMMAND_DISABLE 44 +#define IPW_COMMAND_SET_DESIRED_BSSID 45 +#define IPW_COMMAND_SET_SCAN_OPTIONS 46 +#define IPW_COMMAND_PREPARE_POWER_DOWN 58 +#define IPW_COMMAND_DISABLE_PHY 61 +#define IPW_COMMAND_SET_MSDU_TX_RATES 62 +#define IPW_COMMAND_SET_SECURITY_INFO 67 +#define IPW_COMMAND_SET_WPA_IE 69 + +// values for IPW_COMMAND_SET_POWER_MODE +#define IPW_POWER_MODE_CAM 0 +#define IPW_POWER_MODE_AUTOMATIC 6 + +// values for IPW_COMMAND_SET_MODE +#define IPW_MODE_BSS 1 +#define IPW_MODE_MONITOR 2 +#define IPW_MODE_IBSS 3 + +// values for IPW_COMMAND_SET_WEP_FLAGS +#define IPW_WEP_FLAGS_HW_DECRYPT 0x00000001 +#define IPW_WEP_FLAGS_HW_ENCRYPT 0x00000008 + +// structure for IPW_COMMAND_SET_WEP_KEY +struct ipw_wep_key { + uint8 index; + uint8 length; + uint8 key[13]; +} _PACKED; + +// structure for IPW_COMMAND_SET_SECURITY_INFO +struct ipw_security { + uint32 ciphers; + uint16 reserved1; + uint8 auth_mode; + uint16 reserved2; +} _PACKED; + +#define IPW_CIPHER_NONE 0x00000001 +#define IPW_CIPHER_WEP40 0x00000002 +#define IPW_CIPHER_TKIP 0x00000004 +#define IPW_CIPHER_CCMP 0x00000010 +#define IPW_CIPHER_WEP104 0x00000020 +#define IPW_CIPHER_CKIP 0x00000040 + +#define IPW_AUTH_MODE_OPEN 0 +#define IPW_AUTH_MODE_SHARED 1 + +// structure for IPW_COMMAND_SET_SCAN_OPTIONS +struct ipw_scan_options { + uint32 flags; + uint32 channels; +} _PACKED; + +#define IPW_SCAN_DO_NOT_ASSOCIATE 0x00000001 +#define IPW_SCAN_MIXED_CELL 0x00000002 +#define IPW_SCAN_PASSIVE 0x00000008 + +// structure for IPW_COMMAND_SET_CONFIGURATION +struct ipw_configuration { + uint32 flags; + uint32 bss_channel_mask; + uint32 ibss_channel_mask; +} _PACKED; + +#define IPW_CONFIG_PROMISCUOUS 0x00000004 +#define IPW_CONFIG_PREAMBLE_AUTO 0x00000010 +#define IPW_CONFIG_IBSS_AUTO_START 0x00000020 +#define IPW_CONFIG_802_1X_ENABLE 0x00004000 +#define IPW_CONFIG_BSS_MASK 0x00008000 +#define IPW_CONFIG_IBSS_MASK 0x00010000 + +// default values +#define IPW_BSS_CHANNEL_MASK 0x000003fff +#define IPW_IBSS_CHANNEL_MASK 0x0000087ff +#define IPW_DEFAULT_BEACON_INTERVAL 100 +#define IPW_DEFAULT_TX_POWER 32 + +// structure for IPW_COMMAND_SET_WPA_IE +struct wpa_ie { + uint8 id; + uint8 length; + uint8 oui[3]; + uint8 oui_type; + uint16 version; + uint32 multicast_cipher; + uint16 unicast_cipher_count; + uint32 unicast_ciphers[8]; + uint16 auth_selector_count; + uint32 auth_selectors[8]; + uint16 capabilities; + uint16 pmkid_count; + uint16 pmkids[8]; +} _PACKED; + +struct ipw_wpa_ie { + uint16 mask; + uint16 capability_info; + uint16 lintval; + uint8 bssid[6]; + uint32 length; + wpa_ie wpa; +} _PACKED; + +// bitmask for IPW_COMMAND_SET_[BASIC|MSDU]_TX_RATES +#define IPW_TX_RATE_1_MBIT 0x00000001 +#define IPW_TX_RATE_2_MBIT 0x00000002 +#define IPW_TX_RATE_5_5_MBIT 0x00000004 +#define IPW_TX_RATE_11_MBIT 0x00000008 +#define IPW_TX_RATE_ALL 0x0000000f + +// values for IPW_COMMAND_SET_RTS_THRESHOLD +#define IPW_RTS_THRESHOLD_MIN 1 +#define IPW_RTS_THRESHOLD_MAX 2304 +#define IPW_RTS_THRESHOLD_DEFAULT 1000 + +// buffers +#define IPW_TX_BUFFER_COUNT 128 +#define IPW_TX_BUFFER_SIZE (IPW_TX_BUFFER_COUNT * sizeof(ipw_bd)) +#define IPW_TX_PACKET_SIZE (IPW_TX_BUFFER_COUNT * sizeof(ipw_tx)) +#define IPW_RX_BUFFER_COUNT 128 +#define IPW_RX_BUFFER_SIZE (IPW_RX_BUFFER_COUNT * sizeof(ipw_bd)) +#define IPW_RX_PACKET_SIZE (IPW_RX_BUFFER_COUNT * sizeof(ipw_rx)) +#define IPW_STATUS_BUFFER_COUNT IPW_RX_BUFFER_COUNT +#define IPW_STATUS_BUFFER_SIZE (IPW_STATUS_BUFFER_COUNT * sizeof(ipw_status)) + +// registers +#define IPW_REG_INTERRUPT 0x0008 +#define IPW_REG_INTERRUPT_MASK 0x000c +#define IPW_REG_INDIRECT_ADDRESS 0x0010 +#define IPW_REG_INDIRECT_DATA 0x0014 +#define IPW_REG_RESET 0x0020 +#define IPW_REG_CONTROL 0x0024 +#define IPW_REG_IO 0x0030 +#define IPW_REG_DEBUG 0x0090 +#define IPW_REG_TX_BASE 0x0200 +#define IPW_REG_TX_SIZE 0x0204 +#define IPW_REG_TX_READ 0x0280 +#define IPW_REG_TX_WRITE 0x0f80 +#define IPW_REG_RX_BASE 0x0240 +#define IPW_REG_RX_SIZE 0x0248 +#define IPW_REG_RX_READ 0x02a0 +#define IPW_REG_RX_WRITE 0x0fa0 +#define IPW_REG_STATUS_BASE 0x0244 + +#define IPW_INDIRECT_ADDRESS_MASK 0x00fffffc + +// flags for IPW_REG_INTERRUPT(_MASK) +#define IPW_INTERRUPT_TX_TRANSFER 0x00000001 +#define IPW_INTERRUPT_RX_TRANSFER 0x00000002 +#define IPW_INTERRUPT_STATUS_CHANGE 0x00000010 +#define IPW_INTERRUPT_COMMAND_DONE 0x00010000 +#define IPW_INTERRUPT_FW_INIT_DONE 0x01000000 +#define IPW_INTERRUPT_FATAL_ERROR 0x40000000 +#define IPW_INTERRUPT_PARITY_ERROR 0x80000000 + +// flags for IPW_REG_RESET +#define IPW_RESET_PRINCETON_RESET 0x00000001 +#define IPW_RESET_SW_RESET 0x00000080 +#define IPW_RESET_MASTER_DISABLED 0x00000100 +#define IPW_RESET_STOP_MASTER 0x00000200 + +// flags for IPW_REG_CONTROL +#define IPW_CONTROL_CLOCK_READY 0x00000001 +#define IPW_CONTROL_ALLOW_STANDBY 0x00000002 +#define IPW_CONTROL_INIT_COMPLETE 0x00000004 + +// flags for IPW_REG_IO +#define IPW_IO_GPIO1_ENABLE 0x00000008 +#define IPW_IO_GPIO1_MASK 0x0000000c +#define IPW_IO_GPIO3_MASK 0x000000c0 +#define IPW_IO_LED_OFF 0x00002000 +#define IPW_IO_RADIO_DISABLED 0x00010000 + +// the value at the debug register base is always +// the magic data value. used to verify register access. +#define IPW_DEBUG_DATA 0xd55555d5 + +// shared memory +#define IPW_SHARED_MEMORY_BASE_0 0x0002f200 +#define IPW_SHARED_MEMORY_LENGTH_0 784 +#define IPW_SHARED_MEMORY_BASE_1 0x0002f610 +#define IPW_SHARED_MEMORY_LENGTH_1 32 +#define IPW_SHARED_MEMORY_BASE_2 0x0002fa00 +#define IPW_SHARED_MEMORY_LENGTH_2 32 +#define IPW_SHARED_MEMORY_BASE_3 0x0002fc00 +#define IPW_SHARED_MEMORY_LENGTH_3 16 +#define IPW_INTERRUPT_MEMORY_BASE 0x0002ff80 +#define IPW_INTERRUPT_MEMORY_LENGTH 128 + +// ordinal tables +#define IPW_ORDINAL_TABLE_1_ADDRESS 0x00000380 +#define IPW_ORDINAL_TABLE_2_ADDRESS 0x00000384 +#define IPW_ORDINAL_TABLE_1_START 1 +#define IPW_ORDINAL_TABLE_2_START 1000 + +enum ipw_ordinal_table_1 { + IPW_ORD_FIRMWARE_DB_LOCK = 120, + IPW_ORD_CARD_DISABLED = 157, + IPW_ORD_GET_AP_BSSID = 1014, +}; + +// firmware db lock +#define IPW_FIRMWARE_LOCK_NONE 0 +#define IPW_FIRMWARE_LOCK_DRIVER 1 +#define IPW_FIRMWARE_LOCK_FIRMWARE 2 + +// firmware binary image header +struct ipw_firmware_header { + uint16 version; + uint16 mode; + uint32 main_size; + uint32 ucode_size; +} _PACKED; + +// symbol alive response to check microcode +struct symbol_alive_response { + uint8 command_id; + uint8 sequence_number; + uint8 microcode_revision; + uint8 eeprom_valid; + uint16 valid_flags; + uint8 ieee_address[6]; + uint16 flags; + uint16 pcb_revision; + uint16 clock_settle_time; + uint16 powerup_settle_time; + uint16 hop_settle_time; + uint8 date[3]; + uint8 time[2]; + uint8 microcode_valid; +} _PACKED; + +#endif // _IPW2100_HW_H_ diff --git a/src/add-ons/kernel/drivers/network/ipw2100/kernel_cpp.c b/src/add-ons/kernel/drivers/network/ipw2100/kernel_cpp.c new file mode 100644 index 0000000000..670c98f6f9 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/ipw2100/kernel_cpp.c @@ -0,0 +1,4 @@ +void +__throw() +{ +} diff --git a/src/add-ons/kernel/drivers/network/ipw2100/kernel_cpp.h b/src/add-ons/kernel/drivers/network/ipw2100/kernel_cpp.h new file mode 100644 index 0000000000..2b94d5b126 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/ipw2100/kernel_cpp.h @@ -0,0 +1,39 @@ +#ifndef _KERNEL_CPP_H_ +#define _KERNEL_CPP_H_ + +#include + +inline void * +operator new(size_t size) +{ + return malloc(size); +} + + +inline void * +operator new[](size_t size) +{ + return malloc(size); +} + + +inline void +operator delete(void *pointer) +{ + free(pointer); +} + + +inline void +operator delete[](void *pointer) +{ + free(pointer); +} + + +inline void +terminate(void) +{ +} + +#endif // _KERNEL_CPP_H_