2.2.0-final
[davej-history.git] / net / irda / irlmp_frame.c
blob9d5ac0c35cc612ae2d12a5e04b80ce9a9ab04e88
1 /*********************************************************************
2 *
3 * Filename: irlmp_frame.c
4 * Version: 0.8
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)
42 __u8 *frame;
44 ASSERT( self != NULL, return;);
45 ASSERT( self->magic == LMP_LAP_MAGIC, return;);
46 ASSERT( skb != NULL, return;);
48 frame = skb->data;
50 frame[0] = dlsap;
51 frame[1] = slsap;
53 if ( expedited) {
54 DEBUG( 4, __FUNCTION__ "(), sending expedited data\n");
55 irlap_data_request( self->irlap, skb, FALSE);
56 } else {
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)
70 __u8 *frame;
72 DEBUG( 4, __FUNCTION__ "()\n");
74 ASSERT( self != NULL, return;);
75 ASSERT( self->magic == LMP_LAP_MAGIC, return;);
76 ASSERT( skb != NULL, return;);
78 frame = skb->data;
80 frame[0] = dlsap | CONTROL_BIT;
81 frame[1] = slsap;
83 frame[2] = opcode;
85 if (opcode == DISCONNECT)
86 frame[3] = 0x01; /* Service user request */
87 else
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,
101 struct sk_buff *skb)
103 __u8 *fp;
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;);
113 fp = skb->data;
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;
120 dlsap_sel = fp[1];
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 */
135 if ( !lsap)
136 lsap = irlmp_find_lsap( self, dlsap_sel, slsap_sel, 0,
137 self->lsaps);
138 } else
139 lsap = irlmp_find_lsap( self, dlsap_sel, slsap_sel, 0,
140 self->lsaps);
142 if ( lsap == NULL) {
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,
146 dlsap_sel);
147 if ( fp[0] & CONTROL_BIT) {
148 DEBUG( 0, __FUNCTION__
149 "(), received control frame %02x\n", fp[2]);
150 } else {
151 DEBUG( 0, __FUNCTION__ "(), received data frame\n");
153 dev_kfree_skb( skb);
154 return;
158 * Check if we received a control frame?
160 if ( fp[0] & CONTROL_BIT) {
161 switch( fp[2]) {
162 case CONNECT_CMD:
163 lsap->lap = self;
164 irlmp_do_lsap_event( lsap, LM_CONNECT_INDICATION, skb);
165 break;
166 case CONNECT_CNF:
167 irlmp_do_lsap_event( lsap, LM_CONNECT_CONFIRM, skb);
168 break;
169 case DISCONNECT:
170 DEBUG( 4, __FUNCTION__ "(), Disconnect indication!\n");
171 irlmp_do_lsap_event( lsap, LM_DISCONNECT_INDICATION,
172 skb);
173 break;
174 case ACCESSMODE_CMD:
175 DEBUG( 0, "Access mode cmd not implemented!\n");
176 break;
177 case ACCESSMODE_CNF:
178 DEBUG( 0, "Access mode cnf not implemented!\n");
179 break;
180 default:
181 DEBUG( 0, __FUNCTION__
182 "(), Unknown control frame %02x\n", fp[2]);
183 break;
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,
201 LAP_REASON reason,
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,
226 struct sk_buff *skb)
228 DEBUG( 4, __FUNCTION__ "()\n");
230 /* Copy QoS settings for this session */
231 self->qos = qos;
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 */
252 self->qos = qos;
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; */
268 hashbin_t *old_log;
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
280 * of the normal one.
282 if ( !log) {
283 irlmp_start_discovery_timer( irlmp, 150);
284 return;
287 #if 0
288 discovery = hashbin_remove_first( log);
289 while ( discovery) {
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);
301 #endif
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;
320 #endif
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,
330 hashbin_t *queue)
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);
352 #endif
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",
366 dlsap_sel);
367 lsap->dlsap_sel = dlsap_sel;
369 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
370 irlmp_update_cache( lsap);
371 #endif
372 return 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);
382 #endif
383 return lsap;
385 lsap = ( struct lsap_cb *) hashbin_get_next( queue);
388 /* Sorry not found! */
389 return NULL;