/*********************************************************************
*
* Filename: irlap.c
* Version: 1.0
* Description: IrLAP implementation for Linux
* Status: Stable
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Aug 4 20:40:53 1997
* Modified at: Tue Dec 14 09:26:44 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998-1999 Dag Brattli, 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
********************************************************************/
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/random.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <net/irda/irda.h>
#include <net/irda/irda_device.h>
#include <net/irda/irqueue.h>
#include <net/irda/irlmp.h>
#include <net/irda/irlmp_frame.h>
#include <net/irda/irlap_frame.h>
#include <net/irda/irlap.h>
#include <net/irda/timer.h>
#include <net/irda/qos.h>
static hashbin_t *irlap = NULL;
int sysctl_slot_timeout = SLOT_TIMEOUT * 1000 / HZ;
/* This is the delay of missed pf period before generating an event
* to the application. The spec mandate 3 seconds, but in some cases
* it's way too long. - Jean II */
int sysctl_warn_noreply_time = 3;
extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb);
static void __irlap_close(struct irlap_cb *self);
static void irlap_init_qos_capabilities(struct irlap_cb *self,
struct qos_info *qos_user);
#ifdef CONFIG_IRDA_DEBUG
static const char *const lap_reasons[] = {
"ERROR, NOT USED",
"LAP_DISC_INDICATION",
"LAP_NO_RESPONSE",
"LAP_RESET_INDICATION",
"LAP_FOUND_NONE",
"LAP_MEDIA_BUSY",
"LAP_PRIMARY_CONFLICT",
"ERROR, NOT USED",
};
#endif /* CONFIG_IRDA_DEBUG */
int __init irlap_init(void)
{
/* Check if the compiler did its job properly.
* May happen on some ARM configuration, check with Russell King. */
IRDA_ASSERT(sizeof(struct xid_frame) == 14, ;);
IRDA_ASSERT(sizeof(struct test_frame) == 10, ;);
IRDA_ASSERT(sizeof(struct ua_frame) == 10, ;);
IRDA_ASSERT(sizeof(struct snrm_frame) == 11, ;);
/* Allocate master array */
irlap = hashbin_new(HB_LOCK);
if (irlap == NULL) {
IRDA_ERROR("%s: can't allocate irlap hashbin!\n",
__func__);
return -ENOMEM;
}
return 0;
}
void irlap_cleanup(void)
{
IRDA_ASSERT(irlap != NULL, return;);
hashbin_delete(irlap, (FREE_FUNC) __irlap_close);
}
/*
* Function irlap_open (driver)
*
* Initialize IrLAP layer
*
*/
struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos,
const char *hw_name)
{
struct irlap_cb *self;
IRDA_DEBUG(4, "%s()\n", __func__);
/* Initialize the irlap structure. */
self = kzalloc(sizeof(struct irlap_cb), GFP_KERNEL);
if (self == NULL)
return NULL;
self->magic = LAP_MAGIC;
/* Make a binding between the layers */
self->netdev = dev;
self->qos_dev = qos;
/* Copy hardware name */
if(hw_name != NULL) {
strlcpy(self->hw_name, hw_name, sizeof(self->hw_name));
} else {
self->hw_name[0] = '\0';
}
/* FIXME: should we get our own field? */
dev->atalk_ptr = self;
self->state = LAP_OFFLINE;
/* Initialize transmit queue */
skb_queue_head_init(&self->txq);
skb_queue_head_init(&self->txq_ultra);
skb_queue_head_init(&self->wx_list);
/* My unique IrLAP device address! */
/* We don't want the broadcast address, neither the NULL address
* (most often used to signify "invalid"), and we don't want an
* address already in use (otherwise connect won't be able
* to select the proper link). - Jean II */
do {
get_random_bytes(&self->saddr, sizeof(self->saddr));
} while ((self->saddr == 0x0) || (self->saddr == BROADCAST) ||
(hashbin_lock_find(irlap, self->saddr, NULL)) );
/* Copy to the driver */
memcpy(dev->dev_addr, &self->saddr, 4);
init_timer(&self->slot_timer);
init_timer(&self->query_timer);
init_timer(&self->discovery_timer);
init_timer(&self->final_timer);
init_timer(&self->poll_timer);
init_timer(&self->wd_timer);
init_timer(&self->backoff_timer);
init_timer(&self->media_busy_timer);
irlap_apply_default_connection_parameters(self);
self->N3 = 3; /* # connections attempts to try before giving up */
self->state = LAP_NDM;
hashbin_insert(irlap, (irda_queue_t *) self, self->saddr, NULL);
irlmp_register_link(self, self->saddr, &self->notify);
return self;
}
EXPORT_SYMBOL(irlap_open);
/*
* Function __irlap_close (self)
*
* Remove IrLAP and all allocated memory. Stop any pending timers.
*
*/
static void __irlap_close(struct irlap_cb *self)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
/* Stop timers */
del_timer(&self->slot_timer);
del_timer(&self->query_timer);
del_timer(&self->discovery_timer);
del_timer(&self->final_timer
|