alistair23-linux/drivers/staging/winbond/wbusb.c
Johannes Berg 7bb4568372 mac80211: make tx() operation return void
The return value of the tx operation is commonly
misused by drivers, leading to errors. All drivers
will drop frames if they fail to TX the frame, and
they must also properly manage the queues (if they
didn't, mac80211 would already warn).

Removing the ability for drivers to return a BUSY
value also allows significant cleanups of the TX
TX handling code in mac80211.

Note that this also fixes a bug in ath9k_htc, the
old "return -1" there was wrong.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Tested-by: Sedat Dilek <sedat.dilek@googlemail.com> [ath5k]
Acked-by: Gertjan van Wingerde <gwingerde@gmail.com> [rt2x00]
Acked-by: Larry Finger <Larry.Finger@lwfinger.net> [b43, rtl8187, rtlwifi]
Acked-by: Luciano Coelho <coelho@ti.com> [wl12xx]
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2011-02-25 15:32:34 -05:00

879 lines
23 KiB
C

/*
* Copyright 2008 Pavel Machek <pavel@ucw.cz>
*
* Distribute under GPLv2.
*
* The original driver was written by:
* Jeff Lee <YY_Lee@issc.com.tw>
*
* and was adapted to the 2.6 kernel by:
* Costantino Leandro (Rxart Desktop) <le_costantino@pixartargentina.com.ar>
*/
#include <net/mac80211.h>
#include <linux/usb.h>
#include "core.h"
#include "mds_f.h"
#include "mto.h"
#include "wbhal.h"
#include "wb35reg_f.h"
#include "wb35tx_f.h"
#include "wb35rx_f.h"
MODULE_DESCRIPTION("IS89C35 802.11bg WLAN USB Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");
static const struct usb_device_id wb35_table[] __devinitconst = {
{ USB_DEVICE(0x0416, 0x0035) },
{ USB_DEVICE(0x18E8, 0x6201) },
{ USB_DEVICE(0x18E8, 0x6206) },
{ USB_DEVICE(0x18E8, 0x6217) },
{ USB_DEVICE(0x18E8, 0x6230) },
{ USB_DEVICE(0x18E8, 0x6233) },
{ USB_DEVICE(0x1131, 0x2035) },
{ 0, }
};
MODULE_DEVICE_TABLE(usb, wb35_table);
static struct ieee80211_rate wbsoft_rates[] = {
{ .bitrate = 10, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
};
static struct ieee80211_channel wbsoft_channels[] = {
{ .center_freq = 2412 },
};
static struct ieee80211_supported_band wbsoft_band_2GHz = {
.channels = wbsoft_channels,
.n_channels = ARRAY_SIZE(wbsoft_channels),
.bitrates = wbsoft_rates,
.n_bitrates = ARRAY_SIZE(wbsoft_rates),
};
static void hal_set_beacon_period(struct hw_data *pHwData, u16 beacon_period)
{
u32 tmp;
if (pHwData->SurpriseRemove)
return;
pHwData->BeaconPeriod = beacon_period;
tmp = pHwData->BeaconPeriod << 16;
tmp |= pHwData->ProbeDelay;
Wb35Reg_Write(pHwData, 0x0848, tmp);
}
static int wbsoft_add_interface(struct ieee80211_hw *dev,
struct ieee80211_vif *vif)
{
struct wbsoft_priv *priv = dev->priv;
hal_set_beacon_period(&priv->sHwData, vif->bss_conf.beacon_int);
return 0;
}
static void wbsoft_remove_interface(struct ieee80211_hw *dev,
struct ieee80211_vif *vif)
{
printk("wbsoft_remove interface called\n");
}
static void wbsoft_stop(struct ieee80211_hw *hw)
{
printk(KERN_INFO "%s called\n", __func__);
}
static int wbsoft_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats)
{
printk(KERN_INFO "%s called\n", __func__);
return 0;
}
static u64 wbsoft_prepare_multicast(struct ieee80211_hw *hw,
struct netdev_hw_addr_list *mc_list)
{
return netdev_hw_addr_list_count(mc_list);
}
static void wbsoft_configure_filter(struct ieee80211_hw *dev,
unsigned int changed_flags,
unsigned int *total_flags,
u64 multicast)
{
unsigned int new_flags;
new_flags = 0;
if (*total_flags & FIF_PROMISC_IN_BSS)
new_flags |= FIF_PROMISC_IN_BSS;
else if ((*total_flags & FIF_ALLMULTI) || (multicast > 32))
new_flags |= FIF_ALLMULTI;
dev->flags &= ~IEEE80211_HW_RX_INCLUDES_FCS;
*total_flags = new_flags;
}
static void wbsoft_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct wbsoft_priv *priv = dev->priv;
if (priv->sMlmeFrame.IsInUsed != PACKET_FREE_TO_USE) {
priv->sMlmeFrame.wNumTxMMPDUDiscarded++;
kfree_skb(skb);
return;
}
priv->sMlmeFrame.IsInUsed = PACKET_COME_FROM_MLME;
priv->sMlmeFrame.pMMPDU = skb->data;
priv->sMlmeFrame.DataType = FRAME_TYPE_802_11_MANAGEMENT;
priv->sMlmeFrame.len = skb->len;
priv->sMlmeFrame.wNumTxMMPDU++;
/*
* H/W will enter power save by set the register. S/W don't send null
* frame with PWRMgt bit enbled to enter power save now.
*/
Mds_Tx(priv);
}
static int wbsoft_start(struct ieee80211_hw *dev)
{
struct wbsoft_priv *priv = dev->priv;
priv->enabled = true;
return 0;
}
static void hal_set_radio_mode(struct hw_data *pHwData, unsigned char radio_off)
{
struct wb35_reg *reg = &pHwData->reg;
if (pHwData->SurpriseRemove)
return;
if (radio_off) { /* disable Baseband receive off */
pHwData->CurrentRadioSw = 1; /* off */
reg->M24_MacControl &= 0xffffffbf;
} else {
pHwData->CurrentRadioSw = 0; /* on */
reg->M24_MacControl |= 0x00000040;
}
Wb35Reg_Write(pHwData, 0x0824, reg->M24_MacControl);
}
static void hal_set_current_channel_ex(struct hw_data *pHwData, struct chan_info channel)
{
struct wb35_reg *reg = &pHwData->reg;
if (pHwData->SurpriseRemove)
return;
printk("Going to channel: %d/%d\n", channel.band, channel.ChanNo);
RFSynthesizer_SwitchingChannel(pHwData, channel); /* Switch channel */
pHwData->Channel = channel.ChanNo;
pHwData->band = channel.band;
pr_debug("Set channel is %d, band =%d\n", pHwData->Channel, pHwData->band);
reg->M28_MacControl &= ~0xff; /* Clean channel information field */
reg->M28_MacControl |= channel.ChanNo;
Wb35Reg_WriteWithCallbackValue(pHwData, 0x0828, reg->M28_MacControl,
(s8 *) &channel,
sizeof(struct chan_info));
}
static void hal_set_current_channel(struct hw_data *pHwData, struct chan_info channel)
{
hal_set_current_channel_ex(pHwData, channel);
}
static void hal_set_accept_broadcast(struct hw_data *pHwData, u8 enable)
{
struct wb35_reg *reg = &pHwData->reg;
if (pHwData->SurpriseRemove)
return;
reg->M00_MacControl &= ~0x02000000; /* The HW value */
if (enable)
reg->M00_MacControl |= 0x02000000; /* The HW value */
Wb35Reg_Write(pHwData, 0x0800, reg->M00_MacControl);
}
/* For wep key error detection, we need to accept broadcast packets to be received temporary. */
static void hal_set_accept_promiscuous(struct hw_data *pHwData, u8 enable)
{
struct wb35_reg *reg = &pHwData->reg;
if (pHwData->SurpriseRemove)
return;
if (enable) {
reg->M00_MacControl |= 0x00400000;
Wb35Reg_Write(pHwData, 0x0800, reg->M00_MacControl);
} else {
reg->M00_MacControl &= ~0x00400000;
Wb35Reg_Write(pHwData, 0x0800, reg->M00_MacControl);
}
}
static void hal_set_accept_multicast(struct hw_data *pHwData, u8 enable)
{
struct wb35_reg *reg = &pHwData->reg;
if (pHwData->SurpriseRemove)
return;
reg->M00_MacControl &= ~0x01000000; /* The HW value */
if (enable)
reg->M00_MacControl |= 0x01000000; /* The HW value */
Wb35Reg_Write(pHwData, 0x0800, reg->M00_MacControl);
}
static void hal_set_accept_beacon(struct hw_data *pHwData, u8 enable)
{
struct wb35_reg *reg = &pHwData->reg;
if (pHwData->SurpriseRemove)
return;
if (!enable) /* Due to SME and MLME are not suitable for 35 */
return;
reg->M00_MacControl &= ~0x04000000; /* The HW value */
if (enable)
reg->M00_MacControl |= 0x04000000; /* The HW value */
Wb35Reg_Write(pHwData, 0x0800, reg->M00_MacControl);
}
static int wbsoft_config(struct ieee80211_hw *dev, u32 changed)
{
struct wbsoft_priv *priv = dev->priv;
struct chan_info ch;
printk("wbsoft_config called\n");
/* Should use channel_num, or something, as that is already pre-translated */
ch.band = 1;
ch.ChanNo = 1;
hal_set_current_channel(&priv->sHwData, ch);
hal_set_accept_broadcast(&priv->sHwData, 1);
hal_set_accept_promiscuous(&priv->sHwData, 1);
hal_set_accept_multicast(&priv->sHwData, 1);
hal_set_accept_beacon(&priv->sHwData, 1);
hal_set_radio_mode(&priv->sHwData, 0);
return 0;
}
static u64 wbsoft_get_tsf(struct ieee80211_hw *dev)
{
printk("wbsoft_get_tsf called\n");
return 0;
}
static const struct ieee80211_ops wbsoft_ops = {
.tx = wbsoft_tx,
.start = wbsoft_start,
.stop = wbsoft_stop,
.add_interface = wbsoft_add_interface,
.remove_interface = wbsoft_remove_interface,
.config = wbsoft_config,
.prepare_multicast = wbsoft_prepare_multicast,
.configure_filter = wbsoft_configure_filter,
.get_stats = wbsoft_get_stats,
.get_tsf = wbsoft_get_tsf,
};
static void hal_set_ethernet_address(struct hw_data *pHwData, u8 *current_address)
{
u32 ltmp[2];
if (pHwData->SurpriseRemove)
return;
memcpy(pHwData->CurrentMacAddress, current_address, ETH_ALEN);
ltmp[0] = cpu_to_le32(*(u32 *) pHwData->CurrentMacAddress);
ltmp[1] = cpu_to_le32(*(u32 *) (pHwData->CurrentMacAddress + 4)) & 0xffff;
Wb35Reg_BurstWrite(pHwData, 0x03e8, ltmp, 2, AUTO_INCREMENT);
}
static void hal_get_permanent_address(struct hw_data *pHwData, u8 *pethernet_address)
{
if (pHwData->SurpriseRemove)
return;
memcpy(pethernet_address, pHwData->PermanentMacAddress, 6);
}
static void hal_stop(struct hw_data *pHwData)
{
struct wb35_reg *reg = &pHwData->reg;
pHwData->Wb35Rx.rx_halt = 1;
Wb35Rx_stop(pHwData);
pHwData->Wb35Tx.tx_halt = 1;
Wb35Tx_stop(pHwData);
reg->D00_DmaControl &= ~0xc0000000; /* Tx Off, Rx Off */
Wb35Reg_Write(pHwData, 0x0400, reg->D00_DmaControl);
}
static unsigned char hal_idle(struct hw_data *pHwData)
{
struct wb35_reg *reg = &pHwData->reg;
if (!pHwData->SurpriseRemove && reg->EP0vm_state != VM_STOP)
return false;
return true;
}
u8 hal_get_antenna_number(struct hw_data *pHwData)
{
struct wb35_reg *reg = &pHwData->reg;
if ((reg->BB2C & BIT(11)) == 0)
return 0;
else
return 1;
}
/* 0 : radio on; 1: radio off */
static u8 hal_get_hw_radio_off(struct hw_data *pHwData)
{
struct wb35_reg *reg = &pHwData->reg;
if (pHwData->SurpriseRemove)
return 1;
/* read the bit16 of register U1B0 */
Wb35Reg_Read(pHwData, 0x3b0, &reg->U1B0);
if ((reg->U1B0 & 0x00010000)) {
pHwData->CurrentRadioHw = 1;
return 1;
} else {
pHwData->CurrentRadioHw = 0;
return 0;
}
}
static u8 LED_GRAY[20] = {
0, 3, 4, 6, 8, 10, 11, 12, 13, 14, 15, 14, 13, 12, 11, 10, 8, 6, 4, 2
};
static u8 LED_GRAY2[30] = {
7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 15, 14, 13, 12, 11, 10, 9, 8
};
static void hal_led_control(unsigned long data)
{
struct wbsoft_priv *adapter = (struct wbsoft_priv *)data;
struct hw_data *pHwData = &adapter->sHwData;
struct wb35_reg *reg = &pHwData->reg;
u32 LEDSet = (pHwData->SoftwareSet & HAL_LED_SET_MASK) >> HAL_LED_SET_SHIFT;
u32 TimeInterval = 500, ltmp, ltmp2;
ltmp = 0;
if (pHwData->SurpriseRemove)
return;
if (pHwData->LED_control) {
ltmp2 = pHwData->LED_control & 0xff;
if (ltmp2 == 5) { /* 5 is WPS mode */
TimeInterval = 100;
ltmp2 = (pHwData->LED_control >> 8) & 0xff;
switch (ltmp2) {
case 1: /* [0.2 On][0.1 Off]... */
pHwData->LED_Blinking %= 3;
ltmp = 0x1010; /* Led 1 & 0 Green and Red */
if (pHwData->LED_Blinking == 2) /* Turn off */
ltmp = 0;
break;
case 2: /* [0.1 On][0.1 Off]... */
pHwData->LED_Blinking %= 2;
ltmp = 0x0010; /* Led 0 red color */
if (pHwData->LED_Blinking) /* Turn off */
ltmp = 0;
break;
case 3: /* [0.1 On][0.1 Off][0.1 On][0.1 Off][0.1 On][0.1 Off][0.1 On][0.1 Off][0.1 On][0.1 Off][0.5 Off]... */
pHwData->LED_Blinking %= 15;
ltmp = 0x0010; /* Led 0 red color */
if ((pHwData->LED_Blinking >= 9) || (pHwData->LED_Blinking % 2)) /* Turn off 0.6 sec */
ltmp = 0;
break;
case 4: /* [300 On][ off ] */
ltmp = 0x1000; /* Led 1 Green color */
if (pHwData->LED_Blinking >= 3000)
ltmp = 0; /* led maybe on after 300sec * 32bit counter overlap. */
break;
}
pHwData->LED_Blinking++;
reg->U1BC_LEDConfigure = ltmp;
if (LEDSet != 7) { /* Only 111 mode has 2 LEDs on PCB. */
reg->U1BC_LEDConfigure |= (ltmp & 0xff) << 8; /* Copy LED result to each LED control register */
reg->U1BC_LEDConfigure |= (ltmp & 0xff00) >> 8;
}
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure);
}
} else if (pHwData->CurrentRadioSw || pHwData->CurrentRadioHw) { /* If radio off */
if (reg->U1BC_LEDConfigure & 0x1010) {
reg->U1BC_LEDConfigure &= ~0x1010;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure);
}
} else {
switch (LEDSet) {
case 4: /* [100] Only 1 Led be placed on PCB and use pin 21 of IC. Use LED_0 for showing */
if (!pHwData->LED_LinkOn) { /* Blink only if not Link On */
/* Blinking if scanning is on progress */
if (pHwData->LED_Scanning) {
if (pHwData->LED_Blinking == 0) {
reg->U1BC_LEDConfigure |= 0x10;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_0 On */
pHwData->LED_Blinking = 1;
TimeInterval = 300;
} else {
reg->U1BC_LEDConfigure &= ~0x10;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_0 Off */
pHwData->LED_Blinking = 0;
TimeInterval = 300;
}
} else {
/* Turn Off LED_0 */
if (reg->U1BC_LEDConfigure & 0x10) {
reg->U1BC_LEDConfigure &= ~0x10;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_0 Off */
}
}
} else {
/* Turn On LED_0 */
if ((reg->U1BC_LEDConfigure & 0x10) == 0) {
reg->U1BC_LEDConfigure |= 0x10;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_0 Off */
}
}
break;
case 6: /* [110] Only 1 Led be placed on PCB and use pin 21 of IC. Use LED_0 for showing */
if (!pHwData->LED_LinkOn) { /* Blink only if not Link On */
/* Blinking if scanning is on progress */
if (pHwData->LED_Scanning) {
if (pHwData->LED_Blinking == 0) {
reg->U1BC_LEDConfigure &= ~0xf;
reg->U1BC_LEDConfigure |= 0x10;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_0 On */
pHwData->LED_Blinking = 1;
TimeInterval = 300;
} else {
reg->U1BC_LEDConfigure &= ~0x1f;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_0 Off */
pHwData->LED_Blinking = 0;
TimeInterval = 300;
}
} else {
/* Gray blinking if in disconnect state and not scanning */
ltmp = reg->U1BC_LEDConfigure;
reg->U1BC_LEDConfigure &= ~0x1f;
if (LED_GRAY2[(pHwData->LED_Blinking % 30)]) {
reg->U1BC_LEDConfigure |= 0x10;
reg->U1BC_LEDConfigure |=
LED_GRAY2[(pHwData->LED_Blinking % 30)];
}
pHwData->LED_Blinking++;
if (reg->U1BC_LEDConfigure != ltmp)
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_0 Off */
TimeInterval = 100;
}
} else {
/* Turn On LED_0 */
if ((reg->U1BC_LEDConfigure & 0x10) == 0) {
reg->U1BC_LEDConfigure |= 0x10;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_0 Off */
}
}
break;
case 5: /* [101] Only 1 Led be placed on PCB and use LED_1 for showing */
if (!pHwData->LED_LinkOn) { /* Blink only if not Link On */
/* Blinking if scanning is on progress */
if (pHwData->LED_Scanning) {
if (pHwData->LED_Blinking == 0) {
reg->U1BC_LEDConfigure |= 0x1000;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_1 On */
pHwData->LED_Blinking = 1;
TimeInterval = 300;
} else {
reg->U1BC_LEDConfigure &= ~0x1000;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_1 Off */
pHwData->LED_Blinking = 0;
TimeInterval = 300;
}
} else {
/* Turn Off LED_1 */
if (reg->U1BC_LEDConfigure & 0x1000) {
reg->U1BC_LEDConfigure &= ~0x1000;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_1 Off */
}
}
} else {
/* Is transmitting/receiving ?? */
if ((adapter->RxByteCount !=
pHwData->RxByteCountLast)
|| (adapter->TxByteCount !=
pHwData->TxByteCountLast)) {
if ((reg->U1BC_LEDConfigure & 0x3000) !=
0x3000) {
reg->U1BC_LEDConfigure |= 0x3000;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_1 On */
}
/* Update variable */
pHwData->RxByteCountLast =
adapter->RxByteCount;
pHwData->TxByteCountLast =
adapter->TxByteCount;
TimeInterval = 200;
} else {
/* Turn On LED_1 and blinking if transmitting/receiving */
if ((reg->U1BC_LEDConfigure & 0x3000) !=
0x1000) {
reg->U1BC_LEDConfigure &=
~0x3000;
reg->U1BC_LEDConfigure |=
0x1000;
Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); /* LED_1 On */
}
}
}
break;
default: /* Default setting. 2 LED be placed on PCB. LED_0: Link On LED_1 Active */
if ((reg->U1BC_LEDConfigure & 0x3000) != 0x3000) {
reg->U1BC_LEDConfigure |= 0x3000; /* LED_1 is always on and event enable */
Wb35Reg_Write(pHwData, 0x03bc,
reg->U1BC_LEDConfigure);
}
if (pHwData->LED_Blinking) {
/* Gray blinking */
reg->U1BC_LEDConfigure &= ~0x0f;
reg->U1BC_LEDConfigure |= 0x10;
reg->U1BC_LEDConfigure |=
LED_GRAY[(pHwData->LED_Blinking - 1) % 20];
Wb35Reg_Write(pHwData, 0x03bc,
reg->U1BC_LEDConfigure);
pHwData->LED_Blinking += 2;
if (pHwData->LED_Blinking < 40)
TimeInterval = 100;
else {
pHwData->LED_Blinking = 0; /* Stop blinking */
reg->U1BC_LEDConfigure &= ~0x0f;
Wb35Reg_Write(pHwData, 0x03bc,
reg->U1BC_LEDConfigure);
}
break;
}
if (pHwData->LED_LinkOn) {
if (!(reg->U1BC_LEDConfigure & 0x10)) { /* Check the LED_0 */
/* Try to turn ON LED_0 after gray blinking */
reg->U1BC_LEDConfigure |= 0x10;
pHwData->LED_Blinking = 1; /* Start blinking */
TimeInterval = 50;
}
} else {
if (reg->U1BC_LEDConfigure & 0x10) { /* Check the LED_0 */
reg->U1BC_LEDConfigure &= ~0x10;
Wb35Reg_Write(pHwData, 0x03bc,
reg->U1BC_LEDConfigure);
}
}
break;
}
}
pHwData->time_count += TimeInterval;
Wb35Tx_CurrentTime(adapter, pHwData->time_count);
pHwData->LEDTimer.expires = jiffies + msecs_to_jiffies(TimeInterval);
add_timer(&pHwData->LEDTimer);
}
static int hal_init_hardware(struct ieee80211_hw *hw)
{
struct wbsoft_priv *priv = hw->priv;
struct hw_data *pHwData = &priv->sHwData;
u16 SoftwareSet;
pHwData->MaxReceiveLifeTime = DEFAULT_MSDU_LIFE_TIME;
pHwData->FragmentThreshold = DEFAULT_FRAGMENT_THRESHOLD;
if (!Wb35Reg_initial(pHwData))
goto error_reg_destroy;
if (!Wb35Tx_initial(pHwData))
goto error_tx_destroy;
if (!Wb35Rx_initial(pHwData))
goto error_rx_destroy;
init_timer(&pHwData->LEDTimer);
pHwData->LEDTimer.function = hal_led_control;
pHwData->LEDTimer.data = (unsigned long)priv;
pHwData->LEDTimer.expires = jiffies + msecs_to_jiffies(1000);
add_timer(&pHwData->LEDTimer);
SoftwareSet = hal_software_set(pHwData);
Wb35Rx_start(hw);
Wb35Tx_EP2VM_start(priv);
return 0;
error_rx_destroy:
Wb35Rx_destroy(pHwData);
error_tx_destroy:
Wb35Tx_destroy(pHwData);
error_reg_destroy:
Wb35Reg_destroy(pHwData);
pHwData->SurpriseRemove = 1;
return -EINVAL;
}
static int wb35_hw_init(struct ieee80211_hw *hw)
{
struct wbsoft_priv *priv = hw->priv;
struct hw_data *pHwData = &priv->sHwData;
u8 EEPROM_region;
u8 HwRadioOff;
u8 *pMacAddr2;
u8 *pMacAddr;
int err;
pHwData->phy_type = RF_DECIDE_BY_INF;
priv->Mds.TxRTSThreshold = DEFAULT_RTSThreshold;
priv->Mds.TxFragmentThreshold = DEFAULT_FRAGMENT_THRESHOLD;
priv->sLocalPara.region_INF = REGION_AUTO;
priv->sLocalPara.TxRateMode = RATE_AUTO;
priv->sLocalPara.bMacOperationMode = MODE_802_11_BG;
priv->sLocalPara.MTUsize = MAX_ETHERNET_PACKET_SIZE;
priv->sLocalPara.bPreambleMode = AUTO_MODE;
priv->sLocalPara.bWepKeyError = false;
priv->sLocalPara.bToSelfPacketReceived = false;
priv->sLocalPara.WepKeyDetectTimerCount = 2 * 100; /* 2 seconds */
priv->sLocalPara.RadioOffStatus.boSwRadioOff = false;
err = hal_init_hardware(hw);
if (err)
goto error;
EEPROM_region = hal_get_region_from_EEPROM(pHwData);
if (EEPROM_region != REGION_AUTO)
priv->sLocalPara.region = EEPROM_region;
else {
if (priv->sLocalPara.region_INF != REGION_AUTO)
priv->sLocalPara.region = priv->sLocalPara.region_INF;
else
priv->sLocalPara.region = REGION_USA; /* default setting */
}
Mds_initial(priv);
/*
* If no user-defined address in the registry, use the address
* "burned" on the NIC instead.
*/
pMacAddr = priv->sLocalPara.ThisMacAddress;
pMacAddr2 = priv->sLocalPara.PermanentAddress;
/* Reading ethernet address from EEPROM */
hal_get_permanent_address(pHwData, priv->sLocalPara.PermanentAddress);
if (memcmp(pMacAddr, "\x00\x00\x00\x00\x00\x00", MAC_ADDR_LENGTH) == 0)
memcpy(pMacAddr, pMacAddr2, MAC_ADDR_LENGTH);
else {
/* Set the user define MAC address */
hal_set_ethernet_address(pHwData,
priv->sLocalPara.ThisMacAddress);
}
priv->sLocalPara.bAntennaNo = hal_get_antenna_number(pHwData);
pr_debug("Driver init, antenna no = %d\n", priv->sLocalPara.bAntennaNo);
hal_get_hw_radio_off(pHwData);
/* Waiting for HAL setting OK */
while (!hal_idle(pHwData))
msleep(10);
MTO_Init(priv);
HwRadioOff = hal_get_hw_radio_off(pHwData);
priv->sLocalPara.RadioOffStatus.boHwRadioOff = !!HwRadioOff;
hal_set_radio_mode(pHwData,
(unsigned char)(priv->sLocalPara.RadioOffStatus.
boSwRadioOff
|| priv->sLocalPara.RadioOffStatus.
boHwRadioOff));
/* Notify hal that the driver is ready now. */
hal_driver_init_OK(pHwData) = 1;
error:
return err;
}
static int wb35_probe(struct usb_interface *intf,
const struct usb_device_id *id_table)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_endpoint_descriptor *endpoint;
struct usb_host_interface *interface;
struct ieee80211_hw *dev;
struct wbsoft_priv *priv;
int nr, err;
u32 ltmp;
usb_get_dev(udev);
/* Check the device if it already be opened */
nr = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
0x01,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
0x0, 0x400, &ltmp, 4, HZ * 100);
if (nr < 0) {
err = nr;
goto error;
}
/* Is already initialized? */
ltmp = cpu_to_le32(ltmp);
if (ltmp) {
err = -EBUSY;
goto error;
}
dev = ieee80211_alloc_hw(sizeof(*priv), &wbsoft_ops);
if (!dev) {
err = -ENOMEM;
goto error;
}
priv = dev->priv;
priv->sHwData.udev = udev;
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
if (endpoint[2].wMaxPacketSize == 512)
printk("[w35und] Working on USB 2.0\n");
err = wb35_hw_init(dev);
if (err)
goto error_free_hw;
SET_IEEE80211_DEV(dev, &udev->dev);
{
struct hw_data *pHwData = &priv->sHwData;
unsigned char dev_addr[MAX_ADDR_LEN];
hal_get_permanent_address(pHwData, dev_addr);
SET_IEEE80211_PERM_ADDR(dev, dev_addr);
}
dev->extra_tx_headroom = 12; /* FIXME */
dev->flags = IEEE80211_HW_SIGNAL_UNSPEC;
dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
dev->channel_change_time = 1000;
dev->max_signal = 100;
dev->queues = 1;
dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &wbsoft_band_2GHz;
err = ieee80211_register_hw(dev);
if (err)
goto error_free_hw;
usb_set_intfdata(intf, dev);
return 0;
error_free_hw:
ieee80211_free_hw(dev);
error:
usb_put_dev(udev);
return err;
}
static void hal_halt(struct hw_data *pHwData)
{
del_timer_sync(&pHwData->LEDTimer);
/* XXX: Wait for Timer DPC exit. */
msleep(100);
Wb35Rx_destroy(pHwData);
Wb35Tx_destroy(pHwData);
Wb35Reg_destroy(pHwData);
}
static void wb35_hw_halt(struct wbsoft_priv *adapter)
{
/* Turn off Rx and Tx hardware ability */
hal_stop(&adapter->sHwData);
pr_debug("[w35und] Hal_stop O.K.\n");
/* Waiting Irp completed */
msleep(100);
hal_halt(&adapter->sHwData);
}
static void wb35_disconnect(struct usb_interface *intf)
{
struct ieee80211_hw *hw = usb_get_intfdata(intf);
struct wbsoft_priv *priv = hw->priv;
wb35_hw_halt(priv);
ieee80211_stop_queues(hw);
ieee80211_unregister_hw(hw);
ieee80211_free_hw(hw);
usb_set_intfdata(intf, NULL);
usb_put_dev(interface_to_usbdev(intf));
}
static struct usb_driver wb35_driver = {
.name = "w35und",
.id_table = wb35_table,
.probe = wb35_probe,
.disconnect = wb35_disconnect,
};
static int __init wb35_init(void)
{
return usb_register(&wb35_driver);
}
static void __exit wb35_exit(void)
{
usb_deregister(&wb35_driver);
}
module_init(wb35_init);
module_exit(wb35_exit);