📄 irlmp.c
字号:
/********************************************************************* * * Filename: irlmp.c * Version: 1.0 * Description: IrDA Link Management Protocol (LMP) layer * Status: Stable. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun Aug 17 20:54:32 1997 * Modified at: Wed Jan 5 11:26:03 2000 * Modified by: Dag Brattli <dagb@cs.uit.no> * * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>, * All Rights Reserved. * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * ********************************************************************/#include <linux/module.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/skbuff.h>#include <linux/types.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <linux/kmod.h>#include <linux/random.h>#include <linux/seq_file.h>#include <net/irda/irda.h>#include <net/irda/timer.h>#include <net/irda/qos.h>#include <net/irda/irlap.h>#include <net/irda/iriap.h>#include <net/irda/irlmp.h>#include <net/irda/irlmp_frame.h>#include <asm/unaligned.h>static __u8 irlmp_find_free_slsap(void);static int irlmp_slsap_inuse(__u8 slsap_sel);/* Master structure */struct irlmp_cb *irlmp = NULL;/* These can be altered by the sysctl interface */int sysctl_discovery = 0;int sysctl_discovery_timeout = 3; /* 3 seconds by default */int sysctl_discovery_slots = 6; /* 6 slots by default */int sysctl_lap_keepalive_time = LM_IDLE_TIMEOUT * 1000 / HZ;char sysctl_devname[65];const char *irlmp_reasons[] = { "ERROR, NOT USED", "LM_USER_REQUEST", "LM_LAP_DISCONNECT", "LM_CONNECT_FAILURE", "LM_LAP_RESET", "LM_INIT_DISCONNECT", "ERROR, NOT USED",};/* * Function irlmp_init (void) * * Create (allocate) the main IrLMP structure * */int __init irlmp_init(void){ IRDA_DEBUG(1, "%s()\n", __FUNCTION__); /* Initialize the irlmp structure. */ irlmp = kzalloc( sizeof(struct irlmp_cb), GFP_KERNEL); if (irlmp == NULL) return -ENOMEM; irlmp->magic = LMP_MAGIC; irlmp->clients = hashbin_new(HB_LOCK); irlmp->services = hashbin_new(HB_LOCK); irlmp->links = hashbin_new(HB_LOCK); irlmp->unconnected_lsaps = hashbin_new(HB_LOCK); irlmp->cachelog = hashbin_new(HB_NOLOCK); if ((irlmp->clients == NULL) || (irlmp->services == NULL) || (irlmp->links == NULL) || (irlmp->unconnected_lsaps == NULL) || (irlmp->cachelog == NULL)) { return -ENOMEM; } spin_lock_init(&irlmp->cachelog->hb_spinlock); irlmp->last_lsap_sel = 0x0f; /* Reserved 0x00-0x0f */ strcpy(sysctl_devname, "Linux"); /* Do discovery every 3 seconds */ init_timer(&irlmp->discovery_timer); irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout*HZ); return 0;}/* * Function irlmp_cleanup (void) * * Remove IrLMP layer * */void irlmp_cleanup(void){ /* Check for main structure */ IRDA_ASSERT(irlmp != NULL, return;); IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;); del_timer(&irlmp->discovery_timer); hashbin_delete(irlmp->links, (FREE_FUNC) kfree); hashbin_delete(irlmp->unconnected_lsaps, (FREE_FUNC) kfree); hashbin_delete(irlmp->clients, (FREE_FUNC) kfree); hashbin_delete(irlmp->services, (FREE_FUNC) kfree); hashbin_delete(irlmp->cachelog, (FREE_FUNC) kfree); /* De-allocate main structure */ kfree(irlmp); irlmp = NULL;}/* * Function irlmp_open_lsap (slsap, notify) * * Register with IrLMP and create a local LSAP, * returns handle to LSAP. */struct lsap_cb *irlmp_open_lsap(__u8 slsap_sel, notify_t *notify, __u8 pid){ struct lsap_cb *self; IRDA_ASSERT(notify != NULL, return NULL;); IRDA_ASSERT(irlmp != NULL, return NULL;); IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return NULL;); IRDA_ASSERT(notify->instance != NULL, return NULL;); /* Does the client care which Source LSAP selector it gets? */ if (slsap_sel == LSAP_ANY) { slsap_sel = irlmp_find_free_slsap(); if (!slsap_sel) return NULL; } else if (irlmp_slsap_inuse(slsap_sel)) return NULL; /* Allocate new instance of a LSAP connection */ self = kzalloc(sizeof(struct lsap_cb), GFP_ATOMIC); if (self == NULL) { IRDA_ERROR("%s: can't allocate memory\n", __FUNCTION__); return NULL; } self->magic = LMP_LSAP_MAGIC; self->slsap_sel = slsap_sel; /* Fix connectionless LSAP's */ if (slsap_sel == LSAP_CONNLESS) {#ifdef CONFIG_IRDA_ULTRA self->dlsap_sel = LSAP_CONNLESS; self->pid = pid;#endif /* CONFIG_IRDA_ULTRA */ } else self->dlsap_sel = LSAP_ANY; /* self->connected = FALSE; -> already NULL via memset() */ init_timer(&self->watchdog_timer); self->notify = *notify; self->lsap_state = LSAP_DISCONNECTED; /* Insert into queue of unconnected LSAPs */ hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, (long) self, NULL); return self;}EXPORT_SYMBOL(irlmp_open_lsap);/* * Function __irlmp_close_lsap (self) * * Remove an instance of LSAP */static void __irlmp_close_lsap(struct lsap_cb *self){ IRDA_DEBUG(4, "%s()\n", __FUNCTION__); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); /* * Set some of the variables to preset values */ self->magic = 0; del_timer(&self->watchdog_timer); /* Important! */ if (self->conn_skb) dev_kfree_skb(self->conn_skb); kfree(self);}/* * Function irlmp_close_lsap (self) * * Close and remove LSAP * */void irlmp_close_lsap(struct lsap_cb *self){ struct lap_cb *lap; struct lsap_cb *lsap = NULL; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); /* * Find out if we should remove this LSAP from a link or from the * list of unconnected lsaps (not associated with a link) */ lap = self->lap; if (lap) { IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); /* We might close a LSAP before it has completed the * connection setup. In those case, higher layers won't * send a proper disconnect request. Harmless, except * that we will forget to close LAP... - Jean II */ if(self->lsap_state != LSAP_DISCONNECTED) { self->lsap_state = LSAP_DISCONNECTED; irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); } /* Now, remove from the link */ lsap = hashbin_remove(lap->lsaps, (long) self, NULL);#ifdef CONFIG_IRDA_CACHE_LAST_LSAP lap->cache.valid = FALSE;#endif } self->lap = NULL; /* Check if we found the LSAP! If not then try the unconnected lsaps */ if (!lsap) { lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, NULL); } if (!lsap) { IRDA_DEBUG(0, "%s(), Looks like somebody has removed me already!\n", __FUNCTION__); return; } __irlmp_close_lsap(self);}EXPORT_SYMBOL(irlmp_close_lsap);/* * Function irlmp_register_irlap (saddr, notify) * * Register IrLAP layer with IrLMP. There is possible to have multiple * instances of the IrLAP layer, each connected to different IrDA ports * */void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify){ struct lap_cb *lap; IRDA_ASSERT(irlmp != NULL, return;); IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;); IRDA_ASSERT(notify != NULL, return;); /* * Allocate new instance of a LSAP connection */ lap = kzalloc(sizeof(struct lap_cb), GFP_KERNEL); if (lap == NULL) { IRDA_ERROR("%s: unable to kmalloc\n", __FUNCTION__); return; } lap->irlap = irlap; lap->magic = LMP_LAP_MAGIC; lap->saddr = saddr; lap->daddr = DEV_ADDR_ANY;#ifdef CONFIG_IRDA_CACHE_LAST_LSAP lap->cache.valid = FALSE;#endif lap->lsaps = hashbin_new(HB_LOCK); if (lap->lsaps == NULL) { IRDA_WARNING("%s(), unable to kmalloc lsaps\n", __FUNCTION__); kfree(lap); return; } lap->lap_state = LAP_STANDBY; init_timer(&lap->idle_timer); /* * Insert into queue of LMP links */ hashbin_insert(irlmp->links, (irda_queue_t *) lap, lap->saddr, NULL); /* * We set only this variable so IrLAP can tell us on which link the * different events happened on */ irda_notify_init(notify); notify->instance = lap;}/* * Function irlmp_unregister_irlap (saddr) * * IrLAP layer has been removed! * */void irlmp_unregister_link(__u32 saddr){ struct lap_cb *link; IRDA_DEBUG(4, "%s()\n", __FUNCTION__); /* We must remove ourselves from the hashbin *first*. This ensure * that no more LSAPs will be open on this link and no discovery * will be triggered anymore. Jean II */ link = hashbin_remove(irlmp->links, saddr, NULL); if (link) { IRDA_ASSERT(link->magic == LMP_LAP_MAGIC, return;); /* Kill all the LSAPs on this link. Jean II */ link->reason = LAP_DISC_INDICATION; link->daddr = DEV_ADDR_ANY; irlmp_do_lap_event(link, LM_LAP_DISCONNECT_INDICATION, NULL); /* Remove all discoveries discovered at this link */ irlmp_expire_discoveries(irlmp->cachelog, link->saddr, TRUE); /* Final cleanup */ del_timer(&link->idle_timer); link->magic = 0; hashbin_delete(link->lsaps, (FREE_FUNC) __irlmp_close_lsap); kfree(link); }}/* * Function irlmp_connect_request (handle, dlsap, userdata) * * Connect with a peer LSAP * */int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, __u32 saddr, __u32 daddr, struct qos_info *qos, struct sk_buff *userdata){ struct sk_buff *tx_skb = userdata; struct lap_cb *lap; struct lsap_cb *lsap; int ret; IRDA_ASSERT(self != NULL, return -EBADR;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EBADR;); IRDA_DEBUG(2, "%s(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n", __FUNCTION__, self->slsap_sel, dlsap_sel, saddr, daddr); if (test_bit(0, &self->connected)) { ret = -EISCONN; goto err; } /* Client must supply destination device address */ if (!daddr) { ret = -EINVAL; goto err; } /* Any userdata? */ if (tx_skb == NULL) { tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); if (!tx_skb) return -ENOMEM; skb_reserve(tx_skb, LMP_MAX_HEADER); } /* Make room for MUX control header (3 bytes) */ IRDA_ASSERT(skb_headroom(tx_skb) >= LMP_CONTROL_HEADER, return -1;); skb_push(tx_skb, LMP_CONTROL_HEADER); self->dlsap_sel = dlsap_sel; /* * Find the link to where we should try to connect since there may * be more than one IrDA port on this machine. If the client has * passed us the saddr (and already knows which link to use), then * we use that to find the link, if not then we have to look in the * discovery log and check if any of the links has discovered a * device with the given daddr */ if ((!saddr) || (saddr == DEV_ADDR_ANY)) { discovery_t *discovery; unsigned long flags; spin_lock_irqsave(&irlmp->cachelog->hb_spinlock, flags); if (daddr != DEV_ADDR_ANY) discovery = hashbin_find(irlmp->cachelog, daddr, NULL); else { IRDA_DEBUG(2, "%s(), no daddr\n", __FUNCTION__); discovery = (discovery_t *) hashbin_get_first(irlmp->cachelog); } if (discovery) { saddr = discovery->data.saddr; daddr = discovery->data.daddr; } spin_unlock_irqrestore(&irlmp->cachelog->hb_spinlock, flags); } lap = hashbin_lock_find(irlmp->links, saddr, NULL); if (lap == NULL) { IRDA_DEBUG(1, "%s(), Unable to find a usable link!\n", __FUNCTION__); ret = -EHOSTUNREACH; goto err; } /* Check if LAP is disconnected or already connected */ if (lap->daddr == DEV_ADDR_ANY) lap->daddr = daddr; else if (lap->daddr != daddr) { /* Check if some LSAPs are active on this LAP */ if (HASHBIN_GET_SIZE(lap->lsaps) == 0) { /* No active connection, but LAP hasn't been * disconnected yet (waiting for timeout in LAP). * Maybe we could give LAP a bit of help in this case. */ IRDA_DEBUG(0, "%s(), sorry, but I'm waiting for LAP to timeout!\n", __FUNCTION__); ret = -EAGAIN; goto err; } /* LAP is already connected to a different node, and LAP * can only talk to one node at a time */ IRDA_DEBUG(0, "%s(), sorry, but link is busy!\n", __FUNCTION__); ret = -EBUSY; goto err; } self->lap = lap; /* * Remove LSAP from list of unconnected LSAPs and insert it into the * list of connected LSAPs for the particular link */ lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, NULL); IRDA_ASSERT(lsap != NULL, return -1;); IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;); IRDA_ASSERT(lsap->lap != NULL, return -1;); IRDA_ASSERT(lsap->lap->magic == LMP_LAP_MAGIC, return -1;); hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (long) self, NULL); set_bit(0, &self->connected); /* TRUE */ /* * User supplied qos specifications? */ if (qos) self->qos = *qos; irlmp_do_lsap_event(self, LM_CONNECT_REQUEST, tx_skb); /* Drop reference count - see irlap_data_request(). */ dev_kfree_skb(tx_skb); return 0;err: /* Cleanup */ if(tx_skb) dev_kfree_skb(tx_skb); return ret;}EXPORT_SYMBOL(irlmp_connect_request);/* * Function irlmp_connect_indication (self) *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -