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: Sat Jan 16 22:14:04 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( 4, __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
, struct qos_info
*qos
,
228 DEBUG( 4, __FUNCTION__
"()\n");
230 /* Copy QoS settings for this session */
233 irlmp_do_lap_event( self
, LM_LAP_CONNECT_INDICATION
, skb
);
237 * Function irlmp_link_connect_confirm (qos)
239 * LAP connection confirmed!
242 void irlmp_link_connect_confirm( struct lap_cb
*self
, struct qos_info
*qos
,
243 struct sk_buff
*userdata
)
245 DEBUG( 4, "irlmp_link_connect_confirm()\n");
247 ASSERT( self
!= NULL
, return;);
248 ASSERT( self
->magic
== LMP_LAP_MAGIC
, return;);
249 ASSERT( qos
!= NULL
, return;);
251 /* Copy QoS settings for this session */
254 irlmp_do_lap_event( self
, LM_LAP_CONNECT_CONFIRM
, NULL
);
258 * Function irlmp_link_discovery_confirm (self, log)
260 * Called by IrLAP with a list of discoveries after the discovery
261 * request has been carried out. A NULL log is received if IrLAP
262 * was unable to carry out the discovery request
265 void irlmp_link_discovery_confirm( struct lap_cb
*self
, hashbin_t
*log
)
267 /* DISCOVERY *discovery; */
270 DEBUG( 4, __FUNCTION__
"()\n");
272 ASSERT( self
!= NULL
, return;);
273 ASSERT( self
->magic
== LMP_LAP_MAGIC
, return;);
275 ASSERT( self
->cachelog
!= NULL
, return;);
278 * If log is missing this means that IrLAP was unable to perform the
279 * discovery, so restart discovery again with just the half timeout
283 irlmp_start_discovery_timer( irlmp
, 150);
288 discovery
= hashbin_remove_first( log
);
290 DEBUG( 0, __FUNCTION__
"(), found %s\n", discovery
->info
);
292 /* Remove any old discovery of this device */
293 hashbin_remove( self
->cachelog
, discovery
->daddr
, NULL
);
295 /* Insert the new one */
296 hashbin_insert( self
->cachelog
, (QUEUE
*) discovery
,
297 discovery
->daddr
, NULL
);
299 discovery
= hashbin_remove_first( log
);
302 old_log
= self
->cachelog
;
303 self
->cachelog
= log
;
304 hashbin_delete( old_log
, (FREE_FUNC
) kfree
);
306 irlmp_do_lap_event( self
, LM_LAP_DISCOVERY_CONFIRM
, NULL
);
308 DEBUG( 4, __FUNCTION__
"() -->\n");
311 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
312 __inline__
void irlmp_update_cache( struct lsap_cb
*self
)
314 /* Update cache entry */
315 irlmp
->cache
.dlsap_sel
= self
->dlsap_sel
;
316 irlmp
->cache
.slsap_sel
= self
->slsap_sel
;
317 irlmp
->cache
.lsap
= self
;
318 irlmp
->cache
.valid
= TRUE
;
323 * Function irlmp_find_handle (self, dlsap_sel, slsap_sel, status, queue)
325 * Find handle assosiated with destination and source LSAP
328 static struct lsap_cb
*irlmp_find_lsap( struct lap_cb
*self
, __u8 dlsap_sel
,
329 __u8 slsap_sel
, int status
,
332 struct lsap_cb
*lsap
;
334 ASSERT( self
!= NULL
, return NULL
;);
335 ASSERT( self
->magic
== LMP_LAP_MAGIC
, return NULL
;);
338 * Optimize for the common case. We assume that the last frame
339 * received is in the same connection as the last one, so check in
340 * cache first to avoid the linear search
342 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
343 ASSERT( irlmp
!= NULL
, return NULL
;);
345 if (( irlmp
->cache
.valid
) &&
346 ( irlmp
->cache
.slsap_sel
== slsap_sel
) &&
347 ( irlmp
->cache
.dlsap_sel
== dlsap_sel
))
349 DEBUG( 4, __FUNCTION__
"(), Using cached LSAP\n");
350 return ( irlmp
->cache
.lsap
);
353 lsap
= ( struct lsap_cb
*) hashbin_get_first( queue
);
354 while ( lsap
!= NULL
) {
356 * If this is an incomming connection, then the destination
357 * LSAP selector may have been specified as LM_ANY so that
358 * any client can connect. In that case we only need to check
359 * if the source LSAP (in our view!) match!
361 if (( status
== CONNECT_CMD
) &&
362 ( lsap
->slsap_sel
== slsap_sel
) &&
363 ( lsap
->dlsap_sel
== LSAP_ANY
))
365 DEBUG( 4,"Incoming connection: Setting dlsap_sel=%d\n",
367 lsap
->dlsap_sel
= dlsap_sel
;
369 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
370 irlmp_update_cache( lsap
);
375 * Check if source LSAP and dest LSAP selectors match.
377 if (( lsap
->slsap_sel
== slsap_sel
) &&
378 ( lsap
->dlsap_sel
== dlsap_sel
))
380 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
381 irlmp_update_cache( lsap
);
385 lsap
= ( struct lsap_cb
*) hashbin_get_next( queue
);
388 /* Sorry not found! */