]>
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" |
9ce922fd PE |
10 | #include "mlmetxrx_f.h" |
11 | #include "wbhal_f.h" | |
12 | #include "wblinux_f.h" | |
66101de1 | 13 | |
dd38da46 PE |
14 | MODULE_AUTHOR(DRIVER_AUTHOR); |
15 | MODULE_DESCRIPTION(DRIVER_DESC); | |
66101de1 PM |
16 | MODULE_LICENSE("GPL"); |
17 | MODULE_VERSION("0.1"); | |
18 | ||
dd38da46 PE |
19 | static struct usb_device_id wb35_table[] __devinitdata = { |
20 | {USB_DEVICE(0x0416, 0x0035)}, | |
21 | {USB_DEVICE(0x18E8, 0x6201)}, | |
22 | {USB_DEVICE(0x18E8, 0x6206)}, | |
23 | {USB_DEVICE(0x18E8, 0x6217)}, | |
24 | {USB_DEVICE(0x18E8, 0x6230)}, | |
25 | {USB_DEVICE(0x18E8, 0x6233)}, | |
26 | {USB_DEVICE(0x1131, 0x2035)}, | |
68ab0c96 | 27 | { 0, } |
66101de1 PM |
28 | }; |
29 | ||
dd38da46 | 30 | MODULE_DEVICE_TABLE(usb, wb35_table); |
66101de1 | 31 | |
68ab0c96 | 32 | static struct ieee80211_rate wbsoft_rates[] = { |
66101de1 PM |
33 | { .bitrate = 10, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, |
34 | }; | |
35 | ||
68ab0c96 | 36 | static struct ieee80211_channel wbsoft_channels[] = { |
66101de1 PM |
37 | { .center_freq = 2412}, |
38 | }; | |
39 | ||
a36e0894 PE |
40 | static struct ieee80211_supported_band wbsoft_band_2GHz = { |
41 | .channels = wbsoft_channels, | |
42 | .n_channels = ARRAY_SIZE(wbsoft_channels), | |
43 | .bitrates = wbsoft_rates, | |
44 | .n_bitrates = ARRAY_SIZE(wbsoft_rates), | |
45 | }; | |
46 | ||
66101de1 PM |
47 | int wbsoft_enabled; |
48 | struct ieee80211_hw *my_dev; | |
66101de1 PM |
49 | |
50 | static int wbsoft_add_interface(struct ieee80211_hw *dev, | |
51 | struct ieee80211_if_init_conf *conf) | |
52 | { | |
53 | printk("wbsoft_add interface called\n"); | |
54 | return 0; | |
55 | } | |
56 | ||
57 | static void wbsoft_remove_interface(struct ieee80211_hw *dev, | |
58 | struct ieee80211_if_init_conf *conf) | |
59 | { | |
60 | printk("wbsoft_remove interface called\n"); | |
61 | } | |
62 | ||
68ab0c96 | 63 | static void wbsoft_stop(struct ieee80211_hw *hw) |
66101de1 | 64 | { |
68ab0c96 GKH |
65 | printk(KERN_INFO "%s called\n", __func__); |
66 | } | |
67 | ||
68 | static int wbsoft_get_stats(struct ieee80211_hw *hw, | |
69 | struct ieee80211_low_level_stats *stats) | |
70 | { | |
71 | printk(KERN_INFO "%s called\n", __func__); | |
72 | return 0; | |
73 | } | |
74 | ||
75 | static int wbsoft_get_tx_stats(struct ieee80211_hw *hw, | |
76 | struct ieee80211_tx_queue_stats *stats) | |
77 | { | |
78 | printk(KERN_INFO "%s called\n", __func__); | |
66101de1 PM |
79 | return 0; |
80 | } | |
81 | ||
82 | static void wbsoft_configure_filter(struct ieee80211_hw *dev, | |
83 | unsigned int changed_flags, | |
84 | unsigned int *total_flags, | |
85 | int mc_count, struct dev_mc_list *mclist) | |
86 | { | |
87 | unsigned int bit_nr, new_flags; | |
88 | u32 mc_filter[2]; | |
89 | int i; | |
90 | ||
91 | new_flags = 0; | |
92 | ||
93 | if (*total_flags & FIF_PROMISC_IN_BSS) { | |
94 | new_flags |= FIF_PROMISC_IN_BSS; | |
95 | mc_filter[1] = mc_filter[0] = ~0; | |
96 | } else if ((*total_flags & FIF_ALLMULTI) || (mc_count > 32)) { | |
97 | new_flags |= FIF_ALLMULTI; | |
98 | mc_filter[1] = mc_filter[0] = ~0; | |
99 | } else { | |
100 | mc_filter[1] = mc_filter[0] = 0; | |
101 | for (i = 0; i < mc_count; i++) { | |
102 | if (!mclist) | |
103 | break; | |
104 | printk("Should call ether_crc here\n"); | |
105 | //bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; | |
106 | bit_nr = 0; | |
107 | ||
108 | bit_nr &= 0x3F; | |
109 | mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); | |
110 | mclist = mclist->next; | |
111 | } | |
112 | } | |
113 | ||
114 | dev->flags &= ~IEEE80211_HW_RX_INCLUDES_FCS; | |
115 | ||
116 | *total_flags = new_flags; | |
117 | } | |
118 | ||
68ab0c96 | 119 | static int wbsoft_tx(struct ieee80211_hw *dev, struct sk_buff *skb) |
66101de1 | 120 | { |
cc180710 PE |
121 | struct wbsoft_priv *priv = dev->priv; |
122 | ||
123 | MLMESendFrame(priv->adapter, skb->data, skb->len, FRAME_TYPE_802_11_MANAGEMENT); | |
16d3659f | 124 | |
66101de1 PM |
125 | return NETDEV_TX_OK; |
126 | } | |
127 | ||
128 | ||
129 | static int wbsoft_start(struct ieee80211_hw *dev) | |
130 | { | |
131 | wbsoft_enabled = 1; | |
132 | printk("wbsoft_start called\n"); | |
133 | return 0; | |
134 | } | |
135 | ||
136 | static int wbsoft_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf) | |
137 | { | |
cc180710 PE |
138 | struct wbsoft_priv *priv = dev->priv; |
139 | ||
66101de1 PM |
140 | ChanInfo ch; |
141 | printk("wbsoft_config called\n"); | |
142 | ||
143 | ch.band = 1; | |
144 | ch.ChanNo = 1; /* Should use channel_num, or something, as that is already pre-translated */ | |
145 | ||
146 | ||
cc180710 PE |
147 | hal_set_current_channel(&priv->adapter->sHwData, ch); |
148 | hal_set_beacon_period(&priv->adapter->sHwData, conf->beacon_int); | |
149 | // hal_set_cap_info(&priv->adapter->sHwData, ?? ); | |
8b384e0c | 150 | // hal_set_ssid(phw_data_t pHwData, u8 * pssid, u8 ssid_len); ?? |
cc180710 PE |
151 | hal_set_accept_broadcast(&priv->adapter->sHwData, 1); |
152 | hal_set_accept_promiscuous(&priv->adapter->sHwData, 1); | |
153 | hal_set_accept_multicast(&priv->adapter->sHwData, 1); | |
154 | hal_set_accept_beacon(&priv->adapter->sHwData, 1); | |
155 | hal_set_radio_mode(&priv->adapter->sHwData, 0); | |
66101de1 PM |
156 | //hal_set_antenna_number( phw_data_t pHwData, u8 number ) |
157 | //hal_set_rf_power(phw_data_t pHwData, u8 PowerIndex) | |
158 | ||
159 | ||
cc180710 | 160 | // hal_start_bss(&priv->adapter->sHwData, WLAN_BSSTYPE_INFRASTRUCTURE); ?? |
66101de1 | 161 | |
8b384e0c | 162 | //void hal_set_rates(phw_data_t pHwData, u8 * pbss_rates, |
66101de1 PM |
163 | // u8 length, unsigned char basic_rate_set) |
164 | ||
165 | return 0; | |
166 | } | |
167 | ||
168 | static int wbsoft_config_interface(struct ieee80211_hw *dev, | |
169 | struct ieee80211_vif *vif, | |
170 | struct ieee80211_if_conf *conf) | |
171 | { | |
172 | printk("wbsoft_config_interface called\n"); | |
173 | return 0; | |
174 | } | |
175 | ||
176 | static u64 wbsoft_get_tsf(struct ieee80211_hw *dev) | |
177 | { | |
178 | printk("wbsoft_get_tsf called\n"); | |
179 | return 0; | |
180 | } | |
181 | ||
182 | static const struct ieee80211_ops wbsoft_ops = { | |
183 | .tx = wbsoft_tx, | |
184 | .start = wbsoft_start, /* Start can be pretty much empty as we do WbWLanInitialize() during probe? */ | |
68ab0c96 | 185 | .stop = wbsoft_stop, |
66101de1 PM |
186 | .add_interface = wbsoft_add_interface, |
187 | .remove_interface = wbsoft_remove_interface, | |
188 | .config = wbsoft_config, | |
189 | .config_interface = wbsoft_config_interface, | |
190 | .configure_filter = wbsoft_configure_filter, | |
68ab0c96 GKH |
191 | .get_stats = wbsoft_get_stats, |
192 | .get_tx_stats = wbsoft_get_tx_stats, | |
66101de1 PM |
193 | .get_tsf = wbsoft_get_tsf, |
194 | // conf_tx: hal_set_cwmin()/hal_set_cwmax; | |
195 | }; | |
196 | ||
302bae85 | 197 | static int wb35_probe(struct usb_interface *intf, const struct usb_device_id *id_table) |
66101de1 | 198 | { |
88ebc4b9 | 199 | struct wb35_adapter *adapter; |
66101de1 PM |
200 | PWBUSB pWbUsb; |
201 | struct usb_host_interface *interface; | |
202 | struct usb_endpoint_descriptor *endpoint; | |
66101de1 PM |
203 | u32 ltmp; |
204 | struct usb_device *udev = interface_to_usbdev(intf); | |
1523ddc4 PE |
205 | struct wbsoft_priv *priv; |
206 | struct ieee80211_hw *dev; | |
1523ddc4 | 207 | int err; |
66101de1 PM |
208 | |
209 | usb_get_dev(udev); | |
210 | ||
dc7e04fe | 211 | // 20060630.2 Check the device if it already be opened |
1523ddc4 | 212 | err = usb_control_msg(udev, usb_rcvctrlpipe( udev, 0 ), |
dc7e04fe PE |
213 | 0x01, USB_TYPE_VENDOR|USB_RECIP_DEVICE|USB_DIR_IN, |
214 | 0x0, 0x400, <mp, 4, HZ*100 ); | |
1523ddc4 | 215 | if (err) |
dc7e04fe | 216 | goto error; |
66101de1 | 217 | |
dc7e04fe | 218 | ltmp = cpu_to_le32(ltmp); |
1523ddc4 PE |
219 | if (ltmp) { // Is already initialized? |
220 | err = -EBUSY; | |
dc7e04fe | 221 | goto error; |
1523ddc4 | 222 | } |
66101de1 | 223 | |
88ebc4b9 | 224 | adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); |
1523ddc4 PE |
225 | if (!adapter) { |
226 | err = -ENOMEM; | |
227 | goto error; | |
228 | } | |
66101de1 | 229 | |
88ebc4b9 | 230 | pWbUsb = &adapter->sHwData.WbUsb; |
dc7e04fe | 231 | pWbUsb->udev = udev; |
66101de1 | 232 | |
dc7e04fe PE |
233 | interface = intf->cur_altsetting; |
234 | endpoint = &interface->endpoint[0].desc; | |
66101de1 | 235 | |
dc7e04fe PE |
236 | if (endpoint[2].wMaxPacketSize == 512) { |
237 | printk("[w35und] Working on USB 2.0\n"); | |
238 | pWbUsb->IsUsb20 = 1; | |
239 | } | |
66101de1 | 240 | |
88ebc4b9 | 241 | if (!WbWLanInitialize(adapter)) { |
1523ddc4 PE |
242 | err = -EINVAL; |
243 | goto error_free_adapter; | |
dc7e04fe | 244 | } |
66101de1 | 245 | |
1523ddc4 PE |
246 | dev = ieee80211_alloc_hw(sizeof(*priv), &wbsoft_ops); |
247 | if (!dev) | |
248 | goto error_free_adapter; | |
66101de1 | 249 | |
cc180710 PE |
250 | priv = dev->priv; |
251 | priv->adapter = adapter; | |
252 | ||
1523ddc4 | 253 | my_dev = dev; |
66101de1 | 254 | |
1523ddc4 PE |
255 | SET_IEEE80211_DEV(dev, &udev->dev); |
256 | { | |
257 | phw_data_t pHwData = &adapter->sHwData; | |
9ce922fd | 258 | unsigned char dev_addr[MAX_ADDR_LEN]; |
1523ddc4 PE |
259 | hal_get_permanent_address(pHwData, dev_addr); |
260 | SET_IEEE80211_PERM_ADDR(dev, dev_addr); | |
261 | } | |
66101de1 | 262 | |
1523ddc4 PE |
263 | dev->extra_tx_headroom = 12; /* FIXME */ |
264 | dev->flags = 0; | |
66101de1 | 265 | |
1523ddc4 PE |
266 | dev->channel_change_time = 1000; |
267 | dev->queues = 1; | |
dc7e04fe | 268 | |
a36e0894 | 269 | dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &wbsoft_band_2GHz; |
66101de1 | 270 | |
1523ddc4 PE |
271 | err = ieee80211_register_hw(dev); |
272 | if (err) | |
273 | goto error_free_hw; | |
66101de1 | 274 | |
1523ddc4 | 275 | usb_set_intfdata(intf, adapter); |
66101de1 | 276 | |
dc7e04fe | 277 | return 0; |
1523ddc4 PE |
278 | |
279 | error_free_hw: | |
280 | ieee80211_free_hw(dev); | |
281 | error_free_adapter: | |
282 | kfree(adapter); | |
dc7e04fe | 283 | error: |
4af12e55 | 284 | usb_put_dev(udev); |
1523ddc4 | 285 | return err; |
66101de1 PM |
286 | } |
287 | ||
288 | void packet_came(char *pRxBufferAddress, int PacketSize) | |
289 | { | |
290 | struct sk_buff *skb; | |
291 | struct ieee80211_rx_status rx_status = {0}; | |
292 | ||
293 | if (!wbsoft_enabled) | |
294 | return; | |
295 | ||
296 | skb = dev_alloc_skb(PacketSize); | |
297 | if (!skb) { | |
298 | printk("Not enough memory for packet, FIXME\n"); | |
299 | return; | |
300 | } | |
301 | ||
302 | memcpy(skb_put(skb, PacketSize), | |
303 | pRxBufferAddress, | |
304 | PacketSize); | |
305 | ||
306 | /* | |
307 | rx_status.rate = 10; | |
308 | rx_status.channel = 1; | |
309 | rx_status.freq = 12345; | |
310 | rx_status.phymode = MODE_IEEE80211B; | |
311 | */ | |
312 | ||
313 | ieee80211_rx_irqsafe(my_dev, skb, &rx_status); | |
314 | } | |
315 | ||
302bae85 | 316 | static void wb35_disconnect(struct usb_interface *intf) |
66101de1 | 317 | { |
4af12e55 | 318 | struct wb35_adapter *adapter = usb_get_intfdata(intf); |
66101de1 | 319 | |
88ebc4b9 | 320 | WbWlanHalt(adapter); |
66101de1 | 321 | |
4af12e55 PE |
322 | usb_set_intfdata(intf, NULL); |
323 | usb_put_dev(interface_to_usbdev(intf)); | |
66101de1 PM |
324 | } |
325 | ||
dd38da46 PE |
326 | static struct usb_driver wb35_driver = { |
327 | .name = "w35und", | |
328 | .id_table = wb35_table, | |
329 | .probe = wb35_probe, | |
330 | .disconnect = wb35_disconnect, | |
331 | }; | |
332 | ||
333 | static int __init wb35_init(void) | |
334 | { | |
335 | return usb_register(&wb35_driver); | |
336 | } | |
337 | ||
338 | static void __exit wb35_exit(void) | |
339 | { | |
340 | usb_deregister(&wb35_driver); | |
341 | } | |
66101de1 | 342 | |
dd38da46 PE |
343 | module_init(wb35_init); |
344 | module_exit(wb35_exit); |