blob: 745279c528a24143a7c0a6b312cd45e6f37b432b [file] [log] [blame]
Pavel Machek66101de2008-10-01 14:36:56 +02001/*
2 * Copyright 2008 Pavel Machek <pavel@suse.cz>
3 *
4 * Distribute under GPLv2.
Pekka Enberg7c49a0a2009-04-08 11:14:05 +03005 *
6 * The original driver was written by:
7 * Jeff Lee <YY_Lee@issc.com.tw>
8 *
9 * and was adapted to the 2.6 kernel by:
10 * Costantino Leandro (Rxart Desktop) <le_costantino@pixartargentina.com.ar>
Pavel Machek66101de2008-10-01 14:36:56 +020011 */
Pavel Machek66101de2008-10-01 14:36:56 +020012#include <net/mac80211.h>
Pekka Enberg80aba532008-10-30 13:04:29 +020013#include <linux/usb.h>
14
Pekka Enbergcc180712008-10-30 16:14:35 +020015#include "core.h"
Pekka Enberg912b2092008-10-30 18:12:02 +020016#include "mds_f.h"
Pekka Enberg9ce922f2008-10-30 13:05:42 +020017#include "mlmetxrx_f.h"
Pekka Enberg64328c82009-01-07 17:33:45 +020018#include "mto.h"
Pekka Enberg9ce922f2008-10-30 13:05:42 +020019#include "wbhal_f.h"
20#include "wblinux_f.h"
Pavel Machek66101de2008-10-01 14:36:56 +020021
Pekka Enberg7b9a79b2008-10-30 18:12:01 +020022MODULE_DESCRIPTION("IS89C35 802.11bg WLAN USB Driver");
Pavel Machek66101de2008-10-01 14:36:56 +020023MODULE_LICENSE("GPL");
24MODULE_VERSION("0.1");
25
Pekka Enbergdd38da42008-10-21 13:01:42 +030026static struct usb_device_id wb35_table[] __devinitdata = {
Pekka Enberga32b9812009-04-08 11:14:04 +030027 { USB_DEVICE(0x0416, 0x0035) },
28 { USB_DEVICE(0x18E8, 0x6201) },
29 { USB_DEVICE(0x18E8, 0x6206) },
30 { USB_DEVICE(0x18E8, 0x6217) },
31 { USB_DEVICE(0x18E8, 0x6230) },
32 { USB_DEVICE(0x18E8, 0x6233) },
33 { USB_DEVICE(0x1131, 0x2035) },
Greg Kroah-Hartman68ab0c92008-10-21 10:41:45 -070034 { 0, }
Pavel Machek66101de2008-10-01 14:36:56 +020035};
36
Pekka Enbergdd38da42008-10-21 13:01:42 +030037MODULE_DEVICE_TABLE(usb, wb35_table);
Pavel Machek66101de2008-10-01 14:36:56 +020038
Greg Kroah-Hartman68ab0c92008-10-21 10:41:45 -070039static struct ieee80211_rate wbsoft_rates[] = {
Pavel Machek66101de2008-10-01 14:36:56 +020040 { .bitrate = 10, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
41};
42
Greg Kroah-Hartman68ab0c92008-10-21 10:41:45 -070043static struct ieee80211_channel wbsoft_channels[] = {
Pekka Enberga32b9812009-04-08 11:14:04 +030044 { .center_freq = 2412 },
Pavel Machek66101de2008-10-01 14:36:56 +020045};
46
Pekka Enberga36e0892008-10-28 00:20:03 +020047static struct ieee80211_supported_band wbsoft_band_2GHz = {
48 .channels = wbsoft_channels,
49 .n_channels = ARRAY_SIZE(wbsoft_channels),
50 .bitrates = wbsoft_rates,
51 .n_bitrates = ARRAY_SIZE(wbsoft_rates),
52};
53
Pavel Machek66101de2008-10-01 14:36:56 +020054static int wbsoft_add_interface(struct ieee80211_hw *dev,
Pekka Enberga32b9812009-04-08 11:14:04 +030055 struct ieee80211_if_init_conf *conf)
Pavel Machek66101de2008-10-01 14:36:56 +020056{
57 printk("wbsoft_add interface called\n");
58 return 0;
59}
60
61static void wbsoft_remove_interface(struct ieee80211_hw *dev,
Pekka Enberga32b9812009-04-08 11:14:04 +030062 struct ieee80211_if_init_conf *conf)
Pavel Machek66101de2008-10-01 14:36:56 +020063{
64 printk("wbsoft_remove interface called\n");
65}
66
Greg Kroah-Hartman68ab0c92008-10-21 10:41:45 -070067static void wbsoft_stop(struct ieee80211_hw *hw)
Pavel Machek66101de2008-10-01 14:36:56 +020068{
Greg Kroah-Hartman68ab0c92008-10-21 10:41:45 -070069 printk(KERN_INFO "%s called\n", __func__);
70}
71
72static int wbsoft_get_stats(struct ieee80211_hw *hw,
73 struct ieee80211_low_level_stats *stats)
74{
75 printk(KERN_INFO "%s called\n", __func__);
76 return 0;
77}
78
79static int wbsoft_get_tx_stats(struct ieee80211_hw *hw,
80 struct ieee80211_tx_queue_stats *stats)
81{
82 printk(KERN_INFO "%s called\n", __func__);
Pavel Machek66101de2008-10-01 14:36:56 +020083 return 0;
84}
85
86static void wbsoft_configure_filter(struct ieee80211_hw *dev,
Pekka Enberga32b9812009-04-08 11:14:04 +030087 unsigned int changed_flags,
88 unsigned int *total_flags,
89 int mc_count, struct dev_mc_list *mclist)
Pavel Machek66101de2008-10-01 14:36:56 +020090{
Pekka Enberg6ab32122009-03-27 19:46:45 +020091 unsigned int new_flags;
Pavel Machek66101de2008-10-01 14:36:56 +020092
93 new_flags = 0;
94
Pekka Enberg6ab32122009-03-27 19:46:45 +020095 if (*total_flags & FIF_PROMISC_IN_BSS)
Pavel Machek66101de2008-10-01 14:36:56 +020096 new_flags |= FIF_PROMISC_IN_BSS;
Pekka Enberg6ab32122009-03-27 19:46:45 +020097 else if ((*total_flags & FIF_ALLMULTI) || (mc_count > 32))
Pavel Machek66101de2008-10-01 14:36:56 +020098 new_flags |= FIF_ALLMULTI;
Pavel Machek66101de2008-10-01 14:36:56 +020099
100 dev->flags &= ~IEEE80211_HW_RX_INCLUDES_FCS;
101
102 *total_flags = new_flags;
103}
104
Greg Kroah-Hartman68ab0c92008-10-21 10:41:45 -0700105static int wbsoft_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
Pavel Machek66101de2008-10-01 14:36:56 +0200106{
Pekka Enbergcc180712008-10-30 16:14:35 +0200107 struct wbsoft_priv *priv = dev->priv;
108
Pekka Enberg1e8a2b62008-10-30 16:14:38 +0200109 MLMESendFrame(priv, skb->data, skb->len, FRAME_TYPE_802_11_MANAGEMENT);
Pekka Enberg16d36592008-10-28 00:14:38 +0200110
Pavel Machek66101de2008-10-01 14:36:56 +0200111 return NETDEV_TX_OK;
112}
113
Pavel Machek66101de2008-10-01 14:36:56 +0200114static int wbsoft_start(struct ieee80211_hw *dev)
115{
Pekka Enbergc930e0c02008-10-30 18:12:04 +0200116 struct wbsoft_priv *priv = dev->priv;
117
118 priv->enabled = true;
119
Pavel Machek66101de2008-10-01 14:36:56 +0200120 return 0;
121}
122
Pekka Enberg22a82bc2009-04-08 11:51:20 +0300123static void hal_set_radio_mode(struct hw_data *pHwData, unsigned char radio_off)
124{
125 struct wb35_reg *reg = &pHwData->reg;
126
127 if (pHwData->SurpriseRemove)
128 return;
129
130 if (radio_off) //disable Baseband receive off
131 {
132 pHwData->CurrentRadioSw = 1; // off
133 reg->M24_MacControl &= 0xffffffbf;
134 } else {
135 pHwData->CurrentRadioSw = 0; // on
136 reg->M24_MacControl |= 0x00000040;
137 }
138 Wb35Reg_Write(pHwData, 0x0824, reg->M24_MacControl);
139}
140
141static void hal_set_beacon_period(struct hw_data *pHwData, u16 beacon_period)
142{
143 u32 tmp;
144
145 if (pHwData->SurpriseRemove)
146 return;
147
148 pHwData->BeaconPeriod = beacon_period;
149 tmp = pHwData->BeaconPeriod << 16;
150 tmp |= pHwData->ProbeDelay;
151 Wb35Reg_Write(pHwData, 0x0848, tmp);
152}
153
154static void
155hal_set_current_channel_ex(struct hw_data *pHwData, ChanInfo channel)
156{
157 struct wb35_reg *reg = &pHwData->reg;
158
159 if (pHwData->SurpriseRemove)
160 return;
161
162 printk("Going to channel: %d/%d\n", channel.band, channel.ChanNo);
163
164 RFSynthesizer_SwitchingChannel(pHwData, channel); // Switch channel
165 pHwData->Channel = channel.ChanNo;
166 pHwData->band = channel.band;
167#ifdef _PE_STATE_DUMP_
168 printk("Set channel is %d, band =%d\n", pHwData->Channel,
169 pHwData->band);
170#endif
171 reg->M28_MacControl &= ~0xff; // Clean channel information field
172 reg->M28_MacControl |= channel.ChanNo;
173 Wb35Reg_WriteWithCallbackValue(pHwData, 0x0828, reg->M28_MacControl,
174 (s8 *) & channel, sizeof(ChanInfo));
175}
176
177static void hal_set_current_channel(struct hw_data *pHwData, ChanInfo channel)
178{
179 hal_set_current_channel_ex(pHwData, channel);
180}
181
182static void hal_set_accept_broadcast(struct hw_data *pHwData, u8 enable)
183{
184 struct wb35_reg *reg = &pHwData->reg;
185
186 if (pHwData->SurpriseRemove)
187 return;
188
189 reg->M00_MacControl &= ~0x02000000; //The HW value
190
191 if (enable)
192 reg->M00_MacControl |= 0x02000000; //The HW value
193
194 Wb35Reg_Write(pHwData, 0x0800, reg->M00_MacControl);
195}
196
197//for wep key error detection, we need to accept broadcast packets to be received temporary.
198static void hal_set_accept_promiscuous(struct hw_data *pHwData, u8 enable)
199{
200 struct wb35_reg *reg = &pHwData->reg;
201
202 if (pHwData->SurpriseRemove)
203 return;
204 if (enable) {
205 reg->M00_MacControl |= 0x00400000;
206 Wb35Reg_Write(pHwData, 0x0800, reg->M00_MacControl);
207 } else {
208 reg->M00_MacControl &= ~0x00400000;
209 Wb35Reg_Write(pHwData, 0x0800, reg->M00_MacControl);
210 }
211}
212
213static void hal_set_accept_multicast(struct hw_data *pHwData, u8 enable)
214{
215 struct wb35_reg *reg = &pHwData->reg;
216
217 if (pHwData->SurpriseRemove)
218 return;
219
220 reg->M00_MacControl &= ~0x01000000; //The HW value
221 if (enable)
222 reg->M00_MacControl |= 0x01000000; //The HW value
223 Wb35Reg_Write(pHwData, 0x0800, reg->M00_MacControl);
224}
225
226static void hal_set_accept_beacon(struct hw_data *pHwData, u8 enable)
227{
228 struct wb35_reg *reg = &pHwData->reg;
229
230 if (pHwData->SurpriseRemove)
231 return;
232
233 // 20040108 debug
234 if (!enable) //Due to SME and MLME are not suitable for 35
235 return;
236
237 reg->M00_MacControl &= ~0x04000000; //The HW value
238 if (enable)
239 reg->M00_MacControl |= 0x04000000; //The HW value
240
241 Wb35Reg_Write(pHwData, 0x0800, reg->M00_MacControl);
242}
243
Greg Kroah-Hartmanf02466f2009-01-05 10:46:00 -0800244static int wbsoft_config(struct ieee80211_hw *dev, u32 changed)
Pavel Machek66101de2008-10-01 14:36:56 +0200245{
Pekka Enbergcc180712008-10-30 16:14:35 +0200246 struct wbsoft_priv *priv = dev->priv;
Greg Kroah-Hartmanf02466f2009-01-05 10:46:00 -0800247 struct ieee80211_conf *conf = &dev->conf;
Pavel Machek66101de2008-10-01 14:36:56 +0200248 ChanInfo ch;
Greg Kroah-Hartmanf02466f2009-01-05 10:46:00 -0800249
Pavel Machek66101de2008-10-01 14:36:56 +0200250 printk("wbsoft_config called\n");
251
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300252 /* Should use channel_num, or something, as that is already pre-translated */
Pavel Machek66101de2008-10-01 14:36:56 +0200253 ch.band = 1;
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300254 ch.ChanNo = 1;
Pavel Machek66101de2008-10-01 14:36:56 +0200255
Pekka Enberg1e8a2b62008-10-30 16:14:38 +0200256 hal_set_current_channel(&priv->sHwData, ch);
257 hal_set_beacon_period(&priv->sHwData, conf->beacon_int);
Pekka Enberg1e8a2b62008-10-30 16:14:38 +0200258 hal_set_accept_broadcast(&priv->sHwData, 1);
Pekka Enberga32b9812009-04-08 11:14:04 +0300259 hal_set_accept_promiscuous(&priv->sHwData, 1);
260 hal_set_accept_multicast(&priv->sHwData, 1);
261 hal_set_accept_beacon(&priv->sHwData, 1);
262 hal_set_radio_mode(&priv->sHwData, 0);
Pavel Machek66101de2008-10-01 14:36:56 +0200263
264 return 0;
265}
266
Pavel Machek66101de2008-10-01 14:36:56 +0200267static u64 wbsoft_get_tsf(struct ieee80211_hw *dev)
268{
269 printk("wbsoft_get_tsf called\n");
270 return 0;
271}
272
273static const struct ieee80211_ops wbsoft_ops = {
274 .tx = wbsoft_tx,
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300275 .start = wbsoft_start,
Greg Kroah-Hartman68ab0c92008-10-21 10:41:45 -0700276 .stop = wbsoft_stop,
Pavel Machek66101de2008-10-01 14:36:56 +0200277 .add_interface = wbsoft_add_interface,
278 .remove_interface = wbsoft_remove_interface,
279 .config = wbsoft_config,
Pavel Machek66101de2008-10-01 14:36:56 +0200280 .configure_filter = wbsoft_configure_filter,
Greg Kroah-Hartman68ab0c92008-10-21 10:41:45 -0700281 .get_stats = wbsoft_get_stats,
282 .get_tx_stats = wbsoft_get_tx_stats,
Pavel Machek66101de2008-10-01 14:36:56 +0200283 .get_tsf = wbsoft_get_tsf,
Pavel Machek66101de2008-10-01 14:36:56 +0200284};
285
Pekka Enberg22a82bc2009-04-08 11:51:20 +0300286static void
287hal_set_ethernet_address(struct hw_data *pHwData, u8 * current_address)
288{
289 u32 ltmp[2];
290
291 if (pHwData->SurpriseRemove)
292 return;
293
294 memcpy(pHwData->CurrentMacAddress, current_address, ETH_ALEN);
295
296 ltmp[0] = cpu_to_le32(*(u32 *) pHwData->CurrentMacAddress);
297 ltmp[1] =
298 cpu_to_le32(*(u32 *) (pHwData->CurrentMacAddress + 4)) & 0xffff;
299
300 Wb35Reg_BurstWrite(pHwData, 0x03e8, ltmp, 2, AUTO_INCREMENT);
301}
302
303static void
304hal_get_permanent_address(struct hw_data *pHwData, u8 * pethernet_address)
305{
306 if (pHwData->SurpriseRemove)
307 return;
308
309 memcpy(pethernet_address, pHwData->PermanentMacAddress, 6);
310}
311
312static void hal_stop(struct hw_data *pHwData)
313{
314 struct wb35_reg *reg = &pHwData->reg;
315
316 pHwData->Wb35Rx.rx_halt = 1;
317 Wb35Rx_stop(pHwData);
318
319 pHwData->Wb35Tx.tx_halt = 1;
320 Wb35Tx_stop(pHwData);
321
322 reg->D00_DmaControl &= ~0xc0000000; //Tx Off, Rx Off
323 Wb35Reg_Write(pHwData, 0x0400, reg->D00_DmaControl);
324}
325
326static unsigned char hal_idle(struct hw_data *pHwData)
327{
328 struct wb35_reg *reg = &pHwData->reg;
329 struct wb_usb *pWbUsb = &pHwData->WbUsb;
330
331 if (!pHwData->SurpriseRemove
332 && (pWbUsb->DetectCount || reg->EP0vm_state != VM_STOP))
333 return false;
334
335 return true;
336}
337
338u8 hal_get_antenna_number(struct hw_data *pHwData)
339{
340 struct wb35_reg *reg = &pHwData->reg;
341
342 if ((reg->BB2C & BIT(11)) == 0)
343 return 0;
344 else
345 return 1;
346}
347
348/* 0 : radio on; 1: radio off */
349static u8 hal_get_hw_radio_off(struct hw_data * pHwData)
350{
351 struct wb35_reg *reg = &pHwData->reg;
352
353 if (pHwData->SurpriseRemove)
354 return 1;
355
356 //read the bit16 of register U1B0
357 Wb35Reg_Read(pHwData, 0x3b0, &reg->U1B0);
358 if ((reg->U1B0 & 0x00010000)) {
359 pHwData->CurrentRadioHw = 1;
360 return 1;
361 } else {
362 pHwData->CurrentRadioHw = 0;
363 return 0;
364 }
365}
366
Pekka Enberg833d0cd2009-04-08 11:14:06 +0300367static u8 LED_GRAY[20] = {
368 0, 3, 4, 6, 8, 10, 11, 12, 13, 14, 15, 14, 13, 12, 11, 10, 8, 6, 4, 2
369};
370
371static u8 LED_GRAY2[30] = {
372 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
373 0, 15, 14, 13, 12, 11, 10, 9, 8
374};
375
Pekka Enberg80767e62009-04-08 11:13:57 +0300376static void hal_led_control(unsigned long data)
377{
Pekka Enberga32b9812009-04-08 11:14:04 +0300378 struct wbsoft_priv *adapter = (struct wbsoft_priv *)data;
379 struct hw_data *pHwData = &adapter->sHwData;
Pekka Enberg80767e62009-04-08 11:13:57 +0300380 struct wb35_reg *reg = &pHwData->reg;
Pekka Enberga32b9812009-04-08 11:14:04 +0300381 u32 LEDSet = (pHwData->SoftwareSet & HAL_LED_SET_MASK) >> HAL_LED_SET_SHIFT;
Pekka Enberga32b9812009-04-08 11:14:04 +0300382 u32 TimeInterval = 500, ltmp, ltmp2;
383 ltmp = 0;
Pekka Enberg80767e62009-04-08 11:13:57 +0300384
Pekka Enberga32b9812009-04-08 11:14:04 +0300385 if (pHwData->SurpriseRemove)
386 return;
Pekka Enberg80767e62009-04-08 11:13:57 +0300387
Pekka Enberga32b9812009-04-08 11:14:04 +0300388 if (pHwData->LED_control) {
Pekka Enberg80767e62009-04-08 11:13:57 +0300389 ltmp2 = pHwData->LED_control & 0xff;
Pekka Enberga32b9812009-04-08 11:14:04 +0300390 if (ltmp2 == 5) // 5 is WPS mode
Pekka Enberg80767e62009-04-08 11:13:57 +0300391 {
392 TimeInterval = 100;
Pekka Enberga32b9812009-04-08 11:14:04 +0300393 ltmp2 = (pHwData->LED_control >> 8) & 0xff;
394 switch (ltmp2) {
395 case 1: // [0.2 On][0.1 Off]...
396 pHwData->LED_Blinking %= 3;
397 ltmp = 0x1010; // Led 1 & 0 Green and Red
398 if (pHwData->LED_Blinking == 2) // Turn off
399 ltmp = 0;
400 break;
401 case 2: // [0.1 On][0.1 Off]...
402 pHwData->LED_Blinking %= 2;
403 ltmp = 0x0010; // Led 0 red color
404 if (pHwData->LED_Blinking) // Turn off
405 ltmp = 0;
406 break;
407 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]...
408 pHwData->LED_Blinking %= 15;
409 ltmp = 0x0010; // Led 0 red color
410 if ((pHwData->LED_Blinking >= 9) || (pHwData->LED_Blinking % 2)) // Turn off 0.6 sec
411 ltmp = 0;
412 break;
413 case 4: // [300 On][ off ]
414 ltmp = 0x1000; // Led 1 Green color
415 if (pHwData->LED_Blinking >= 3000)
416 ltmp = 0; // led maybe on after 300sec * 32bit counter overlap.
417 break;
Pekka Enberg80767e62009-04-08 11:13:57 +0300418 }
419 pHwData->LED_Blinking++;
420
421 reg->U1BC_LEDConfigure = ltmp;
Pekka Enberga32b9812009-04-08 11:14:04 +0300422 if (LEDSet != 7) // Only 111 mode has 2 LEDs on PCB.
Pekka Enberg80767e62009-04-08 11:13:57 +0300423 {
Pekka Enberga32b9812009-04-08 11:14:04 +0300424 reg->U1BC_LEDConfigure |= (ltmp & 0xff) << 8; // Copy LED result to each LED control register
425 reg->U1BC_LEDConfigure |= (ltmp & 0xff00) >> 8;
Pekka Enberg80767e62009-04-08 11:13:57 +0300426 }
Pekka Enberga32b9812009-04-08 11:14:04 +0300427 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure);
Pekka Enberg80767e62009-04-08 11:13:57 +0300428 }
Pekka Enberga32b9812009-04-08 11:14:04 +0300429 } else if (pHwData->CurrentRadioSw || pHwData->CurrentRadioHw) // If radio off
Pekka Enberg80767e62009-04-08 11:13:57 +0300430 {
Pekka Enberga32b9812009-04-08 11:14:04 +0300431 if (reg->U1BC_LEDConfigure & 0x1010) {
Pekka Enberg80767e62009-04-08 11:13:57 +0300432 reg->U1BC_LEDConfigure &= ~0x1010;
Pekka Enberga32b9812009-04-08 11:14:04 +0300433 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure);
Pekka Enberg80767e62009-04-08 11:13:57 +0300434 }
Pekka Enberga32b9812009-04-08 11:14:04 +0300435 } else {
436 switch (LEDSet) {
437 case 4: // [100] Only 1 Led be placed on PCB and use pin 21 of IC. Use LED_0 for showing
438 if (!pHwData->LED_LinkOn) // Blink only if not Link On
439 {
440 // Blinking if scanning is on progress
441 if (pHwData->LED_Scanning) {
442 if (pHwData->LED_Blinking == 0) {
Pekka Enberg80767e62009-04-08 11:13:57 +0300443 reg->U1BC_LEDConfigure |= 0x10;
Pekka Enberga32b9812009-04-08 11:14:04 +0300444 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_0 On
445 pHwData->LED_Blinking = 1;
446 TimeInterval = 300;
447 } else {
Pekka Enberg80767e62009-04-08 11:13:57 +0300448 reg->U1BC_LEDConfigure &= ~0x10;
Pekka Enberga32b9812009-04-08 11:14:04 +0300449 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_0 Off
450 pHwData->LED_Blinking = 0;
451 TimeInterval = 300;
452 }
453 } else {
454 //Turn Off LED_0
455 if (reg->U1BC_LEDConfigure & 0x10) {
456 reg->U1BC_LEDConfigure &= ~0x10;
457 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_0 Off
Pekka Enberg80767e62009-04-08 11:13:57 +0300458 }
459 }
Pekka Enberga32b9812009-04-08 11:14:04 +0300460 } else {
461 // Turn On LED_0
462 if ((reg->U1BC_LEDConfigure & 0x10) == 0) {
463 reg->U1BC_LEDConfigure |= 0x10;
464 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_0 Off
465 }
466 }
467 break;
468
469 case 6: // [110] Only 1 Led be placed on PCB and use pin 21 of IC. Use LED_0 for showing
470 if (!pHwData->LED_LinkOn) // Blink only if not Link On
471 {
472 // Blinking if scanning is on progress
473 if (pHwData->LED_Scanning) {
474 if (pHwData->LED_Blinking == 0) {
475 reg->U1BC_LEDConfigure &= ~0xf;
476 reg->U1BC_LEDConfigure |= 0x10;
477 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_0 On
478 pHwData->LED_Blinking = 1;
479 TimeInterval = 300;
480 } else {
481 reg->U1BC_LEDConfigure &= ~0x1f;
482 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_0 Off
483 pHwData->LED_Blinking = 0;
484 TimeInterval = 300;
485 }
486 } else {
487 // 20060901 Gray blinking if in disconnect state and not scanning
488 ltmp = reg->U1BC_LEDConfigure;
489 reg->U1BC_LEDConfigure &= ~0x1f;
Pekka Enberg833d0cd2009-04-08 11:14:06 +0300490 if (LED_GRAY2[(pHwData->LED_Blinking % 30)]) {
Pekka Enberga32b9812009-04-08 11:14:04 +0300491 reg->U1BC_LEDConfigure |= 0x10;
492 reg->U1BC_LEDConfigure |=
Pekka Enberg833d0cd2009-04-08 11:14:06 +0300493 LED_GRAY2[(pHwData->LED_Blinking % 30)];
Pekka Enberga32b9812009-04-08 11:14:04 +0300494 }
495 pHwData->LED_Blinking++;
496 if (reg->U1BC_LEDConfigure != ltmp)
497 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_0 Off
498 TimeInterval = 100;
499 }
500 } else {
501 // Turn On LED_0
502 if ((reg->U1BC_LEDConfigure & 0x10) == 0) {
503 reg->U1BC_LEDConfigure |= 0x10;
504 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_0 Off
505 }
506 }
507 break;
508
509 case 5: // [101] Only 1 Led be placed on PCB and use LED_1 for showing
510 if (!pHwData->LED_LinkOn) // Blink only if not Link On
511 {
512 // Blinking if scanning is on progress
513 if (pHwData->LED_Scanning) {
514 if (pHwData->LED_Blinking == 0) {
515 reg->U1BC_LEDConfigure |=
516 0x1000;
517 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_1 On
518 pHwData->LED_Blinking = 1;
519 TimeInterval = 300;
520 } else {
521 reg->U1BC_LEDConfigure &=
522 ~0x1000;
523 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_1 Off
524 pHwData->LED_Blinking = 0;
525 TimeInterval = 300;
526 }
527 } else {
528 //Turn Off LED_1
529 if (reg->U1BC_LEDConfigure & 0x1000) {
530 reg->U1BC_LEDConfigure &=
531 ~0x1000;
532 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_1 Off
533 }
534 }
535 } else {
536 // Is transmitting/receiving ??
537 if ((adapter->RxByteCount !=
538 pHwData->RxByteCountLast)
539 || (adapter->TxByteCount !=
540 pHwData->TxByteCountLast)) {
541 if ((reg->U1BC_LEDConfigure & 0x3000) !=
542 0x3000) {
543 reg->U1BC_LEDConfigure |=
544 0x3000;
545 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_1 On
546 }
547 // Update variable
548 pHwData->RxByteCountLast =
549 adapter->RxByteCount;
550 pHwData->TxByteCountLast =
551 adapter->TxByteCount;
552 TimeInterval = 200;
553 } else {
554 // Turn On LED_1 and blinking if transmitting/receiving
555 if ((reg->U1BC_LEDConfigure & 0x3000) !=
556 0x1000) {
557 reg->U1BC_LEDConfigure &=
558 ~0x3000;
559 reg->U1BC_LEDConfigure |=
560 0x1000;
561 Wb35Reg_Write(pHwData, 0x03bc, reg->U1BC_LEDConfigure); // LED_1 On
562 }
563 }
564 }
565 break;
566
567 default: // Default setting. 2 LED be placed on PCB. LED_0: Link On LED_1 Active
568 if ((reg->U1BC_LEDConfigure & 0x3000) != 0x3000) {
569 reg->U1BC_LEDConfigure |= 0x3000; // LED_1 is always on and event enable
570 Wb35Reg_Write(pHwData, 0x03bc,
571 reg->U1BC_LEDConfigure);
572 }
573
574 if (pHwData->LED_Blinking) {
575 // Gray blinking
576 reg->U1BC_LEDConfigure &= ~0x0f;
577 reg->U1BC_LEDConfigure |= 0x10;
578 reg->U1BC_LEDConfigure |=
Pekka Enberg833d0cd2009-04-08 11:14:06 +0300579 LED_GRAY[(pHwData->LED_Blinking - 1) % 20];
Pekka Enberga32b9812009-04-08 11:14:04 +0300580 Wb35Reg_Write(pHwData, 0x03bc,
581 reg->U1BC_LEDConfigure);
582
583 pHwData->LED_Blinking += 2;
584 if (pHwData->LED_Blinking < 40)
585 TimeInterval = 100;
586 else {
587 pHwData->LED_Blinking = 0; // Stop blinking
588 reg->U1BC_LEDConfigure &= ~0x0f;
589 Wb35Reg_Write(pHwData, 0x03bc,
590 reg->U1BC_LEDConfigure);
591 }
Pekka Enberg80767e62009-04-08 11:13:57 +0300592 break;
Pekka Enberga32b9812009-04-08 11:14:04 +0300593 }
594
595 if (pHwData->LED_LinkOn) {
596 if (!(reg->U1BC_LEDConfigure & 0x10)) // Check the LED_0
597 {
598 //Try to turn ON LED_0 after gray blinking
599 reg->U1BC_LEDConfigure |= 0x10;
600 pHwData->LED_Blinking = 1; //Start blinking
601 TimeInterval = 50;
602 }
603 } else {
604 if (reg->U1BC_LEDConfigure & 0x10) // Check the LED_0
605 {
606 reg->U1BC_LEDConfigure &= ~0x10;
607 Wb35Reg_Write(pHwData, 0x03bc,
608 reg->U1BC_LEDConfigure);
609 }
610 }
611 break;
Pekka Enberg80767e62009-04-08 11:13:57 +0300612 }
613
614 //20060828.1 Active send null packet to avoid AP disconnect
Pekka Enberga32b9812009-04-08 11:14:04 +0300615 if (pHwData->LED_LinkOn) {
Pekka Enberg80767e62009-04-08 11:13:57 +0300616 pHwData->NullPacketCount += TimeInterval;
Pekka Enberga32b9812009-04-08 11:14:04 +0300617 if (pHwData->NullPacketCount >=
618 DEFAULT_NULL_PACKET_COUNT) {
Pekka Enberg80767e62009-04-08 11:13:57 +0300619 pHwData->NullPacketCount = 0;
620 }
621 }
622 }
623
624 pHwData->time_count += TimeInterval;
Pekka Enberga32b9812009-04-08 11:14:04 +0300625 Wb35Tx_CurrentTime(adapter, pHwData->time_count); // 20060928 add
Pekka Enberg80767e62009-04-08 11:13:57 +0300626 pHwData->LEDTimer.expires = jiffies + msecs_to_jiffies(TimeInterval);
627 add_timer(&pHwData->LEDTimer);
628}
629
Pekka Enbergcfe31f82009-04-08 11:13:58 +0300630static int hal_init_hardware(struct ieee80211_hw *hw)
Pekka Enberg80767e62009-04-08 11:13:57 +0300631{
632 struct wbsoft_priv *priv = hw->priv;
Pekka Enberga32b9812009-04-08 11:14:04 +0300633 struct hw_data *pHwData = &priv->sHwData;
Pekka Enberg80767e62009-04-08 11:13:57 +0300634 u16 SoftwareSet;
635
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300636 pHwData->MaxReceiveLifeTime = DEFAULT_MSDU_LIFE_TIME;
637 pHwData->FragmentThreshold = DEFAULT_FRAGMENT_THRESHOLD;
Pekka Enberg80767e62009-04-08 11:13:57 +0300638
Pekka Enberg00e2e052009-04-08 11:13:59 +0300639 if (!Wb35Reg_initial(pHwData))
Pekka Enberga39ee672009-04-08 11:14:00 +0300640 goto error_reg_destroy;
Pekka Enberg80767e62009-04-08 11:13:57 +0300641
Pekka Enberg00e2e052009-04-08 11:13:59 +0300642 if (!Wb35Tx_initial(pHwData))
Pekka Enberga39ee672009-04-08 11:14:00 +0300643 goto error_tx_destroy;
Pekka Enberg80767e62009-04-08 11:13:57 +0300644
Pekka Enberg00e2e052009-04-08 11:13:59 +0300645 if (!Wb35Rx_initial(pHwData))
Pekka Enberga39ee672009-04-08 11:14:00 +0300646 goto error_rx_destroy;
Pekka Enberg80767e62009-04-08 11:13:57 +0300647
Pekka Enberg00e2e052009-04-08 11:13:59 +0300648 init_timer(&pHwData->LEDTimer);
649 pHwData->LEDTimer.function = hal_led_control;
Pekka Enberga32b9812009-04-08 11:14:04 +0300650 pHwData->LEDTimer.data = (unsigned long)priv;
Pekka Enberg00e2e052009-04-08 11:13:59 +0300651 pHwData->LEDTimer.expires = jiffies + msecs_to_jiffies(1000);
652 add_timer(&pHwData->LEDTimer);
Pekka Enberg80767e62009-04-08 11:13:57 +0300653
Pekka Enberga32b9812009-04-08 11:14:04 +0300654 SoftwareSet = hal_software_set(pHwData);
Pekka Enberg80767e62009-04-08 11:13:57 +0300655
Pekka Enberga32b9812009-04-08 11:14:04 +0300656#ifdef Vendor2
Pekka Enberg00e2e052009-04-08 11:13:59 +0300657 // Try to make sure the EEPROM contain
658 SoftwareSet >>= 8;
Pekka Enberga32b9812009-04-08 11:14:04 +0300659 if (SoftwareSet != 0x82)
Pekka Enberg00e2e052009-04-08 11:13:59 +0300660 return false;
Pekka Enberga32b9812009-04-08 11:14:04 +0300661#endif
Pekka Enberg00e2e052009-04-08 11:13:59 +0300662
663 Wb35Rx_start(hw);
664 Wb35Tx_EP2VM_start(priv);
665
666 return 0;
667
Pekka Enberga39ee672009-04-08 11:14:00 +0300668error_rx_destroy:
669 Wb35Rx_destroy(pHwData);
670error_tx_destroy:
671 Wb35Tx_destroy(pHwData);
672error_reg_destroy:
673 Wb35Reg_destroy(pHwData);
674
Pekka Enberg80767e62009-04-08 11:13:57 +0300675 pHwData->SurpriseRemove = 1;
Pekka Enbergcfe31f82009-04-08 11:13:58 +0300676 return -EINVAL;
Pekka Enberg80767e62009-04-08 11:13:57 +0300677}
678
Pekka Enberg26598512009-04-08 11:13:56 +0300679static int wb35_hw_init(struct ieee80211_hw *hw)
Pekka Enberg912b2092008-10-30 18:12:02 +0200680{
681 struct wbsoft_priv *priv = hw->priv;
Pekka Enberga32b9812009-04-08 11:14:04 +0300682 struct hw_data *pHwData = &priv->sHwData;
683 u8 EEPROM_region;
684 u8 HwRadioOff;
685 u8 *pMacAddr2;
686 u8 *pMacAddr;
Pekka Enberg26598512009-04-08 11:13:56 +0300687 int err;
Pekka Enberg912b2092008-10-30 18:12:02 +0200688
Pekka Enberg9ca748c2009-04-08 11:14:03 +0300689 pHwData->phy_type = RF_DECIDE_BY_INF;
Pekka Enberg912b2092008-10-30 18:12:02 +0200690
Pekka Enberga32b9812009-04-08 11:14:04 +0300691 priv->Mds.TxRTSThreshold = DEFAULT_RTSThreshold;
692 priv->Mds.TxFragmentThreshold = DEFAULT_FRAGMENT_THRESHOLD;
693
694 priv->sLocalPara.region_INF = REGION_AUTO;
695 priv->sLocalPara.TxRateMode = RATE_AUTO;
696 priv->sLocalPara.bMacOperationMode = MODE_802_11_BG;
697 priv->sLocalPara.MTUsize = MAX_ETHERNET_PACKET_SIZE;
698 priv->sLocalPara.bPreambleMode = AUTO_MODE;
699 priv->sLocalPara.bWepKeyError = false;
700 priv->sLocalPara.bToSelfPacketReceived = false;
701 priv->sLocalPara.WepKeyDetectTimerCount = 2 * 100; /* 2 seconds */
702
703 priv->sLocalPara.RadioOffStatus.boSwRadioOff = false;
Pekka Enberg912b2092008-10-30 18:12:02 +0200704
Pekka Enbergcfe31f82009-04-08 11:13:58 +0300705 err = hal_init_hardware(hw);
706 if (err)
Pekka Enberg912b2092008-10-30 18:12:02 +0200707 goto error;
708
Pekka Enberga32b9812009-04-08 11:14:04 +0300709 EEPROM_region = hal_get_region_from_EEPROM(pHwData);
Pekka Enberg912b2092008-10-30 18:12:02 +0200710 if (EEPROM_region != REGION_AUTO)
711 priv->sLocalPara.region = EEPROM_region;
712 else {
713 if (priv->sLocalPara.region_INF != REGION_AUTO)
714 priv->sLocalPara.region = priv->sLocalPara.region_INF;
715 else
Pekka Enberga32b9812009-04-08 11:14:04 +0300716 priv->sLocalPara.region = REGION_USA; /* default setting */
Pekka Enberg912b2092008-10-30 18:12:02 +0200717 }
718
719 // Get Software setting flag from hal
720 priv->sLocalPara.boAntennaDiversity = false;
721 if (hal_software_set(pHwData) & 0x00000001)
722 priv->sLocalPara.boAntennaDiversity = true;
723
Pekka Enberg912b2092008-10-30 18:12:02 +0200724 Mds_initial(priv);
725
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300726 /*
727 * If no user-defined address in the registry, use the addresss
728 * "burned" on the NIC instead.
729 */
Pekka Enberg912b2092008-10-30 18:12:02 +0200730 pMacAddr = priv->sLocalPara.ThisMacAddress;
731 pMacAddr2 = priv->sLocalPara.PermanentAddress;
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300732
733 /* Reading ethernet address from EEPROM */
Pekka Enberga32b9812009-04-08 11:14:04 +0300734 hal_get_permanent_address(pHwData, priv->sLocalPara.PermanentAddress);
Pekka Enberg912b2092008-10-30 18:12:02 +0200735 if (memcmp(pMacAddr, "\x00\x00\x00\x00\x00\x00", MAC_ADDR_LENGTH) == 0)
736 memcpy(pMacAddr, pMacAddr2, MAC_ADDR_LENGTH);
737 else {
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300738 /* Set the user define MAC address */
Pekka Enberga32b9812009-04-08 11:14:04 +0300739 hal_set_ethernet_address(pHwData,
740 priv->sLocalPara.ThisMacAddress);
Pekka Enberg912b2092008-10-30 18:12:02 +0200741 }
742
Pekka Enberg912b2092008-10-30 18:12:02 +0200743 priv->sLocalPara.bAntennaNo = hal_get_antenna_number(pHwData);
744#ifdef _PE_STATE_DUMP_
Pekka Enberg0c59dba2009-01-08 11:31:59 +0200745 printk("Driver init, antenna no = %d\n", psLOCAL->bAntennaNo);
Pekka Enberg912b2092008-10-30 18:12:02 +0200746#endif
Pekka Enberga32b9812009-04-08 11:14:04 +0300747 hal_get_hw_radio_off(pHwData);
Pekka Enberg912b2092008-10-30 18:12:02 +0200748
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300749 /* Waiting for HAL setting OK */
Pekka Enberg912b2092008-10-30 18:12:02 +0200750 while (!hal_idle(pHwData))
751 msleep(10);
752
753 MTO_Init(priv);
754
Pekka Enberga32b9812009-04-08 11:14:04 +0300755 HwRadioOff = hal_get_hw_radio_off(pHwData);
Pekka Enberg912b2092008-10-30 18:12:02 +0200756 priv->sLocalPara.RadioOffStatus.boHwRadioOff = !!HwRadioOff;
757
Pekka Enberga32b9812009-04-08 11:14:04 +0300758 hal_set_radio_mode(pHwData,
759 (unsigned char)(priv->sLocalPara.RadioOffStatus.
760 boSwRadioOff
761 || priv->sLocalPara.RadioOffStatus.
762 boHwRadioOff));
Pekka Enberg912b2092008-10-30 18:12:02 +0200763
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300764 /* Notify hal that the driver is ready now. */
765 hal_driver_init_OK(pHwData) = 1;
Pekka Enberg912b2092008-10-30 18:12:02 +0200766
767error:
Pekka Enberg26598512009-04-08 11:13:56 +0300768 return err;
Pekka Enberg912b2092008-10-30 18:12:02 +0200769}
770
Pekka Enberga32b9812009-04-08 11:14:04 +0300771static int wb35_probe(struct usb_interface *intf,
772 const struct usb_device_id *id_table)
Pavel Machek66101de2008-10-01 14:36:56 +0200773{
Pavel Machek66101de2008-10-01 14:36:56 +0200774 struct usb_device *udev = interface_to_usbdev(intf);
Pekka Enberga32b9812009-04-08 11:14:04 +0300775 struct usb_endpoint_descriptor *endpoint;
776 struct usb_host_interface *interface;
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300777 struct ieee80211_hw *dev;
Pekka Enberga32b9812009-04-08 11:14:04 +0300778 struct wbsoft_priv *priv;
779 struct wb_usb *pWbUsb;
Pekka Enbergacfa5112009-01-30 11:32:47 +0200780 int nr, err;
Pekka Enberga32b9812009-04-08 11:14:04 +0300781 u32 ltmp;
Pavel Machek66101de2008-10-01 14:36:56 +0200782
783 usb_get_dev(udev);
784
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300785 /* Check the device if it already be opened */
Pekka Enberga32b9812009-04-08 11:14:04 +0300786 nr = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
787 0x01,
788 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
789 0x0, 0x400, &ltmp, 4, HZ * 100);
Pekka Enbergacfa5112009-01-30 11:32:47 +0200790 if (nr < 0) {
791 err = nr;
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300792 goto error;
Pekka Enbergacfa5112009-01-30 11:32:47 +0200793 }
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300794
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300795 /* Is already initialized? */
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300796 ltmp = cpu_to_le32(ltmp);
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300797 if (ltmp) {
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300798 err = -EBUSY;
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300799 goto error;
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300800 }
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300801
Pekka Enberg1e8a2b62008-10-30 16:14:38 +0200802 dev = ieee80211_alloc_hw(sizeof(*priv), &wbsoft_ops);
Pekka Enbergacfa5112009-01-30 11:32:47 +0200803 if (!dev) {
804 err = -ENOMEM;
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300805 goto error;
Pekka Enbergacfa5112009-01-30 11:32:47 +0200806 }
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300807
Pekka Enberg1e8a2b62008-10-30 16:14:38 +0200808 priv = dev->priv;
Pekka Enberg1e8a2b62008-10-30 16:14:38 +0200809
Pekka Enberg912b2092008-10-30 18:12:02 +0200810 spin_lock_init(&priv->SpinLock);
811
Pekka Enberg1e8a2b62008-10-30 16:14:38 +0200812 pWbUsb = &priv->sHwData.WbUsb;
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300813 pWbUsb->udev = udev;
814
Pekka Enberga32b9812009-04-08 11:14:04 +0300815 interface = intf->cur_altsetting;
816 endpoint = &interface->endpoint[0].desc;
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300817
818 if (endpoint[2].wMaxPacketSize == 512) {
819 printk("[w35und] Working on USB 2.0\n");
820 pWbUsb->IsUsb20 = 1;
821 }
822
Pekka Enberg26598512009-04-08 11:13:56 +0300823 err = wb35_hw_init(dev);
824 if (err)
Pekka Enberg1e8a2b62008-10-30 16:14:38 +0200825 goto error_free_hw;
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300826
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300827 SET_IEEE80211_DEV(dev, &udev->dev);
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300828 {
Pekka Enberga32b9812009-04-08 11:14:04 +0300829 struct hw_data *pHwData = &priv->sHwData;
830 unsigned char dev_addr[MAX_ADDR_LEN];
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300831 hal_get_permanent_address(pHwData, dev_addr);
832 SET_IEEE80211_PERM_ADDR(dev, dev_addr);
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300833 }
Pavel Machek66101de2008-10-01 14:36:56 +0200834
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300835 dev->extra_tx_headroom = 12; /* FIXME */
Pavel Machek05e361c2009-01-30 10:05:25 +0100836 dev->flags = IEEE80211_HW_SIGNAL_UNSPEC;
837 dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
Pavel Machek66101de2008-10-01 14:36:56 +0200838
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300839 dev->channel_change_time = 1000;
Pavel Machek05e361c2009-01-30 10:05:25 +0100840 dev->max_signal = 100;
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300841 dev->queues = 1;
842
Pekka Enberga36e0892008-10-28 00:20:03 +0200843 dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &wbsoft_band_2GHz;
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300844
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300845 err = ieee80211_register_hw(dev);
846 if (err)
847 goto error_free_hw;
848
Pekka Enberg82fbb012009-04-16 14:43:14 +0300849 usb_set_intfdata(intf, dev);
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300850
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300851 return 0;
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300852
853error_free_hw:
854 ieee80211_free_hw(dev);
Pekka Enbergdc7e04f2008-10-21 12:14:58 +0300855error:
Pekka Enberg4af12e52008-10-27 23:29:31 +0200856 usb_put_dev(udev);
Pekka Enberg1523ddc2008-10-22 11:04:23 +0300857 return err;
Pavel Machek66101de2008-10-01 14:36:56 +0200858}
859
Pekka Enbergf592a852009-04-08 11:14:01 +0300860static void hal_halt(struct hw_data *pHwData)
861{
862 del_timer_sync(&pHwData->LEDTimer);
863 /* XXX: Wait for Timer DPC exit. */
864 msleep(100);
865 Wb35Rx_destroy(pHwData);
866 Wb35Tx_destroy(pHwData);
867 Wb35Reg_destroy(pHwData);
868}
869
Pekka Enberg912b2092008-10-30 18:12:02 +0200870static void wb35_hw_halt(struct wbsoft_priv *adapter)
871{
Pekka Enberga32b9812009-04-08 11:14:04 +0300872 Mds_Destroy(adapter);
Pekka Enberg912b2092008-10-30 18:12:02 +0200873
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300874 /* Turn off Rx and Tx hardware ability */
Pekka Enberga32b9812009-04-08 11:14:04 +0300875 hal_stop(&adapter->sHwData);
Pekka Enberg912b2092008-10-30 18:12:02 +0200876#ifdef _PE_USB_INI_DUMP_
Pekka Enberg0c59dba2009-01-08 11:31:59 +0200877 printk("[w35und] Hal_stop O.K.\n");
Pekka Enberg912b2092008-10-30 18:12:02 +0200878#endif
Pekka Enbergbdbb8832009-04-08 11:14:02 +0300879 /* Waiting Irp completed */
880 msleep(100);
Pekka Enberg912b2092008-10-30 18:12:02 +0200881
Pekka Enbergf592a852009-04-08 11:14:01 +0300882 hal_halt(&adapter->sHwData);
Pekka Enberg912b2092008-10-30 18:12:02 +0200883}
884
Pekka Enberg302bae82008-10-22 19:05:54 +0300885static void wb35_disconnect(struct usb_interface *intf)
Pavel Machek66101de2008-10-01 14:36:56 +0200886{
Pekka Enberg82fbb012009-04-16 14:43:14 +0300887 struct ieee80211_hw *hw = usb_get_intfdata(intf);
888 struct wbsoft_priv *priv = hw->priv;
Pavel Machek66101de2008-10-01 14:36:56 +0200889
Pekka Enberg912b2092008-10-30 18:12:02 +0200890 wb35_hw_halt(priv);
Pavel Machek66101de2008-10-01 14:36:56 +0200891
Pekka Enberg82fbb012009-04-16 14:43:14 +0300892 ieee80211_stop_queues(hw);
893 ieee80211_unregister_hw(hw);
894 ieee80211_free_hw(hw);
895
Pekka Enberg4af12e52008-10-27 23:29:31 +0200896 usb_set_intfdata(intf, NULL);
897 usb_put_dev(interface_to_usbdev(intf));
Pavel Machek66101de2008-10-01 14:36:56 +0200898}
899
Pekka Enbergdd38da42008-10-21 13:01:42 +0300900static struct usb_driver wb35_driver = {
901 .name = "w35und",
902 .id_table = wb35_table,
903 .probe = wb35_probe,
904 .disconnect = wb35_disconnect,
905};
Pavel Machek66101de2008-10-01 14:36:56 +0200906
Pekka Enbergdd38da42008-10-21 13:01:42 +0300907static int __init wb35_init(void)
908{
909 return usb_register(&wb35_driver);
910}
911
912static void __exit wb35_exit(void)
913{
914 usb_deregister(&wb35_driver);
915}
916
917module_init(wb35_init);
918module_exit(wb35_exit);