Linux 2.2.3pre3
[davej-history.git] / net / irda / irlmp_frame.c
blobbf993d4902c4833a33d4c33a624fa1451d0816e1
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: 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)
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(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,
227 struct sk_buff *skb)
229 DEBUG( 4, __FUNCTION__ "()\n");
231 /* Copy QoS settings for this session */
232 self->qos = qos;
234 /* Update destination device address */
235 self->daddr = daddr;
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 */
257 self->qos = qos;
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; */
273 hashbin_t *old_log;
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
285 * of the normal one.
287 if ( !log) {
288 irlmp_start_discovery_timer( irlmp, 150);
289 return;
292 #if 0
293 discovery = hashbin_remove_first( log);
294 while ( discovery) {
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);
306 #endif
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;
325 #endif
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,
335 hashbin_t *queue)
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);
357 #endif
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",
371 dlsap_sel);
372 lsap->dlsap_sel = dlsap_sel;
374 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
375 irlmp_update_cache( lsap);
376 #endif
377 return 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);
387 #endif
388 return lsap;
390 lsap = ( struct lsap_cb *) hashbin_get_next( queue);
393 /* Sorry not found! */
394 return NULL;