~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/net/irda/irlmp_frame.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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  *     
 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.
 19  *
 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.
 23  *
 24  ********************************************************************/
 25 
 26 #include <linux/config.h>
 27 #include <linux/skbuff.h>
 28 #include <linux/kernel.h>
 29 
 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>
 35 #include <net/irda/discovery.h>
 36 
 37 #define DISCO_SMALL_DELAY       250     /* Delay for some discoveries in ms */
 38 struct timer_list disco_delay;          /* The timer associated */
 39 
 40 static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap, 
 41                                        __u8 slsap, int status, hashbin_t *);
 42 
 43 inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
 44                                 int expedited, struct sk_buff *skb)
 45 {
 46         skb->data[0] = dlsap;
 47         skb->data[1] = slsap;
 48 
 49         if (expedited) {
 50                 IRDA_DEBUG(4, __FUNCTION__ "(), sending expedited data\n");
 51                 irlap_data_request(self->irlap, skb, TRUE);
 52         } else
 53                 irlap_data_request(self->irlap, skb, FALSE);
 54 }
 55 
 56 /*
 57  * Function irlmp_send_lcf_pdu (dlsap, slsap, opcode,skb)
 58  *
 59  *    Send Link Control Frame to IrLAP
 60  */
 61 void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
 62                         __u8 opcode, struct sk_buff *skb) 
 63 {
 64         __u8 *frame;
 65         
 66         IRDA_DEBUG(2, __FUNCTION__ "()\n");
 67 
 68         ASSERT(self != NULL, return;);
 69         ASSERT(self->magic == LMP_LAP_MAGIC, return;);
 70         ASSERT(skb != NULL, return;);
 71         
 72         frame = skb->data;
 73         
 74         frame[0] = dlsap | CONTROL_BIT;
 75         frame[1] = slsap;
 76 
 77         frame[2] = opcode;
 78 
 79         if (opcode == DISCONNECT)
 80                 frame[3] = 0x01; /* Service user request */
 81         else
 82                 frame[3] = 0x00; /* rsvd */
 83 
 84         irlap_data_request(self->irlap, skb, FALSE);
 85 }
 86 
 87 /*
 88  * Function irlmp_input (skb)
 89  *
 90  *    Used by IrLAP to pass received data frames to IrLMP layer
 91  *
 92  */
 93 void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb, 
 94                                 int unreliable)
 95 {
 96         struct lsap_cb *lsap;
 97         __u8   slsap_sel;   /* Source (this) LSAP address */
 98         __u8   dlsap_sel;   /* Destination LSAP address */
 99         __u8   *fp;
100         
101         IRDA_DEBUG(4, __FUNCTION__ "()\n");
102 
103         ASSERT(self != NULL, return;);
104         ASSERT(self->magic == LMP_LAP_MAGIC, return;);
105         ASSERT(skb->len > 2, return;);
106 
107         fp = skb->data;
108 
109         /*
110          *  The next statements may be confusing, but we do this so that 
111          *  destination LSAP of received frame is source LSAP in our view
112          */
113         slsap_sel = fp[0] & LSAP_MASK; 
114         dlsap_sel = fp[1];      
115 
116         /*
117          *  Check if this is an incoming connection, since we must deal with
118          *  it in a different way than other established connections.
119          */
120         if ((fp[0] & CONTROL_BIT) && (fp[2] == CONNECT_CMD)) {
121                 IRDA_DEBUG(3, __FUNCTION__ "(), incoming connection, "
122                            "source LSAP=%d, dest LSAP=%d\n",
123                            slsap_sel, dlsap_sel);
124                 
125                 /* Try to find LSAP among the unconnected LSAPs */
126                 lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, CONNECT_CMD,
127                                        irlmp->unconnected_lsaps);
128                 
129                 /* Maybe LSAP was already connected, so try one more time */
130                 if (!lsap) {
131                         IRDA_DEBUG(1, __FUNCTION__ "(), incoming connection for LSAP already connected\n");
132                         lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
133                                                self->lsaps);
134                 }
135         } else
136                 lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0, 
137                                        self->lsaps);
138         
139         if (lsap == NULL) {
140                 IRDA_DEBUG(2, "IrLMP, Sorry, no LSAP for received frame!\n");
141                 IRDA_DEBUG(2, __FUNCTION__ 
142                       "(), slsap_sel = %02x, dlsap_sel = %02x\n", slsap_sel, 
143                       dlsap_sel);
144                 if (fp[0] & CONTROL_BIT) {
145                         IRDA_DEBUG(2, __FUNCTION__ 
146                               "(), received control frame %02x\n", fp[2]);
147                 } else {
148                         IRDA_DEBUG(2, __FUNCTION__ "(), received data frame\n");
149                 }
150                 dev_kfree_skb(skb);
151                 return;
152         }
153 
154         /* 
155          *  Check if we received a control frame? 
156          */
157         if (fp[0] & CONTROL_BIT) {
158                 switch (fp[2]) {
159                 case CONNECT_CMD:
160                         lsap->lap = self;
161                         irlmp_do_lsap_event(lsap, LM_CONNECT_INDICATION, skb);
162                         break;
163                 case CONNECT_CNF:
164                         irlmp_do_lsap_event(lsap, LM_CONNECT_CONFIRM, skb);
165                         break;
166                 case DISCONNECT:
167                         IRDA_DEBUG(4, __FUNCTION__ 
168                                    "(), Disconnect indication!\n");
169                         irlmp_do_lsap_event(lsap, LM_DISCONNECT_INDICATION, 
170                                             skb);
171                         break;
172                 case ACCESSMODE_CMD:
173                         IRDA_DEBUG(0, "Access mode cmd not implemented!\n");
174                         dev_kfree_skb(skb);
175                         break;
176                 case ACCESSMODE_CNF:
177                         IRDA_DEBUG(0, "Access mode cnf not implemented!\n");
178                         dev_kfree_skb(skb);
179                         break;
180                 default:
181                         IRDA_DEBUG(0, __FUNCTION__ 
182                                    "(), Unknown control frame %02x\n", fp[2]);
183                         dev_kfree_skb(skb);
184                         break;
185                 }
186         } else if (unreliable) {
187                 /* Optimize and bypass the state machine if possible */
188                 if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
189                         irlmp_udata_indication(lsap, skb);
190                 else
191                         irlmp_do_lsap_event(lsap, LM_UDATA_INDICATION, skb);
192         } else {        
193                 /* Optimize and bypass the state machine if possible */
194                 if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
195                         irlmp_data_indication(lsap, skb);
196                 else
197                         irlmp_do_lsap_event(lsap, LM_DATA_INDICATION, skb);
198         }
199 }
200 
201 /*
202  * Function irlmp_link_unitdata_indication (self, skb)
203  *
204  *    
205  *
206  */
207 #ifdef CONFIG_IRDA_ULTRA
208 void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
209 {
210         struct lsap_cb *lsap;
211         __u8   slsap_sel;   /* Source (this) LSAP address */
212         __u8   dlsap_sel;   /* Destination LSAP address */
213         __u8   pid;         /* Protocol identifier */
214         __u8   *fp;
215         
216         IRDA_DEBUG(4, __FUNCTION__ "()\n");
217 
218         ASSERT(self != NULL, return;);
219         ASSERT(self->magic == LMP_LAP_MAGIC, return;);
220         ASSERT(skb->len > 2, return;);
221 
222         fp = skb->data;
223 
224         /*
225          *  The next statements may be confusing, but we do this so that 
226          *  destination LSAP of received frame is source LSAP in our view
227          */
228         slsap_sel = fp[0] & LSAP_MASK; 
229         dlsap_sel = fp[1];
230         pid       = fp[2];
231         
232         if (pid & 0x80) {
233                 IRDA_DEBUG(0, __FUNCTION__ "(), extension in PID not supp!\n");
234                 dev_kfree_skb(skb);
235 
236                 return;
237         }
238 
239         /* Check if frame is addressed to the connectionless LSAP */
240         if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) {
241                 IRDA_DEBUG(0, __FUNCTION__ "(), dropping frame!\n");
242                 dev_kfree_skb(skb);
243                 
244                 return;
245         }
246         
247         lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
248         while (lsap != NULL) {
249                 /*
250                  *  Check if source LSAP and dest LSAP selectors and PID match.
251                  */
252                 if ((lsap->slsap_sel == slsap_sel) && 
253                     (lsap->dlsap_sel == dlsap_sel) && 
254                     (lsap->pid == pid)) 
255                 {                       
256                         break;
257                 }
258                 lsap = (struct lsap_cb *) hashbin_get_next(irlmp->unconnected_lsaps);
259         }
260         if (lsap)
261                 irlmp_connless_data_indication(lsap, skb);
262         else {
263                 IRDA_DEBUG(0, __FUNCTION__ "(), found no matching LSAP!\n");
264                 dev_kfree_skb(skb);
265         }
266 }
267 #endif /* CONFIG_IRDA_ULTRA */
268 
269 /*
270  * Function irlmp_link_disconnect_indication (reason, userdata)
271  *
272  *    IrLAP has disconnected 
273  *
274  */
275 void irlmp_link_disconnect_indication(struct lap_cb *lap, 
276                                       struct irlap_cb *irlap, 
277                                       LAP_REASON reason, 
278                                       struct sk_buff *userdata)
279 {
280         IRDA_DEBUG(2, __FUNCTION__ "()\n");
281 
282         ASSERT(lap != NULL, return;);
283         ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
284 
285         lap->reason = reason;
286         lap->daddr = DEV_ADDR_ANY;
287 
288         /* FIXME: must do something with the userdata if any */
289         if (userdata)
290                 dev_kfree_skb(userdata);
291         
292         /*
293          *  Inform station state machine
294          */
295         irlmp_do_lap_event(lap, LM_LAP_DISCONNECT_INDICATION, NULL);
296 }
297 
298 /*
299  * Function irlmp_link_connect_indication (qos)
300  *
301  *    Incoming LAP connection!
302  *
303  */
304 void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr, 
305                                    __u32 daddr, struct qos_info *qos,
306                                    struct sk_buff *skb) 
307 {
308         IRDA_DEBUG(4, __FUNCTION__ "()\n");
309 
310         /* Copy QoS settings for this session */
311         self->qos = qos;
312 
313         /* Update destination device address */
314         self->daddr = daddr;
315         ASSERT(self->saddr == saddr, return;);
316 
317         irlmp_do_lap_event(self, LM_LAP_CONNECT_INDICATION, skb);
318 }
319 
320 /*
321  * Function irlmp_link_connect_confirm (qos)
322  *
323  *    LAP connection confirmed!
324  *
325  */
326 void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos, 
327                                 struct sk_buff *userdata)
328 {
329         IRDA_DEBUG(4, __FUNCTION__ "()\n");
330 
331         ASSERT(self != NULL, return;);
332         ASSERT(self->magic == LMP_LAP_MAGIC, return;);
333         ASSERT(qos != NULL, return;);
334 
335         /* Don't need use the userdata for now */
336         if (userdata)
337                 dev_kfree_skb(userdata);
338 
339         /* Copy QoS settings for this session */
340         self->qos = qos;
341 
342         irlmp_do_lap_event(self, LM_LAP_CONNECT_CONFIRM, NULL);
343 }
344 
345 /*
346  * Function irlmp_discovery_timeout (priv)
347  *
348  *    Create a discovery event to the state machine (called after a delay)
349  *
350  * Note : irlmp_do_lap_event will handle the very rare case where the LAP
351  * is destroyed while we were sleeping.
352  */
353 static void irlmp_discovery_timeout(u_long      priv)
354 {
355         struct lap_cb *self;
356 
357         IRDA_DEBUG(2, __FUNCTION__ "()\n");
358 
359         self = (struct lap_cb *) priv;
360         ASSERT(self != NULL, return;);
361 
362         /* Just handle it the same way as a discovery confirm */
363         irlmp_do_lap_event(self, LM_LAP_DISCOVERY_CONFIRM, NULL);
364 }
365 
366 /*
367  * Function irlmp_link_discovery_indication (self, log)
368  *
369  *    Device is discovering us
370  *
371  * It's not an answer to our own discoveries, just another device trying
372  * to perform discovery, but we don't want to miss the opportunity
373  * to exploit this information, because :
374  *      o We may not actively perform discovery (just passive discovery)
375  *      o This type of discovery is much more reliable. In some cases, it
376  *        seem that less than 50% of our discoveries get an answer, while
377  *        we always get ~100% of these.
378  *      o Make faster discovery, statistically divide time of discovery
379  *        events by 2 (important for the latency aspect and user feel)
380  * However, when both devices discover each other, they might attempt to
381  * connect to each other, and it would create collisions on the medium.
382  * The trick here is to defer the event by a little delay to avoid both
383  * devices to jump in exactly at the same time...
384  *
385  * The delay is currently set to 0.25s, which leave enough time to perform
386  * a connection and don't interfer with next discovery (the lowest discovery
387  * period/timeout that may be set is 1s). The message triggering this
388  * event was the last of the discovery, so the medium is now free...
389  * Maybe more testing is needed to get the value right...
390  */
391 void irlmp_link_discovery_indication(struct lap_cb *self, 
392                                      discovery_t *discovery)
393 {
394         ASSERT(self != NULL, return;);
395         ASSERT(self->magic == LMP_LAP_MAGIC, return;);
396 
397         irlmp_add_discovery(irlmp->cachelog, discovery);
398         
399         /* If delay was activated, kill it! */
400         if(timer_pending(&disco_delay))
401                 del_timer(&disco_delay);
402         /* Set delay timer to expire in 0.25s. */
403         disco_delay.expires = jiffies + (DISCO_SMALL_DELAY * HZ/1000);
404         disco_delay.function = irlmp_discovery_timeout;
405         disco_delay.data = (unsigned long) self;
406         add_timer(&disco_delay);
407 }
408 
409 /*
410  * Function irlmp_link_discovery_confirm (self, log)
411  *
412  *    Called by IrLAP with a list of discoveries after the discovery
413  *    request has been carried out. A NULL log is received if IrLAP
414  *    was unable to carry out the discovery request
415  *
416  */
417 void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log)
418 {
419         IRDA_DEBUG(4, __FUNCTION__ "()\n");
420 
421         ASSERT(self != NULL, return;);
422         ASSERT(self->magic == LMP_LAP_MAGIC, return;);
423         
424         irlmp_add_discovery_log(irlmp->cachelog, log);
425 
426         /* If discovery delay was activated, kill it! */
427         if(timer_pending(&disco_delay))
428                 del_timer(&disco_delay);
429 
430         /* Propagate event to the state machine */
431         irlmp_do_lap_event(self, LM_LAP_DISCOVERY_CONFIRM, NULL);
432 }
433 
434 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
435 inline void irlmp_update_cache(struct lsap_cb *self)
436 {
437         /* Update cache entry */
438         irlmp->cache.dlsap_sel = self->dlsap_sel;
439         irlmp->cache.slsap_sel = self->slsap_sel;
440         irlmp->cache.lsap = self;
441         irlmp->cache.valid = TRUE;
442 }
443 #endif
444 
445 /*
446  * Function irlmp_find_handle (self, dlsap_sel, slsap_sel, status, queue)
447  *
448  *    Find handle assosiated with destination and source LSAP
449  *
450  */
451 static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
452                                        __u8 slsap_sel, int status,
453                                        hashbin_t *queue) 
454 {
455         struct lsap_cb *lsap;
456         
457         /* 
458          *  Optimize for the common case. We assume that the last frame
459          *  received is in the same connection as the last one, so check in
460          *  cache first to avoid the linear search
461          */
462 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
463         if ((irlmp->cache.valid) && 
464             (irlmp->cache.slsap_sel == slsap_sel) && 
465             (irlmp->cache.dlsap_sel == dlsap_sel)) 
466         {
467                 return (irlmp->cache.lsap);
468         }
469 #endif
470         lsap = (struct lsap_cb *) hashbin_get_first(queue);
471         while (lsap != NULL) {
472                 /* 
473                  *  If this is an incomming connection, then the destination 
474                  *  LSAP selector may have been specified as LM_ANY so that 
475                  *  any client can connect. In that case we only need to check
476                  *  if the source LSAP (in our view!) match!
477                  */
478                 if ((status == CONNECT_CMD) && 
479                     (lsap->slsap_sel == slsap_sel) &&      
480                     (lsap->dlsap_sel == LSAP_ANY)) 
481                 {
482                         lsap->dlsap_sel = dlsap_sel;
483                         
484 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
485                         irlmp_update_cache(lsap);
486 #endif
487                         return lsap;
488                 }
489                 /*
490                  *  Check if source LSAP and dest LSAP selectors match.
491                  */
492                 if ((lsap->slsap_sel == slsap_sel) && 
493                     (lsap->dlsap_sel == dlsap_sel)) 
494                 {
495 #ifdef CONFIG_IRDA_CACHE_LAST_LSAP
496                         irlmp_update_cache(lsap);
497 #endif
498                         return lsap;
499                 }
500                 lsap = (struct lsap_cb *) hashbin_get_next(queue);
501         }
502 
503         /* Sorry not found! */
504         return NULL;
505 }
506 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.