1 /*********************************************************************
3 * Filename: irlmp_frame.c
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: Thu Feb 18 08:48:28 1999
10 * Modified by: Dag Brattli <dagb@cs.uit.no>
12 * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>
13 * All Rights Reserved.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License as
17 * published by the Free Software Foundation; either version 2 of
18 * the License, or (at your option) any later version.
20 * Neither Dag Brattli nor University of Tromsø admit liability nor
21 * provide warranty for any of this software. This material is
22 * provided "AS-IS" and at no charge.
24 ********************************************************************/
26 #include <linux/config.h>
27 #include <linux/skbuff.h>
28 #include <linux/kernel.h>
30 #include <net/irda/irda.h>
31 #include <net/irda/irlap.h>
32 #include <net/irda/timer.h>
33 #include <net/irda/irlmp.h>
34 #include <net/irda/irlmp_frame.h>
36 static struct lsap_cb
*irlmp_find_lsap( struct lap_cb
*self
, __u8 dlsap
,
37 __u8 slsap
, int status
, hashbin_t
*);
39 inline void irlmp_send_data_pdu( struct lap_cb
*self
, __u8 dlsap
, __u8 slsap
,
40 int expedited
, struct sk_buff
*skb
)
44 ASSERT( self
!= NULL
, return;);
45 ASSERT( self
->magic
== LMP_LAP_MAGIC
, return;);
46 ASSERT( skb
!= NULL
, return;);
54 DEBUG( 4, __FUNCTION__
"(), sending expedited data\n");
55 irlap_data_request( self
->irlap
, skb
, FALSE
);
57 DEBUG( 4, __FUNCTION__
"(), sending reliable data\n");
58 irlap_data_request( self
->irlap
, skb
, TRUE
);
63 * Function irlmp_send_lcf_pdu (dlsap, slsap, opcode,skb)
65 * Send Link Control Frame to IrLAP
67 void irlmp_send_lcf_pdu( struct lap_cb
*self
, __u8 dlsap
, __u8 slsap
,
68 __u8 opcode
, struct sk_buff
*skb
)
72 DEBUG( 4, __FUNCTION__
"()\n");
74 ASSERT( self
!= NULL
, return;);
75 ASSERT( self
->magic
== LMP_LAP_MAGIC
, return;);
76 ASSERT( skb
!= NULL
, return;);
80 frame
[0] = dlsap
| CONTROL_BIT
;
85 if (opcode
== DISCONNECT
)
86 frame
[3] = 0x01; /* Service user request */
88 frame
[3] = 0x00; /* rsvd */
90 ASSERT( self
->irlap
!= NULL
, return;);
91 irlap_data_request( self
->irlap
, skb
, TRUE
);
95 * Function irlmp_input (skb)
97 * Used by IrLAP to pass received data frames to IrLMP layer
100 void irlmp_link_data_indication( struct lap_cb
*self
, int reliable
,
104 __u8 slsap_sel
; /* Source (this) LSAP address */
105 __u8 dlsap_sel
; /* Destination LSAP address */
106 struct lsap_cb
*lsap
;
108 ASSERT( self
!= NULL
, return;);
109 ASSERT( self
->magic
== LMP_LAP_MAGIC
, return;);
110 ASSERT( skb
!= NULL
, return;);
111 ASSERT( skb
->len
> 2, return;);
116 * The next statements may be confusing, but we do this so that
117 * destination LSAP of received frame is source LSAP in our view
119 slsap_sel
= fp
[0] & LSAP_MASK
;
123 * Check if this is an incoming connection, since we must deal with
124 * it in a different way than other established connections.
126 if (( fp
[0] & CONTROL_BIT
) && ( fp
[2] == CONNECT_CMD
)) {
127 DEBUG( 4,"Incoming connection, source LSAP=%d, dest LSAP=%d\n",
128 slsap_sel
, dlsap_sel
);
130 /* Try to find LSAP among the unconnected LSAPs */
131 lsap
= irlmp_find_lsap( self
, dlsap_sel
, slsap_sel
,
132 CONNECT_CMD
, irlmp
->unconnected_lsaps
);
134 /* Maybe LSAP was already connected, so try one more time */
136 lsap
= irlmp_find_lsap( self
, dlsap_sel
, slsap_sel
, 0,
139 lsap
= irlmp_find_lsap( self
, dlsap_sel
, slsap_sel
, 0,
143 DEBUG( 0, "IrLMP, Sorry, no LSAP for received frame!\n");
144 DEBUG( 0, __FUNCTION__
145 "(), slsap_sel = %02x, dlsap_sel = %02x\n", slsap_sel
,
147 if ( fp
[0] & CONTROL_BIT
) {
148 DEBUG( 0, __FUNCTION__
149 "(), received control frame %02x\n", fp
[2]);
151 DEBUG( 0, __FUNCTION__
"(), received data frame\n");
158 * Check if we received a control frame?
160 if ( fp
[0] & CONTROL_BIT
) {
164 irlmp_do_lsap_event( lsap
, LM_CONNECT_INDICATION
, skb
);
167 irlmp_do_lsap_event( lsap
, LM_CONNECT_CONFIRM
, skb
);
170 DEBUG( 4, __FUNCTION__
"(), Disconnect indication!\n");
171 irlmp_do_lsap_event( lsap
, LM_DISCONNECT_INDICATION
,
175 DEBUG( 0, "Access mode cmd not implemented!\n");
178 DEBUG( 0, "Access mode cnf not implemented!\n");
181 DEBUG( 0, __FUNCTION__
182 "(), Unknown control frame %02x\n", fp
[2]);
185 } else if ( reliable
== LAP_RELIABLE
) {
186 /* Must be pure data */
187 irlmp_do_lsap_event( lsap
, LM_DATA_INDICATION
, skb
);
188 } else if ( reliable
== LAP_UNRELIABLE
) {
189 irlmp_do_lsap_event( lsap
, LM_UDATA_INDICATION
, skb
);
194 * Function irlmp_link_disconnect_indication (reason, userdata)
196 * IrLAP has disconnected
199 void irlmp_link_disconnect_indication(struct lap_cb
*lap
,
200 struct irlap_cb
*irlap
,
202 struct sk_buff
*userdata
)
204 DEBUG(2, __FUNCTION__
"()\n");
206 ASSERT(lap
!= NULL
, return;);
207 ASSERT(lap
->magic
== LMP_LAP_MAGIC
, return;);
209 lap
->reason
= reason
;
211 /* FIXME: must do something with the userdata if any */
214 * Inform station state machine
216 irlmp_do_lap_event(lap
, LM_LAP_DISCONNECT_INDICATION
, NULL
);
220 * Function irlmp_link_connect_indication (qos)
222 * Incoming LAP connection!
225 void irlmp_link_connect_indication( struct lap_cb
*self
, __u32 saddr
,
226 __u32 daddr
, struct qos_info
*qos
,
229 DEBUG( 4, __FUNCTION__
"()\n");
231 /* Copy QoS settings for this session */
234 /* Update destination device address */
236 ASSERT(self
->saddr
== saddr
, return;);
238 irlmp_do_lap_event( self
, LM_LAP_CONNECT_INDICATION
, skb
);
242 * Function irlmp_link_connect_confirm (qos)
244 * LAP connection confirmed!
247 void irlmp_link_connect_confirm( struct lap_cb
*self
, struct qos_info
*qos
,
248 struct sk_buff
*userdata
)
250 DEBUG( 4, "irlmp_link_connect_confirm()\n");
252 ASSERT( self
!= NULL
, return;);
253 ASSERT( self
->magic
== LMP_LAP_MAGIC
, return;);
254 ASSERT( qos
!= NULL
, return;);
256 /* Copy QoS settings for this session */
259 irlmp_do_lap_event( self
, LM_LAP_CONNECT_CONFIRM
, NULL
);
263 * Function irlmp_link_discovery_confirm (self, log)
265 * Called by IrLAP with a list of discoveries after the discovery
266 * request has been carried out. A NULL log is received if IrLAP
267 * was unable to carry out the discovery request
270 void irlmp_link_discovery_confirm( struct lap_cb
*self
, hashbin_t
*log
)
272 /* DISCOVERY *discovery; */
275 DEBUG( 4, __FUNCTION__
"()\n");
277 ASSERT( self
!= NULL
, return;);
278 ASSERT( self
->magic
== LMP_LAP_MAGIC
, return;);
280 ASSERT( self
->cachelog
!= NULL
, return;);
283 * If log is missing this means that IrLAP was unable to perform the
284 * discovery, so restart discovery again with just the half timeout
288 irlmp_start_discovery_timer( irlmp
, 150);
293 discovery
= hashbin_remove_first( log
);
295 DEBUG( 0, __FUNCTION__
"(), found %s\n", discovery
->info
);
297 /* Remove any old discovery of this device */
298 hashbin_remove( self
->cachelog
, discovery
->daddr
, NULL
);
300 /* Insert the new one */
301 hashbin_insert( self
->cachelog
, (QUEUE
*) discovery
,
302 discovery
->daddr
, NULL
);
304 discovery
= hashbin_remove_first( log
);
307 old_log
= self
->cachelog
;
308 self
->cachelog
= log
;
309 hashbin_delete( old_log
, (FREE_FUNC
) kfree
);
311 irlmp_do_lap_event( self
, LM_LAP_DISCOVERY_CONFIRM
, NULL
);
313 DEBUG( 4, __FUNCTION__
"() -->\n");
316 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
317 __inline__
void irlmp_update_cache( struct lsap_cb
*self
)
319 /* Update cache entry */
320 irlmp
->cache
.dlsap_sel
= self
->dlsap_sel
;
321 irlmp
->cache
.slsap_sel
= self
->slsap_sel
;
322 irlmp
->cache
.lsap
= self
;
323 irlmp
->cache
.valid
= TRUE
;
328 * Function irlmp_find_handle (self, dlsap_sel, slsap_sel, status, queue)
330 * Find handle assosiated with destination and source LSAP
333 static struct lsap_cb
*irlmp_find_lsap( struct lap_cb
*self
, __u8 dlsap_sel
,
334 __u8 slsap_sel
, int status
,
337 struct lsap_cb
*lsap
;
339 ASSERT( self
!= NULL
, return NULL
;);
340 ASSERT( self
->magic
== LMP_LAP_MAGIC
, return NULL
;);
343 * Optimize for the common case. We assume that the last frame
344 * received is in the same connection as the last one, so check in
345 * cache first to avoid the linear search
347 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
348 ASSERT( irlmp
!= NULL
, return NULL
;);
350 if (( irlmp
->cache
.valid
) &&
351 ( irlmp
->cache
.slsap_sel
== slsap_sel
) &&
352 ( irlmp
->cache
.dlsap_sel
== dlsap_sel
))
354 DEBUG( 4, __FUNCTION__
"(), Using cached LSAP\n");
355 return ( irlmp
->cache
.lsap
);
358 lsap
= ( struct lsap_cb
*) hashbin_get_first( queue
);
359 while ( lsap
!= NULL
) {
361 * If this is an incomming connection, then the destination
362 * LSAP selector may have been specified as LM_ANY so that
363 * any client can connect. In that case we only need to check
364 * if the source LSAP (in our view!) match!
366 if (( status
== CONNECT_CMD
) &&
367 ( lsap
->slsap_sel
== slsap_sel
) &&
368 ( lsap
->dlsap_sel
== LSAP_ANY
))
370 DEBUG( 4,"Incoming connection: Setting dlsap_sel=%d\n",
372 lsap
->dlsap_sel
= dlsap_sel
;
374 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
375 irlmp_update_cache( lsap
);
380 * Check if source LSAP and dest LSAP selectors match.
382 if (( lsap
->slsap_sel
== slsap_sel
) &&
383 ( lsap
->dlsap_sel
== dlsap_sel
))
385 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
386 irlmp_update_cache( lsap
);
390 lsap
= ( struct lsap_cb
*) hashbin_get_next( queue
);
393 /* Sorry not found! */