⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 irlmp.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/********************************************************************* * * 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 + -