1 /*********************************************************************
2  *
3  * Filename:      irlmp_frame.c
4  * Version:       0.9
5  * Description:   IrLMP frame implementation
6  * Status:        Experimental.
7  * Author:        Dag Brattli <dagb@cs.uit.no>
8  * Created at:    Tue Aug 19 02:09:59 1997
9  * Modified at:   Mon Dec 13 13:41:12 1999
10  * Modified by:   Dag Brattli <dagb@cs.uit.no>
11  *
12  *     Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
13  *     All Rights Reserved.
14  *     Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
15  *
16  *     This program is free software; you can redistribute it and/or
17  *     modify it under the terms of the GNU General Public License as
18  *     published by the Free Software Foundation; either version 2 of
19  *     the License, or (at your option) any later version.
20  *
21  *     Neither Dag Brattli nor University of Troms� admit liability nor
22  *     provide warranty for any of this software. This material is
23  *     provided "AS-IS" and at no charge.
24  *
25  ********************************************************************/
26 
27 #include <linux/config.h>
28 #include <linux/skbuff.h>
29 #include <linux/kernel.h>
30 
31 #include <net/irda/irda.h>
32 #include <net/irda/irlap.h>
33 #include <net/irda/timer.h>
34 #include <net/irda/irlmp.h>
35 #include <net/irda/irlmp_frame.h>
36 #include <net/irda/discovery.h>
37 
38 static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap,
39 				       __u8 slsap, int status, hashbin_t *);
40 
irlmp_send_data_pdu(struct lap_cb * self,__u8 dlsap,__u8 slsap,int expedited,struct sk_buff * skb)41 inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
42 				int expedited, struct sk_buff *skb)
43 {
44 	skb->data[0] = dlsap;
45 	skb->data[1] = slsap;
46 
47 	if (expedited) {
48 		IRDA_DEBUG(4, "%s(), sending expedited data\n", __FUNCTION__);
49 		irlap_data_request(self->irlap, skb, TRUE);
50 	} else
51 		irlap_data_request(self->irlap, skb, FALSE);
52 }
53 
54 /*
55  * Function irlmp_send_lcf_pdu (dlsap, slsap, opcode,skb)
56  *
57  *    Send Link Control Frame to IrLAP
58  */
irlmp_send_lcf_pdu(struct lap_cb * self,__u8 dlsap,__u8 slsap,__u8 opcode,struct sk_buff * skb)59 void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
60 			__u8 opcode, struct sk_buff *skb)
61 {
62 	__u8 *frame;
63 
64 	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
65 
66 	ASSERT(self != NULL, return;);
67 	ASSERT(self->magic == LMP_LAP_MAGIC, return;);
68 	ASSERT(skb != NULL, return;);
69 
70 	frame = skb->data;
71 
72 	frame[0] = dlsap | CONTROL_BIT;
73 	frame[1] = slsap;
74 
75 	frame[2] = opcode;
76 
77 	if (opcode == DISCONNECT)
78 		frame[3] = 0x01; /* Service user request */
79 	else
80 		frame[3] = 0x00; /* rsvd */
81 
82 	irlap_data_request(self->irlap, skb, FALSE);
83 }
84 
85 /*
86  * Function irlmp_input (skb)
87  *
88  *    Used by IrLAP to pass received data frames to IrLMP layer
89  *
90  */
irlmp_link_data_indication(struct lap_cb * self,struct sk_buff * skb,int unreliable)91 void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
92 				int unreliable)
93 {
94 	struct lsap_cb *lsap;
95 	__u8   slsap_sel;   /* Source (this) LSAP address */
96 	__u8   dlsap_sel;   /* Destination LSAP address */
97 	__u8   *fp;
98 
99 	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
100 
101 	ASSERT(self != NULL, return;);
102 	ASSERT(self->magic == LMP_LAP_MAGIC, return;);
103 	ASSERT(skb->len > 2, return;);
104 
105 	fp = skb->data;
106 
107 	/*
108 	 *  The next statements may be confusing, but we do this so that
109 	 *  destination LSAP of received frame is source LSAP in our view
110 	 */
111 	slsap_sel = fp[0] & LSAP_MASK;
112 	dlsap_sel = fp[1];
113 
114 	/*
115 	 *  Check if this is an incoming connection, since we must deal with
116 	 *  it in a different way than other established connections.
117 	 */
118 	if ((fp[0] & CONTROL_BIT) && (fp[2] == CONNECT_CMD)) {
119 		IRDA_DEBUG(3, "%s(), incoming connection, "
120 			   "source LSAP=%d, dest LSAP=%d\n",
121 			   __FUNCTION__, slsap_sel, dlsap_sel);
122 
123 		/* Try to find LSAP among the unconnected LSAPs */
124 		lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, CONNECT_CMD,
125 				       irlmp->unconnected_lsaps);
126 
127 		/* Maybe LSAP was already connected, so try one more time */
128 		if (!lsap) {
129 			IRDA_DEBUG(1, "%s(), incoming connection for LSAP already connected\n", __FUNCTION__);
130 			lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
131 					       self->lsaps);
132 		}
133 	} else
134 		lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
135 				       self->lsaps);
136 
137 	if (lsap == NULL) {
138 		IRDA_DEBUG(2, "IrLMP, Sorry, no LSAP for received frame!\n");
139 		IRDA_DEBUG(2, "%s(), slsap_sel = %02x, dlsap_sel = %02x\n", __FUNCTION__, slsap_sel,
140 		      dlsap_sel);
141 		if (fp[0] & CONTROL_BIT) {
142 			IRDA_DEBUG(2, "%s(), received control frame %02x\n", __FUNCTION__, fp[2]);
143 		} else {
144 			IRDA_DEBUG(2, "%s(), received data frame\n", __FUNCTION__);
145 		}
146 		dev_kfree_skb(skb);
147 		return;
148 	}
149 
150 	/*
151 	 *  Check if we received a control frame?
152 	 */
153 	if (fp[0] & CONTROL_BIT) {
154 		switch (fp[2]) {
155 		case CONNECT_CMD:
156 			lsap->lap = self;
157 			irlmp_do_lsap_event(lsap, LM_CONNECT_INDICATION, skb);
158 			break;
159 		case CONNECT_CNF:
160 			irlmp_do_lsap_event(lsap, LM_CONNECT_CONFIRM, skb);
161 			break;
162 		case DISCONNECT:
163 			IRDA_DEBUG(4, "%s(), Disconnect indication!\n", __FUNCTION__);
164 			irlmp_do_lsap_event(lsap, LM_DISCONNECT_INDICATION,
165 					    skb);
166 			break;
167 		case ACCESSMODE_CMD:
168 			IRDA_DEBUG(0, "Access mode cmd not implemented!\n");
169 			dev_kfree_skb(skb);
170 			break;
171 		case ACCESSMODE_CNF:
172 			IRDA_DEBUG(0, "Access mode cnf not implemented!\n");
173 			dev_kfree_skb(skb);
174 			break;
175 		default:
176 			IRDA_DEBUG(0, "%s(), Unknown control frame %02x\n", __FUNCTION__, fp[2]);
177 			dev_kfree_skb(skb);
178 			break;
179 		}
180 	} else if (unreliable) {
181 		/* Optimize and bypass the state machine if possible */
182 		if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
183 			irlmp_udata_indication(lsap, skb);
184 		else
185 			irlmp_do_lsap_event(lsap, LM_UDATA_INDICATION, skb);
186 	} else {
187 		/* Optimize and bypass the state machine if possible */
188 		if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
189 			irlmp_data_indication(lsap, skb);
190 		else
191 			irlmp_do_lsap_event(lsap, LM_DATA_INDICATION, skb);
192 	}
193 }
194 
195 /*
196  * Function irlmp_link_unitdata_indication (self, skb)
197  *
198  *
199  *
200  */
201 #ifdef CONFIG_IRDA_ULTRA
irlmp_link_unitdata_indication(struct lap_cb * self,struct sk_buff * skb)202 void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
203 {
204 	struct lsap_cb *lsap;
205 	__u8   slsap_sel;   /* Source (this) LSAP address */
206 	__u8   dlsap_sel;   /* Destination LSAP address */
207 	__u8   pid;         /* Protocol identifier */
208 	__u8   *fp;
209 
210 	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
211 
212 	ASSERT(self != NULL, return;);
213 	ASSERT(self->magic == LMP_LAP_MAGIC, return;);
214 	ASSERT(skb->len > 2, return;);
215 
216 	fp = skb->data;
217 
218 	/*
219 	 *  The next statements may be confusing, but we do this so that
220 	 *  destination LSAP of received frame is source LSAP in our view
221 	 */
222 	slsap_sel = fp[0] & LSAP_MASK;
223 	dlsap_sel = fp[1];
224 	pid       = fp[2];
225 
226 	if (pid & 0x80) {
227 		IRDA_DEBUG(0, "%s(), extension in PID not supp!\n", __FUNCTION__);
228 		dev_kfree_skb(skb);
229 
230 		return;
231 	}
232 
233 	/* Check if frame is addressed to the connectionless LSAP */
234 	if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) {
235 		IRDA_DEBUG(0, "%s(), dropping frame!\n", __FUNCTION__);
236 		dev_kfree_skb(skb);
237 
238 		return;
239 	}
240 
241 	lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
242 	while (lsap != NULL) {
243 		/*
244 		 *  Check if source LSAP and dest LSAP selectors and PID match.
245 		 */
246 		if ((lsap->slsap_sel == slsap_sel) &&
247 		    (lsap->dlsap_sel == dlsap_sel) &&
248 		    (lsap->pid == pid))
249 		{
250 			break;
251 		}
252 		lsap = (struct lsap_cb *) hashbin_get_next(irlmp->unconnected_lsaps);
253 	}
254 	if (lsap)
255 		irlmp_connless_data_indication(lsap, skb);
256 	else {
257 		IRDA_DEBUG(0, "%s(), found no matching LSAP!\n", __FUNCTION__);
258 		dev_kfree_skb(skb);
259 	}
260 }
261 #endif /* CONFIG_IRDA_ULTRA */
262 
263 /*
264  * Function irlmp_link_disconnect_indication (reason, userdata)
265  *
266  *    IrLAP has disconnected
267  *
268  */
irlmp_link_disconnect_indication(struct lap_cb * lap,struct irlap_cb * irlap,LAP_REASON reason,struct sk_buff * userdata)269 void irlmp_link_disconnect_indication(struct lap_cb *lap,
270 				      struct irlap_cb *irlap,
271 				      LAP_REASON reason,
272 				      struct sk_buff *userdata)
273 {
274 	IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
275 
276 	ASSERT(lap != NULL, return;);
277 	ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
278 
279 	lap->reason = reason;
280 	lap->daddr = DEV_ADDR_ANY;
281 
282         /* FIXME: must do something with the userdata if any */
283 	if (userdata)
284 		dev_kfree_skb(userdata);
285 
286 	/*
287 	 *  Inform station state machine
288 	 */
289 	irlmp_do_lap_event(lap, LM_LAP_DISCONNECT_INDICATION, NULL);
290 }
291 
292 /*
293  * Function irlmp_link_connect_indication (qos)
294  *
295  *    Incoming LAP connection!
296  *
297  */
irlmp_link_connect_indication(struct lap_cb * self,__u32 saddr,__u32 daddr,struct qos_info * qos,struct sk_buff * skb)298 void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr,
299 				   __u32 daddr, struct qos_info *qos,
300 				   struct sk_buff *skb)
301 {
302 	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
303 
304 	/* Copy QoS settings for this session */
305 	self->qos = qos;
306 
307 	/* Update destination device address */
308 	self->daddr = daddr;
309 	ASSERT(self->saddr == saddr, return;);
310 
311 	irlmp_do_lap_event(self, LM_LAP_CONNECT_INDICATION, skb);
312 }
313 
314 /*
315  * Function irlmp_link_connect_confirm (qos)
316  *
317  *    LAP connection confirmed!
318  *
319  */
irlmp_link_connect_confirm(struct lap_cb * self,struct qos_info * qos,struct sk_buff * userdata)320 void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos,
321 				struct sk_buff *userdata)
322 {
323 	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
324 
325 	ASSERT(self != NULL, return;);
326 	ASSERT(self->magic == LMP_LAP_MAGIC, return;);
327 	ASSERT(qos != NULL, return;);
328 
329 	/* Don't need use the userdata for now */
330 	if (userdata)
331 		dev_kfree_skb(userdata);
332 
333 	/* Copy QoS settings for this session */
334 	self->qos = qos;
335 
336 	irlmp_do_lap_event(self, LM_LAP_CONNECT_CONFIRM, NULL);
337 }
338 
339 /*
340  * Function irlmp_link_discovery_indication (self, log)
341  *
342  *    Device is discovering us
343  *
344  * It's not an answer to our own discoveries, just another device trying
345  * to perform discovery, but we don't want to miss the opportunity
346  * to exploit this information, because :
347  *	o We may not actively perform discovery (just passive discovery)
348  *	o This type of discovery is much more reliable. In some cases, it
349  *	  seem that less than 50% of our discoveries get an answer, while
350  *	  we always get ~100% of these.
351  *	o Make faster discovery, statistically divide time of discovery
352  *	  events by 2 (important for the latency aspect and user feel)
353  *	o Even is we do active discovery, the other node might not
354  *	  answer our discoveries (ex: Palm). The Palm will just perform
355  *	  one active discovery and connect directly to us.
356  *
357  * However, when both devices discover each other, they might attempt to
358  * connect to each other following the discovery event, and it would create
359  * collisions on the medium (SNRM battle).
360  * The "fix" for that is to disable all connection requests in IrLAP
361  * for 100ms after a discovery indication by setting the media_busy flag.
362  * Previously, we used to postpone the event which was quite ugly. Now
363  * that IrLAP takes care of this problem, just pass the event up...
364  *
365  * Jean II
366  */
irlmp_link_discovery_indication(struct lap_cb * self,discovery_t * discovery)367 void irlmp_link_discovery_indication(struct lap_cb *self,
368 				     discovery_t *discovery)
369 {
370 	ASSERT(self != NULL, return;);
371 	ASSERT(self->magic == LMP_LAP_MAGIC, return;);
372 
373 	irlmp_add_discovery(irlmp->cachelog, discovery);
374 
375 	/* Just handle it the same way as a discovery confirm,
376 	 * bypass the LM_LAP state machine (see below) */
377 	irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_PASSIVE);
378 }
379 
380 /*
381  * Function irlmp_link_discovery_confirm (self, log)
382  *
383  *    Called by IrLAP with a list of discoveries after the discovery
384  *    request has been carried out. A NULL log is received if IrLAP
385  *    was unable to carry out the discovery request
386  *
387  */
irlmp_link_discovery_confirm(struct lap_cb * self,hashbin_t * log)388 void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log)
389 {
390 	IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
391 
392 	ASSERT(self != NULL, return;);
393 	ASSERT(self->magic == LMP_LAP_MAGIC, return;);
394 
395 	irlmp_add_discovery_log(irlmp->cachelog, log);
396 
397 	/* Propagate event to various LSAPs registered for it.
398 	 * We bypass the LM_LAP state machine because
399 	 *	1) We do it regardless of the LM_LAP state
400 	 *	2) It doesn't affect the LM_LAP state
401 	 *	3) Faster, slimer, simpler, ...
402 	 * Jean II */
403 	irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_ACTIVE);
404 }
405 
406 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
irlmp_update_cache(struct lsap_cb * self)407 inline void irlmp_update_cache(struct lsap_cb *self)
408 {
409 	/* Update cache entry */
410 	irlmp->cache.dlsap_sel = self->dlsap_sel;
411 	irlmp->cache.slsap_sel = self->slsap_sel;
412 	irlmp->cache.lsap = self;
413 	irlmp->cache.valid = TRUE;
414 }
415 #endif
416 
417 /*
418  * Function irlmp_find_handle (self, dlsap_sel, slsap_sel, status, queue)
419  *
420  *    Find handle assosiated with destination and source LSAP
421  *
422  */
irlmp_find_lsap(struct lap_cb * self,__u8 dlsap_sel,__u8 slsap_sel,int status,hashbin_t * queue)423 static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
424 				       __u8 slsap_sel, int status,
425 				       hashbin_t *queue)
426 {
427 	struct lsap_cb *lsap;
428 
429 	/*
430 	 *  Optimize for the common case. We assume that the last frame
431 	 *  received is in the same connection as the last one, so check in
432 	 *  cache first to avoid the linear search
433 	 */
434 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
435 	if ((irlmp->cache.valid) &&
436 	    (irlmp->cache.slsap_sel == slsap_sel) &&
437 	    (irlmp->cache.dlsap_sel == dlsap_sel))
438 	{
439 		return (irlmp->cache.lsap);
440 	}
441 #endif
442 	lsap = (struct lsap_cb *) hashbin_get_first(queue);
443 	while (lsap != NULL) {
444 		/*
445 		 *  If this is an incoming connection, then the destination
446 		 *  LSAP selector may have been specified as LM_ANY so that
447 		 *  any client can connect. In that case we only need to check
448 		 *  if the source LSAP (in our view!) match!
449 		 */
450 		if ((status == CONNECT_CMD) &&
451 		    (lsap->slsap_sel == slsap_sel) &&
452 		    (lsap->dlsap_sel == LSAP_ANY))
453 		{
454 			lsap->dlsap_sel = dlsap_sel;
455 
456 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
457 			irlmp_update_cache(lsap);
458 #endif
459 			return lsap;
460 		}
461 		/*
462 		 *  Check if source LSAP and dest LSAP selectors match.
463 		 */
464 		if ((lsap->slsap_sel == slsap_sel) &&
465 		    (lsap->dlsap_sel == dlsap_sel))
466 		{
467 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
468 			irlmp_update_cache(lsap);
469 #endif
470 			return lsap;
471 		}
472 		lsap = (struct lsap_cb *) hashbin_get_next(queue);
473 	}
474 
475 	/* Sorry not found! */
476 	return NULL;
477 }
478