blob: 549fe7ff1eeee6441773de99f445f583cbd521b9 [file] [log] [blame]
Chris Kelly1619cb62012-02-20 21:11:28 +00001/* -----------------------------------------------------------------------------
2 * Copyright (c) 2011 Ozmo Inc
3 * Released under the GNU General Public License Version 2 (GPLv2).
4 * -----------------------------------------------------------------------------
5 */
Joe Perches05f608f2013-07-23 13:45:01 +01006
Chris Kelly1619cb62012-02-20 21:11:28 +00007#include <linux/module.h>
8#include <linux/timer.h>
9#include <linux/sched.h>
10#include <linux/netdevice.h>
dingtianhong93dc5e42013-12-26 19:40:47 +080011#include <linux/etherdevice.h>
Chris Kelly1619cb62012-02-20 21:11:28 +000012#include <linux/errno.h>
13#include <linux/ieee80211.h>
Joe Perchesf724b582013-07-23 13:45:00 +010014#include "ozdbg.h"
Chris Kelly1619cb62012-02-20 21:11:28 +000015#include "ozprotocol.h"
16#include "ozeltbuf.h"
17#include "ozpd.h"
18#include "ozproto.h"
19#include "ozusbsvc.h"
Joe Perches05f608f2013-07-23 13:45:01 +010020
Chris Kelly1619cb62012-02-20 21:11:28 +000021#include "ozappif.h"
Chris Kelly1619cb62012-02-20 21:11:28 +000022#include <asm/unaligned.h>
23#include <linux/uaccess.h>
24#include <net/psnap.h>
Rupesh Gujare6e244a82013-08-13 18:24:22 +010025
Chris Kelly1619cb62012-02-20 21:11:28 +000026#define OZ_CF_CONN_SUCCESS 1
27#define OZ_CF_CONN_FAILURE 2
28
29#define OZ_DO_STOP 1
30#define OZ_DO_SLEEP 2
31
Chris Kelly1619cb62012-02-20 21:11:28 +000032struct oz_binding {
33 struct packet_type ptype;
34 char name[OZ_MAX_BINDING_LEN];
Rupesh Gujare8a3cac62013-08-01 18:40:00 +010035 struct list_head link;
Chris Kelly1619cb62012-02-20 21:11:28 +000036};
37
Rupesh Gujare4e7fb822013-08-23 16:11:02 +010038/*
Surendra Patil6b029332014-03-03 23:57:47 -080039 * External variable
40 */
41
42DEFINE_SPINLOCK(g_polling_lock);
43/*
Chris Kelly1619cb62012-02-20 21:11:28 +000044 * Static external variables.
45 */
Chris Kelly1619cb62012-02-20 21:11:28 +000046static LIST_HEAD(g_pd_list);
Rupesh Gujare8a3cac62013-08-01 18:40:00 +010047static LIST_HEAD(g_binding);
Chris Kelly1619cb62012-02-20 21:11:28 +000048static DEFINE_SPINLOCK(g_binding_lock);
49static struct sk_buff_head g_rx_queue;
50static u8 g_session_id;
51static u16 g_apps = 0x1;
52static int g_processing_rx;
Rupesh Gujare6e244a82013-08-13 18:24:22 +010053
Rupesh Gujare4e7fb822013-08-23 16:11:02 +010054/*
Chris Kelly1619cb62012-02-20 21:11:28 +000055 * Context: softirq-serialized
56 */
57static u8 oz_get_new_session_id(u8 exclude)
58{
59 if (++g_session_id == 0)
60 g_session_id = 1;
61 if (g_session_id == exclude) {
62 if (++g_session_id == 0)
63 g_session_id = 1;
64 }
65 return g_session_id;
66}
Rupesh Gujare6e244a82013-08-13 18:24:22 +010067
Rupesh Gujare4e7fb822013-08-23 16:11:02 +010068/*
Chris Kelly1619cb62012-02-20 21:11:28 +000069 * Context: softirq-serialized
70 */
71static void oz_send_conn_rsp(struct oz_pd *pd, u8 status)
72{
73 struct sk_buff *skb;
74 struct net_device *dev = pd->net_dev;
75 struct oz_hdr *oz_hdr;
76 struct oz_elt *elt;
77 struct oz_elt_connect_rsp *body;
Rupesh Gujare18f81912013-08-13 18:24:21 +010078
Chris Kelly1619cb62012-02-20 21:11:28 +000079 int sz = sizeof(struct oz_hdr) + sizeof(struct oz_elt) +
80 sizeof(struct oz_elt_connect_rsp);
Greg Kroah-Hartman6b790d02013-06-04 16:02:46 -070081 skb = alloc_skb(sz + OZ_ALLOCATED_SPACE(dev), GFP_ATOMIC);
Peter Huewe41ebb8a2013-02-15 15:22:25 +010082 if (skb == NULL)
Chris Kelly1619cb62012-02-20 21:11:28 +000083 return;
84 skb_reserve(skb, LL_RESERVED_SPACE(dev));
85 skb_reset_network_header(skb);
86 oz_hdr = (struct oz_hdr *)skb_put(skb, sz);
87 elt = (struct oz_elt *)(oz_hdr+1);
88 body = (struct oz_elt_connect_rsp *)(elt+1);
89 skb->dev = dev;
90 skb->protocol = htons(OZ_ETHERTYPE);
91 /* Fill in device header */
92 if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr,
93 dev->dev_addr, skb->len) < 0) {
Greg Kroah-Hartmane79f8642013-06-04 16:01:33 -070094 kfree_skb(skb);
Chris Kelly1619cb62012-02-20 21:11:28 +000095 return;
96 }
97 oz_hdr->control = (OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT);
98 oz_hdr->last_pkt_num = 0;
99 put_unaligned(0, &oz_hdr->pkt_num);
Chris Kelly1619cb62012-02-20 21:11:28 +0000100 elt->type = OZ_ELT_CONNECT_RSP;
101 elt->length = sizeof(struct oz_elt_connect_rsp);
102 memset(body, 0, sizeof(struct oz_elt_connect_rsp));
103 body->status = status;
104 if (status == 0) {
105 body->mode = pd->mode;
106 body->session_id = pd->session_id;
107 put_unaligned(cpu_to_le16(pd->total_apps), &body->apps);
108 }
Joe Perchesf724b582013-07-23 13:45:00 +0100109 oz_dbg(ON, "TX: OZ_ELT_CONNECT_RSP %d", status);
Chris Kelly1619cb62012-02-20 21:11:28 +0000110 dev_queue_xmit(skb);
111 return;
112}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100113
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100114/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000115 * Context: softirq-serialized
116 */
117static void pd_set_keepalive(struct oz_pd *pd, u8 kalive)
118{
119 unsigned long keep_alive = kalive & OZ_KALIVE_VALUE_MASK;
120
121 switch (kalive & OZ_KALIVE_TYPE_MASK) {
122 case OZ_KALIVE_SPECIAL:
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100123 pd->keep_alive = keep_alive * 1000*60*60*24*20;
Chris Kelly1619cb62012-02-20 21:11:28 +0000124 break;
125 case OZ_KALIVE_SECS:
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100126 pd->keep_alive = keep_alive*1000;
Chris Kelly1619cb62012-02-20 21:11:28 +0000127 break;
128 case OZ_KALIVE_MINS:
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100129 pd->keep_alive = keep_alive*1000*60;
Chris Kelly1619cb62012-02-20 21:11:28 +0000130 break;
131 case OZ_KALIVE_HOURS:
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100132 pd->keep_alive = keep_alive*1000*60*60;
Chris Kelly1619cb62012-02-20 21:11:28 +0000133 break;
134 default:
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100135 pd->keep_alive = 0;
Chris Kelly1619cb62012-02-20 21:11:28 +0000136 }
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100137 oz_dbg(ON, "Keepalive = %lu mSec\n", pd->keep_alive);
Chris Kelly1619cb62012-02-20 21:11:28 +0000138}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100139
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100140/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000141 * Context: softirq-serialized
142 */
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100143static void pd_set_presleep(struct oz_pd *pd, u8 presleep, u8 start_timer)
Chris Kelly1619cb62012-02-20 21:11:28 +0000144{
145 if (presleep)
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100146 pd->presleep = presleep*100;
Chris Kelly1619cb62012-02-20 21:11:28 +0000147 else
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100148 pd->presleep = OZ_PRESLEEP_TOUT;
149 if (start_timer) {
150 spin_unlock(&g_polling_lock);
151 oz_timer_add(pd, OZ_TIMER_TOUT, pd->presleep);
152 spin_lock(&g_polling_lock);
153 }
154 oz_dbg(ON, "Presleep time = %lu mSec\n", pd->presleep);
Chris Kelly1619cb62012-02-20 21:11:28 +0000155}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100156
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100157/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000158 * Context: softirq-serialized
159 */
160static struct oz_pd *oz_connect_req(struct oz_pd *cur_pd, struct oz_elt *elt,
Peter Huewedc7f5b32013-02-15 21:17:24 +0100161 const u8 *pd_addr, struct net_device *net_dev)
Chris Kelly1619cb62012-02-20 21:11:28 +0000162{
163 struct oz_pd *pd;
164 struct oz_elt_connect_req *body =
165 (struct oz_elt_connect_req *)(elt+1);
166 u8 rsp_status = OZ_STATUS_SUCCESS;
167 u8 stop_needed = 0;
168 u16 new_apps = g_apps;
Peter Huewe41ebb8a2013-02-15 15:22:25 +0100169 struct net_device *old_net_dev = NULL;
170 struct oz_pd *free_pd = NULL;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100171
Chris Kelly1619cb62012-02-20 21:11:28 +0000172 if (cur_pd) {
173 pd = cur_pd;
174 spin_lock_bh(&g_polling_lock);
175 } else {
Peter Huewe41ebb8a2013-02-15 15:22:25 +0100176 struct oz_pd *pd2 = NULL;
Chris Kelly1619cb62012-02-20 21:11:28 +0000177 struct list_head *e;
178 pd = oz_pd_alloc(pd_addr);
Peter Huewe41ebb8a2013-02-15 15:22:25 +0100179 if (pd == NULL)
180 return NULL;
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100181 getnstimeofday(&pd->last_rx_timestamp);
Chris Kelly1619cb62012-02-20 21:11:28 +0000182 spin_lock_bh(&g_polling_lock);
183 list_for_each(e, &g_pd_list) {
184 pd2 = container_of(e, struct oz_pd, link);
dingtianhong93dc5e42013-12-26 19:40:47 +0800185 if (ether_addr_equal(pd2->mac_addr, pd_addr)) {
Chris Kelly1619cb62012-02-20 21:11:28 +0000186 free_pd = pd;
187 pd = pd2;
188 break;
189 }
190 }
191 if (pd != pd2)
192 list_add_tail(&pd->link, &g_pd_list);
193 }
Peter Huewe41ebb8a2013-02-15 15:22:25 +0100194 if (pd == NULL) {
Chris Kelly1619cb62012-02-20 21:11:28 +0000195 spin_unlock_bh(&g_polling_lock);
Peter Huewe41ebb8a2013-02-15 15:22:25 +0100196 return NULL;
Chris Kelly1619cb62012-02-20 21:11:28 +0000197 }
198 if (pd->net_dev != net_dev) {
199 old_net_dev = pd->net_dev;
200 dev_hold(net_dev);
201 pd->net_dev = net_dev;
202 }
Joe Perchesf724b582013-07-23 13:45:00 +0100203 oz_dbg(ON, "Host vendor: %d\n", body->host_vendor);
Chris Kelly1619cb62012-02-20 21:11:28 +0000204 pd->max_tx_size = OZ_MAX_TX_SIZE;
205 pd->mode = body->mode;
206 pd->pd_info = body->pd_info;
207 if (pd->mode & OZ_F_ISOC_NO_ELTS) {
Chris Kelly1619cb62012-02-20 21:11:28 +0000208 pd->ms_per_isoc = body->ms_per_isoc;
209 if (!pd->ms_per_isoc)
210 pd->ms_per_isoc = 4;
Rupesh Gujare86d03a02012-07-23 18:49:46 +0100211
212 switch (body->ms_isoc_latency & OZ_LATENCY_MASK) {
213 case OZ_ONE_MS_LATENCY:
214 pd->isoc_latency = (body->ms_isoc_latency &
215 ~OZ_LATENCY_MASK) / pd->ms_per_isoc;
216 break;
217 case OZ_TEN_MS_LATENCY:
218 pd->isoc_latency = ((body->ms_isoc_latency &
219 ~OZ_LATENCY_MASK) * 10) / pd->ms_per_isoc;
220 break;
221 default:
222 pd->isoc_latency = OZ_MAX_TX_QUEUE_ISOC;
223 }
Chris Kelly1619cb62012-02-20 21:11:28 +0000224 }
225 if (body->max_len_div16)
226 pd->max_tx_size = ((u16)body->max_len_div16)<<4;
Joe Perchesf724b582013-07-23 13:45:00 +0100227 oz_dbg(ON, "Max frame:%u Ms per isoc:%u\n",
228 pd->max_tx_size, pd->ms_per_isoc);
Chris Kelly1619cb62012-02-20 21:11:28 +0000229 pd->max_stream_buffering = 3*1024;
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100230 pd->pulse_period = OZ_QUANTUM;
231 pd_set_presleep(pd, body->presleep, 0);
Chris Kelly1619cb62012-02-20 21:11:28 +0000232 pd_set_keepalive(pd, body->keep_alive);
233
234 new_apps &= le16_to_cpu(get_unaligned(&body->apps));
235 if ((new_apps & 0x1) && (body->session_id)) {
236 if (pd->session_id) {
237 if (pd->session_id != body->session_id) {
238 rsp_status = OZ_STATUS_SESSION_MISMATCH;
239 goto done;
240 }
241 } else {
242 new_apps &= ~0x1; /* Resume not permitted */
243 pd->session_id =
244 oz_get_new_session_id(body->session_id);
245 }
246 } else {
247 if (pd->session_id && !body->session_id) {
248 rsp_status = OZ_STATUS_SESSION_TEARDOWN;
249 stop_needed = 1;
250 } else {
251 new_apps &= ~0x1; /* Resume not permitted */
252 pd->session_id =
253 oz_get_new_session_id(body->session_id);
254 }
255 }
256done:
257 if (rsp_status == OZ_STATUS_SUCCESS) {
258 u16 start_apps = new_apps & ~pd->total_apps & ~0x1;
259 u16 stop_apps = pd->total_apps & ~new_apps & ~0x1;
260 u16 resume_apps = new_apps & pd->paused_apps & ~0x1;
261 spin_unlock_bh(&g_polling_lock);
262 oz_pd_set_state(pd, OZ_PD_S_CONNECTED);
Joe Perchesf724b582013-07-23 13:45:00 +0100263 oz_dbg(ON, "new_apps=0x%x total_apps=0x%x paused_apps=0x%x\n",
264 new_apps, pd->total_apps, pd->paused_apps);
Chris Kelly1619cb62012-02-20 21:11:28 +0000265 if (start_apps) {
266 if (oz_services_start(pd, start_apps, 0))
267 rsp_status = OZ_STATUS_TOO_MANY_PDS;
268 }
269 if (resume_apps)
270 if (oz_services_start(pd, resume_apps, 1))
271 rsp_status = OZ_STATUS_TOO_MANY_PDS;
272 if (stop_apps)
273 oz_services_stop(pd, stop_apps, 0);
274 oz_pd_request_heartbeat(pd);
275 } else {
276 spin_unlock_bh(&g_polling_lock);
277 }
278 oz_send_conn_rsp(pd, rsp_status);
279 if (rsp_status != OZ_STATUS_SUCCESS) {
280 if (stop_needed)
281 oz_pd_stop(pd);
282 oz_pd_put(pd);
Peter Huewe41ebb8a2013-02-15 15:22:25 +0100283 pd = NULL;
Chris Kelly1619cb62012-02-20 21:11:28 +0000284 }
285 if (old_net_dev)
286 dev_put(old_net_dev);
287 if (free_pd)
288 oz_pd_destroy(free_pd);
289 return pd;
290}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100291
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100292/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000293 * Context: softirq-serialized
294 */
295static void oz_add_farewell(struct oz_pd *pd, u8 ep_num, u8 index,
Peter Huewedc7f5b32013-02-15 21:17:24 +0100296 const u8 *report, u8 len)
Chris Kelly1619cb62012-02-20 21:11:28 +0000297{
298 struct oz_farewell *f;
299 struct oz_farewell *f2;
300 int found = 0;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100301
Rupesh Gujareb2271b52013-08-05 12:28:33 +0100302 f = kmalloc(sizeof(struct oz_farewell) + len, GFP_ATOMIC);
Chris Kelly1619cb62012-02-20 21:11:28 +0000303 if (!f)
304 return;
305 f->ep_num = ep_num;
306 f->index = index;
Rupesh Gujareb50c4602013-08-01 18:45:02 +0100307 f->len = len;
Chris Kelly1619cb62012-02-20 21:11:28 +0000308 memcpy(f->report, report, len);
Joe Perchesf724b582013-07-23 13:45:00 +0100309 oz_dbg(ON, "RX: Adding farewell report\n");
Chris Kelly1619cb62012-02-20 21:11:28 +0000310 spin_lock(&g_polling_lock);
311 list_for_each_entry(f2, &pd->farewell_list, link) {
312 if ((f2->ep_num == ep_num) && (f2->index == index)) {
313 found = 1;
314 list_del(&f2->link);
315 break;
316 }
317 }
318 list_add_tail(&f->link, &pd->farewell_list);
319 spin_unlock(&g_polling_lock);
320 if (found)
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800321 kfree(f2);
Chris Kelly1619cb62012-02-20 21:11:28 +0000322}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100323
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100324/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000325 * Context: softirq-serialized
326 */
327static void oz_rx_frame(struct sk_buff *skb)
328{
329 u8 *mac_hdr;
330 u8 *src_addr;
331 struct oz_elt *elt;
332 int length;
Peter Huewe41ebb8a2013-02-15 15:22:25 +0100333 struct oz_pd *pd = NULL;
Chris Kelly1619cb62012-02-20 21:11:28 +0000334 struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb);
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100335 struct timespec current_time;
Chris Kelly1619cb62012-02-20 21:11:28 +0000336 int dup = 0;
337 u32 pkt_num;
338
Joe Perchesf724b582013-07-23 13:45:00 +0100339 oz_dbg(RX_FRAMES, "RX frame PN=0x%x LPN=0x%x control=0x%x\n",
340 oz_hdr->pkt_num, oz_hdr->last_pkt_num, oz_hdr->control);
Chris Kelly1619cb62012-02-20 21:11:28 +0000341 mac_hdr = skb_mac_header(skb);
Matina Maria Trompouki2dce6742013-11-11 00:22:51 +0000342 src_addr = &mac_hdr[ETH_ALEN];
Chris Kelly1619cb62012-02-20 21:11:28 +0000343 length = skb->len;
344
345 /* Check the version field */
346 if (oz_get_prot_ver(oz_hdr->control) != OZ_PROTOCOL_VERSION) {
Joe Perchesf724b582013-07-23 13:45:00 +0100347 oz_dbg(ON, "Incorrect protocol version: %d\n",
348 oz_get_prot_ver(oz_hdr->control));
Chris Kelly1619cb62012-02-20 21:11:28 +0000349 goto done;
350 }
351
352 pkt_num = le32_to_cpu(get_unaligned(&oz_hdr->pkt_num));
353
354 pd = oz_pd_find(src_addr);
355 if (pd) {
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100356 if (!(pd->state & OZ_PD_S_CONNECTED))
357 oz_pd_set_state(pd, OZ_PD_S_CONNECTED);
358 getnstimeofday(&current_time);
359 if ((current_time.tv_sec != pd->last_rx_timestamp.tv_sec) ||
360 (pd->presleep < MSEC_PER_SEC)) {
361 oz_timer_add(pd, OZ_TIMER_TOUT, pd->presleep);
362 pd->last_rx_timestamp = current_time;
363 }
Chris Kelly1619cb62012-02-20 21:11:28 +0000364 if (pkt_num != pd->last_rx_pkt_num) {
365 pd->last_rx_pkt_num = pkt_num;
366 } else {
367 dup = 1;
Joe Perchesf724b582013-07-23 13:45:00 +0100368 oz_dbg(ON, "Duplicate frame\n");
Chris Kelly1619cb62012-02-20 21:11:28 +0000369 }
370 }
371
372 if (pd && !dup && ((pd->mode & OZ_MODE_MASK) == OZ_MODE_TRIGGERED)) {
Joe Perchesf724b582013-07-23 13:45:00 +0100373 oz_dbg(RX_FRAMES, "Received TRIGGER Frame\n");
Chris Kelly1619cb62012-02-20 21:11:28 +0000374 pd->last_sent_frame = &pd->tx_queue;
375 if (oz_hdr->control & OZ_F_ACK) {
376 /* Retire completed frames */
377 oz_retire_tx_frames(pd, oz_hdr->last_pkt_num);
378 }
379 if ((oz_hdr->control & OZ_F_ACK_REQUESTED) &&
380 (pd->state == OZ_PD_S_CONNECTED)) {
381 int backlog = pd->nb_queued_frames;
382 pd->trigger_pkt_num = pkt_num;
383 /* Send queued frames */
Chris Kelly1619cb62012-02-20 21:11:28 +0000384 oz_send_queued_frames(pd, backlog);
385 }
386 }
387
388 length -= sizeof(struct oz_hdr);
389 elt = (struct oz_elt *)((u8 *)oz_hdr + sizeof(struct oz_hdr));
390
391 while (length >= sizeof(struct oz_elt)) {
392 length -= sizeof(struct oz_elt) + elt->length;
393 if (length < 0)
394 break;
395 switch (elt->type) {
396 case OZ_ELT_CONNECT_REQ:
Joe Perchesf724b582013-07-23 13:45:00 +0100397 oz_dbg(ON, "RX: OZ_ELT_CONNECT_REQ\n");
Chris Kelly1619cb62012-02-20 21:11:28 +0000398 pd = oz_connect_req(pd, elt, src_addr, skb->dev);
399 break;
400 case OZ_ELT_DISCONNECT:
Joe Perchesf724b582013-07-23 13:45:00 +0100401 oz_dbg(ON, "RX: OZ_ELT_DISCONNECT\n");
Chris Kelly1619cb62012-02-20 21:11:28 +0000402 if (pd)
403 oz_pd_sleep(pd);
404 break;
405 case OZ_ELT_UPDATE_PARAM_REQ: {
406 struct oz_elt_update_param *body =
407 (struct oz_elt_update_param *)(elt + 1);
Joe Perchesf724b582013-07-23 13:45:00 +0100408 oz_dbg(ON, "RX: OZ_ELT_UPDATE_PARAM_REQ\n");
Chris Kelly1619cb62012-02-20 21:11:28 +0000409 if (pd && (pd->state & OZ_PD_S_CONNECTED)) {
410 spin_lock(&g_polling_lock);
411 pd_set_keepalive(pd, body->keepalive);
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100412 pd_set_presleep(pd, body->presleep, 1);
Chris Kelly1619cb62012-02-20 21:11:28 +0000413 spin_unlock(&g_polling_lock);
414 }
415 }
416 break;
417 case OZ_ELT_FAREWELL_REQ: {
418 struct oz_elt_farewell *body =
419 (struct oz_elt_farewell *)(elt + 1);
Joe Perchesf724b582013-07-23 13:45:00 +0100420 oz_dbg(ON, "RX: OZ_ELT_FAREWELL_REQ\n");
Chris Kelly1619cb62012-02-20 21:11:28 +0000421 oz_add_farewell(pd, body->ep_num,
422 body->index, body->report,
423 elt->length + 1 - sizeof(*body));
424 }
425 break;
426 case OZ_ELT_APP_DATA:
427 if (pd && (pd->state & OZ_PD_S_CONNECTED)) {
428 struct oz_app_hdr *app_hdr =
429 (struct oz_app_hdr *)(elt+1);
430 if (dup)
431 break;
432 oz_handle_app_elt(pd, app_hdr->app_id, elt);
433 }
434 break;
435 default:
Joe Perchesf724b582013-07-23 13:45:00 +0100436 oz_dbg(ON, "RX: Unknown elt %02x\n", elt->type);
Chris Kelly1619cb62012-02-20 21:11:28 +0000437 }
438 elt = oz_next_elt(elt);
439 }
440done:
441 if (pd)
442 oz_pd_put(pd);
443 consume_skb(skb);
444}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100445
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100446/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000447 * Context: process
448 */
449void oz_protocol_term(void)
450{
Rupesh Gujare8a3cac62013-08-01 18:40:00 +0100451 struct oz_binding *b, *t;
452
Chris Kelly1619cb62012-02-20 21:11:28 +0000453 /* Walk the list of bindings and remove each one.
454 */
455 spin_lock_bh(&g_binding_lock);
Rupesh Gujare8a3cac62013-08-01 18:40:00 +0100456 list_for_each_entry_safe(b, t, &g_binding, link) {
457 list_del(&b->link);
Chris Kelly1619cb62012-02-20 21:11:28 +0000458 spin_unlock_bh(&g_binding_lock);
459 dev_remove_pack(&b->ptype);
460 if (b->ptype.dev)
461 dev_put(b->ptype.dev);
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800462 kfree(b);
Chris Kelly1619cb62012-02-20 21:11:28 +0000463 spin_lock_bh(&g_binding_lock);
464 }
465 spin_unlock_bh(&g_binding_lock);
466 /* Walk the list of PDs and stop each one. This causes the PD to be
467 * removed from the list so we can just pull each one from the head
468 * of the list.
469 */
470 spin_lock_bh(&g_polling_lock);
471 while (!list_empty(&g_pd_list)) {
472 struct oz_pd *pd =
473 list_first_entry(&g_pd_list, struct oz_pd, link);
474 oz_pd_get(pd);
475 spin_unlock_bh(&g_polling_lock);
476 oz_pd_stop(pd);
477 oz_pd_put(pd);
478 spin_lock_bh(&g_polling_lock);
479 }
Chris Kelly1619cb62012-02-20 21:11:28 +0000480 spin_unlock_bh(&g_polling_lock);
Joe Perchesf724b582013-07-23 13:45:00 +0100481 oz_dbg(ON, "Protocol stopped\n");
Chris Kelly1619cb62012-02-20 21:11:28 +0000482}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100483
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100484/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000485 * Context: softirq
486 */
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100487void oz_pd_heartbeat_handler(unsigned long data)
Chris Kelly1619cb62012-02-20 21:11:28 +0000488{
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100489 struct oz_pd *pd = (struct oz_pd *)data;
490 u16 apps = 0;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100491
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100492 spin_lock_bh(&g_polling_lock);
493 if (pd->state & OZ_PD_S_CONNECTED)
494 apps = pd->total_apps;
495 spin_unlock_bh(&g_polling_lock);
496 if (apps)
497 oz_pd_heartbeat(pd, apps);
Rupesh Gujareb75d7d42013-08-22 17:38:50 +0100498 oz_pd_put(pd);
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100499}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100500
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100501/*
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100502 * Context: softirq
503 */
504void oz_pd_timeout_handler(unsigned long data)
505{
506 int type;
507 struct oz_pd *pd = (struct oz_pd *)data;
508
509 spin_lock_bh(&g_polling_lock);
510 type = pd->timeout_type;
511 spin_unlock_bh(&g_polling_lock);
Chris Kelly1619cb62012-02-20 21:11:28 +0000512 switch (type) {
513 case OZ_TIMER_TOUT:
514 oz_pd_sleep(pd);
515 break;
516 case OZ_TIMER_STOP:
517 oz_pd_stop(pd);
518 break;
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100519 }
Rupesh Gujareb75d7d42013-08-22 17:38:50 +0100520 oz_pd_put(pd);
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100521}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100522
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100523/*
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100524 * Context: Interrupt
525 */
526enum hrtimer_restart oz_pd_heartbeat_event(struct hrtimer *timer)
527{
528 struct oz_pd *pd;
529
530 pd = container_of(timer, struct oz_pd, heartbeat);
531 hrtimer_forward_now(timer, ktime_set(pd->pulse_period /
532 MSEC_PER_SEC, (pd->pulse_period % MSEC_PER_SEC) * NSEC_PER_MSEC));
Rupesh Gujareb75d7d42013-08-22 17:38:50 +0100533 oz_pd_get(pd);
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100534 tasklet_schedule(&pd->heartbeat_tasklet);
535 return HRTIMER_RESTART;
536}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100537
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100538/*
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100539 * Context: Interrupt
540 */
541enum hrtimer_restart oz_pd_timeout_event(struct hrtimer *timer)
542{
543 struct oz_pd *pd;
544
545 pd = container_of(timer, struct oz_pd, timeout);
Rupesh Gujareb75d7d42013-08-22 17:38:50 +0100546 oz_pd_get(pd);
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100547 tasklet_schedule(&pd->timeout_tasklet);
548 return HRTIMER_NORESTART;
549}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100550
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100551/*
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100552 * Context: softirq or process
553 */
554void oz_timer_add(struct oz_pd *pd, int type, unsigned long due_time)
555{
556 spin_lock_bh(&g_polling_lock);
557 switch (type) {
558 case OZ_TIMER_TOUT:
559 case OZ_TIMER_STOP:
560 if (hrtimer_active(&pd->timeout)) {
561 hrtimer_set_expires(&pd->timeout, ktime_set(due_time /
562 MSEC_PER_SEC, (due_time % MSEC_PER_SEC) *
563 NSEC_PER_MSEC));
564 hrtimer_start_expires(&pd->timeout, HRTIMER_MODE_REL);
565 } else {
566 hrtimer_start(&pd->timeout, ktime_set(due_time /
567 MSEC_PER_SEC, (due_time % MSEC_PER_SEC) *
568 NSEC_PER_MSEC), HRTIMER_MODE_REL);
Chris Kelly1619cb62012-02-20 21:11:28 +0000569 }
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100570 pd->timeout_type = type;
571 break;
572 case OZ_TIMER_HEARTBEAT:
573 if (!hrtimer_active(&pd->heartbeat))
574 hrtimer_start(&pd->heartbeat, ktime_set(due_time /
575 MSEC_PER_SEC, (due_time % MSEC_PER_SEC) *
576 NSEC_PER_MSEC), HRTIMER_MODE_REL);
Chris Kelly1619cb62012-02-20 21:11:28 +0000577 break;
578 }
Chris Kelly1619cb62012-02-20 21:11:28 +0000579 spin_unlock_bh(&g_polling_lock);
Chris Kelly1619cb62012-02-20 21:11:28 +0000580}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100581
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100582/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000583 * Context: softirq or process
584 */
585void oz_pd_request_heartbeat(struct oz_pd *pd)
586{
Rupesh Gujare8fd07002013-07-30 13:31:50 +0100587 oz_timer_add(pd, OZ_TIMER_HEARTBEAT, pd->pulse_period > 0 ?
588 pd->pulse_period : OZ_QUANTUM);
Chris Kelly1619cb62012-02-20 21:11:28 +0000589}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100590
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100591/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000592 * Context: softirq or process
593 */
Peter Huewedc7f5b32013-02-15 21:17:24 +0100594struct oz_pd *oz_pd_find(const u8 *mac_addr)
Chris Kelly1619cb62012-02-20 21:11:28 +0000595{
596 struct oz_pd *pd;
597 struct list_head *e;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100598
Chris Kelly1619cb62012-02-20 21:11:28 +0000599 spin_lock_bh(&g_polling_lock);
600 list_for_each(e, &g_pd_list) {
601 pd = container_of(e, struct oz_pd, link);
dingtianhong93dc5e42013-12-26 19:40:47 +0800602 if (ether_addr_equal(pd->mac_addr, mac_addr)) {
Chris Kelly1619cb62012-02-20 21:11:28 +0000603 atomic_inc(&pd->ref_count);
604 spin_unlock_bh(&g_polling_lock);
605 return pd;
606 }
607 }
608 spin_unlock_bh(&g_polling_lock);
Peter Huewe41ebb8a2013-02-15 15:22:25 +0100609 return NULL;
Chris Kelly1619cb62012-02-20 21:11:28 +0000610}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100611
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100612/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000613 * Context: process
614 */
615void oz_app_enable(int app_id, int enable)
616{
Christoph Jaegera9686e72014-08-04 14:54:52 +0200617 if (app_id < OZ_NB_APPS) {
Chris Kelly1619cb62012-02-20 21:11:28 +0000618 spin_lock_bh(&g_polling_lock);
619 if (enable)
620 g_apps |= (1<<app_id);
621 else
622 g_apps &= ~(1<<app_id);
623 spin_unlock_bh(&g_polling_lock);
624 }
625}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100626
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100627/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000628 * Context: softirq
629 */
630static int oz_pkt_recv(struct sk_buff *skb, struct net_device *dev,
631 struct packet_type *pt, struct net_device *orig_dev)
632{
Chris Kelly1619cb62012-02-20 21:11:28 +0000633 skb = skb_share_check(skb, GFP_ATOMIC);
Peter Huewe41ebb8a2013-02-15 15:22:25 +0100634 if (skb == NULL)
Chris Kelly1619cb62012-02-20 21:11:28 +0000635 return 0;
636 spin_lock_bh(&g_rx_queue.lock);
637 if (g_processing_rx) {
638 /* We already hold the lock so use __ variant.
639 */
640 __skb_queue_head(&g_rx_queue, skb);
641 spin_unlock_bh(&g_rx_queue.lock);
642 } else {
643 g_processing_rx = 1;
644 do {
645
646 spin_unlock_bh(&g_rx_queue.lock);
647 oz_rx_frame(skb);
648 spin_lock_bh(&g_rx_queue.lock);
649 if (skb_queue_empty(&g_rx_queue)) {
650 g_processing_rx = 0;
651 spin_unlock_bh(&g_rx_queue.lock);
652 break;
653 }
654 /* We already hold the lock so use __ variant.
655 */
656 skb = __skb_dequeue(&g_rx_queue);
657 } while (1);
658 }
659 return 0;
660}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100661
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100662/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000663 * Context: process
664 */
Rupesh Gujare83e48172013-08-01 18:40:01 +0100665void oz_binding_add(const char *net_dev)
Chris Kelly1619cb62012-02-20 21:11:28 +0000666{
667 struct oz_binding *binding;
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800668
Salym Senyongae4b41af2014-01-25 12:54:41 +0300669 binding = kzalloc(sizeof(struct oz_binding), GFP_KERNEL);
Salym Senyongaa44755d2014-01-25 12:54:40 +0300670 if (!binding)
671 return;
672
Jérôme Pinotc6328242014-03-13 10:20:55 +0900673 binding->ptype.type = htons(OZ_ETHERTYPE);
Salym Senyongaa44755d2014-01-25 12:54:40 +0300674 binding->ptype.func = oz_pkt_recv;
675 if (net_dev && *net_dev) {
676 memcpy(binding->name, net_dev, OZ_MAX_BINDING_LEN);
677 oz_dbg(ON, "Adding binding: %s\n", net_dev);
678 binding->ptype.dev = dev_get_by_name(&init_net, net_dev);
679 if (binding->ptype.dev == NULL) {
680 oz_dbg(ON, "Netdev %s not found\n", net_dev);
681 kfree(binding);
682 return;
Chris Kelly1619cb62012-02-20 21:11:28 +0000683 }
Chris Kelly1619cb62012-02-20 21:11:28 +0000684 }
Salym Senyongaa44755d2014-01-25 12:54:40 +0300685 dev_add_pack(&binding->ptype);
686 spin_lock_bh(&g_binding_lock);
687 list_add_tail(&binding->link, &g_binding);
688 spin_unlock_bh(&g_binding_lock);
Chris Kelly1619cb62012-02-20 21:11:28 +0000689}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100690
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100691/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000692 * Context: process
693 */
Chris Kelly1619cb62012-02-20 21:11:28 +0000694static void pd_stop_all_for_device(struct net_device *net_dev)
695{
696 struct list_head h;
697 struct oz_pd *pd;
698 struct oz_pd *n;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100699
Chris Kelly1619cb62012-02-20 21:11:28 +0000700 INIT_LIST_HEAD(&h);
701 spin_lock_bh(&g_polling_lock);
702 list_for_each_entry_safe(pd, n, &g_pd_list, link) {
703 if (pd->net_dev == net_dev) {
704 list_move(&pd->link, &h);
705 oz_pd_get(pd);
706 }
707 }
708 spin_unlock_bh(&g_polling_lock);
709 while (!list_empty(&h)) {
710 pd = list_first_entry(&h, struct oz_pd, link);
711 oz_pd_stop(pd);
712 oz_pd_put(pd);
713 }
714}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100715
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100716/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000717 * Context: process
718 */
Rupesh Gujare83e48172013-08-01 18:40:01 +0100719void oz_binding_remove(const char *net_dev)
Chris Kelly1619cb62012-02-20 21:11:28 +0000720{
Peter Huewe41ebb8a2013-02-15 15:22:25 +0100721 struct oz_binding *binding;
Rupesh Gujare8a3cac62013-08-01 18:40:00 +0100722 int found = 0;
723
Joe Perchesf724b582013-07-23 13:45:00 +0100724 oz_dbg(ON, "Removing binding: %s\n", net_dev);
Chris Kelly1619cb62012-02-20 21:11:28 +0000725 spin_lock_bh(&g_binding_lock);
Rupesh Gujare8a3cac62013-08-01 18:40:00 +0100726 list_for_each_entry(binding, &g_binding, link) {
Rupesh Gujare4d6c9e52013-08-01 18:40:02 +0100727 if (strncmp(binding->name, net_dev, OZ_MAX_BINDING_LEN) == 0) {
Joe Perchesf724b582013-07-23 13:45:00 +0100728 oz_dbg(ON, "Binding '%s' found\n", net_dev);
Rupesh Gujare8a3cac62013-08-01 18:40:00 +0100729 found = 1;
Chris Kelly1619cb62012-02-20 21:11:28 +0000730 break;
Chris Kelly1619cb62012-02-20 21:11:28 +0000731 }
732 }
733 spin_unlock_bh(&g_binding_lock);
Rupesh Gujare8a3cac62013-08-01 18:40:00 +0100734 if (found) {
Chris Kelly1619cb62012-02-20 21:11:28 +0000735 dev_remove_pack(&binding->ptype);
736 if (binding->ptype.dev) {
737 dev_put(binding->ptype.dev);
738 pd_stop_all_for_device(binding->ptype.dev);
739 }
Rupesh Gujare8a3cac62013-08-01 18:40:00 +0100740 list_del(&binding->link);
Greg Kroah-Hartman1ec41a32012-03-02 16:51:09 -0800741 kfree(binding);
Chris Kelly1619cb62012-02-20 21:11:28 +0000742 }
743}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100744
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100745/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000746 * Context: process
747 */
748static char *oz_get_next_device_name(char *s, char *dname, int max_size)
749{
750 while (*s == ',')
751 s++;
752 while (*s && (*s != ',') && max_size > 1) {
753 *dname++ = *s++;
754 max_size--;
755 }
756 *dname = 0;
757 return s;
758}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100759
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100760/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000761 * Context: process
762 */
763int oz_protocol_init(char *devs)
764{
765 skb_queue_head_init(&g_rx_queue);
Daeseok Youn01c48ad2014-05-16 18:29:44 +0900766 if (devs[0] == '*') {
Peter Huewe41ebb8a2013-02-15 15:22:25 +0100767 oz_binding_add(NULL);
Chris Kelly1619cb62012-02-20 21:11:28 +0000768 } else {
769 char d[32];
770 while (*devs) {
771 devs = oz_get_next_device_name(devs, d, sizeof(d));
772 if (d[0])
773 oz_binding_add(d);
774 }
775 }
Chris Kelly1619cb62012-02-20 21:11:28 +0000776 return 0;
777}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100778
Rupesh Gujare4e7fb822013-08-23 16:11:02 +0100779/*
Chris Kelly1619cb62012-02-20 21:11:28 +0000780 * Context: process
781 */
782int oz_get_pd_list(struct oz_mac_addr *addr, int max_count)
783{
784 struct oz_pd *pd;
785 struct list_head *e;
786 int count = 0;
Rupesh Gujare18f81912013-08-13 18:24:21 +0100787
Chris Kelly1619cb62012-02-20 21:11:28 +0000788 spin_lock_bh(&g_polling_lock);
789 list_for_each(e, &g_pd_list) {
790 if (count >= max_count)
791 break;
792 pd = container_of(e, struct oz_pd, link);
Anil Belurae35f262014-05-19 19:42:48 +1000793 ether_addr_copy((u8 *)&addr[count++], pd->mac_addr);
Chris Kelly1619cb62012-02-20 21:11:28 +0000794 }
795 spin_unlock_bh(&g_polling_lock);
796 return count;
797}
Rupesh Gujare6e244a82013-08-13 18:24:22 +0100798