Commit | Line | Data |
---|---|---|
66101de1 PM |
1 | /* |
2 | * Copyright 2008 Pavel Machek <pavel@suse.cz> | |
3 | * | |
4 | * Distribute under GPLv2. | |
5 | */ | |
66101de1 | 6 | #include <net/mac80211.h> |
80aba536 PE |
7 | #include <linux/usb.h> |
8 | ||
cc180710 | 9 | #include "core.h" |
912b209f | 10 | #include "mds_f.h" |
9ce922fd | 11 | #include "mlmetxrx_f.h" |
64328c87 | 12 | #include "mto.h" |
9ce922fd PE |
13 | #include "wbhal_f.h" |
14 | #include "wblinux_f.h" | |
66101de1 | 15 | |
7b9a79bf PE |
16 | MODULE_AUTHOR("Original by: Jeff Lee<YY_Lee@issc.com.tw> Adapted to 2.6.x by Costantino Leandro (Rxart Desktop) <le_costantino@pixartargentina.com.ar>"); |
17 | MODULE_DESCRIPTION("IS89C35 802.11bg WLAN USB Driver"); | |
66101de1 PM |
18 | MODULE_LICENSE("GPL"); |
19 | MODULE_VERSION("0.1"); | |
20 | ||
dd38da46 PE |
21 | static struct usb_device_id wb35_table[] __devinitdata = { |
22 | {USB_DEVICE(0x0416, 0x0035)}, | |
23 | {USB_DEVICE(0x18E8, 0x6201)}, | |
24 | {USB_DEVICE(0x18E8, 0x6206)}, | |
25 | {USB_DEVICE(0x18E8, 0x6217)}, | |
26 | {USB_DEVICE(0x18E8, 0x6230)}, | |
27 | {USB_DEVICE(0x18E8, 0x6233)}, | |
28 | {USB_DEVICE(0x1131, 0x2035)}, | |
68ab0c96 | 29 | { 0, } |
66101de1 PM |
30 | }; |
31 | ||
dd38da46 | 32 | MODULE_DEVICE_TABLE(usb, wb35_table); |
66101de1 | 33 | |
68ab0c96 | 34 | static struct ieee80211_rate wbsoft_rates[] = { |
66101de1 PM |
35 | { .bitrate = 10, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, |
36 | }; | |
37 | ||
68ab0c96 | 38 | static struct ieee80211_channel wbsoft_channels[] = { |
66101de1 PM |
39 | { .center_freq = 2412}, |
40 | }; | |
41 | ||
a36e0894 PE |
42 | static struct ieee80211_supported_band wbsoft_band_2GHz = { |
43 | .channels = wbsoft_channels, | |
44 | .n_channels = ARRAY_SIZE(wbsoft_channels), | |
45 | .bitrates = wbsoft_rates, | |
46 | .n_bitrates = ARRAY_SIZE(wbsoft_rates), | |
47 | }; | |
48 | ||
66101de1 PM |
49 | static int wbsoft_add_interface(struct ieee80211_hw *dev, |
50 | struct ieee80211_if_init_conf *conf) | |
51 | { | |
52 | printk("wbsoft_add interface called\n"); | |
53 | return 0; | |
54 | } | |
55 | ||
56 | static void wbsoft_remove_interface(struct ieee80211_hw *dev, | |
57 | struct ieee80211_if_init_conf *conf) | |
58 | { | |
59 | printk("wbsoft_remove interface called\n"); | |
60 | } | |
61 | ||
68ab0c96 | 62 | static void wbsoft_stop(struct ieee80211_hw *hw) |
66101de1 | 63 | { |
68ab0c96 GKH |
64 | printk(KERN_INFO "%s called\n", __func__); |
65 | } | |
66 | ||
67 | static int wbsoft_get_stats(struct ieee80211_hw *hw, | |
68 | struct ieee80211_low_level_stats *stats) | |
69 | { | |
70 | printk(KERN_INFO "%s called\n", __func__); | |
71 | return 0; | |
72 | } | |
73 | ||
74 | static int wbsoft_get_tx_stats(struct ieee80211_hw *hw, | |
75 | struct ieee80211_tx_queue_stats *stats) | |
76 | { | |
77 | printk(KERN_INFO "%s called\n", __func__); | |
66101de1 PM |
78 | return 0; |
79 | } | |
80 | ||
81 | static void wbsoft_configure_filter(struct ieee80211_hw *dev, | |
82 | unsigned int changed_flags, | |
83 | unsigned int *total_flags, | |
84 | int mc_count, struct dev_mc_list *mclist) | |
85 | { | |
86 | unsigned int bit_nr, new_flags; | |
87 | u32 mc_filter[2]; | |
88 | int i; | |
89 | ||
90 | new_flags = 0; | |
91 | ||
92 | if (*total_flags & FIF_PROMISC_IN_BSS) { | |
93 | new_flags |= FIF_PROMISC_IN_BSS; | |
94 | mc_filter[1] = mc_filter[0] = ~0; | |
95 | } else if ((*total_flags & FIF_ALLMULTI) || (mc_count > 32)) { | |
96 | new_flags |= FIF_ALLMULTI; | |
97 | mc_filter[1] = mc_filter[0] = ~0; | |
98 | } else { | |
99 | mc_filter[1] = mc_filter[0] = 0; | |
100 | for (i = 0; i < mc_count; i++) { | |
101 | if (!mclist) | |
102 | break; | |
103 | printk("Should call ether_crc here\n"); | |
104 | //bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; | |
105 | bit_nr = 0; | |
106 | ||
107 | bit_nr &= 0x3F; | |
108 | mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); | |
109 | mclist = mclist->next; | |
110 | } | |
111 | } | |
112 | ||
113 | dev->flags &= ~IEEE80211_HW_RX_INCLUDES_FCS; | |
114 | ||
115 | *total_flags = new_flags; | |
116 | } | |
117 | ||
68ab0c96 | 118 | static int wbsoft_tx(struct ieee80211_hw *dev, struct sk_buff *skb) |
66101de1 | 119 | { |
cc180710 PE |
120 | struct wbsoft_priv *priv = dev->priv; |
121 | ||
1e8a2b60 | 122 | MLMESendFrame(priv, skb->data, skb->len, FRAME_TYPE_802_11_MANAGEMENT); |
16d3659f | 123 | |
66101de1 PM |
124 | return NETDEV_TX_OK; |
125 | } | |
126 | ||
127 | ||
128 | static int wbsoft_start(struct ieee80211_hw *dev) | |
129 | { | |
c930e0c0 PE |
130 | struct wbsoft_priv *priv = dev->priv; |
131 | ||
132 | priv->enabled = true; | |
133 | ||
66101de1 PM |
134 | return 0; |
135 | } | |
136 | ||
f02466fc | 137 | static int wbsoft_config(struct ieee80211_hw *dev, u32 changed) |
66101de1 | 138 | { |
cc180710 | 139 | struct wbsoft_priv *priv = dev->priv; |
f02466fc | 140 | struct ieee80211_conf *conf = &dev->conf; |
66101de1 | 141 | ChanInfo ch; |
f02466fc | 142 | |
66101de1 PM |
143 | printk("wbsoft_config called\n"); |
144 | ||
145 | ch.band = 1; | |
146 | ch.ChanNo = 1; /* Should use channel_num, or something, as that is already pre-translated */ | |
147 | ||
148 | ||
1e8a2b60 PE |
149 | hal_set_current_channel(&priv->sHwData, ch); |
150 | hal_set_beacon_period(&priv->sHwData, conf->beacon_int); | |
151 | // hal_set_cap_info(&priv->sHwData, ?? ); | |
8e41b4b6 | 152 | // hal_set_ssid(struct hw_data * pHwData, u8 * pssid, u8 ssid_len); ?? |
1e8a2b60 PE |
153 | hal_set_accept_broadcast(&priv->sHwData, 1); |
154 | hal_set_accept_promiscuous(&priv->sHwData, 1); | |
155 | hal_set_accept_multicast(&priv->sHwData, 1); | |
156 | hal_set_accept_beacon(&priv->sHwData, 1); | |
157 | hal_set_radio_mode(&priv->sHwData, 0); | |
8e41b4b6 PE |
158 | //hal_set_antenna_number( struct hw_data * pHwData, u8 number ) |
159 | //hal_set_rf_power(struct hw_data * pHwData, u8 PowerIndex) | |
66101de1 PM |
160 | |
161 | ||
1e8a2b60 | 162 | // hal_start_bss(&priv->sHwData, WLAN_BSSTYPE_INFRASTRUCTURE); ?? |
66101de1 | 163 | |
8e41b4b6 | 164 | //void hal_set_rates(struct hw_data * pHwData, u8 * pbss_rates, |
66101de1 PM |
165 | // u8 length, unsigned char basic_rate_set) |
166 | ||
167 | return 0; | |
168 | } | |
169 | ||
170 | static int wbsoft_config_interface(struct ieee80211_hw *dev, | |
171 | struct ieee80211_vif *vif, | |
172 | struct ieee80211_if_conf *conf) | |
173 | { | |
174 | printk("wbsoft_config_interface called\n"); | |
175 | return 0; | |
176 | } | |
177 | ||
178 | static u64 wbsoft_get_tsf(struct ieee80211_hw *dev) | |
179 | { | |
180 | printk("wbsoft_get_tsf called\n"); | |
181 | return 0; | |
182 | } | |
183 | ||
184 | static const struct ieee80211_ops wbsoft_ops = { | |
185 | .tx = wbsoft_tx, | |
912b209f | 186 | .start = wbsoft_start, /* Start can be pretty much empty as we do wb35_hw_init() during probe? */ |
68ab0c96 | 187 | .stop = wbsoft_stop, |
66101de1 PM |
188 | .add_interface = wbsoft_add_interface, |
189 | .remove_interface = wbsoft_remove_interface, | |
190 | .config = wbsoft_config, | |
191 | .config_interface = wbsoft_config_interface, | |
192 | .configure_filter = wbsoft_configure_filter, | |
68ab0c96 GKH |
193 | .get_stats = wbsoft_get_stats, |
194 | .get_tx_stats = wbsoft_get_tx_stats, | |
66101de1 PM |
195 | .get_tsf = wbsoft_get_tsf, |
196 | // conf_tx: hal_set_cwmin()/hal_set_cwmax; | |
197 | }; | |
198 | ||
912b209f PE |
199 | static unsigned char wb35_hw_init(struct ieee80211_hw *hw) |
200 | { | |
201 | struct wbsoft_priv *priv = hw->priv; | |
8e41b4b6 | 202 | struct hw_data * pHwData; |
912b209f PE |
203 | u8 *pMacAddr; |
204 | u8 *pMacAddr2; | |
205 | u32 InitStep = 0; | |
206 | u8 EEPROM_region; | |
207 | u8 HwRadioOff; | |
208 | ||
209 | // | |
210 | // Setting default value for Linux | |
211 | // | |
212 | priv->sLocalPara.region_INF = REGION_AUTO; | |
213 | priv->sLocalPara.TxRateMode = RATE_AUTO; | |
214 | priv->sLocalPara.bMacOperationMode = MODE_802_11_BG; // B/G mode | |
215 | priv->Mds.TxRTSThreshold = DEFAULT_RTSThreshold; | |
216 | priv->Mds.TxFragmentThreshold = DEFAULT_FRAGMENT_THRESHOLD; | |
217 | hal_set_phy_type( &priv->sHwData, RF_WB_242_1 ); | |
218 | priv->sLocalPara.MTUsize = MAX_ETHERNET_PACKET_SIZE; | |
219 | priv->sLocalPara.bPreambleMode = AUTO_MODE; | |
220 | priv->sLocalPara.RadioOffStatus.boSwRadioOff = false; | |
221 | pHwData = &priv->sHwData; | |
222 | hal_set_phy_type( pHwData, RF_DECIDE_BY_INF ); | |
223 | ||
912b209f PE |
224 | //added by ws for wep key error detection |
225 | priv->sLocalPara.bWepKeyError= false; | |
226 | priv->sLocalPara.bToSelfPacketReceived = false; | |
227 | priv->sLocalPara.WepKeyDetectTimerCount= 2 * 100; /// 2 seconds | |
228 | ||
229 | // Initial USB hal | |
230 | InitStep = 1; | |
231 | pHwData = &priv->sHwData; | |
232 | if (!hal_init_hardware(hw)) | |
233 | goto error; | |
234 | ||
235 | EEPROM_region = hal_get_region_from_EEPROM( pHwData ); | |
236 | if (EEPROM_region != REGION_AUTO) | |
237 | priv->sLocalPara.region = EEPROM_region; | |
238 | else { | |
239 | if (priv->sLocalPara.region_INF != REGION_AUTO) | |
240 | priv->sLocalPara.region = priv->sLocalPara.region_INF; | |
241 | else | |
242 | priv->sLocalPara.region = REGION_USA; //default setting | |
243 | } | |
244 | ||
245 | // Get Software setting flag from hal | |
246 | priv->sLocalPara.boAntennaDiversity = false; | |
247 | if (hal_software_set(pHwData) & 0x00000001) | |
248 | priv->sLocalPara.boAntennaDiversity = true; | |
249 | ||
250 | // | |
251 | // For TS module | |
252 | // | |
253 | InitStep = 2; | |
254 | ||
255 | // For MDS module | |
256 | InitStep = 3; | |
257 | Mds_initial(priv); | |
258 | ||
259 | //======================================= | |
260 | // Initialize the SME, SCAN, MLME, ROAM | |
261 | //======================================= | |
262 | InitStep = 4; | |
263 | InitStep = 5; | |
264 | InitStep = 6; | |
265 | ||
266 | // If no user-defined address in the registry, use the addresss "burned" on the NIC instead. | |
267 | pMacAddr = priv->sLocalPara.ThisMacAddress; | |
268 | pMacAddr2 = priv->sLocalPara.PermanentAddress; | |
269 | hal_get_permanent_address( pHwData, priv->sLocalPara.PermanentAddress );// Reading ethernet address from EEPROM | |
270 | if (memcmp(pMacAddr, "\x00\x00\x00\x00\x00\x00", MAC_ADDR_LENGTH) == 0) | |
271 | memcpy(pMacAddr, pMacAddr2, MAC_ADDR_LENGTH); | |
272 | else { | |
273 | // Set the user define MAC address | |
274 | hal_set_ethernet_address(pHwData, priv->sLocalPara.ThisMacAddress); | |
275 | } | |
276 | ||
277 | //get current antenna | |
278 | priv->sLocalPara.bAntennaNo = hal_get_antenna_number(pHwData); | |
279 | #ifdef _PE_STATE_DUMP_ | |
0c59dbaa | 280 | printk("Driver init, antenna no = %d\n", psLOCAL->bAntennaNo); |
912b209f PE |
281 | #endif |
282 | hal_get_hw_radio_off( pHwData ); | |
283 | ||
284 | // Waiting for HAL setting OK | |
285 | while (!hal_idle(pHwData)) | |
286 | msleep(10); | |
287 | ||
288 | MTO_Init(priv); | |
289 | ||
290 | HwRadioOff = hal_get_hw_radio_off( pHwData ); | |
291 | priv->sLocalPara.RadioOffStatus.boHwRadioOff = !!HwRadioOff; | |
292 | ||
293 | hal_set_radio_mode( pHwData, (unsigned char)(priv->sLocalPara.RadioOffStatus.boSwRadioOff || priv->sLocalPara.RadioOffStatus.boHwRadioOff) ); | |
294 | ||
295 | hal_driver_init_OK(pHwData) = 1; // Notify hal that the driver is ready now. | |
296 | //set a tx power for reference..... | |
297 | // sme_set_tx_power_level(priv, 12); FIXME? | |
298 | return true; | |
299 | ||
300 | error: | |
301 | switch (InitStep) { | |
302 | case 5: | |
303 | case 4: | |
304 | case 3: Mds_Destroy( priv ); | |
305 | case 2: | |
54369cc6 | 306 | case 1: hal_halt( pHwData, NULL ); |
912b209f PE |
307 | case 0: break; |
308 | } | |
309 | ||
310 | return false; | |
311 | } | |
312 | ||
302bae85 | 313 | static int wb35_probe(struct usb_interface *intf, const struct usb_device_id *id_table) |
66101de1 | 314 | { |
eb62f3ea | 315 | struct wb_usb *pWbUsb; |
66101de1 PM |
316 | struct usb_host_interface *interface; |
317 | struct usb_endpoint_descriptor *endpoint; | |
66101de1 PM |
318 | u32 ltmp; |
319 | struct usb_device *udev = interface_to_usbdev(intf); | |
1523ddc4 PE |
320 | struct wbsoft_priv *priv; |
321 | struct ieee80211_hw *dev; | |
acfa5110 | 322 | int nr, err; |
66101de1 PM |
323 | |
324 | usb_get_dev(udev); | |
325 | ||
dc7e04fe | 326 | // 20060630.2 Check the device if it already be opened |
acfa5110 PE |
327 | nr = usb_control_msg(udev, usb_rcvctrlpipe( udev, 0 ), |
328 | 0x01, USB_TYPE_VENDOR|USB_RECIP_DEVICE|USB_DIR_IN, | |
329 | 0x0, 0x400, <mp, 4, HZ*100 ); | |
330 | if (nr < 0) { | |
331 | err = nr; | |
dc7e04fe | 332 | goto error; |
acfa5110 | 333 | } |
66101de1 | 334 | |
dc7e04fe | 335 | ltmp = cpu_to_le32(ltmp); |
1523ddc4 PE |
336 | if (ltmp) { // Is already initialized? |
337 | err = -EBUSY; | |
dc7e04fe | 338 | goto error; |
1523ddc4 | 339 | } |
66101de1 | 340 | |
1e8a2b60 | 341 | dev = ieee80211_alloc_hw(sizeof(*priv), &wbsoft_ops); |
acfa5110 PE |
342 | if (!dev) { |
343 | err = -ENOMEM; | |
1523ddc4 | 344 | goto error; |
acfa5110 | 345 | } |
66101de1 | 346 | |
1e8a2b60 | 347 | priv = dev->priv; |
1e8a2b60 | 348 | |
912b209f PE |
349 | spin_lock_init(&priv->SpinLock); |
350 | ||
1e8a2b60 | 351 | pWbUsb = &priv->sHwData.WbUsb; |
dc7e04fe | 352 | pWbUsb->udev = udev; |
66101de1 | 353 | |
dc7e04fe PE |
354 | interface = intf->cur_altsetting; |
355 | endpoint = &interface->endpoint[0].desc; | |
66101de1 | 356 | |
dc7e04fe PE |
357 | if (endpoint[2].wMaxPacketSize == 512) { |
358 | printk("[w35und] Working on USB 2.0\n"); | |
359 | pWbUsb->IsUsb20 = 1; | |
360 | } | |
66101de1 | 361 | |
912b209f | 362 | if (!wb35_hw_init(dev)) { |
1523ddc4 | 363 | err = -EINVAL; |
1e8a2b60 | 364 | goto error_free_hw; |
dc7e04fe | 365 | } |
66101de1 | 366 | |
1523ddc4 PE |
367 | SET_IEEE80211_DEV(dev, &udev->dev); |
368 | { | |
8e41b4b6 | 369 | struct hw_data * pHwData = &priv->sHwData; |
9ce922fd | 370 | unsigned char dev_addr[MAX_ADDR_LEN]; |
1523ddc4 PE |
371 | hal_get_permanent_address(pHwData, dev_addr); |
372 | SET_IEEE80211_PERM_ADDR(dev, dev_addr); | |
373 | } | |
66101de1 | 374 | |
1523ddc4 | 375 | dev->extra_tx_headroom = 12; /* FIXME */ |
05e361ca PM |
376 | dev->flags = IEEE80211_HW_SIGNAL_UNSPEC; |
377 | dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); | |
66101de1 | 378 | |
1523ddc4 | 379 | dev->channel_change_time = 1000; |
05e361ca | 380 | dev->max_signal = 100; |
1523ddc4 | 381 | dev->queues = 1; |
dc7e04fe | 382 | |
a36e0894 | 383 | dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &wbsoft_band_2GHz; |
66101de1 | 384 | |
1523ddc4 PE |
385 | err = ieee80211_register_hw(dev); |
386 | if (err) | |
387 | goto error_free_hw; | |
66101de1 | 388 | |
82fbb01c | 389 | usb_set_intfdata(intf, dev); |
66101de1 | 390 | |
dc7e04fe | 391 | return 0; |
1523ddc4 PE |
392 | |
393 | error_free_hw: | |
394 | ieee80211_free_hw(dev); | |
dc7e04fe | 395 | error: |
4af12e55 | 396 | usb_put_dev(udev); |
1523ddc4 | 397 | return err; |
66101de1 PM |
398 | } |
399 | ||
912b209f PE |
400 | static void wb35_hw_halt(struct wbsoft_priv *adapter) |
401 | { | |
912b209f PE |
402 | Mds_Destroy( adapter ); |
403 | ||
404 | // Turn off Rx and Tx hardware ability | |
405 | hal_stop( &adapter->sHwData ); | |
406 | #ifdef _PE_USB_INI_DUMP_ | |
0c59dbaa | 407 | printk("[w35und] Hal_stop O.K.\n"); |
912b209f PE |
408 | #endif |
409 | msleep(100);// Waiting Irp completed | |
410 | ||
912b209f PE |
411 | // Halt the HAL |
412 | hal_halt(&adapter->sHwData, NULL); | |
413 | } | |
414 | ||
415 | ||
302bae85 | 416 | static void wb35_disconnect(struct usb_interface *intf) |
66101de1 | 417 | { |
82fbb01c PE |
418 | struct ieee80211_hw *hw = usb_get_intfdata(intf); |
419 | struct wbsoft_priv *priv = hw->priv; | |
66101de1 | 420 | |
912b209f | 421 | wb35_hw_halt(priv); |
66101de1 | 422 | |
82fbb01c PE |
423 | ieee80211_stop_queues(hw); |
424 | ieee80211_unregister_hw(hw); | |
425 | ieee80211_free_hw(hw); | |
426 | ||
4af12e55 PE |
427 | usb_set_intfdata(intf, NULL); |
428 | usb_put_dev(interface_to_usbdev(intf)); | |
66101de1 PM |
429 | } |
430 | ||
dd38da46 PE |
431 | static struct usb_driver wb35_driver = { |
432 | .name = "w35und", | |
433 | .id_table = wb35_table, | |
434 | .probe = wb35_probe, | |
435 | .disconnect = wb35_disconnect, | |
436 | }; | |
437 | ||
438 | static int __init wb35_init(void) | |
439 | { | |
440 | return usb_register(&wb35_driver); | |
441 | } | |
442 | ||
443 | static void __exit wb35_exit(void) | |
444 | { | |
445 | usb_deregister(&wb35_driver); | |
446 | } | |
66101de1 | 447 | |
dd38da46 PE |
448 | module_init(wb35_init); |
449 | module_exit(wb35_exit); |