1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include "hfa384x_usb.c" |
3 | #include "prism2mgmt.c" |
4 | #include "prism2mib.c" |
5 | #include "prism2sta.c" |
6 | #include "prism2fw.c" |
7 | |
8 | #define PRISM_DEV(vid, pid, name) \ |
9 | { USB_DEVICE(vid, pid), \ |
10 | .driver_info = (unsigned long)name } |
11 | |
12 | static const struct usb_device_id usb_prism_tbl[] = { |
13 | PRISM_DEV(0x04bb, 0x0922, "IOData AirPort WN-B11/USBS" ), |
14 | PRISM_DEV(0x07aa, 0x0012, "Corega USB Wireless LAN Stick-11" ), |
15 | PRISM_DEV(0x09aa, 0x3642, "Prism2.x 11Mbps USB WLAN Adapter" ), |
16 | PRISM_DEV(0x1668, 0x0408, "Actiontec Prism2.5 11Mbps USB WLAN Adapter" ), |
17 | PRISM_DEV(0x1668, 0x0421, "Actiontec Prism2.5 11Mbps USB WLAN Adapter" ), |
18 | PRISM_DEV(0x1915, 0x2236, "Linksys WUSB11v3.0 11Mbps USB WLAN Adapter" ), |
19 | PRISM_DEV(0x066b, 0x2212, "Linksys WUSB11v2.5 11Mbps USB WLAN Adapter" ), |
20 | PRISM_DEV(0x066b, 0x2213, "Linksys WUSB12v1.1 11Mbps USB WLAN Adapter" ), |
21 | PRISM_DEV(0x0411, 0x0016, "Melco WLI-USB-S11 11Mbps WLAN Adapter" ), |
22 | PRISM_DEV(0x08de, 0x7a01, "PRISM25 USB IEEE 802.11 Mini Adapter" ), |
23 | PRISM_DEV(0x8086, 0x1111, "Intel PRO/Wireless 2011B USB LAN Adapter" ), |
24 | PRISM_DEV(0x0d8e, 0x7a01, "PRISM25 IEEE 802.11 Mini USB Adapter" ), |
25 | PRISM_DEV(0x045e, 0x006e, "Microsoft MN510 USB Wireless Adapter" ), |
26 | PRISM_DEV(0x0967, 0x0204, "Acer Warplink USB Adapter" ), |
27 | PRISM_DEV(0x0cde, 0x0002, "Z-Com 725/726 Prism2.5 USB/USB Integrated" ), |
28 | PRISM_DEV(0x0cde, 0x0005, "Z-Com Xl735 USB Wireless 802.11b Adapter" ), |
29 | PRISM_DEV(0x413c, 0x8100, "Dell TrueMobile 1180 USB Wireless Adapter" ), |
30 | PRISM_DEV(0x0b3b, 0x1601, "ALLNET 0193 11Mbps USB WLAN Adapter" ), |
31 | PRISM_DEV(0x0b3b, 0x1602, "ZyXEL ZyAIR B200 USB Wireless Adapter" ), |
32 | PRISM_DEV(0x0baf, 0x00eb, "USRobotics USR1120 USB Wireless Adapter" ), |
33 | PRISM_DEV(0x0411, 0x0027, "Melco WLI-USB-KS11G 11Mbps WLAN Adapter" ), |
34 | PRISM_DEV(0x04f1, 0x3009, "JVC MP-XP7250 Builtin USB WLAN Adapter" ), |
35 | PRISM_DEV(0x0846, 0x4110, "NetGear MA111" ), |
36 | PRISM_DEV(0x03f3, 0x0020, "Adaptec AWN-8020 USB WLAN Adapter" ), |
37 | PRISM_DEV(0x2821, 0x3300, "ASUS-WL140 / Hawking HighDB USB Wireless Adapter" ), |
38 | PRISM_DEV(0x2001, 0x3700, "DWL-122 USB Wireless Adapter" ), |
39 | PRISM_DEV(0x2001, 0x3702, "DWL-120 Rev F USB Wireless Adapter" ), |
40 | PRISM_DEV(0x50c2, 0x4013, "Averatec USB WLAN Adapter" ), |
41 | PRISM_DEV(0x2c02, 0x14ea, "Planex GW-US11H USB WLAN Adapter" ), |
42 | PRISM_DEV(0x124a, 0x168b, "Airvast PRISM3 USB WLAN Adapter" ), |
43 | PRISM_DEV(0x083a, 0x3503, "T-Sinus 111 USB WLAN Adapter" ), |
44 | PRISM_DEV(0x0411, 0x0044, "Melco WLI-USB-KB11 11Mbps WLAN Adapter" ), |
45 | PRISM_DEV(0x1668, 0x6106, "ROPEX FreeLan USB 802.11b Adapter" ), |
46 | PRISM_DEV(0x124a, 0x4017, "Pheenet WL-503IA USB 802.11b Adapter" ), |
47 | PRISM_DEV(0x0bb2, 0x0302, "Ambit Microsystems Corp." ), |
48 | PRISM_DEV(0x9016, 0x182d, "Sitecom WL-022 USB 802.11b Adapter" ), |
49 | PRISM_DEV(0x0543, 0x0f01, |
50 | "ViewSonic Airsync USB Adapter 11Mbps (Prism2.5)" ), |
51 | PRISM_DEV(0x067c, 0x1022, |
52 | "Siemens SpeedStream 1022 11Mbps USB WLAN Adapter" ), |
53 | PRISM_DEV(0x049f, 0x0033, |
54 | "Compaq/Intel W100 PRO/Wireless 11Mbps multiport WLAN Adapter" ), |
55 | { } /* terminator */ |
56 | }; |
57 | MODULE_DEVICE_TABLE(usb, usb_prism_tbl); |
58 | |
59 | static int prism2sta_probe_usb(struct usb_interface *interface, |
60 | const struct usb_device_id *id) |
61 | { |
62 | struct usb_device *dev; |
63 | struct usb_endpoint_descriptor *bulk_in, *bulk_out; |
64 | struct usb_host_interface *iface_desc = interface->cur_altsetting; |
65 | struct wlandevice *wlandev = NULL; |
66 | struct hfa384x *hw = NULL; |
67 | int result = 0; |
68 | |
69 | result = usb_find_common_endpoints(alt: iface_desc, bulk_in: &bulk_in, bulk_out: &bulk_out, NULL, NULL); |
70 | if (result) |
71 | goto failed; |
72 | |
73 | dev = interface_to_usbdev(interface); |
74 | wlandev = create_wlan(); |
75 | if (!wlandev) { |
76 | dev_err(&interface->dev, "Memory allocation failure.\n" ); |
77 | result = -EIO; |
78 | goto failed; |
79 | } |
80 | hw = wlandev->priv; |
81 | |
82 | if (wlan_setup(wlandev, physdev: &interface->dev) != 0) { |
83 | dev_err(&interface->dev, "wlan_setup() failed.\n" ); |
84 | result = -EIO; |
85 | goto failed; |
86 | } |
87 | |
88 | /* Initialize the hw data */ |
89 | hw->endp_in = usb_rcvbulkpipe(dev, bulk_in->bEndpointAddress); |
90 | hw->endp_out = usb_sndbulkpipe(dev, bulk_out->bEndpointAddress); |
91 | hfa384x_create(hw, usb: dev); |
92 | hw->wlandev = wlandev; |
93 | |
94 | /* Register the wlandev, this gets us a name and registers the |
95 | * linux netdevice. |
96 | */ |
97 | SET_NETDEV_DEV(wlandev->netdev, &interface->dev); |
98 | |
99 | /* Do a chip-level reset on the MAC */ |
100 | if (prism2_doreset) { |
101 | result = hfa384x_corereset(hw, |
102 | holdtime: prism2_reset_holdtime, |
103 | settletime: prism2_reset_settletime, genesis: 0); |
104 | if (result != 0) { |
105 | result = -EIO; |
106 | dev_err(&interface->dev, |
107 | "hfa384x_corereset() failed.\n" ); |
108 | goto failed_reset; |
109 | } |
110 | } |
111 | |
112 | usb_get_dev(dev); |
113 | |
114 | wlandev->msdstate = WLAN_MSD_HWPRESENT; |
115 | |
116 | /* Try and load firmware, then enable card before we register */ |
117 | prism2_fwtry(udev: dev, wlandev); |
118 | prism2sta_ifstate(wlandev, P80211ENUM_ifstate_enable); |
119 | |
120 | if (register_wlandev(wlandev) != 0) { |
121 | dev_err(&interface->dev, "register_wlandev() failed.\n" ); |
122 | result = -EIO; |
123 | goto failed_register; |
124 | } |
125 | |
126 | goto done; |
127 | |
128 | failed_register: |
129 | usb_put_dev(dev); |
130 | failed_reset: |
131 | wlan_unsetup(wlandev); |
132 | failed: |
133 | kfree(objp: wlandev); |
134 | kfree(objp: hw); |
135 | wlandev = NULL; |
136 | |
137 | done: |
138 | usb_set_intfdata(intf: interface, data: wlandev); |
139 | return result; |
140 | } |
141 | |
142 | static void prism2sta_disconnect_usb(struct usb_interface *interface) |
143 | { |
144 | struct wlandevice *wlandev; |
145 | |
146 | wlandev = usb_get_intfdata(intf: interface); |
147 | if (wlandev) { |
148 | LIST_HEAD(cleanlist); |
149 | struct hfa384x_usbctlx *ctlx, *temp; |
150 | unsigned long flags; |
151 | |
152 | struct hfa384x *hw = wlandev->priv; |
153 | |
154 | if (!hw) |
155 | goto exit; |
156 | |
157 | spin_lock_irqsave(&hw->ctlxq.lock, flags); |
158 | |
159 | p80211netdev_hwremoved(wlandev); |
160 | list_splice_init(list: &hw->ctlxq.reapable, head: &cleanlist); |
161 | list_splice_init(list: &hw->ctlxq.completing, head: &cleanlist); |
162 | list_splice_init(list: &hw->ctlxq.pending, head: &cleanlist); |
163 | list_splice_init(list: &hw->ctlxq.active, head: &cleanlist); |
164 | |
165 | spin_unlock_irqrestore(lock: &hw->ctlxq.lock, flags); |
166 | |
167 | /* There's no hardware to shutdown, but the driver |
168 | * might have some tasks that must be stopped before |
169 | * we can tear everything down. |
170 | */ |
171 | prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable); |
172 | |
173 | timer_shutdown_sync(timer: &hw->throttle); |
174 | timer_shutdown_sync(timer: &hw->reqtimer); |
175 | timer_shutdown_sync(timer: &hw->resptimer); |
176 | |
177 | /* Unlink all the URBs. This "removes the wheels" |
178 | * from the entire CTLX handling mechanism. |
179 | */ |
180 | usb_kill_urb(urb: &hw->rx_urb); |
181 | usb_kill_urb(urb: &hw->tx_urb); |
182 | usb_kill_urb(urb: &hw->ctlx_urb); |
183 | |
184 | cancel_work_sync(work: &hw->completion_bh); |
185 | cancel_work_sync(work: &hw->reaper_bh); |
186 | |
187 | cancel_work_sync(work: &hw->link_bh); |
188 | cancel_work_sync(work: &hw->commsqual_bh); |
189 | cancel_work_sync(work: &hw->usb_work); |
190 | |
191 | /* Now we complete any outstanding commands |
192 | * and tell everyone who is waiting for their |
193 | * responses that we have shut down. |
194 | */ |
195 | list_for_each_entry(ctlx, &cleanlist, list) |
196 | complete(&ctlx->done); |
197 | |
198 | /* Give any outstanding synchronous commands |
199 | * a chance to complete. All they need to do |
200 | * is "wake up", so that's easy. |
201 | * (I'd like a better way to do this, really.) |
202 | */ |
203 | msleep(msecs: 100); |
204 | |
205 | /* Now delete the CTLXs, because no-one else can now. */ |
206 | list_for_each_entry_safe(ctlx, temp, &cleanlist, list) |
207 | kfree(objp: ctlx); |
208 | |
209 | /* Unhook the wlandev */ |
210 | unregister_wlandev(wlandev); |
211 | wlan_unsetup(wlandev); |
212 | |
213 | usb_put_dev(dev: hw->usb); |
214 | |
215 | hfa384x_destroy(hw); |
216 | kfree(objp: hw); |
217 | |
218 | kfree(objp: wlandev); |
219 | } |
220 | |
221 | exit: |
222 | usb_set_intfdata(intf: interface, NULL); |
223 | } |
224 | |
225 | #ifdef CONFIG_PM |
226 | static int prism2sta_suspend(struct usb_interface *interface, |
227 | pm_message_t message) |
228 | { |
229 | struct hfa384x *hw = NULL; |
230 | struct wlandevice *wlandev; |
231 | |
232 | wlandev = usb_get_intfdata(intf: interface); |
233 | if (!wlandev) |
234 | return -ENODEV; |
235 | |
236 | hw = wlandev->priv; |
237 | if (!hw) |
238 | return -ENODEV; |
239 | |
240 | prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable); |
241 | |
242 | usb_kill_urb(urb: &hw->rx_urb); |
243 | usb_kill_urb(urb: &hw->tx_urb); |
244 | usb_kill_urb(urb: &hw->ctlx_urb); |
245 | |
246 | return 0; |
247 | } |
248 | |
249 | static int prism2sta_resume(struct usb_interface *interface) |
250 | { |
251 | int result = 0; |
252 | struct hfa384x *hw = NULL; |
253 | struct wlandevice *wlandev; |
254 | |
255 | wlandev = usb_get_intfdata(intf: interface); |
256 | if (!wlandev) |
257 | return -ENODEV; |
258 | |
259 | hw = wlandev->priv; |
260 | if (!hw) |
261 | return -ENODEV; |
262 | |
263 | /* Do a chip-level reset on the MAC */ |
264 | if (prism2_doreset) { |
265 | result = hfa384x_corereset(hw, |
266 | holdtime: prism2_reset_holdtime, |
267 | settletime: prism2_reset_settletime, genesis: 0); |
268 | if (result != 0) { |
269 | unregister_wlandev(wlandev); |
270 | hfa384x_destroy(hw); |
271 | dev_err(&interface->dev, "hfa384x_corereset() failed.\n" ); |
272 | kfree(objp: wlandev); |
273 | kfree(objp: hw); |
274 | wlandev = NULL; |
275 | return -ENODEV; |
276 | } |
277 | } |
278 | |
279 | prism2sta_ifstate(wlandev, P80211ENUM_ifstate_enable); |
280 | |
281 | return 0; |
282 | } |
283 | #else |
284 | #define prism2sta_suspend NULL |
285 | #define prism2sta_resume NULL |
286 | #endif /* CONFIG_PM */ |
287 | |
288 | static struct usb_driver prism2_usb_driver = { |
289 | .name = "prism2_usb" , |
290 | .probe = prism2sta_probe_usb, |
291 | .disconnect = prism2sta_disconnect_usb, |
292 | .id_table = usb_prism_tbl, |
293 | .suspend = prism2sta_suspend, |
294 | .resume = prism2sta_resume, |
295 | .reset_resume = prism2sta_resume, |
296 | /* fops, minor? */ |
297 | }; |
298 | |
299 | module_usb_driver(prism2_usb_driver); |
300 | |