Commit | Line | Data |
---|---|---|
84caf800 VT |
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 | ||
92332993 VT |
26 | ar->state = ATH6KL_STATE_RECOVERY; |
27 | ||
28 | del_timer_sync(&ar->fw_recovery.hb_timer); | |
29 | ||
84caf800 | 30 | ath6kl_init_hw_restart(ar); |
92332993 VT |
31 | |
32 | ar->state = ATH6KL_STATE_ON; | |
77565794 VT |
33 | clear_bit(WMI_CTRL_EP_FULL, &ar->flag); |
34 | ||
92332993 VT |
35 | ar->fw_recovery.err_reason = 0; |
36 | ||
9d918840 | 37 | if (ar->fw_recovery.hb_poll) |
92332993 VT |
38 | mod_timer(&ar->fw_recovery.hb_timer, jiffies + |
39 | msecs_to_jiffies(ar->fw_recovery.hb_poll)); | |
84caf800 VT |
40 | } |
41 | ||
42 | void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason) | |
43 | { | |
66ddcc39 VT |
44 | if (!ar->fw_recovery.enable) |
45 | return; | |
46 | ||
84caf800 VT |
47 | ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Fw error detected, reason:%d\n", |
48 | reason); | |
49 | ||
50 | set_bit(reason, &ar->fw_recovery.err_reason); | |
51 | ||
a3561706 VT |
52 | if (!test_bit(RECOVERY_CLEANUP, &ar->flag) && |
53 | ar->state != ATH6KL_STATE_RECOVERY) | |
84caf800 VT |
54 | queue_work(ar->ath6kl_wq, &ar->fw_recovery.recovery_work); |
55 | } | |
56 | ||
92332993 VT |
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(unsigned long data) | |
64 | { | |
65 | struct ath6kl *ar = (struct ath6kl *) data; | |
66 | int err; | |
67 | ||
a3561706 VT |
68 | if (test_bit(RECOVERY_CLEANUP, &ar->flag) || |
69 | (ar->state == ATH6KL_STATE_RECOVERY)) | |
92332993 VT |
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, 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(ar->wmi, | |
89 | ar->fw_recovery.seq_num, 0); | |
90 | if (err) | |
91 | ath6kl_warn("Failed to send hb challenge request, err:%d\n", | |
92 | err); | |
93 | ||
92332993 VT |
94 | mod_timer(&ar->fw_recovery.hb_timer, jiffies + |
95 | msecs_to_jiffies(ar->fw_recovery.hb_poll)); | |
96 | } | |
97 | ||
84caf800 VT |
98 | void ath6kl_recovery_init(struct ath6kl *ar) |
99 | { | |
100 | struct ath6kl_fw_recovery *recovery = &ar->fw_recovery; | |
101 | ||
a3561706 | 102 | clear_bit(RECOVERY_CLEANUP, &ar->flag); |
84caf800 | 103 | INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work); |
92332993 VT |
104 | recovery->seq_num = 0; |
105 | recovery->hb_misscnt = 0; | |
106 | ar->fw_recovery.hb_pending = false; | |
107 | ar->fw_recovery.hb_timer.function = ath6kl_recovery_hb_timer; | |
108 | ar->fw_recovery.hb_timer.data = (unsigned long) ar; | |
109 | init_timer_deferrable(&ar->fw_recovery.hb_timer); | |
110 | ||
111 | if (ar->fw_recovery.hb_poll) | |
112 | mod_timer(&ar->fw_recovery.hb_timer, jiffies + | |
113 | msecs_to_jiffies(ar->fw_recovery.hb_poll)); | |
84caf800 VT |
114 | } |
115 | ||
116 | void ath6kl_recovery_cleanup(struct ath6kl *ar) | |
117 | { | |
66ddcc39 VT |
118 | if (!ar->fw_recovery.enable) |
119 | return; | |
120 | ||
a3561706 | 121 | set_bit(RECOVERY_CLEANUP, &ar->flag); |
84caf800 | 122 | |
92332993 | 123 | del_timer_sync(&ar->fw_recovery.hb_timer); |
84caf800 VT |
124 | cancel_work_sync(&ar->fw_recovery.recovery_work); |
125 | } | |
126 | ||
127 | void ath6kl_recovery_suspend(struct ath6kl *ar) | |
128 | { | |
66ddcc39 VT |
129 | if (!ar->fw_recovery.enable) |
130 | return; | |
131 | ||
84caf800 VT |
132 | ath6kl_recovery_cleanup(ar); |
133 | ||
92332993 VT |
134 | if (!ar->fw_recovery.err_reason) |
135 | return; | |
136 | ||
84caf800 | 137 | /* Process pending fw error detection */ |
92332993 VT |
138 | ar->fw_recovery.err_reason = 0; |
139 | WARN_ON(ar->state != ATH6KL_STATE_ON); | |
140 | ar->state = ATH6KL_STATE_RECOVERY; | |
141 | ath6kl_init_hw_restart(ar); | |
142 | ar->state = ATH6KL_STATE_ON; | |
143 | } | |
144 | ||
145 | void ath6kl_recovery_resume(struct ath6kl *ar) | |
146 | { | |
66ddcc39 VT |
147 | if (!ar->fw_recovery.enable) |
148 | return; | |
149 | ||
a3561706 | 150 | clear_bit(RECOVERY_CLEANUP, &ar->flag); |
92332993 VT |
151 | |
152 | if (!ar->fw_recovery.hb_poll) | |
153 | return; | |
154 | ||
155 | ar->fw_recovery.hb_pending = false; | |
156 | ar->fw_recovery.seq_num = 0; | |
157 | ar->fw_recovery.hb_misscnt = 0; | |
158 | mod_timer(&ar->fw_recovery.hb_timer, | |
159 | jiffies + msecs_to_jiffies(ar->fw_recovery.hb_poll)); | |
84caf800 | 160 | } |