blob: 780f60efa96515c0335d8ac11d27abe12016dc00 [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29
30#include <bcmos_system.h>
31#include <bcmolt_msg.h>
32#include <bcmolt_api.h>
33#include <bcmolt_model_types.h>
34#include <bcmolt_model_ids.h>
35#include "bcm_keep_alive.h"
36
37keep_alive_msg_orig2str_t keep_alive_msg_orig2str[] =
38{
39 {BCM_KEEP_ALIVE_MSG_ORIG_DEVICE, "device"},
40 {BCM_KEEP_ALIVE_MSG_ORIG_HOST, "host"},
41 {-1}
42};
43
44static void keep_alive_tx_handler(bcmos_keep_alive_info *ka_info);
45
46static bcmos_timer_rc keep_alive_timer_handler(bcmos_timer *timer, long data)
47{
48 bcmos_keep_alive_info *ka_info;
49 uint32_t last_recv_ka_ts;
50 uint32_t ka_interval;
51 uint32_t ka_tolerance;
52 uint32_t time_diff_from_prev_ka;
53 uint32_t ka_process_start_ts;
54 uint32_t prev_ka_ts;
55 ka_info = (bcmos_keep_alive_info *)data;
56 last_recv_ka_ts = ka_info->last_recv_ka_timestamp;
57 ka_interval = ka_info->ka_interval;
58 ka_tolerance = ka_info->ka_tolerance;
59 ka_process_start_ts = ka_info->ka_process_start_ts;
60
61 /* The first keep alive message that arrives changes last_recv_ka_ts to a value different than zero.
62 * If no keep alive message has arrived yet then the previous keep alive time stamp is the time the process has
63 * started. */
64 prev_ka_ts = (last_recv_ka_ts ? last_recv_ka_ts : ka_process_start_ts);
65
66 /* Time difference between current time and the previous keep alive message */
67 time_diff_from_prev_ka = bcmos_timestamp() - prev_ka_ts;
68
69 /* Check if keep alive message has been received in the past keep alive interval, both part of the equation can't overflow:
70 * The left part is subtraction of two unsigned integers and the right part is restricted by ka_interval max value and ka_tolerance max value -
71 * their multiplication can't exceed max unsigned integer */
72 if (time_diff_from_prev_ka > ka_interval * (ka_tolerance + 1))
73 {
74 if (last_recv_ka_ts)
75 {
76 BCMOS_TRACE_INFO(
77 "Didn't receive keep alive message for %u seconds - about to disconnect\n",
78 (bcmos_timestamp() - last_recv_ka_ts) / BCMOS_MICROSECONDS_IN_SECONDS);
79 }
80 else
81 {
82 BCMOS_TRACE_INFO(
83 "Didn't receive keep alive message since keep alive process started %u seconds ago - "
84 "about to disconnect\n",
85 (bcmos_timestamp() - ka_process_start_ts) / BCMOS_MICROSECONDS_IN_SECONDS);
86 }
87
88 if (ka_info->disconnect_handler(ka_info->device) != BCM_ERR_OK)
89 {
90 if (ka_info->orig == BCM_KEEP_ALIVE_MSG_ORIG_HOST)
91 BCMOS_TRACE_ERR("Error while trying to send disconnect message (device=%d)\n", ka_info->device);
92 else
93 BCMOS_TRACE_ERR("Error while trying to invoke disconnect from the host procedure\n");
94 }
95 return BCMOS_TIMER_STOP;
96 }
97
98 /* Send Keep alive message to the remote side */
99 keep_alive_tx_handler(ka_info);
100
101 return BCMOS_TIMER_OK;
102}
103
104static void keep_alive_tx_handler(bcmos_keep_alive_info *ka_info)
105{
106 bcmos_keep_alive_msg ka_msg = {};
107 bcmolt_device_key key = {};
108 if (ka_info->orig == BCM_KEEP_ALIVE_MSG_ORIG_HOST)
109 {
110 BCMOLT_OPER_INIT(&ka_msg.host, device, host_keep_alive, key);
111 ka_msg.host.hdr.hdr.type = BCMOLT_MSG_TYPE_SET;
112 BCMOLT_OPER_PROP_SET(&ka_msg.host, device, host_keep_alive, sequence_number, ka_info->last_sent_ka_seq++);
113 BCMOLT_OPER_PROP_SET(&ka_msg.host, device, host_keep_alive, time_stamp, bcmos_timestamp());
114 }
115 else
116 {
117 BCMOLT_AUTO_INIT(&ka_msg.device, device, device_keep_alive);
118 ka_msg.device.hdr.hdr.type = BCMOLT_MSG_TYPE_SET;
119 ka_msg.device.data.sequence_number = ka_info->last_sent_ka_seq++;
120 ka_msg.device.data.time_stamp = bcmos_timestamp();
121 }
122
123 if (ka_info->send_handler(ka_info->device, (bcmolt_msg *)&ka_msg) != BCM_ERR_OK)
124 {
125 BCMOS_TRACE_INFO("Error while trying to send keep alive sequence number %u\n", ka_info->last_sent_ka_seq);
126 }
127}
128
129void keep_alive_rx_handler(const bcmos_keep_alive_data_msg *ka_data_msg, bcmos_keep_alive_info *ka_info)
130{
131 uint32_t seq_diff;
132 uint32_t rx_msg_seq;
133 uint32_t rx_msg_ts;
134
135 /* Tricky part - rx_msg_seq is NOT from the originator but from the other side */
136 if (ka_info->orig == BCM_KEEP_ALIVE_MSG_ORIG_HOST)
137 {
138 rx_msg_seq = ka_data_msg->device.sequence_number;
139 rx_msg_ts = ka_data_msg->device.time_stamp;
140 }
141 else
142 {
143 rx_msg_seq = ka_data_msg->host.sequence_number;
144 rx_msg_ts = ka_data_msg->host.time_stamp;
145 }
146
147 /* If last_recv_ka_seq and last_recv_ka_timestamp are both zeros then it's the first message */
148 if (ka_info->last_recv_ka_seq || ka_info->last_recv_ka_timestamp)
149 {
150 /* All part of the equation are unsigned integers so warp around is already taken care of */
151 seq_diff = rx_msg_seq - ka_info->last_recv_ka_seq;
152
153 /* In the normal case seq_diff should be one */
154 if (seq_diff > 1UL)
155 {
156 BCMOS_TRACE_INFO(
157 "Current keep alive sequence number is %u while previous keep alive sequence number was %u\n",
158 rx_msg_seq,
159 ka_info->last_recv_ka_seq);
160 BCMOS_TRACE_INFO(
161 "Current time stamp is %u while previous time stamp was %u, receive timestamp is %u\n",
162 bcmos_timestamp(),
163 ka_info->last_recv_ka_timestamp,rx_msg_ts);
164 if (seq_diff <= ka_info->ka_tolerance)
165 {
166 BCMOS_TRACE_INFO(
167 "Missed %u keep alive messages (keep alive tolerance is to lose %d messages)\n",
168 seq_diff,
169 ka_info->ka_tolerance);
170 }
171 else
172 {
173 BCMOS_TRACE_ERR(
174 "Missed %u keep alive messages (keep alive tolerance is to lose %d messages - "
175 "about to disconnect)\n",
176 seq_diff,
177 ka_info->ka_tolerance);
178 stop_keep_alive_process(ka_info);
179 if (ka_info->disconnect_handler(ka_info->device) != BCM_ERR_OK)
180 {
181 if (ka_info->orig == BCM_KEEP_ALIVE_MSG_ORIG_HOST)
182 {
183 BCMOS_TRACE_ERR(
184 "Error while trying to send disconnect message (device=%d)\n",
185 ka_info->device);
186 }
187 else
188 {
189 BCMOS_TRACE_ERR("Error while trying to send disconnect from the host message\n");
190 }
191 }
192 }
193 }
194 }
195 /* Stroe at the recieving side's database, the sequence number of the last message that came */
196 ka_info->last_recv_ka_seq = rx_msg_seq;
197
198 /* The time stamp is taken from the clock at the receiving side , not what came in the message */
199 ka_info->last_recv_ka_timestamp = bcmos_timestamp();
200}
201
202bcmos_errno create_keep_alive_timer(bcmos_keep_alive_info *ka_info, bcmos_module_id module_id)
203{
204 bcmos_timer_parm timer_params = {};
205 bcmos_errno rc = BCM_ERR_OK;
206 ka_info->last_recv_ka_timestamp = 0;
207 ka_info->last_recv_ka_seq = 0;
208
209 snprintf(ka_info->ka_timer.name, sizeof(ka_info->ka_timer.name), "keep_alive_t%u", ka_info->device);
210 timer_params.name = ka_info->ka_timer.name;
211 timer_params.owner = module_id;
212 timer_params.periodic = BCMOS_TRUE;
213 timer_params.handler = keep_alive_timer_handler;
214 timer_params.data = (long)ka_info;
215 rc = bcmos_timer_create(&ka_info->ka_timer.timer, &timer_params);
216 if (rc != BCM_ERR_OK)
217 {
218 BCMOS_TRACE_ERR(
219 "keep alive timer creation failed, bcmos_timer_create returned error %s (%d)\n",
220 bcmos_strerror(rc),
221 rc);
222 }
223 return rc;
224}
225
226void start_keep_alive_process(bcmos_keep_alive_info *ka_info)
227{
228 ka_info->last_recv_ka_timestamp = 0;
229 ka_info->last_recv_ka_seq = 0;
230 ka_info->ka_process_start_ts = bcmos_timestamp();
231 bcmos_timer_start(&ka_info->ka_timer.timer, ka_info->ka_interval);
232}
233
234void stop_keep_alive_process(bcmos_keep_alive_info *ka_info)
235{
236 bcmos_timer_stop(&ka_info->ka_timer.timer);
237}
238
239#ifdef __KERNEL__
240EXPORT_SYMBOL(create_keep_alive_timer);
241EXPORT_SYMBOL(keep_alive_rx_handler);
242
243MODULE_DESCRIPTION("BCM device keep-alive support");
244MODULE_LICENSE("Dual BSD/GPL");
245#endif