import of ftp.dlink.com/GPL/DSMG-600_reB/ppclinux.tar.gz
[linux-2.4.21-pre4.git] / net / irda / irlmp_frame.c
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
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, __FUNCTION__ "(), sending expedited data\n");
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  */
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, __FUNCTION__ "()\n");
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  */
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, __FUNCTION__ "()\n");
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, __FUNCTION__ "(), incoming connection, "
120                            "source LSAP=%d, dest LSAP=%d\n",
121                            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, __FUNCTION__ "(), incoming connection for LSAP already connected\n");
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, __FUNCTION__ 
140                       "(), slsap_sel = %02x, dlsap_sel = %02x\n", slsap_sel, 
141                       dlsap_sel);
142                 if (fp[0] & CONTROL_BIT) {
143                         IRDA_DEBUG(2, __FUNCTION__ 
144                               "(), received control frame %02x\n", fp[2]);
145                 } else {
146                         IRDA_DEBUG(2, __FUNCTION__ "(), received data frame\n");
147                 }
148                 dev_kfree_skb(skb);
149                 return;
150         }
151
152         /* 
153          *  Check if we received a control frame? 
154          */
155         if (fp[0] & CONTROL_BIT) {
156                 switch (fp[2]) {
157                 case CONNECT_CMD:
158                         lsap->lap = self;
159                         irlmp_do_lsap_event(lsap, LM_CONNECT_INDICATION, skb);
160                         break;
161                 case CONNECT_CNF:
162                         irlmp_do_lsap_event(lsap, LM_CONNECT_CONFIRM, skb);
163                         break;
164                 case DISCONNECT:
165                         IRDA_DEBUG(4, __FUNCTION__ 
166                                    "(), Disconnect indication!\n");
167                         irlmp_do_lsap_event(lsap, LM_DISCONNECT_INDICATION, 
168                                             skb);
169                         break;
170                 case ACCESSMODE_CMD:
171                         IRDA_DEBUG(0, "Access mode cmd not implemented!\n");
172                         dev_kfree_skb(skb);
173                         break;
174                 case ACCESSMODE_CNF:
175                         IRDA_DEBUG(0, "Access mode cnf not implemented!\n");
176                         dev_kfree_skb(skb);
177                         break;
178                 default:
179                         IRDA_DEBUG(0, __FUNCTION__ 
180                                    "(), Unknown control frame %02x\n", fp[2]);
181                         dev_kfree_skb(skb);
182                         break;
183                 }
184         } else if (unreliable) {
185                 /* Optimize and bypass the state machine if possible */
186                 if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
187                         irlmp_udata_indication(lsap, skb);
188                 else
189                         irlmp_do_lsap_event(lsap, LM_UDATA_INDICATION, skb);
190         } else {        
191                 /* Optimize and bypass the state machine if possible */
192                 if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
193                         irlmp_data_indication(lsap, skb);
194                 else
195                         irlmp_do_lsap_event(lsap, LM_DATA_INDICATION, skb);
196         }
197 }
198
199 /*
200  * Function irlmp_link_unitdata_indication (self, skb)
201  *
202  *    
203  *
204  */
205 #ifdef CONFIG_IRDA_ULTRA
206 void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
207 {
208         struct lsap_cb *lsap;
209         __u8   slsap_sel;   /* Source (this) LSAP address */
210         __u8   dlsap_sel;   /* Destination LSAP address */
211         __u8   pid;         /* Protocol identifier */
212         __u8   *fp;
213         
214         IRDA_DEBUG(4, __FUNCTION__ "()\n");
215
216         ASSERT(self != NULL, return;);
217         ASSERT(self->magic == LMP_LAP_MAGIC, return;);
218         ASSERT(skb->len > 2, return;);
219
220         fp = skb->data;
221
222         /*
223          *  The next statements may be confusing, but we do this so that 
224          *  destination LSAP of received frame is source LSAP in our view
225          */
226         slsap_sel = fp[0] & LSAP_MASK; 
227         dlsap_sel = fp[1];
228         pid       = fp[2];
229         
230         if (pid & 0x80) {
231                 IRDA_DEBUG(0, __FUNCTION__ "(), extension in PID not supp!\n");
232                 dev_kfree_skb(skb);
233
234                 return;
235         }
236
237         /* Check if frame is addressed to the connectionless LSAP */
238         if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) {
239                 IRDA_DEBUG(0, __FUNCTION__ "(), dropping frame!\n");
240                 dev_kfree_skb(skb);
241                 
242                 return;
243         }
244         
245         lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
246         while (lsap != NULL) {
247                 /*
248                  *  Check if source LSAP and dest LSAP selectors and PID match.
249                  */
250                 if ((lsap->slsap_sel == slsap_sel) && 
251                     (lsap->dlsap_sel == dlsap_sel) && 
252                     (lsap->pid == pid)) 
253                 {                       
254                         break;
255                 }
256                 lsap = (struct lsap_cb *) hashbin_get_next(irlmp->unconnected_lsaps);
257         }
258         if (lsap)
259                 irlmp_connless_data_indication(lsap, skb);
260         else {
261                 IRDA_DEBUG(0, __FUNCTION__ "(), found no matching LSAP!\n");
262                 dev_kfree_skb(skb);
263         }
264 }
265 #endif /* CONFIG_IRDA_ULTRA */
266
267 /*
268  * Function irlmp_link_disconnect_indication (reason, userdata)
269  *
270  *    IrLAP has disconnected 
271  *
272  */
273 void irlmp_link_disconnect_indication(struct lap_cb *lap, 
274                                       struct irlap_cb *irlap, 
275                                       LAP_REASON reason, 
276                                       struct sk_buff *userdata)
277 {
278         IRDA_DEBUG(2, __FUNCTION__ "()\n");
279
280         ASSERT(lap != NULL, return;);
281         ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
282
283         lap->reason = reason;
284         lap->daddr = DEV_ADDR_ANY;
285
286         /* FIXME: must do something with the userdata if any */
287         if (userdata)
288                 dev_kfree_skb(userdata);
289         
290         /*
291          *  Inform station state machine
292          */
293         irlmp_do_lap_event(lap, LM_LAP_DISCONNECT_INDICATION, NULL);
294 }
295
296 /*
297  * Function irlmp_link_connect_indication (qos)
298  *
299  *    Incoming LAP connection!
300  *
301  */
302 void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr, 
303                                    __u32 daddr, struct qos_info *qos,
304                                    struct sk_buff *skb) 
305 {
306         IRDA_DEBUG(4, __FUNCTION__ "()\n");
307
308         /* Copy QoS settings for this session */
309         self->qos = qos;
310
311         /* Update destination device address */
312         self->daddr = daddr;
313         ASSERT(self->saddr == saddr, return;);
314
315         irlmp_do_lap_event(self, LM_LAP_CONNECT_INDICATION, skb);
316 }
317
318 /*
319  * Function irlmp_link_connect_confirm (qos)
320  *
321  *    LAP connection confirmed!
322  *
323  */
324 void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos, 
325                                 struct sk_buff *userdata)
326 {
327         IRDA_DEBUG(4, __FUNCTION__ "()\n");
328
329         ASSERT(self != NULL, return;);
330         ASSERT(self->magic == LMP_LAP_MAGIC, return;);
331         ASSERT(qos != NULL, return;);
332
333         /* Don't need use the userdata for now */
334         if (userdata)
335                 dev_kfree_skb(userdata);
336
337         /* Copy QoS settings for this session */
338         self->qos = qos;
339
340         irlmp_do_lap_event(self, LM_LAP_CONNECT_CONFIRM, NULL);
341 }
342
343 /*
344  * Function irlmp_link_discovery_indication (self, log)
345  *
346  *    Device is discovering us
347  *
348  * It's not an answer to our own discoveries, just another device trying
349  * to perform discovery, but we don't want to miss the opportunity
350  * to exploit this information, because :
351  *      o We may not actively perform discovery (just passive discovery)
352  *      o This type of discovery is much more reliable. In some cases, it
353  *        seem that less than 50% of our discoveries get an answer, while
354  *        we always get ~100% of these.
355  *      o Make faster discovery, statistically divide time of discovery
356  *        events by 2 (important for the latency aspect and user feel)
357  *      o Even is we do active discovery, the other node might not
358  *        answer our discoveries (ex: Palm). The Palm will just perform
359  *        one active discovery and connect directly to us.
360  *
361  * However, when both devices discover each other, they might attempt to
362  * connect to each other following the discovery event, and it would create
363  * collisions on the medium (SNRM battle).
364  * The "fix" for that is to disable all connection requests in IrLAP
365  * for 100ms after a discovery indication by setting the media_busy flag.
366  * Previously, we used to postpone the event which was quite ugly. Now
367  * that IrLAP takes care of this problem, just pass the event up...
368  *
369  * Jean II
370  */
371 void irlmp_link_discovery_indication(struct lap_cb *self, 
372                                      discovery_t *discovery)
373 {
374         ASSERT(self != NULL, return;);
375         ASSERT(self->magic == LMP_LAP_MAGIC, return;);
376
377         irlmp_add_discovery(irlmp->cachelog, discovery);
378         
379         /* Just handle it the same way as a discovery confirm,
380          * bypass the LM_LAP state machine (see below) */
381         irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_PASSIVE);
382 }
383
384 /*
385  * Function irlmp_link_discovery_confirm (self, log)
386  *
387  *    Called by IrLAP with a list of discoveries after the discovery
388  *    request has been carried out. A NULL log is received if IrLAP
389  *    was unable to carry out the discovery request
390  *
391  */
392 void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log)
393 {
394         IRDA_DEBUG(4, __FUNCTION__ "()\n");
395
396         ASSERT(self != NULL, return;);
397         ASSERT(self->magic == LMP_LAP_MAGIC, return;);
398         
399         irlmp_add_discovery_log(irlmp->cachelog, log);
400
401         /* Propagate event to various LSAPs registered for it.
402          * We bypass the LM_LAP state machine because
403          *      1) We do it regardless of the LM_LAP state
404          *      2) It doesn't affect the LM_LAP state
405          *      3) Faster, slimer, simpler, ...
406          * Jean II */
407         irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_ACTIVE);
408 }
409
410 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
411 inline void irlmp_update_cache(struct lsap_cb *self)
412 {
413         /* Update cache entry */
414         irlmp->cache.dlsap_sel = self->dlsap_sel;
415         irlmp->cache.slsap_sel = self->slsap_sel;
416         irlmp->cache.lsap = self;
417         irlmp->cache.valid = TRUE;
418 }
419 #endif
420
421 /*
422  * Function irlmp_find_handle (self, dlsap_sel, slsap_sel, status, queue)
423  *
424  *    Find handle assosiated with destination and source LSAP
425  *
426  */
427 static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
428                                        __u8 slsap_sel, int status,
429                                        hashbin_t *queue) 
430 {
431         struct lsap_cb *lsap;
432         
433         /* 
434          *  Optimize for the common case. We assume that the last frame
435          *  received is in the same connection as the last one, so check in
436          *  cache first to avoid the linear search
437          */
438 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
439         if ((irlmp->cache.valid) && 
440             (irlmp->cache.slsap_sel == slsap_sel) && 
441             (irlmp->cache.dlsap_sel == dlsap_sel)) 
442         {
443                 return (irlmp->cache.lsap);
444         }
445 #endif
446         lsap = (struct lsap_cb *) hashbin_get_first(queue);
447         while (lsap != NULL) {
448                 /* 
449                  *  If this is an incoming connection, then the destination 
450                  *  LSAP selector may have been specified as LM_ANY so that 
451                  *  any client can connect. In that case we only need to check
452                  *  if the source LSAP (in our view!) match!
453                  */
454                 if ((status == CONNECT_CMD) && 
455                     (lsap->slsap_sel == slsap_sel) &&      
456                     (lsap->dlsap_sel == LSAP_ANY)) 
457                 {
458                         lsap->dlsap_sel = dlsap_sel;
459                         
460 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
461                         irlmp_update_cache(lsap);
462 #endif
463                         return lsap;
464                 }
465                 /*
466                  *  Check if source LSAP and dest LSAP selectors match.
467                  */
468                 if ((lsap->slsap_sel == slsap_sel) && 
469                     (lsap->dlsap_sel == dlsap_sel)) 
470                 {
471 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
472                         irlmp_update_cache(lsap);
473 #endif
474                         return lsap;
475                 }
476                 lsap = (struct lsap_cb *) hashbin_get_next(queue);
477         }
478
479         /* Sorry not found! */
480         return NULL;
481 }