1 | /* |
2 | * Copyright (c) 2014 Redpine Signals Inc. |
3 | * |
4 | * Permission to use, copy, modify, and/or distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | |
17 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
18 | |
19 | #include <linux/module.h> |
20 | #include <linux/firmware.h> |
21 | #include <net/rsi_91x.h> |
22 | #include "rsi_mgmt.h" |
23 | #include "rsi_common.h" |
24 | #include "rsi_coex.h" |
25 | #include "rsi_hal.h" |
26 | #include "rsi_usb.h" |
27 | |
28 | u32 rsi_zone_enabled = /* INFO_ZONE | |
29 | INIT_ZONE | |
30 | MGMT_TX_ZONE | |
31 | MGMT_RX_ZONE | |
32 | DATA_TX_ZONE | |
33 | DATA_RX_ZONE | |
34 | FSM_ZONE | |
35 | ISR_ZONE | */ |
36 | ERR_ZONE | |
37 | 0; |
38 | EXPORT_SYMBOL_GPL(rsi_zone_enabled); |
39 | |
40 | #ifdef CONFIG_RSI_COEX |
41 | static struct rsi_proto_ops g_proto_ops = { |
42 | .coex_send_pkt = rsi_coex_send_pkt, |
43 | .get_host_intf = rsi_get_host_intf, |
44 | .set_bt_context = rsi_set_bt_context, |
45 | }; |
46 | #endif |
47 | |
48 | /** |
49 | * rsi_dbg() - This function outputs informational messages. |
50 | * @zone: Zone of interest for output message. |
51 | * @fmt: printf-style format for output message. |
52 | * |
53 | * Return: none |
54 | */ |
55 | void rsi_dbg(u32 zone, const char *fmt, ...) |
56 | { |
57 | struct va_format vaf; |
58 | va_list args; |
59 | |
60 | va_start(args, fmt); |
61 | |
62 | vaf.fmt = fmt; |
63 | vaf.va = &args; |
64 | |
65 | if (zone & rsi_zone_enabled) |
66 | pr_info("%pV" , &vaf); |
67 | va_end(args); |
68 | } |
69 | EXPORT_SYMBOL_GPL(rsi_dbg); |
70 | |
71 | static char *opmode_str(int oper_mode) |
72 | { |
73 | switch (oper_mode) { |
74 | case DEV_OPMODE_WIFI_ALONE: |
75 | return "Wi-Fi alone" ; |
76 | case DEV_OPMODE_BT_ALONE: |
77 | return "BT EDR alone" ; |
78 | case DEV_OPMODE_BT_LE_ALONE: |
79 | return "BT LE alone" ; |
80 | case DEV_OPMODE_BT_DUAL: |
81 | return "BT Dual" ; |
82 | case DEV_OPMODE_STA_BT: |
83 | return "Wi-Fi STA + BT EDR" ; |
84 | case DEV_OPMODE_STA_BT_LE: |
85 | return "Wi-Fi STA + BT LE" ; |
86 | case DEV_OPMODE_STA_BT_DUAL: |
87 | return "Wi-Fi STA + BT DUAL" ; |
88 | case DEV_OPMODE_AP_BT: |
89 | return "Wi-Fi AP + BT EDR" ; |
90 | case DEV_OPMODE_AP_BT_DUAL: |
91 | return "Wi-Fi AP + BT DUAL" ; |
92 | } |
93 | |
94 | return "Unknown" ; |
95 | } |
96 | |
97 | void rsi_print_version(struct rsi_common *common) |
98 | { |
99 | rsi_dbg(ERR_ZONE, "================================================\n" ); |
100 | rsi_dbg(ERR_ZONE, "================ RSI Version Info ==============\n" ); |
101 | rsi_dbg(ERR_ZONE, "================================================\n" ); |
102 | rsi_dbg(ERR_ZONE, "FW Version\t: %d.%d.%d\n" , |
103 | common->lmac_ver.major, common->lmac_ver.minor, |
104 | common->lmac_ver.release_num); |
105 | rsi_dbg(ERR_ZONE, "Operating mode\t: %d [%s]" , |
106 | common->oper_mode, opmode_str(oper_mode: common->oper_mode)); |
107 | rsi_dbg(ERR_ZONE, "Firmware file\t: %s" , common->priv->fw_file_name); |
108 | rsi_dbg(ERR_ZONE, "================================================\n" ); |
109 | } |
110 | |
111 | /** |
112 | * rsi_prepare_skb() - This function prepares the skb. |
113 | * @common: Pointer to the driver private structure. |
114 | * @buffer: Pointer to the packet data. |
115 | * @pkt_len: Length of the packet. |
116 | * @extended_desc: Extended descriptor. |
117 | * |
118 | * Return: Successfully skb. |
119 | */ |
120 | static struct sk_buff *rsi_prepare_skb(struct rsi_common *common, |
121 | u8 *buffer, |
122 | u32 pkt_len, |
123 | u8 extended_desc) |
124 | { |
125 | struct sk_buff *skb = NULL; |
126 | u8 payload_offset; |
127 | |
128 | if (WARN(!pkt_len, "%s: Dummy pkt received" , __func__)) |
129 | return NULL; |
130 | |
131 | if (pkt_len > (RSI_RCV_BUFFER_LEN * 4)) { |
132 | rsi_dbg(ERR_ZONE, "%s: Pkt size > max rx buf size %d\n" , |
133 | __func__, pkt_len); |
134 | pkt_len = RSI_RCV_BUFFER_LEN * 4; |
135 | } |
136 | |
137 | pkt_len -= extended_desc; |
138 | skb = dev_alloc_skb(length: pkt_len + FRAME_DESC_SZ); |
139 | if (skb == NULL) |
140 | return NULL; |
141 | |
142 | payload_offset = (extended_desc + FRAME_DESC_SZ); |
143 | skb_put(skb, len: pkt_len); |
144 | memcpy((skb->data), (buffer + payload_offset), skb->len); |
145 | |
146 | return skb; |
147 | } |
148 | |
149 | /** |
150 | * rsi_read_pkt() - This function reads frames from the card. |
151 | * @common: Pointer to the driver private structure. |
152 | * @rx_pkt: Received pkt. |
153 | * @rcv_pkt_len: Received pkt length. In case of USB it is 0. |
154 | * |
155 | * Return: 0 on success, -1 on failure. |
156 | */ |
157 | int rsi_read_pkt(struct rsi_common *common, u8 *rx_pkt, s32 rcv_pkt_len) |
158 | { |
159 | u8 *frame_desc = NULL, extended_desc = 0; |
160 | u32 index, length = 0, queueno = 0; |
161 | u16 actual_length = 0, offset; |
162 | struct sk_buff *skb = NULL; |
163 | #ifdef CONFIG_RSI_COEX |
164 | u8 bt_pkt_type; |
165 | #endif |
166 | |
167 | index = 0; |
168 | do { |
169 | frame_desc = &rx_pkt[index]; |
170 | actual_length = *(u16 *)&frame_desc[0]; |
171 | offset = *(u16 *)&frame_desc[2]; |
172 | if (!rcv_pkt_len && offset > |
173 | RSI_MAX_RX_USB_PKT_SIZE - FRAME_DESC_SZ) |
174 | goto fail; |
175 | |
176 | queueno = rsi_get_queueno(addr: frame_desc, offset); |
177 | length = rsi_get_length(addr: frame_desc, offset); |
178 | |
179 | /* Extended descriptor is valid for WLAN queues only */ |
180 | if (queueno == RSI_WIFI_DATA_Q || queueno == RSI_WIFI_MGMT_Q) |
181 | extended_desc = rsi_get_extended_desc(addr: frame_desc, |
182 | offset); |
183 | |
184 | switch (queueno) { |
185 | case RSI_COEX_Q: |
186 | #ifdef CONFIG_RSI_COEX |
187 | if (common->coex_mode > 1) |
188 | rsi_coex_recv_pkt(common, msg: frame_desc + offset); |
189 | else |
190 | #endif |
191 | rsi_mgmt_pkt_recv(common, |
192 | msg: (frame_desc + offset)); |
193 | break; |
194 | |
195 | case RSI_WIFI_DATA_Q: |
196 | skb = rsi_prepare_skb(common, |
197 | buffer: (frame_desc + offset), |
198 | pkt_len: length, |
199 | extended_desc); |
200 | if (skb == NULL) |
201 | goto fail; |
202 | |
203 | rsi_indicate_pkt_to_os(common, skb); |
204 | break; |
205 | |
206 | case RSI_WIFI_MGMT_Q: |
207 | rsi_mgmt_pkt_recv(common, msg: (frame_desc + offset)); |
208 | break; |
209 | |
210 | #ifdef CONFIG_RSI_COEX |
211 | case RSI_BT_MGMT_Q: |
212 | case RSI_BT_DATA_Q: |
213 | #define BT_RX_PKT_TYPE_OFST 14 |
214 | #define BT_CARD_READY_IND 0x89 |
215 | bt_pkt_type = frame_desc[offset + BT_RX_PKT_TYPE_OFST]; |
216 | if (bt_pkt_type == BT_CARD_READY_IND) { |
217 | rsi_dbg(INFO_ZONE, "BT Card ready recvd\n" ); |
218 | if (common->fsm_state == FSM_MAC_INIT_DONE) |
219 | rsi_attach_bt(common); |
220 | else |
221 | common->bt_defer_attach = true; |
222 | } else { |
223 | if (common->bt_adapter) |
224 | rsi_bt_ops.recv_pkt(common->bt_adapter, |
225 | frame_desc + offset); |
226 | } |
227 | break; |
228 | #endif |
229 | |
230 | default: |
231 | rsi_dbg(ERR_ZONE, "%s: pkt from invalid queue: %d\n" , |
232 | __func__, queueno); |
233 | goto fail; |
234 | } |
235 | |
236 | index += actual_length; |
237 | rcv_pkt_len -= actual_length; |
238 | } while (rcv_pkt_len > 0); |
239 | |
240 | return 0; |
241 | fail: |
242 | return -EINVAL; |
243 | } |
244 | EXPORT_SYMBOL_GPL(rsi_read_pkt); |
245 | |
246 | /** |
247 | * rsi_tx_scheduler_thread() - This function is a kernel thread to send the |
248 | * packets to the device. |
249 | * @common: Pointer to the driver private structure. |
250 | * |
251 | * Return: None. |
252 | */ |
253 | static void rsi_tx_scheduler_thread(struct rsi_common *common) |
254 | { |
255 | struct rsi_hw *adapter = common->priv; |
256 | u32 timeout = EVENT_WAIT_FOREVER; |
257 | |
258 | do { |
259 | if (adapter->determine_event_timeout) |
260 | timeout = adapter->determine_event_timeout(adapter); |
261 | rsi_wait_event(event: &common->tx_thread.event, timeout); |
262 | rsi_reset_event(event: &common->tx_thread.event); |
263 | |
264 | if (common->init_done) |
265 | rsi_core_qos_processor(common); |
266 | } while (atomic_read(v: &common->tx_thread.thread_done) == 0); |
267 | kthread_complete_and_exit(&common->tx_thread.completion, 0); |
268 | } |
269 | |
270 | #ifdef CONFIG_RSI_COEX |
271 | enum rsi_host_intf rsi_get_host_intf(void *priv) |
272 | { |
273 | struct rsi_common *common = priv; |
274 | |
275 | return common->priv->rsi_host_intf; |
276 | } |
277 | |
278 | void rsi_set_bt_context(void *priv, void *bt_context) |
279 | { |
280 | struct rsi_common *common = priv; |
281 | |
282 | common->bt_adapter = bt_context; |
283 | } |
284 | #endif |
285 | |
286 | void rsi_attach_bt(struct rsi_common *common) |
287 | { |
288 | #ifdef CONFIG_RSI_COEX |
289 | if (rsi_bt_ops.attach(common, &g_proto_ops)) |
290 | rsi_dbg(ERR_ZONE, |
291 | "Failed to attach BT module\n" ); |
292 | #endif |
293 | } |
294 | |
295 | /** |
296 | * rsi_91x_init() - This function initializes os interface operations. |
297 | * @oper_mode: One of DEV_OPMODE_*. |
298 | * |
299 | * Return: Pointer to the adapter structure on success, NULL on failure . |
300 | */ |
301 | struct rsi_hw *rsi_91x_init(u16 oper_mode) |
302 | { |
303 | struct rsi_hw *adapter = NULL; |
304 | struct rsi_common *common = NULL; |
305 | u8 ii = 0; |
306 | |
307 | adapter = kzalloc(size: sizeof(*adapter), GFP_KERNEL); |
308 | if (!adapter) |
309 | return NULL; |
310 | |
311 | adapter->priv = kzalloc(size: sizeof(*common), GFP_KERNEL); |
312 | if (adapter->priv == NULL) { |
313 | rsi_dbg(ERR_ZONE, "%s: Failed in allocation of memory\n" , |
314 | __func__); |
315 | kfree(objp: adapter); |
316 | return NULL; |
317 | } else { |
318 | common = adapter->priv; |
319 | common->priv = adapter; |
320 | } |
321 | |
322 | for (ii = 0; ii < NUM_SOFT_QUEUES; ii++) |
323 | skb_queue_head_init(list: &common->tx_queue[ii]); |
324 | |
325 | rsi_init_event(pevent: &common->tx_thread.event); |
326 | mutex_init(&common->mutex); |
327 | mutex_init(&common->tx_lock); |
328 | mutex_init(&common->rx_lock); |
329 | mutex_init(&common->tx_bus_mutex); |
330 | |
331 | if (rsi_create_kthread(common, |
332 | thread: &common->tx_thread, |
333 | func_ptr: rsi_tx_scheduler_thread, |
334 | name: "Tx-Thread" )) { |
335 | rsi_dbg(ERR_ZONE, "%s: Unable to init tx thrd\n" , __func__); |
336 | goto err; |
337 | } |
338 | |
339 | rsi_default_ps_params(hw: adapter); |
340 | init_bgscan_params(common); |
341 | spin_lock_init(&adapter->ps_lock); |
342 | timer_setup(&common->roc_timer, rsi_roc_timeout, 0); |
343 | init_completion(x: &common->wlan_init_completion); |
344 | adapter->device_model = RSI_DEV_9113; |
345 | common->oper_mode = oper_mode; |
346 | |
347 | /* Determine coex mode */ |
348 | switch (common->oper_mode) { |
349 | case DEV_OPMODE_STA_BT_DUAL: |
350 | case DEV_OPMODE_STA_BT: |
351 | case DEV_OPMODE_STA_BT_LE: |
352 | case DEV_OPMODE_BT_ALONE: |
353 | case DEV_OPMODE_BT_LE_ALONE: |
354 | case DEV_OPMODE_BT_DUAL: |
355 | common->coex_mode = 2; |
356 | break; |
357 | case DEV_OPMODE_AP_BT_DUAL: |
358 | case DEV_OPMODE_AP_BT: |
359 | common->coex_mode = 4; |
360 | break; |
361 | case DEV_OPMODE_WIFI_ALONE: |
362 | common->coex_mode = 1; |
363 | break; |
364 | default: |
365 | common->oper_mode = 1; |
366 | common->coex_mode = 1; |
367 | } |
368 | rsi_dbg(INFO_ZONE, "%s: oper_mode = %d, coex_mode = %d\n" , |
369 | __func__, common->oper_mode, common->coex_mode); |
370 | |
371 | adapter->device_model = RSI_DEV_9113; |
372 | #ifdef CONFIG_RSI_COEX |
373 | if (common->coex_mode > 1) { |
374 | if (rsi_coex_attach(common)) { |
375 | rsi_dbg(ERR_ZONE, "Failed to init coex module\n" ); |
376 | rsi_kill_thread(handle: &common->tx_thread); |
377 | goto err; |
378 | } |
379 | } |
380 | #endif |
381 | |
382 | common->init_done = true; |
383 | return adapter; |
384 | |
385 | err: |
386 | kfree(objp: common); |
387 | kfree(objp: adapter); |
388 | return NULL; |
389 | } |
390 | EXPORT_SYMBOL_GPL(rsi_91x_init); |
391 | |
392 | /** |
393 | * rsi_91x_deinit() - This function de-intializes os intf operations. |
394 | * @adapter: Pointer to the adapter structure. |
395 | * |
396 | * Return: None. |
397 | */ |
398 | void rsi_91x_deinit(struct rsi_hw *adapter) |
399 | { |
400 | struct rsi_common *common = adapter->priv; |
401 | u8 ii; |
402 | |
403 | rsi_dbg(INFO_ZONE, "%s: Performing deinit os ops\n" , __func__); |
404 | |
405 | rsi_kill_thread(handle: &common->tx_thread); |
406 | |
407 | for (ii = 0; ii < NUM_SOFT_QUEUES; ii++) |
408 | skb_queue_purge(list: &common->tx_queue[ii]); |
409 | |
410 | #ifdef CONFIG_RSI_COEX |
411 | if (common->coex_mode > 1) { |
412 | if (common->bt_adapter) { |
413 | rsi_bt_ops.detach(common->bt_adapter); |
414 | common->bt_adapter = NULL; |
415 | } |
416 | rsi_coex_detach(common); |
417 | } |
418 | #endif |
419 | |
420 | common->init_done = false; |
421 | |
422 | kfree(objp: common); |
423 | kfree(objp: adapter->rsi_dev); |
424 | kfree(objp: adapter); |
425 | } |
426 | EXPORT_SYMBOL_GPL(rsi_91x_deinit); |
427 | |
428 | /** |
429 | * rsi_91x_hal_module_init() - This function is invoked when the module is |
430 | * loaded into the kernel. |
431 | * It registers the client driver. |
432 | * @void: Void. |
433 | * |
434 | * Return: 0 on success, -1 on failure. |
435 | */ |
436 | static int rsi_91x_hal_module_init(void) |
437 | { |
438 | rsi_dbg(INIT_ZONE, "%s: Module init called\n" , __func__); |
439 | return 0; |
440 | } |
441 | |
442 | /** |
443 | * rsi_91x_hal_module_exit() - This function is called at the time of |
444 | * removing/unloading the module. |
445 | * It unregisters the client driver. |
446 | * @void: Void. |
447 | * |
448 | * Return: None. |
449 | */ |
450 | static void rsi_91x_hal_module_exit(void) |
451 | { |
452 | rsi_dbg(INIT_ZONE, "%s: Module exit called\n" , __func__); |
453 | } |
454 | |
455 | module_init(rsi_91x_hal_module_init); |
456 | module_exit(rsi_91x_hal_module_exit); |
457 | MODULE_AUTHOR("Redpine Signals Inc" ); |
458 | MODULE_DESCRIPTION("Station driver for RSI 91x devices" ); |
459 | MODULE_VERSION("0.1" ); |
460 | MODULE_LICENSE("Dual BSD/GPL" ); |
461 | |