1 | /* |
2 | * Copyright (c) 2012 Qualcomm Atheros, 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 | #include "core.h" |
18 | #include "cfg80211.h" |
19 | #include "debug.h" |
20 | |
21 | static void ath6kl_recovery_work(struct work_struct *work) |
22 | { |
23 | struct ath6kl *ar = container_of(work, struct ath6kl, |
24 | fw_recovery.recovery_work); |
25 | |
26 | ar->state = ATH6KL_STATE_RECOVERY; |
27 | |
28 | del_timer_sync(timer: &ar->fw_recovery.hb_timer); |
29 | |
30 | ath6kl_init_hw_restart(ar); |
31 | |
32 | ar->state = ATH6KL_STATE_ON; |
33 | clear_bit(nr: WMI_CTRL_EP_FULL, addr: &ar->flag); |
34 | |
35 | ar->fw_recovery.err_reason = 0; |
36 | |
37 | if (ar->fw_recovery.hb_poll) |
38 | mod_timer(timer: &ar->fw_recovery.hb_timer, expires: jiffies + |
39 | msecs_to_jiffies(m: ar->fw_recovery.hb_poll)); |
40 | } |
41 | |
42 | void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason) |
43 | { |
44 | if (!ar->fw_recovery.enable) |
45 | return; |
46 | |
47 | ath6kl_dbg(mask: ATH6KL_DBG_RECOVERY, fmt: "Fw error detected, reason:%d\n" , |
48 | reason); |
49 | |
50 | set_bit(nr: reason, addr: &ar->fw_recovery.err_reason); |
51 | |
52 | if (!test_bit(RECOVERY_CLEANUP, &ar->flag) && |
53 | ar->state != ATH6KL_STATE_RECOVERY) |
54 | queue_work(wq: ar->ath6kl_wq, work: &ar->fw_recovery.recovery_work); |
55 | } |
56 | |
57 | void ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie) |
58 | { |
59 | if (cookie == ar->fw_recovery.seq_num) |
60 | ar->fw_recovery.hb_pending = false; |
61 | } |
62 | |
63 | static void ath6kl_recovery_hb_timer(struct timer_list *t) |
64 | { |
65 | struct ath6kl *ar = from_timer(ar, t, fw_recovery.hb_timer); |
66 | int err; |
67 | |
68 | if (test_bit(RECOVERY_CLEANUP, &ar->flag) || |
69 | (ar->state == ATH6KL_STATE_RECOVERY)) |
70 | return; |
71 | |
72 | if (ar->fw_recovery.hb_pending) |
73 | ar->fw_recovery.hb_misscnt++; |
74 | else |
75 | ar->fw_recovery.hb_misscnt = 0; |
76 | |
77 | if (ar->fw_recovery.hb_misscnt > ATH6KL_HB_RESP_MISS_THRES) { |
78 | ar->fw_recovery.hb_misscnt = 0; |
79 | ar->fw_recovery.seq_num = 0; |
80 | ar->fw_recovery.hb_pending = false; |
81 | ath6kl_recovery_err_notify(ar, reason: ATH6KL_FW_HB_RESP_FAILURE); |
82 | return; |
83 | } |
84 | |
85 | ar->fw_recovery.seq_num++; |
86 | ar->fw_recovery.hb_pending = true; |
87 | |
88 | err = ath6kl_wmi_get_challenge_resp_cmd(wmi: ar->wmi, |
89 | cookie: ar->fw_recovery.seq_num, source: 0); |
90 | if (err) |
91 | ath6kl_warn(fmt: "Failed to send hb challenge request, err:%d\n" , |
92 | err); |
93 | |
94 | mod_timer(timer: &ar->fw_recovery.hb_timer, expires: jiffies + |
95 | msecs_to_jiffies(m: ar->fw_recovery.hb_poll)); |
96 | } |
97 | |
98 | void ath6kl_recovery_init(struct ath6kl *ar) |
99 | { |
100 | struct ath6kl_fw_recovery *recovery = &ar->fw_recovery; |
101 | |
102 | clear_bit(nr: RECOVERY_CLEANUP, addr: &ar->flag); |
103 | INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work); |
104 | recovery->seq_num = 0; |
105 | recovery->hb_misscnt = 0; |
106 | ar->fw_recovery.hb_pending = false; |
107 | timer_setup(&ar->fw_recovery.hb_timer, ath6kl_recovery_hb_timer, |
108 | TIMER_DEFERRABLE); |
109 | |
110 | if (ar->fw_recovery.hb_poll) |
111 | mod_timer(timer: &ar->fw_recovery.hb_timer, expires: jiffies + |
112 | msecs_to_jiffies(m: ar->fw_recovery.hb_poll)); |
113 | } |
114 | |
115 | void ath6kl_recovery_cleanup(struct ath6kl *ar) |
116 | { |
117 | if (!ar->fw_recovery.enable) |
118 | return; |
119 | |
120 | set_bit(nr: RECOVERY_CLEANUP, addr: &ar->flag); |
121 | |
122 | del_timer_sync(timer: &ar->fw_recovery.hb_timer); |
123 | cancel_work_sync(work: &ar->fw_recovery.recovery_work); |
124 | } |
125 | |
126 | void ath6kl_recovery_suspend(struct ath6kl *ar) |
127 | { |
128 | if (!ar->fw_recovery.enable) |
129 | return; |
130 | |
131 | ath6kl_recovery_cleanup(ar); |
132 | |
133 | if (!ar->fw_recovery.err_reason) |
134 | return; |
135 | |
136 | /* Process pending fw error detection */ |
137 | ar->fw_recovery.err_reason = 0; |
138 | WARN_ON(ar->state != ATH6KL_STATE_ON); |
139 | ar->state = ATH6KL_STATE_RECOVERY; |
140 | ath6kl_init_hw_restart(ar); |
141 | ar->state = ATH6KL_STATE_ON; |
142 | } |
143 | |
144 | void ath6kl_recovery_resume(struct ath6kl *ar) |
145 | { |
146 | if (!ar->fw_recovery.enable) |
147 | return; |
148 | |
149 | clear_bit(nr: RECOVERY_CLEANUP, addr: &ar->flag); |
150 | |
151 | if (!ar->fw_recovery.hb_poll) |
152 | return; |
153 | |
154 | ar->fw_recovery.hb_pending = false; |
155 | ar->fw_recovery.seq_num = 0; |
156 | ar->fw_recovery.hb_misscnt = 0; |
157 | mod_timer(timer: &ar->fw_recovery.hb_timer, |
158 | expires: jiffies + msecs_to_jiffies(m: ar->fw_recovery.hb_poll)); |
159 | } |
160 | |