aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/winbond/wbusb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/winbond/wbusb.c')
-rw-r--r--drivers/staging/winbond/wbusb.c853
1 files changed, 853 insertions, 0 deletions
diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c
new file mode 100644
index 00000000000..0d29624416c
--- /dev/null
+++ b/drivers/staging/winbond/wbusb.c
@@ -0,0 +1,853 @@
+/*
+ * 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 <linux/module.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[] = {
+ { 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)
+{
+}
+
+static void wbsoft_stop(struct ieee80211_hw *hw)
+{
+}
+
+static int wbsoft_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ 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 ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct wbsoft_priv *priv = dev->priv;
+
+ if (priv->sMlmeFrame.is_in_used != PACKET_FREE_TO_USE) {
+ priv->sMlmeFrame.wNumTxMMPDUDiscarded++;
+ kfree_skb(skb);
+ return;
+ }
+
+ priv->sMlmeFrame.is_in_used = PACKET_COME_FROM_MLME;
+
+ priv->sMlmeFrame.pMMPDU = skb->data;
+ priv->sMlmeFrame.data_type = 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;
+
+ RFSynthesizer_SwitchingChannel(pHwData, channel); /* Switch channel */
+ pHwData->Channel = channel.ChanNo;
+ pHwData->band = channel.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;
+
+ /* 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, struct ieee80211_vif *vif)
+{
+ 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);
+ 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 err;
+ u32 ltmp;
+
+ usb_get_dev(udev);
+
+ /* Check the device if it already be opened */
+ err = 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 (err < 0)
+ 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;
+
+ 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->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);
+ /* 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,
+};
+
+module_usb_driver(wb35_driver);