Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

96
net/irda/Kconfig Normal file
View File

@@ -0,0 +1,96 @@
#
# IrDA protocol configuration
#
menuconfig IRDA
depends on NET
tristate "IrDA (infrared) subsystem support"
select CRC_CCITT
---help---
Say Y here if you want to build support for the IrDA (TM) protocols.
The Infrared Data Associations (tm) specifies standards for wireless
infrared communication and is supported by most laptops and PDA's.
To use Linux support for the IrDA (tm) protocols, you will also need
some user-space utilities like irattach. For more information, see
the file <file:Documentation/networking/irda.txt>. You also want to
read the IR-HOWTO, available at
<http://www.tldp.org/docs.html#howto>.
If you want to exchange bits of data (vCal, vCard) with a PDA, you
will need to install some OBEX application, such as OpenObex :
<http://sourceforge.net/projects/openobex/>
To compile this support as a module, choose M here: the module will
be called irda.
comment "IrDA protocols"
depends on IRDA
source "net/irda/irlan/Kconfig"
source "net/irda/irnet/Kconfig"
source "net/irda/ircomm/Kconfig"
config IRDA_ULTRA
bool "Ultra (connectionless) protocol"
depends on IRDA
help
Say Y here to support the connectionless Ultra IRDA protocol.
Ultra allows to exchange data over IrDA with really simple devices
(watch, beacon) without the overhead of the IrDA protocol (no handshaking,
no management frames, simple fixed header).
Ultra is available as a special socket : socket(AF_IRDA, SOCK_DGRAM, 1);
comment "IrDA options"
depends on IRDA
config IRDA_CACHE_LAST_LSAP
bool "Cache last LSAP"
depends on IRDA
help
Say Y here if you want IrLMP to cache the last LSAP used. This
makes sense since most frames will be sent/received on the same
connection. Enabling this option will save a hash-lookup per frame.
If unsure, say Y.
config IRDA_FAST_RR
bool "Fast RRs (low latency)"
depends on IRDA
---help---
Say Y here is you want IrLAP to send fast RR (Receive Ready) frames
when acting as a primary station.
Disabling this option will make latency over IrDA very bad. Enabling
this option will make the IrDA stack send more packet than strictly
necessary, thus reduce your battery life (but not that much).
Fast RR will make IrLAP send out a RR frame immediately when
receiving a frame if its own transmit queue is currently empty. This
will give a lot of speed improvement when receiving much data since
the secondary station will not have to wait the max. turn around
time (usually 500ms) before it is allowed to transmit the next time.
If the transmit queue of the secondary is also empty, the primary will
start backing-off before sending another RR frame, waiting longer
each time until the back-off reaches the max. turn around time.
This back-off increase in controlled via
/proc/sys/net/irda/fast_poll_increase
If unsure, say Y.
config IRDA_DEBUG
bool "Debug information"
depends on IRDA
help
Say Y here if you want the IrDA subsystem to write debug information
to your syslog. You can change the debug level in
/proc/sys/net/irda/debug .
When this option is enabled, the IrDA also perform many extra internal
verifications which will usually prevent the kernel to crash in case of
bugs.
If unsure, say Y (since it makes it easier to find the bugs).
source "drivers/net/irda/Kconfig"

15
net/irda/Makefile Normal file
View File

@@ -0,0 +1,15 @@
#
# Makefile for the Linux IrDA protocol layer.
#
obj-$(CONFIG_IRDA) += irda.o
obj-$(CONFIG_IRLAN) += irlan/
obj-$(CONFIG_IRNET) += irnet/
obj-$(CONFIG_IRCOMM) += ircomm/
irda-y := iriap.o iriap_event.o irlmp.o irlmp_event.o irlmp_frame.o \
irlap.o irlap_event.o irlap_frame.o timer.o qos.o irqueue.o \
irttp.o irda_device.o irias_object.o wrapper.o af_irda.o \
discovery.o parameters.o irmod.o
irda-$(CONFIG_PROC_FS) += irproc.o
irda-$(CONFIG_SYSCTL) += irsysctl.o

2586
net/irda/af_irda.c Normal file

File diff suppressed because it is too large Load Diff

419
net/irda/discovery.c Normal file
View File

@@ -0,0 +1,419 @@
/*********************************************************************
*
* Filename: discovery.c
* Version: 0.1
* Description: Routines for handling discoveries at the IrLMP layer
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Apr 6 15:33:50 1999
* Modified at: Sat Oct 9 17:11:31 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Modified at: Fri May 28 3:11 CST 1999
* Modified by: Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
*
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
*
* 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/string.h>
#include <linux/socket.h>
#include <linux/seq_file.h>
#include <net/irda/irda.h>
#include <net/irda/irlmp.h>
#include <net/irda/discovery.h>
/*
* Function irlmp_add_discovery (cachelog, discovery)
*
* Add a new discovery to the cachelog, and remove any old discoveries
* from the same device
*
* Note : we try to preserve the time this device was *first* discovered
* (as opposed to the time of last discovery used for cleanup). This is
* used by clients waiting for discovery events to tell if the device
* discovered is "new" or just the same old one. They can't rely there
* on a binary flag (new/old), because not all discovery events are
* propagated to them, and they might not always listen, so they would
* miss some new devices popping up...
* Jean II
*/
void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new)
{
discovery_t *discovery, *node;
unsigned long flags;
/* Set time of first discovery if node is new (see below) */
new->firststamp = new->timestamp;
spin_lock_irqsave(&cachelog->hb_spinlock, flags);
/*
* Remove all discoveries of devices that has previously been
* discovered on the same link with the same name (info), or the
* same daddr. We do this since some devices (mostly PDAs) change
* their device address between every discovery.
*/
discovery = (discovery_t *) hashbin_get_first(cachelog);
while (discovery != NULL ) {
node = discovery;
/* Be sure to stay one item ahead */
discovery = (discovery_t *) hashbin_get_next(cachelog);
if ((node->data.saddr == new->data.saddr) &&
((node->data.daddr == new->data.daddr) ||
(strcmp(node->data.info, new->data.info) == 0)))
{
/* This discovery is a previous discovery
* from the same device, so just remove it
*/
hashbin_remove_this(cachelog, (irda_queue_t *) node);
/* Check if hints bits are unchanged */
if(u16ho(node->data.hints) == u16ho(new->data.hints))
/* Set time of first discovery for this node */
new->firststamp = node->firststamp;
kfree(node);
}
}
/* Insert the new and updated version */
hashbin_insert(cachelog, (irda_queue_t *) new, new->data.daddr, NULL);
spin_unlock_irqrestore(&cachelog->hb_spinlock, flags);
}
/*
* Function irlmp_add_discovery_log (cachelog, log)
*
* Merge a disovery log into the cachelog.
*
*/
void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log)
{
discovery_t *discovery;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
/*
* If log is missing this means that IrLAP was unable to perform the
* discovery, so restart discovery again with just the half timeout
* of the normal one.
*/
/* Well... It means that there was nobody out there - Jean II */
if (log == NULL) {
/* irlmp_start_discovery_timer(irlmp, 150); */
return;
}
/*
* Locking : we are the only owner of this discovery log, so
* no need to lock it.
* We just need to lock the global log in irlmp_add_discovery().
*/
discovery = (discovery_t *) hashbin_remove_first(log);
while (discovery != NULL) {
irlmp_add_discovery(cachelog, discovery);
discovery = (discovery_t *) hashbin_remove_first(log);
}
/* Delete the now empty log */
hashbin_delete(log, (FREE_FUNC) kfree);
}
/*
* Function irlmp_expire_discoveries (log, saddr, force)
*
* Go through all discoveries and expire all that has stayed too long
*
* Note : this assume that IrLAP won't change its saddr, which
* currently is a valid assumption...
*/
void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
{
discovery_t * discovery;
discovery_t * curr;
unsigned long flags;
discinfo_t * buffer = NULL;
int n; /* Size of the full log */
int i = 0; /* How many we expired */
IRDA_ASSERT(log != NULL, return;);
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
spin_lock_irqsave(&log->hb_spinlock, flags);
discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) {
/* Be sure to be one item ahead */
curr = discovery;
discovery = (discovery_t *) hashbin_get_next(log);
/* Test if it's time to expire this discovery */
if ((curr->data.saddr == saddr) &&
(force ||
((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT)))
{
/* Create buffer as needed.
* As this function get called a lot and most time
* we don't have anything to put in the log (we are
* quite picky), we can save a lot of overhead
* by not calling kmalloc. Jean II */
if(buffer == NULL) {
/* Create the client specific buffer */
n = HASHBIN_GET_SIZE(log);
buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
if (buffer == NULL) {
spin_unlock_irqrestore(&log->hb_spinlock, flags);
return;
}
}
/* Copy discovery information */
memcpy(&(buffer[i]), &(curr->data),
sizeof(discinfo_t));
i++;
/* Remove it from the log */
curr = hashbin_remove_this(log, (irda_queue_t *) curr);
if (curr)
kfree(curr);
}
}
/* Drop the spinlock before calling the higher layers, as
* we can't guarantee they won't call us back and create a
* deadlock. We will work on our own private data, so we
* don't care to be interupted. - Jean II */
spin_unlock_irqrestore(&log->hb_spinlock, flags);
if(buffer == NULL)
return;
/* Tell IrLMP and registered clients about it */
irlmp_discovery_expiry(buffer, i);
/* Free up our buffer */
kfree(buffer);
}
#if 0
/*
* Function irlmp_dump_discoveries (log)
*
* Print out all discoveries in log
*
*/
void irlmp_dump_discoveries(hashbin_t *log)
{
discovery_t *discovery;
IRDA_ASSERT(log != NULL, return;);
discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) {
IRDA_DEBUG(0, "Discovery:\n");
IRDA_DEBUG(0, " daddr=%08x\n", discovery->data.daddr);
IRDA_DEBUG(0, " saddr=%08x\n", discovery->data.saddr);
IRDA_DEBUG(0, " nickname=%s\n", discovery->data.info);
discovery = (discovery_t *) hashbin_get_next(log);
}
}
#endif
/*
* Function irlmp_copy_discoveries (log, pn, mask)
*
* Copy all discoveries in a buffer
*
* This function implement a safe way for lmp clients to access the
* discovery log. The basic problem is that we don't want the log
* to change (add/remove) while the client is reading it. If the
* lmp client manipulate directly the hashbin, he is sure to get
* into troubles...
* The idea is that we copy all the current discovery log in a buffer
* which is specific to the client and pass this copy to him. As we
* do this operation with the spinlock grabbed, we are safe...
* Note : we don't want those clients to grab the spinlock, because
* we have no control on how long they will hold it...
* Note : we choose to copy the log in "struct irda_device_info" to
* save space...
* Note : the client must kfree himself() the log...
* Jean II
*/
struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn,
__u16 mask, int old_entries)
{
discovery_t * discovery;
unsigned long flags;
discinfo_t * buffer = NULL;
int j_timeout = (sysctl_discovery_timeout * HZ);
int n; /* Size of the full log */
int i = 0; /* How many we picked */
IRDA_ASSERT(pn != NULL, return NULL;);
IRDA_ASSERT(log != NULL, return NULL;);
/* Save spin lock */
spin_lock_irqsave(&log->hb_spinlock, flags);
discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) {
/* Mask out the ones we don't want :
* We want to match the discovery mask, and to get only
* the most recent one (unless we want old ones) */
if ((u16ho(discovery->data.hints) & mask) &&
((old_entries) ||
((jiffies - discovery->firststamp) < j_timeout)) ) {
/* Create buffer as needed.
* As this function get called a lot and most time
* we don't have anything to put in the log (we are
* quite picky), we can save a lot of overhead
* by not calling kmalloc. Jean II */
if(buffer == NULL) {
/* Create the client specific buffer */
n = HASHBIN_GET_SIZE(log);
buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC);
if (buffer == NULL) {
spin_unlock_irqrestore(&log->hb_spinlock, flags);
return NULL;
}
}
/* Copy discovery information */
memcpy(&(buffer[i]), &(discovery->data),
sizeof(discinfo_t));
i++;
}
discovery = (discovery_t *) hashbin_get_next(log);
}
spin_unlock_irqrestore(&log->hb_spinlock, flags);
/* Get the actual number of device in the buffer and return */
*pn = i;
return(buffer);
}
#ifdef CONFIG_PROC_FS
static inline discovery_t *discovery_seq_idx(loff_t pos)
{
discovery_t *discovery;
for (discovery = (discovery_t *) hashbin_get_first(irlmp->cachelog);
discovery != NULL;
discovery = (discovery_t *) hashbin_get_next(irlmp->cachelog)) {
if (pos-- == 0)
break;
}
return discovery;
}
static void *discovery_seq_start(struct seq_file *seq, loff_t *pos)
{
spin_lock_irq(&irlmp->cachelog->hb_spinlock);
return *pos ? discovery_seq_idx(*pos - 1) : SEQ_START_TOKEN;
}
static void *discovery_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
++*pos;
return (v == SEQ_START_TOKEN)
? (void *) hashbin_get_first(irlmp->cachelog)
: (void *) hashbin_get_next(irlmp->cachelog);
}
static void discovery_seq_stop(struct seq_file *seq, void *v)
{
spin_unlock_irq(&irlmp->cachelog->hb_spinlock);
}
static int discovery_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
seq_puts(seq, "IrLMP: Discovery log:\n\n");
else {
const discovery_t *discovery = v;
seq_printf(seq, "nickname: %s, hint: 0x%02x%02x",
discovery->data.info,
discovery->data.hints[0],
discovery->data.hints[1]);
#if 0
if ( discovery->data.hints[0] & HINT_PNP)
seq_puts(seq, "PnP Compatible ");
if ( discovery->data.hints[0] & HINT_PDA)
seq_puts(seq, "PDA/Palmtop ");
if ( discovery->data.hints[0] & HINT_COMPUTER)
seq_puts(seq, "Computer ");
if ( discovery->data.hints[0] & HINT_PRINTER)
seq_puts(seq, "Printer ");
if ( discovery->data.hints[0] & HINT_MODEM)
seq_puts(seq, "Modem ");
if ( discovery->data.hints[0] & HINT_FAX)
seq_puts(seq, "Fax ");
if ( discovery->data.hints[0] & HINT_LAN)
seq_puts(seq, "LAN Access ");
if ( discovery->data.hints[1] & HINT_TELEPHONY)
seq_puts(seq, "Telephony ");
if ( discovery->data.hints[1] & HINT_FILE_SERVER)
seq_puts(seq, "File Server ");
if ( discovery->data.hints[1] & HINT_COMM)
seq_puts(seq, "IrCOMM ");
if ( discovery->data.hints[1] & HINT_OBEX)
seq_puts(seq, "IrOBEX ");
#endif
seq_printf(seq,", saddr: 0x%08x, daddr: 0x%08x\n\n",
discovery->data.saddr,
discovery->data.daddr);
seq_putc(seq, '\n');
}
return 0;
}
static struct seq_operations discovery_seq_ops = {
.start = discovery_seq_start,
.next = discovery_seq_next,
.stop = discovery_seq_stop,
.show = discovery_seq_show,
};
static int discovery_seq_open(struct inode *inode, struct file *file)
{
IRDA_ASSERT(irlmp != NULL, return -EINVAL;);
return seq_open(file, &discovery_seq_ops);
}
struct file_operations discovery_seq_fops = {
.owner = THIS_MODULE,
.open = discovery_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
#endif

12
net/irda/ircomm/Kconfig Normal file
View File

@@ -0,0 +1,12 @@
config IRCOMM
tristate "IrCOMM protocol"
depends on IRDA
help
Say Y here if you want to build support for the IrCOMM protocol.
To compile it as modules, choose M here: the modules will be
called ircomm and ircomm_tty.
IrCOMM implements serial port emulation, and makes it possible to
use all existing applications that understands TTY's with an
infrared link. Thus you should be able to use application like PPP,
minicom and others.

8
net/irda/ircomm/Makefile Normal file
View File

@@ -0,0 +1,8 @@
#
# Makefile for the Linux IrDA IrCOMM protocol layer.
#
obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o
ircomm-objs := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o
ircomm-tty-objs := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o

View File

@@ -0,0 +1,587 @@
/*********************************************************************
*
* Filename: ircomm_core.c
* Version: 1.0
* Description: IrCOMM service interface
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Jun 6 20:37:34 1999
* Modified at: Tue Dec 21 13:26:41 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 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/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/init.h>
#include <net/irda/irda.h>
#include <net/irda/irmod.h>
#include <net/irda/irlmp.h>
#include <net/irda/iriap.h>
#include <net/irda/irttp.h>
#include <net/irda/irias_object.h>
#include <net/irda/ircomm_event.h>
#include <net/irda/ircomm_lmp.h>
#include <net/irda/ircomm_ttp.h>
#include <net/irda/ircomm_param.h>
#include <net/irda/ircomm_core.h>
static int __ircomm_close(struct ircomm_cb *self);
static void ircomm_control_indication(struct ircomm_cb *self,
struct sk_buff *skb, int clen);
#ifdef CONFIG_PROC_FS
extern struct proc_dir_entry *proc_irda;
static int ircomm_seq_open(struct inode *, struct file *);
static struct file_operations ircomm_proc_fops = {
.owner = THIS_MODULE,
.open = ircomm_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
#endif /* CONFIG_PROC_FS */
hashbin_t *ircomm = NULL;
static int __init ircomm_init(void)
{
ircomm = hashbin_new(HB_LOCK);
if (ircomm == NULL) {
IRDA_ERROR("%s(), can't allocate hashbin!\n", __FUNCTION__);
return -ENOMEM;
}
#ifdef CONFIG_PROC_FS
{ struct proc_dir_entry *ent;
ent = create_proc_entry("ircomm", 0, proc_irda);
if (ent)
ent->proc_fops = &ircomm_proc_fops;
}
#endif /* CONFIG_PROC_FS */
IRDA_MESSAGE("IrCOMM protocol (Dag Brattli)\n");
return 0;
}
static void __exit ircomm_cleanup(void)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close);
#ifdef CONFIG_PROC_FS
remove_proc_entry("ircomm", proc_irda);
#endif /* CONFIG_PROC_FS */
}
/*
* Function ircomm_open (client_notify)
*
* Start a new IrCOMM instance
*
*/
struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line)
{
struct ircomm_cb *self = NULL;
int ret;
IRDA_DEBUG(2, "%s(), service_type=0x%02x\n", __FUNCTION__ ,
service_type);
IRDA_ASSERT(ircomm != NULL, return NULL;);
self = kmalloc(sizeof(struct ircomm_cb), GFP_ATOMIC);
if (self == NULL)
return NULL;
memset(self, 0, sizeof(struct ircomm_cb));
self->notify = *notify;
self->magic = IRCOMM_MAGIC;
/* Check if we should use IrLMP or IrTTP */
if (service_type & IRCOMM_3_WIRE_RAW) {
self->flow_status = FLOW_START;
ret = ircomm_open_lsap(self);
} else
ret = ircomm_open_tsap(self);
if (ret < 0) {
kfree(self);
return NULL;
}
self->service_type = service_type;
self->line = line;
hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL);
ircomm_next_state(self, IRCOMM_IDLE);
return self;
}
EXPORT_SYMBOL(ircomm_open);
/*
* Function ircomm_close_instance (self)
*
* Remove IrCOMM instance
*
*/
static int __ircomm_close(struct ircomm_cb *self)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
/* Disconnect link if any */
ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL);
/* Remove TSAP */
if (self->tsap) {
irttp_close_tsap(self->tsap);
self->tsap = NULL;
}
/* Remove LSAP */
if (self->lsap) {
irlmp_close_lsap(self->lsap);
self->lsap = NULL;
}
self->magic = 0;
kfree(self);
return 0;
}
/*
* Function ircomm_close (self)
*
* Closes and removes the specified IrCOMM instance
*
*/
int ircomm_close(struct ircomm_cb *self)
{
struct ircomm_cb *entry;
IRDA_ASSERT(self != NULL, return -EIO;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;);
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
entry = hashbin_remove(ircomm, self->line, NULL);
IRDA_ASSERT(entry == self, return -1;);
return __ircomm_close(self);
}
EXPORT_SYMBOL(ircomm_close);
/*
* Function ircomm_connect_request (self, service_type)
*
* Impl. of this function is differ from one of the reference. This
* function does discovery as well as sending connect request
*
*/
int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel,
__u32 saddr, __u32 daddr, struct sk_buff *skb,
__u8 service_type)
{
struct ircomm_info info;
int ret;
IRDA_DEBUG(2 , "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
self->service_type= service_type;
info.dlsap_sel = dlsap_sel;
info.saddr = saddr;
info.daddr = daddr;
ret = ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, skb, &info);
return ret;
}
EXPORT_SYMBOL(ircomm_connect_request);
/*
* Function ircomm_connect_indication (self, qos, skb)
*
* Notify user layer about the incoming connection
*
*/
void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
struct ircomm_info *info)
{
int clen = 0;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
/* Check if the packet contains data on the control channel */
if (skb->len > 0)
clen = skb->data[0];
/*
* If there are any data hiding in the control channel, we must
* deliver it first. The side effect is that the control channel
* will be removed from the skb
*/
if (self->notify.connect_indication)
self->notify.connect_indication(self->notify.instance, self,
info->qos, info->max_data_size,
info->max_header_size, skb);
else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
}
}
/*
* Function ircomm_connect_response (self, userdata, max_sdu_size)
*
* User accepts connection
*
*/
int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata)
{
int ret;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL);
return ret;
}
EXPORT_SYMBOL(ircomm_connect_response);
/*
* Function connect_confirm (self, skb)
*
* Notify user layer that the link is now connected
*
*/
void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb,
struct ircomm_info *info)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
if (self->notify.connect_confirm )
self->notify.connect_confirm(self->notify.instance,
self, info->qos,
info->max_data_size,
info->max_header_size, skb);
else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
}
}
/*
* Function ircomm_data_request (self, userdata)
*
* Send IrCOMM data to peer device
*
*/
int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb)
{
int ret;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -EFAULT;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
IRDA_ASSERT(skb != NULL, return -EFAULT;);
ret = ircomm_do_event(self, IRCOMM_DATA_REQUEST, skb, NULL);
return ret;
}
EXPORT_SYMBOL(ircomm_data_request);
/*
* Function ircomm_data_indication (self, skb)
*
* Data arrived, so deliver it to user
*
*/
void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(skb->len > 0, return;);
if (self->notify.data_indication)
self->notify.data_indication(self->notify.instance, self, skb);
else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
}
}
/*
* Function ircomm_process_data (self, skb)
*
* Data arrived which may contain control channel data
*
*/
void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb)
{
int clen;
IRDA_ASSERT(skb->len > 0, return;);
clen = skb->data[0];
/*
* If there are any data hiding in the control channel, we must
* deliver it first. The side effect is that the control channel
* will be removed from the skb
*/
if (clen > 0)
ircomm_control_indication(self, skb, clen);
/* Remove control channel from data channel */
skb_pull(skb, clen+1);
if (skb->len)
ircomm_data_indication(self, skb);
else {
IRDA_DEBUG(4, "%s(), data was control info only!\n",
__FUNCTION__ );
}
}
/*
* Function ircomm_control_request (self, params)
*
* Send control data to peer device
*
*/
int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb)
{
int ret;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -EFAULT;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
IRDA_ASSERT(skb != NULL, return -EFAULT;);
ret = ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb, NULL);
return ret;
}
EXPORT_SYMBOL(ircomm_control_request);
/*
* Function ircomm_control_indication (self, skb)
*
* Data has arrived on the control channel
*
*/
static void ircomm_control_indication(struct ircomm_cb *self,
struct sk_buff *skb, int clen)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
/* Use udata for delivering data on the control channel */
if (self->notify.udata_indication) {
struct sk_buff *ctrl_skb;
/* We don't own the skb, so clone it */
ctrl_skb = skb_clone(skb, GFP_ATOMIC);
if (!ctrl_skb)
return;
/* Remove data channel from control channel */
skb_trim(ctrl_skb, clen+1);
self->notify.udata_indication(self->notify.instance, self,
ctrl_skb);
/* Drop reference count -
* see ircomm_tty_control_indication(). */
dev_kfree_skb(ctrl_skb);
} else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
}
}
/*
* Function ircomm_disconnect_request (self, userdata, priority)
*
* User layer wants to disconnect the IrCOMM connection
*
*/
int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata)
{
struct ircomm_info info;
int ret;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
ret = ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata,
&info);
return ret;
}
EXPORT_SYMBOL(ircomm_disconnect_request);
/*
* Function disconnect_indication (self, skb)
*
* Tell user that the link has been disconnected
*
*/
void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb,
struct ircomm_info *info)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(info != NULL, return;);
if (self->notify.disconnect_indication) {
self->notify.disconnect_indication(self->notify.instance, self,
info->reason, skb);
} else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
}
}
/*
* Function ircomm_flow_request (self, flow)
*
*
*
*/
void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
if (self->service_type == IRCOMM_3_WIRE_RAW)
return;
irttp_flow_request(self->tsap, flow);
}
EXPORT_SYMBOL(ircomm_flow_request);
#ifdef CONFIG_PROC_FS
static void *ircomm_seq_start(struct seq_file *seq, loff_t *pos)
{
struct ircomm_cb *self;
loff_t off = 0;
spin_lock_irq(&ircomm->hb_spinlock);
for (self = (struct ircomm_cb *) hashbin_get_first(ircomm);
self != NULL;
self = (struct ircomm_cb *) hashbin_get_next(ircomm)) {
if (off++ == *pos)
break;
}
return self;
}
static void *ircomm_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
++*pos;
return (void *) hashbin_get_next(ircomm);
}
static void ircomm_seq_stop(struct seq_file *seq, void *v)
{
spin_unlock_irq(&ircomm->hb_spinlock);
}
static int ircomm_seq_show(struct seq_file *seq, void *v)
{
const struct ircomm_cb *self = v;
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EINVAL; );
if(self->line < 0x10)
seq_printf(seq, "ircomm%d", self->line);
else
seq_printf(seq, "irlpt%d", self->line - 0x10);
seq_printf(seq,
" state: %s, slsap_sel: %#02x, dlsap_sel: %#02x, mode:",
ircomm_state[ self->state],
self->slsap_sel, self->dlsap_sel);
if(self->service_type & IRCOMM_3_WIRE_RAW)
seq_printf(seq, " 3-wire-raw");
if(self->service_type & IRCOMM_3_WIRE)
seq_printf(seq, " 3-wire");
if(self->service_type & IRCOMM_9_WIRE)
seq_printf(seq, " 9-wire");
if(self->service_type & IRCOMM_CENTRONICS)
seq_printf(seq, " Centronics");
seq_putc(seq, '\n');
return 0;
}
static struct seq_operations ircomm_seq_ops = {
.start = ircomm_seq_start,
.next = ircomm_seq_next,
.stop = ircomm_seq_stop,
.show = ircomm_seq_show,
};
static int ircomm_seq_open(struct inode *inode, struct file *file)
{
return seq_open(file, &ircomm_seq_ops);
}
#endif /* CONFIG_PROC_FS */
MODULE_AUTHOR("Dag Brattli <dag@brattli.net>");
MODULE_DESCRIPTION("IrCOMM protocol");
MODULE_LICENSE("GPL");
module_init(ircomm_init);
module_exit(ircomm_cleanup);

View File

@@ -0,0 +1,251 @@
/*********************************************************************
*
* Filename: ircomm_event.c
* Version: 1.0
* Description: IrCOMM layer state machine
* Status: Stable
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Jun 6 20:33:11 1999
* Modified at: Sun Dec 12 13:44:32 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
*
* 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/sched.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <net/irda/irda.h>
#include <net/irda/irlmp.h>
#include <net/irda/iriap.h>
#include <net/irda/irttp.h>
#include <net/irda/irias_object.h>
#include <net/irda/ircomm_core.h>
#include <net/irda/ircomm_event.h>
static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info);
static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info);
static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info);
static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info);
char *ircomm_state[] = {
"IRCOMM_IDLE",
"IRCOMM_WAITI",
"IRCOMM_WAITR",
"IRCOMM_CONN",
};
#ifdef CONFIG_IRDA_DEBUG
static char *ircomm_event[] = {
"IRCOMM_CONNECT_REQUEST",
"IRCOMM_CONNECT_RESPONSE",
"IRCOMM_TTP_CONNECT_INDICATION",
"IRCOMM_LMP_CONNECT_INDICATION",
"IRCOMM_TTP_CONNECT_CONFIRM",
"IRCOMM_LMP_CONNECT_CONFIRM",
"IRCOMM_LMP_DISCONNECT_INDICATION",
"IRCOMM_TTP_DISCONNECT_INDICATION",
"IRCOMM_DISCONNECT_REQUEST",
"IRCOMM_TTP_DATA_INDICATION",
"IRCOMM_LMP_DATA_INDICATION",
"IRCOMM_DATA_REQUEST",
"IRCOMM_CONTROL_REQUEST",
"IRCOMM_CONTROL_INDICATION",
};
#endif /* CONFIG_IRDA_DEBUG */
static int (*state[])(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info) =
{
ircomm_state_idle,
ircomm_state_waiti,
ircomm_state_waitr,
ircomm_state_conn,
};
/*
* Function ircomm_state_idle (self, event, skb)
*
* IrCOMM is currently idle
*
*/
static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info)
{
int ret = 0;
switch (event) {
case IRCOMM_CONNECT_REQUEST:
ircomm_next_state(self, IRCOMM_WAITI);
ret = self->issue.connect_request(self, skb, info);
break;
case IRCOMM_TTP_CONNECT_INDICATION:
case IRCOMM_LMP_CONNECT_INDICATION:
ircomm_next_state(self, IRCOMM_WAITR);
ircomm_connect_indication(self, skb, info);
break;
default:
IRDA_DEBUG(4, "%s(), unknown event: %s\n", __FUNCTION__ ,
ircomm_event[event]);
ret = -EINVAL;
}
return ret;
}
/*
* Function ircomm_state_waiti (self, event, skb)
*
* The IrCOMM user has requested an IrCOMM connection to the remote
* device and is awaiting confirmation
*/
static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info)
{
int ret = 0;
switch (event) {
case IRCOMM_TTP_CONNECT_CONFIRM:
case IRCOMM_LMP_CONNECT_CONFIRM:
ircomm_next_state(self, IRCOMM_CONN);
ircomm_connect_confirm(self, skb, info);
break;
case IRCOMM_TTP_DISCONNECT_INDICATION:
case IRCOMM_LMP_DISCONNECT_INDICATION:
ircomm_next_state(self, IRCOMM_IDLE);
ircomm_disconnect_indication(self, skb, info);
break;
default:
IRDA_DEBUG(0, "%s(), unknown event: %s\n", __FUNCTION__ ,
ircomm_event[event]);
ret = -EINVAL;
}
return ret;
}
/*
* Function ircomm_state_waitr (self, event, skb)
*
* IrCOMM has received an incoming connection request and is awaiting
* response from the user
*/
static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info)
{
int ret = 0;
switch (event) {
case IRCOMM_CONNECT_RESPONSE:
ircomm_next_state(self, IRCOMM_CONN);
ret = self->issue.connect_response(self, skb);
break;
case IRCOMM_DISCONNECT_REQUEST:
ircomm_next_state(self, IRCOMM_IDLE);
ret = self->issue.disconnect_request(self, skb, info);
break;
case IRCOMM_TTP_DISCONNECT_INDICATION:
case IRCOMM_LMP_DISCONNECT_INDICATION:
ircomm_next_state(self, IRCOMM_IDLE);
ircomm_disconnect_indication(self, skb, info);
break;
default:
IRDA_DEBUG(0, "%s(), unknown event = %s\n", __FUNCTION__ ,
ircomm_event[event]);
ret = -EINVAL;
}
return ret;
}
/*
* Function ircomm_state_conn (self, event, skb)
*
* IrCOMM is connected to the peer IrCOMM device
*
*/
static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info)
{
int ret = 0;
switch (event) {
case IRCOMM_DATA_REQUEST:
ret = self->issue.data_request(self, skb, 0);
break;
case IRCOMM_TTP_DATA_INDICATION:
ircomm_process_data(self, skb);
break;
case IRCOMM_LMP_DATA_INDICATION:
ircomm_data_indication(self, skb);
break;
case IRCOMM_CONTROL_REQUEST:
/* Just send a separate frame for now */
ret = self->issue.data_request(self, skb, skb->len);
break;
case IRCOMM_TTP_DISCONNECT_INDICATION:
case IRCOMM_LMP_DISCONNECT_INDICATION:
ircomm_next_state(self, IRCOMM_IDLE);
ircomm_disconnect_indication(self, skb, info);
break;
case IRCOMM_DISCONNECT_REQUEST:
ircomm_next_state(self, IRCOMM_IDLE);
ret = self->issue.disconnect_request(self, skb, info);
break;
default:
IRDA_DEBUG(0, "%s(), unknown event = %s\n", __FUNCTION__ ,
ircomm_event[event]);
ret = -EINVAL;
}
return ret;
}
/*
* Function ircomm_do_event (self, event, skb)
*
* Process event
*
*/
int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info)
{
IRDA_DEBUG(4, "%s: state=%s, event=%s\n", __FUNCTION__ ,
ircomm_state[self->state], ircomm_event[event]);
return (*state[self->state])(self, event, skb, info);
}
/*
* Function ircomm_next_state (self, state)
*
* Switch state
*
*/
void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state)
{
self->state = state;
IRDA_DEBUG(4, "%s: next state=%s, service type=%d\n", __FUNCTION__ ,
ircomm_state[self->state], self->service_type);
}

View File

@@ -0,0 +1,372 @@
/*********************************************************************
*
* Filename: ircomm_lmp.c
* Version: 1.0
* Description: Interface between IrCOMM and IrLMP
* Status: Stable
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Jun 6 20:48:27 1999
* Modified at: Sun Dec 12 13:44:17 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: Previous IrLPT work by Thomas Davis
*
* Copyright (c) 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/sched.h>
#include <linux/init.h>
#include <net/irda/irda.h>
#include <net/irda/irlmp.h>
#include <net/irda/iriap.h>
#include <net/irda/irda_device.h> /* struct irda_skb_cb */
#include <net/irda/ircomm_event.h>
#include <net/irda/ircomm_lmp.h>
/*
* Function ircomm_lmp_connect_request (self, userdata)
*
*
*
*/
static int ircomm_lmp_connect_request(struct ircomm_cb *self,
struct sk_buff *userdata,
struct ircomm_info *info)
{
int ret = 0;
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
/* Don't forget to refcount it - should be NULL anyway */
if(userdata)
skb_get(userdata);
ret = irlmp_connect_request(self->lsap, info->dlsap_sel,
info->saddr, info->daddr, NULL, userdata);
return ret;
}
/*
* Function ircomm_lmp_connect_response (self, skb)
*
*
*
*/
static int ircomm_lmp_connect_response(struct ircomm_cb *self,
struct sk_buff *userdata)
{
struct sk_buff *tx_skb;
int ret;
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
/* Any userdata supplied? */
if (userdata == NULL) {
tx_skb = dev_alloc_skb(64);
if (!tx_skb)
return -ENOMEM;
/* Reserve space for MUX and LAP header */
skb_reserve(tx_skb, LMP_MAX_HEADER);
} else {
/*
* Check that the client has reserved enough space for
* headers
*/
IRDA_ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER,
return -1;);
/* Don't forget to refcount it - should be NULL anyway */
skb_get(userdata);
tx_skb = userdata;
}
ret = irlmp_connect_response(self->lsap, tx_skb);
return 0;
}
static int ircomm_lmp_disconnect_request(struct ircomm_cb *self,
struct sk_buff *userdata,
struct ircomm_info *info)
{
struct sk_buff *tx_skb;
int ret;
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
if (!userdata) {
tx_skb = dev_alloc_skb(64);
if (!tx_skb)
return -ENOMEM;
/* Reserve space for MUX and LAP header */
skb_reserve(tx_skb, LMP_MAX_HEADER);
userdata = tx_skb;
} else {
/* Don't forget to refcount it - should be NULL anyway */
skb_get(userdata);
}
ret = irlmp_disconnect_request(self->lsap, userdata);
return ret;
}
/*
* Function ircomm_lmp_flow_control (skb)
*
* This function is called when a data frame we have sent to IrLAP has
* been deallocated. We do this to make sure we don't flood IrLAP with
* frames, since we are not using the IrTTP flow control mechanism
*/
static void ircomm_lmp_flow_control(struct sk_buff *skb)
{
struct irda_skb_cb *cb;
struct ircomm_cb *self;
int line;
IRDA_ASSERT(skb != NULL, return;);
cb = (struct irda_skb_cb *) skb->cb;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
line = cb->line;
self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL);
if (!self) {
IRDA_DEBUG(2, "%s(), didn't find myself\n", __FUNCTION__ );
return;
}
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
self->pkt_count--;
if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) {
IRDA_DEBUG(2, "%s(), asking TTY to start again!\n", __FUNCTION__ );
self->flow_status = FLOW_START;
if (self->notify.flow_indication)
self->notify.flow_indication(self->notify.instance,
self, FLOW_START);
}
}
/*
* Function ircomm_lmp_data_request (self, userdata)
*
* Send data frame to peer device
*
*/
static int ircomm_lmp_data_request(struct ircomm_cb *self,
struct sk_buff *skb,
int not_used)
{
struct irda_skb_cb *cb;
int ret;
IRDA_ASSERT(skb != NULL, return -1;);
cb = (struct irda_skb_cb *) skb->cb;
cb->line = self->line;
IRDA_DEBUG(4, "%s(), sending frame\n", __FUNCTION__ );
/* Don't forget to refcount it - see ircomm_tty_do_softint() */
skb_get(skb);
skb->destructor = ircomm_lmp_flow_control;
if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) {
IRDA_DEBUG(2, "%s(), asking TTY to slow down!\n", __FUNCTION__ );
self->flow_status = FLOW_STOP;
if (self->notify.flow_indication)
self->notify.flow_indication(self->notify.instance,
self, FLOW_STOP);
}
ret = irlmp_data_request(self->lsap, skb);
if (ret) {
IRDA_ERROR("%s(), failed\n", __FUNCTION__);
/* irlmp_data_request already free the packet */
}
return ret;
}
/*
* Function ircomm_lmp_data_indication (instance, sap, skb)
*
* Incoming data which we must deliver to the state machine, to check
* we are still connected.
*/
static int ircomm_lmp_data_indication(void *instance, void *sap,
struct sk_buff *skb)
{
struct ircomm_cb *self = (struct ircomm_cb *) instance;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;);
ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL);
/* Drop reference count - see ircomm_tty_data_indication(). */
dev_kfree_skb(skb);
return 0;
}
/*
* Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size,
* max_header_size, skb)
*
* Connection has been confirmed by peer device
*
*/
static void ircomm_lmp_connect_confirm(void *instance, void *sap,
struct qos_info *qos,
__u32 max_seg_size,
__u8 max_header_size,
struct sk_buff *skb)
{
struct ircomm_cb *self = (struct ircomm_cb *) instance;
struct ircomm_info info;
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
IRDA_ASSERT(qos != NULL, return;);
info.max_data_size = max_seg_size;
info.max_header_size = max_header_size;
info.qos = qos;
ircomm_do_event(self, IRCOMM_LMP_CONNECT_CONFIRM, skb, &info);
/* Drop reference count - see ircomm_tty_connect_confirm(). */
dev_kfree_skb(skb);
}
/*
* Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size,
* max_header_size, skb)
*
* Peer device wants to make a connection with us
*
*/
static void ircomm_lmp_connect_indication(void *instance, void *sap,
struct qos_info *qos,
__u32 max_seg_size,
__u8 max_header_size,
struct sk_buff *skb)
{
struct ircomm_cb *self = (struct ircomm_cb *)instance;
struct ircomm_info info;
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
IRDA_ASSERT(qos != NULL, return;);
info.max_data_size = max_seg_size;
info.max_header_size = max_header_size;
info.qos = qos;
ircomm_do_event(self, IRCOMM_LMP_CONNECT_INDICATION, skb, &info);
/* Drop reference count - see ircomm_tty_connect_indication(). */
dev_kfree_skb(skb);
}
/*
* Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb)
*
* Peer device has closed the connection, or the link went down for some
* other reason
*/
static void ircomm_lmp_disconnect_indication(void *instance, void *sap,
LM_REASON reason,
struct sk_buff *skb)
{
struct ircomm_cb *self = (struct ircomm_cb *) instance;
struct ircomm_info info;
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
info.reason = reason;
ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info);
/* Drop reference count - see ircomm_tty_disconnect_indication(). */
if(skb)
dev_kfree_skb(skb);
}
/*
* Function ircomm_open_lsap (self)
*
* Open LSAP. This function will only be used when using "raw" services
*
*/
int ircomm_open_lsap(struct ircomm_cb *self)
{
notify_t notify;
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
/* Register callbacks */
irda_notify_init(&notify);
notify.data_indication = ircomm_lmp_data_indication;
notify.connect_confirm = ircomm_lmp_connect_confirm;
notify.connect_indication = ircomm_lmp_connect_indication;
notify.disconnect_indication = ircomm_lmp_disconnect_indication;
notify.instance = self;
strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
self->lsap = irlmp_open_lsap(LSAP_ANY, &notify, 0);
if (!self->lsap) {
IRDA_DEBUG(0,"%sfailed to allocate tsap\n", __FUNCTION__ );
return -1;
}
self->slsap_sel = self->lsap->slsap_sel;
/*
* Initialize the call-table for issuing commands
*/
self->issue.data_request = ircomm_lmp_data_request;
self->issue.connect_request = ircomm_lmp_connect_request;
self->issue.connect_response = ircomm_lmp_connect_response;
self->issue.disconnect_request = ircomm_lmp_disconnect_request;
return 0;
}

View File

@@ -0,0 +1,511 @@
/*********************************************************************
*
* Filename: ircomm_param.c
* Version: 1.0
* Description: Parameter handling for the IrCOMM protocol
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Jun 7 10:25:11 1999
* Modified at: Sun Jan 30 14:32:03 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
*
* 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/sched.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <net/irda/irda.h>
#include <net/irda/parameters.h>
#include <net/irda/ircomm_core.h>
#include <net/irda/ircomm_tty_attach.h>
#include <net/irda/ircomm_tty.h>
#include <net/irda/ircomm_param.h>
static int ircomm_param_service_type(void *instance, irda_param_t *param,
int get);
static int ircomm_param_port_type(void *instance, irda_param_t *param,
int get);
static int ircomm_param_port_name(void *instance, irda_param_t *param,
int get);
static int ircomm_param_service_type(void *instance, irda_param_t *param,
int get);
static int ircomm_param_data_rate(void *instance, irda_param_t *param,
int get);
static int ircomm_param_data_format(void *instance, irda_param_t *param,
int get);
static int ircomm_param_flow_control(void *instance, irda_param_t *param,
int get);
static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get);
static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get);
static int ircomm_param_line_status(void *instance, irda_param_t *param,
int get);
static int ircomm_param_dte(void *instance, irda_param_t *param, int get);
static int ircomm_param_dce(void *instance, irda_param_t *param, int get);
static int ircomm_param_poll(void *instance, irda_param_t *param, int get);
static pi_minor_info_t pi_minor_call_table_common[] = {
{ ircomm_param_service_type, PV_INT_8_BITS },
{ ircomm_param_port_type, PV_INT_8_BITS },
{ ircomm_param_port_name, PV_STRING }
};
static pi_minor_info_t pi_minor_call_table_non_raw[] = {
{ ircomm_param_data_rate, PV_INT_32_BITS | PV_BIG_ENDIAN },
{ ircomm_param_data_format, PV_INT_8_BITS },
{ ircomm_param_flow_control, PV_INT_8_BITS },
{ ircomm_param_xon_xoff, PV_INT_16_BITS },
{ ircomm_param_enq_ack, PV_INT_16_BITS },
{ ircomm_param_line_status, PV_INT_8_BITS }
};
static pi_minor_info_t pi_minor_call_table_9_wire[] = {
{ ircomm_param_dte, PV_INT_8_BITS },
{ ircomm_param_dce, PV_INT_8_BITS },
{ ircomm_param_poll, PV_NO_VALUE },
};
static pi_major_info_t pi_major_call_table[] = {
{ pi_minor_call_table_common, 3 },
{ pi_minor_call_table_non_raw, 6 },
{ pi_minor_call_table_9_wire, 3 }
/* { pi_minor_call_table_centronics } */
};
pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 };
/*
* Function ircomm_param_request (self, pi, flush)
*
* Queue a parameter for the control channel
*
*/
int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush)
{
struct tty_struct *tty;
unsigned long flags;
struct sk_buff *skb;
int count;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
tty = self->tty;
if (!tty)
return 0;
/* Make sure we don't send parameters for raw mode */
if (self->service_type == IRCOMM_3_WIRE_RAW)
return 0;
spin_lock_irqsave(&self->spinlock, flags);
skb = self->ctrl_skb;
if (!skb) {
skb = dev_alloc_skb(256);
if (!skb) {
spin_unlock_irqrestore(&self->spinlock, flags);
return -ENOMEM;
}
skb_reserve(skb, self->max_header_size);
self->ctrl_skb = skb;
}
/*
* Inserting is a little bit tricky since we don't know how much
* room we will need. But this should hopefully work OK
*/
count = irda_param_insert(self, pi, skb->tail, skb_tailroom(skb),
&ircomm_param_info);
if (count < 0) {
IRDA_WARNING("%s(), no room for parameter!\n", __FUNCTION__);
spin_unlock_irqrestore(&self->spinlock, flags);
return -1;
}
skb_put(skb, count);
spin_unlock_irqrestore(&self->spinlock, flags);
IRDA_DEBUG(2, "%s(), skb->len=%d\n", __FUNCTION__ , skb->len);
if (flush) {
/* ircomm_tty_do_softint will take care of the rest */
schedule_work(&self->tqueue);
}
return count;
}
/*
* Function ircomm_param_service_type (self, buf, len)
*
* Handle service type, this function will both be called after the LM-IAS
* query and then the remote device sends its initial parameters
*
*/
static int ircomm_param_service_type(void *instance, irda_param_t *param,
int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
__u8 service_type = (__u8) param->pv.i;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get) {
param->pv.i = self->settings.service_type;
return 0;
}
/* Find all common service types */
service_type &= self->service_type;
if (!service_type) {
IRDA_DEBUG(2,
"%s(), No common service type to use!\n", __FUNCTION__ );
return -1;
}
IRDA_DEBUG(0, "%s(), services in common=%02x\n", __FUNCTION__ ,
service_type);
/*
* Now choose a preferred service type of those available
*/
if (service_type & IRCOMM_CENTRONICS)
self->settings.service_type = IRCOMM_CENTRONICS;
else if (service_type & IRCOMM_9_WIRE)
self->settings.service_type = IRCOMM_9_WIRE;
else if (service_type & IRCOMM_3_WIRE)
self->settings.service_type = IRCOMM_3_WIRE;
else if (service_type & IRCOMM_3_WIRE_RAW)
self->settings.service_type = IRCOMM_3_WIRE_RAW;
IRDA_DEBUG(0, "%s(), resulting service type=0x%02x\n", __FUNCTION__ ,
self->settings.service_type);
/*
* Now the line is ready for some communication. Check if we are a
* server, and send over some initial parameters.
* Client do it in ircomm_tty_state_setup().
* Note : we may get called from ircomm_tty_getvalue_confirm(),
* therefore before we even have open any socket. And self->client
* is initialised to TRUE only later. So, we check if the link is
* really initialised. - Jean II
*/
if ((self->max_header_size != IRCOMM_TTY_HDR_UNINITIALISED) &&
(!self->client) &&
(self->settings.service_type != IRCOMM_3_WIRE_RAW))
{
/* Init connection */
ircomm_tty_send_initial_parameters(self);
ircomm_tty_link_established(self);
}
return 0;
}
/*
* Function ircomm_param_port_type (self, param)
*
* The port type parameter tells if the devices are serial or parallel.
* Since we only advertise serial service, this parameter should only
* be equal to IRCOMM_SERIAL.
*/
static int ircomm_param_port_type(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get)
param->pv.i = IRCOMM_SERIAL;
else {
self->settings.port_type = (__u8) param->pv.i;
IRDA_DEBUG(0, "%s(), port type=%d\n", __FUNCTION__ ,
self->settings.port_type);
}
return 0;
}
/*
* Function ircomm_param_port_name (self, param)
*
* Exchange port name
*
*/
static int ircomm_param_port_name(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get) {
IRDA_DEBUG(0, "%s(), not imp!\n", __FUNCTION__ );
} else {
IRDA_DEBUG(0, "%s(), port-name=%s\n", __FUNCTION__ , param->pv.c);
strncpy(self->settings.port_name, param->pv.c, 32);
}
return 0;
}
/*
* Function ircomm_param_data_rate (self, param)
*
* Exchange data rate to be used in this settings
*
*/
static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get)
param->pv.i = self->settings.data_rate;
else
self->settings.data_rate = param->pv.i;
IRDA_DEBUG(2, "%s(), data rate = %d\n", __FUNCTION__ , param->pv.i);
return 0;
}
/*
* Function ircomm_param_data_format (self, param)
*
* Exchange data format to be used in this settings
*
*/
static int ircomm_param_data_format(void *instance, irda_param_t *param,
int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get)
param->pv.i = self->settings.data_format;
else
self->settings.data_format = (__u8) param->pv.i;
return 0;
}
/*
* Function ircomm_param_flow_control (self, param)
*
* Exchange flow control settings to be used in this settings
*
*/
static int ircomm_param_flow_control(void *instance, irda_param_t *param,
int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get)
param->pv.i = self->settings.flow_control;
else
self->settings.flow_control = (__u8) param->pv.i;
IRDA_DEBUG(1, "%s(), flow control = 0x%02x\n", __FUNCTION__ , (__u8) param->pv.i);
return 0;
}
/*
* Function ircomm_param_xon_xoff (self, param)
*
* Exchange XON/XOFF characters
*
*/
static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get) {
param->pv.i = self->settings.xonxoff[0];
param->pv.i |= self->settings.xonxoff[1] << 8;
} else {
self->settings.xonxoff[0] = (__u16) param->pv.i & 0xff;
self->settings.xonxoff[1] = (__u16) param->pv.i >> 8;
}
IRDA_DEBUG(0, "%s(), XON/XOFF = 0x%02x,0x%02x\n", __FUNCTION__ ,
param->pv.i & 0xff, param->pv.i >> 8);
return 0;
}
/*
* Function ircomm_param_enq_ack (self, param)
*
* Exchange ENQ/ACK characters
*
*/
static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get) {
param->pv.i = self->settings.enqack[0];
param->pv.i |= self->settings.enqack[1] << 8;
} else {
self->settings.enqack[0] = (__u16) param->pv.i & 0xff;
self->settings.enqack[1] = (__u16) param->pv.i >> 8;
}
IRDA_DEBUG(0, "%s(), ENQ/ACK = 0x%02x,0x%02x\n", __FUNCTION__ ,
param->pv.i & 0xff, param->pv.i >> 8);
return 0;
}
/*
* Function ircomm_param_line_status (self, param)
*
*
*
*/
static int ircomm_param_line_status(void *instance, irda_param_t *param,
int get)
{
IRDA_DEBUG(2, "%s(), not impl.\n", __FUNCTION__ );
return 0;
}
/*
* Function ircomm_param_dte (instance, param)
*
* If we get here, there must be some sort of null-modem connection, and
* we are probably working in server mode as well.
*/
static int ircomm_param_dte(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
__u8 dte;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get)
param->pv.i = self->settings.dte;
else {
dte = (__u8) param->pv.i;
self->settings.dce = 0;
if (dte & IRCOMM_DELTA_DTR)
self->settings.dce |= (IRCOMM_DELTA_DSR|
IRCOMM_DELTA_RI |
IRCOMM_DELTA_CD);
if (dte & IRCOMM_DTR)
self->settings.dce |= (IRCOMM_DSR|
IRCOMM_RI |
IRCOMM_CD);
if (dte & IRCOMM_DELTA_RTS)
self->settings.dce |= IRCOMM_DELTA_CTS;
if (dte & IRCOMM_RTS)
self->settings.dce |= IRCOMM_CTS;
/* Take appropriate actions */
ircomm_tty_check_modem_status(self);
/* Null modem cable emulator */
self->settings.null_modem = TRUE;
}
return 0;
}
/*
* Function ircomm_param_dce (instance, param)
*
*
*
*/
static int ircomm_param_dce(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
__u8 dce;
IRDA_DEBUG(1, "%s(), dce = 0x%02x\n", __FUNCTION__ , (__u8) param->pv.i);
dce = (__u8) param->pv.i;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
self->settings.dce = dce;
/* Check if any of the settings have changed */
if (dce & 0x0f) {
if (dce & IRCOMM_DELTA_CTS) {
IRDA_DEBUG(2, "%s(), CTS \n", __FUNCTION__ );
}
}
ircomm_tty_check_modem_status(self);
return 0;
}
/*
* Function ircomm_param_poll (instance, param)
*
* Called when the peer device is polling for the line settings
*
*/
static int ircomm_param_poll(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
/* Poll parameters are always of lenght 0 (just a signal) */
if (!get) {
/* Respond with DTE line settings */
ircomm_param_request(self, IRCOMM_DTE, TRUE);
}
return 0;
}

View File

@@ -0,0 +1,369 @@
/*********************************************************************
*
* Filename: ircomm_ttp.c
* Version: 1.0
* Description: Interface between IrCOMM and IrTTP
* Status: Stable
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Jun 6 20:48:27 1999
* Modified at: Mon Dec 13 11:35:13 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 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/sched.h>
#include <linux/init.h>
#include <net/irda/irda.h>
#include <net/irda/irlmp.h>
#include <net/irda/iriap.h>
#include <net/irda/irttp.h>
#include <net/irda/ircomm_event.h>
#include <net/irda/ircomm_ttp.h>
static int ircomm_ttp_data_indication(void *instance, void *sap,
struct sk_buff *skb);
static void ircomm_ttp_connect_confirm(void *instance, void *sap,
struct qos_info *qos,
__u32 max_sdu_size,
__u8 max_header_size,
struct sk_buff *skb);
static void ircomm_ttp_connect_indication(void *instance, void *sap,
struct qos_info *qos,
__u32 max_sdu_size,
__u8 max_header_size,
struct sk_buff *skb);
static void ircomm_ttp_flow_indication(void *instance, void *sap,
LOCAL_FLOW cmd);
static void ircomm_ttp_disconnect_indication(void *instance, void *sap,
LM_REASON reason,
struct sk_buff *skb);
static int ircomm_ttp_data_request(struct ircomm_cb *self,
struct sk_buff *skb,
int clen);
static int ircomm_ttp_connect_request(struct ircomm_cb *self,
struct sk_buff *userdata,
struct ircomm_info *info);
static int ircomm_ttp_connect_response(struct ircomm_cb *self,
struct sk_buff *userdata);
static int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
struct sk_buff *userdata,
struct ircomm_info *info);
/*
* Function ircomm_open_tsap (self)
*
*
*
*/
int ircomm_open_tsap(struct ircomm_cb *self)
{
notify_t notify;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
/* Register callbacks */
irda_notify_init(&notify);
notify.data_indication = ircomm_ttp_data_indication;
notify.connect_confirm = ircomm_ttp_connect_confirm;
notify.connect_indication = ircomm_ttp_connect_indication;
notify.flow_indication = ircomm_ttp_flow_indication;
notify.disconnect_indication = ircomm_ttp_disconnect_indication;
notify.instance = self;
strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
&notify);
if (!self->tsap) {
IRDA_DEBUG(0, "%sfailed to allocate tsap\n", __FUNCTION__ );
return -1;
}
self->slsap_sel = self->tsap->stsap_sel;
/*
* Initialize the call-table for issuing commands
*/
self->issue.data_request = ircomm_ttp_data_request;
self->issue.connect_request = ircomm_ttp_connect_request;
self->issue.connect_response = ircomm_ttp_connect_response;
self->issue.disconnect_request = ircomm_ttp_disconnect_request;
return 0;
}
/*
* Function ircomm_ttp_connect_request (self, userdata)
*
*
*
*/
static int ircomm_ttp_connect_request(struct ircomm_cb *self,
struct sk_buff *userdata,
struct ircomm_info *info)
{
int ret = 0;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
/* Don't forget to refcount it - should be NULL anyway */
if(userdata)
skb_get(userdata);
ret = irttp_connect_request(self->tsap, info->dlsap_sel,
info->saddr, info->daddr, NULL,
TTP_SAR_DISABLE, userdata);
return ret;
}
/*
* Function ircomm_ttp_connect_response (self, skb)
*
*
*
*/
static int ircomm_ttp_connect_response(struct ircomm_cb *self,
struct sk_buff *userdata)
{
int ret;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
/* Don't forget to refcount it - should be NULL anyway */
if(userdata)
skb_get(userdata);
ret = irttp_connect_response(self->tsap, TTP_SAR_DISABLE, userdata);
return ret;
}
/*
* Function ircomm_ttp_data_request (self, userdata)
*
* Send IrCOMM data to IrTTP layer. Currently we do not try to combine
* control data with pure data, so they will be sent as separate frames.
* Should not be a big problem though, since control frames are rare. But
* some of them are sent after connection establishment, so this can
* increase the latency a bit.
*/
static int ircomm_ttp_data_request(struct ircomm_cb *self,
struct sk_buff *skb,
int clen)
{
int ret;
IRDA_ASSERT(skb != NULL, return -1;);
IRDA_DEBUG(2, "%s(), clen=%d\n", __FUNCTION__ , clen);
/*
* Insert clen field, currently we either send data only, or control
* only frames, to make things easier and avoid queueing
*/
IRDA_ASSERT(skb_headroom(skb) >= IRCOMM_HEADER_SIZE, return -1;);
/* Don't forget to refcount it - see ircomm_tty_do_softint() */
skb_get(skb);
skb_push(skb, IRCOMM_HEADER_SIZE);
skb->data[0] = clen;
ret = irttp_data_request(self->tsap, skb);
if (ret) {
IRDA_ERROR("%s(), failed\n", __FUNCTION__);
/* irttp_data_request already free the packet */
}
return ret;
}
/*
* Function ircomm_ttp_data_indication (instance, sap, skb)
*
* Incoming data
*
*/
static int ircomm_ttp_data_indication(void *instance, void *sap,
struct sk_buff *skb)
{
struct ircomm_cb *self = (struct ircomm_cb *) instance;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;);
ircomm_do_event(self, IRCOMM_TTP_DATA_INDICATION, skb, NULL);
/* Drop reference count - see ircomm_tty_data_indication(). */
dev_kfree_skb(skb);
return 0;
}
static void ircomm_ttp_connect_confirm(void *instance, void *sap,
struct qos_info *qos,
__u32 max_sdu_size,
__u8 max_header_size,
struct sk_buff *skb)
{
struct ircomm_cb *self = (struct ircomm_cb *) instance;
struct ircomm_info info;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
IRDA_ASSERT(qos != NULL, goto out;);
if (max_sdu_size != TTP_SAR_DISABLE) {
IRDA_ERROR("%s(), SAR not allowed for IrCOMM!\n",
__FUNCTION__);
goto out;
}
info.max_data_size = irttp_get_max_seg_size(self->tsap)
- IRCOMM_HEADER_SIZE;
info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE;
info.qos = qos;
ircomm_do_event(self, IRCOMM_TTP_CONNECT_CONFIRM, skb, &info);
out:
/* Drop reference count - see ircomm_tty_connect_confirm(). */
dev_kfree_skb(skb);
}
/*
* Function ircomm_ttp_connect_indication (instance, sap, qos, max_sdu_size,
* max_header_size, skb)
*
*
*
*/
static void ircomm_ttp_connect_indication(void *instance, void *sap,
struct qos_info *qos,
__u32 max_sdu_size,
__u8 max_header_size,
struct sk_buff *skb)
{
struct ircomm_cb *self = (struct ircomm_cb *)instance;
struct ircomm_info info;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
IRDA_ASSERT(qos != NULL, goto out;);
if (max_sdu_size != TTP_SAR_DISABLE) {
IRDA_ERROR("%s(), SAR not allowed for IrCOMM!\n",
__FUNCTION__);
goto out;
}
info.max_data_size = irttp_get_max_seg_size(self->tsap)
- IRCOMM_HEADER_SIZE;
info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE;
info.qos = qos;
ircomm_do_event(self, IRCOMM_TTP_CONNECT_INDICATION, skb, &info);
out:
/* Drop reference count - see ircomm_tty_connect_indication(). */
dev_kfree_skb(skb);
}
/*
* Function ircomm_ttp_disconnect_request (self, userdata, info)
*
*
*
*/
static int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
struct sk_buff *userdata,
struct ircomm_info *info)
{
int ret;
/* Don't forget to refcount it - should be NULL anyway */
if(userdata)
skb_get(userdata);
ret = irttp_disconnect_request(self->tsap, userdata, P_NORMAL);
return ret;
}
/*
* Function ircomm_ttp_disconnect_indication (instance, sap, reason, skb)
*
*
*
*/
static void ircomm_ttp_disconnect_indication(void *instance, void *sap,
LM_REASON reason,
struct sk_buff *skb)
{
struct ircomm_cb *self = (struct ircomm_cb *) instance;
struct ircomm_info info;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
info.reason = reason;
ircomm_do_event(self, IRCOMM_TTP_DISCONNECT_INDICATION, skb, &info);
/* Drop reference count - see ircomm_tty_disconnect_indication(). */
if(skb)
dev_kfree_skb(skb);
}
/*
* Function ircomm_ttp_flow_indication (instance, sap, cmd)
*
* Layer below is telling us to start or stop the flow of data
*
*/
static void ircomm_ttp_flow_indication(void *instance, void *sap,
LOCAL_FLOW cmd)
{
struct ircomm_cb *self = (struct ircomm_cb *) instance;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
if (self->notify.flow_indication)
self->notify.flow_indication(self->notify.instance, self, cmd);
}

1405
net/irda/ircomm/ircomm_tty.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,428 @@
/*********************************************************************
*
* Filename: ircomm_tty_ioctl.c
* Version:
* Description:
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Thu Jun 10 14:39:09 1999
* Modified at: Wed Jan 5 14:45:43 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
*
* 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/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/termios.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <asm/uaccess.h>
#include <net/irda/irda.h>
#include <net/irda/irmod.h>
#include <net/irda/ircomm_core.h>
#include <net/irda/ircomm_param.h>
#include <net/irda/ircomm_tty_attach.h>
#include <net/irda/ircomm_tty.h>
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
/*
* Function ircomm_tty_change_speed (driver)
*
* Change speed of the driver. If the remote device is a DCE, then this
* should make it change the speed of its serial port
*/
static void ircomm_tty_change_speed(struct ircomm_tty_cb *self)
{
unsigned cflag, cval;
int baud;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
if (!self->tty || !self->tty->termios || !self->ircomm)
return;
cflag = self->tty->termios->c_cflag;
/* byte size and parity */
switch (cflag & CSIZE) {
case CS5: cval = IRCOMM_WSIZE_5; break;
case CS6: cval = IRCOMM_WSIZE_6; break;
case CS7: cval = IRCOMM_WSIZE_7; break;
case CS8: cval = IRCOMM_WSIZE_8; break;
default: cval = IRCOMM_WSIZE_5; break;
}
if (cflag & CSTOPB)
cval |= IRCOMM_2_STOP_BIT;
if (cflag & PARENB)
cval |= IRCOMM_PARITY_ENABLE;
if (!(cflag & PARODD))
cval |= IRCOMM_PARITY_EVEN;
/* Determine divisor based on baud rate */
baud = tty_get_baud_rate(self->tty);
if (!baud)
baud = 9600; /* B0 transition handled in rs_set_termios */
self->settings.data_rate = baud;
ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
/* CTS flow control flag and modem status interrupts */
if (cflag & CRTSCTS) {
self->flags |= ASYNC_CTS_FLOW;
self->settings.flow_control |= IRCOMM_RTS_CTS_IN;
/* This got me. Bummer. Jean II */
if (self->service_type == IRCOMM_3_WIRE_RAW)
IRDA_WARNING("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n", __FUNCTION__);
} else {
self->flags &= ~ASYNC_CTS_FLOW;
self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN;
}
if (cflag & CLOCAL)
self->flags &= ~ASYNC_CHECK_CD;
else
self->flags |= ASYNC_CHECK_CD;
#if 0
/*
* Set up parity check flag
*/
if (I_INPCK(self->tty))
driver->read_status_mask |= LSR_FE | LSR_PE;
if (I_BRKINT(driver->tty) || I_PARMRK(driver->tty))
driver->read_status_mask |= LSR_BI;
/*
* Characters to ignore
*/
driver->ignore_status_mask = 0;
if (I_IGNPAR(driver->tty))
driver->ignore_status_mask |= LSR_PE | LSR_FE;
if (I_IGNBRK(self->tty)) {
self->ignore_status_mask |= LSR_BI;
/*
* If we're ignore parity and break indicators, ignore
* overruns too. (For real raw support).
*/
if (I_IGNPAR(self->tty))
self->ignore_status_mask |= LSR_OE;
}
#endif
self->settings.data_format = cval;
ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
}
/*
* Function ircomm_tty_set_termios (tty, old_termios)
*
* This routine allows the tty driver to be notified when device's
* termios settings have changed. Note that a well-designed tty driver
* should be prepared to accept the case where old == NULL, and try to
* do something rational.
*/
void ircomm_tty_set_termios(struct tty_struct *tty,
struct termios *old_termios)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
unsigned int cflag = tty->termios->c_cflag;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
if ((cflag == old_termios->c_cflag) &&
(RELEVANT_IFLAG(tty->termios->c_iflag) ==
RELEVANT_IFLAG(old_termios->c_iflag)))
{
return;
}
ircomm_tty_change_speed(self);
/* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) &&
!(cflag & CBAUD)) {
self->settings.dte &= ~(IRCOMM_DTR|IRCOMM_RTS);
ircomm_param_request(self, IRCOMM_DTE, TRUE);
}
/* Handle transition away from B0 status */
if (!(old_termios->c_cflag & CBAUD) &&
(cflag & CBAUD)) {
self->settings.dte |= IRCOMM_DTR;
if (!(tty->termios->c_cflag & CRTSCTS) ||
!test_bit(TTY_THROTTLED, &tty->flags)) {
self->settings.dte |= IRCOMM_RTS;
}
ircomm_param_request(self, IRCOMM_DTE, TRUE);
}
/* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS))
{
tty->hw_stopped = 0;
ircomm_tty_start(tty);
}
}
/*
* Function ircomm_tty_tiocmget (tty, file)
*
*
*
*/
int ircomm_tty_tiocmget(struct tty_struct *tty, struct file *file)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
unsigned int result;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
result = ((self->settings.dte & IRCOMM_RTS) ? TIOCM_RTS : 0)
| ((self->settings.dte & IRCOMM_DTR) ? TIOCM_DTR : 0)
| ((self->settings.dce & IRCOMM_CD) ? TIOCM_CAR : 0)
| ((self->settings.dce & IRCOMM_RI) ? TIOCM_RNG : 0)
| ((self->settings.dce & IRCOMM_DSR) ? TIOCM_DSR : 0)
| ((self->settings.dce & IRCOMM_CTS) ? TIOCM_CTS : 0);
return result;
}
/*
* Function ircomm_tty_tiocmset (tty, file, set, clear)
*
*
*
*/
int ircomm_tty_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (set & TIOCM_RTS)
self->settings.dte |= IRCOMM_RTS;
if (set & TIOCM_DTR)
self->settings.dte |= IRCOMM_DTR;
if (clear & TIOCM_RTS)
self->settings.dte &= ~IRCOMM_RTS;
if (clear & TIOCM_DTR)
self->settings.dte &= ~IRCOMM_DTR;
if ((set|clear) & TIOCM_RTS)
self->settings.dte |= IRCOMM_DELTA_RTS;
if ((set|clear) & TIOCM_DTR)
self->settings.dte |= IRCOMM_DELTA_DTR;
ircomm_param_request(self, IRCOMM_DTE, TRUE);
return 0;
}
/*
* Function get_serial_info (driver, retinfo)
*
*
*
*/
static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self,
struct serial_struct __user *retinfo)
{
struct serial_struct info;
if (!retinfo)
return -EFAULT;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
memset(&info, 0, sizeof(info));
info.line = self->line;
info.flags = self->flags;
info.baud_base = self->settings.data_rate;
info.close_delay = self->close_delay;
info.closing_wait = self->closing_wait;
/* For compatibility */
info.type = PORT_16550A;
info.port = 0;
info.irq = 0;
info.xmit_fifo_size = 0;
info.hub6 = 0;
info.custom_divisor = 0;
if (copy_to_user(retinfo, &info, sizeof(*retinfo)))
return -EFAULT;
return 0;
}
/*
* Function set_serial_info (driver, new_info)
*
*
*
*/
static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self,
struct serial_struct __user *new_info)
{
#if 0
struct serial_struct new_serial;
struct ircomm_tty_cb old_state, *state;
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
return -EFAULT;
state = self
old_state = *self;
if (!capable(CAP_SYS_ADMIN)) {
if ((new_serial.baud_base != state->settings.data_rate) ||
(new_serial.close_delay != state->close_delay) ||
((new_serial.flags & ~ASYNC_USR_MASK) !=
(self->flags & ~ASYNC_USR_MASK)))
return -EPERM;
state->flags = ((state->flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
self->flags = ((self->flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
/* self->custom_divisor = new_serial.custom_divisor; */
goto check_and_exit;
}
/*
* OK, past this point, all the error checking has been done.
* At this point, we start making changes.....
*/
if (self->settings.data_rate != new_serial.baud_base) {
self->settings.data_rate = new_serial.baud_base;
ircomm_param_request(self, IRCOMM_DATA_RATE, TRUE);
}
self->close_delay = new_serial.close_delay * HZ/100;
self->closing_wait = new_serial.closing_wait * HZ/100;
/* self->custom_divisor = new_serial.custom_divisor; */
self->flags = ((self->flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS));
self->tty->low_latency = (self->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
check_and_exit:
if (self->flags & ASYNC_INITIALIZED) {
if (((old_state.flags & ASYNC_SPD_MASK) !=
(self->flags & ASYNC_SPD_MASK)) ||
(old_driver.custom_divisor != driver->custom_divisor)) {
if ((driver->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
driver->tty->alt_speed = 57600;
if ((driver->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
driver->tty->alt_speed = 115200;
if ((driver->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
driver->tty->alt_speed = 230400;
if ((driver->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
driver->tty->alt_speed = 460800;
ircomm_tty_change_speed(driver);
}
}
#endif
return 0;
}
/*
* Function ircomm_tty_ioctl (tty, file, cmd, arg)
*
*
*
*/
int ircomm_tty_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
int ret = 0;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
(cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
}
switch (cmd) {
case TIOCGSERIAL:
ret = ircomm_tty_get_serial_info(self, (struct serial_struct __user *) arg);
break;
case TIOCSSERIAL:
ret = ircomm_tty_set_serial_info(self, (struct serial_struct __user *) arg);
break;
case TIOCMIWAIT:
IRDA_DEBUG(0, "(), TIOCMIWAIT, not impl!\n");
break;
case TIOCGICOUNT:
IRDA_DEBUG(0, "%s(), TIOCGICOUNT not impl!\n", __FUNCTION__ );
#if 0
save_flags(flags); cli();
cnow = driver->icount;
restore_flags(flags);
p_cuser = (struct serial_icounter_struct __user *) arg;
if (put_user(cnow.cts, &p_cuser->cts) ||
put_user(cnow.dsr, &p_cuser->dsr) ||
put_user(cnow.rng, &p_cuser->rng) ||
put_user(cnow.dcd, &p_cuser->dcd) ||
put_user(cnow.rx, &p_cuser->rx) ||
put_user(cnow.tx, &p_cuser->tx) ||
put_user(cnow.frame, &p_cuser->frame) ||
put_user(cnow.overrun, &p_cuser->overrun) ||
put_user(cnow.parity, &p_cuser->parity) ||
put_user(cnow.brk, &p_cuser->brk) ||
put_user(cnow.buf_overrun, &p_cuser->buf_overrun))
return -EFAULT;
#endif
return 0;
default:
ret = -ENOIOCTLCMD; /* ioctls which we must ignore */
}
return ret;
}

489
net/irda/irda_device.c Normal file
View File

@@ -0,0 +1,489 @@
/*********************************************************************
*
* Filename: irda_device.c
* Version: 0.9
* Description: Utility functions used by the device drivers
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sat Oct 9 09:22:27 1999
* Modified at: Sun Jan 23 17:41:24 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
* Copyright (c) 2000-2001 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/config.h>
#include <linux/string.h>
#include <linux/proc_fs.h>
#include <linux/skbuff.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/netdevice.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/spinlock.h>
#include <asm/ioctls.h>
#include <asm/uaccess.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <net/irda/irda_device.h>
#include <net/irda/irlap.h>
#include <net/irda/timer.h>
#include <net/irda/wrapper.h>
static void __irda_task_delete(struct irda_task *task);
static hashbin_t *dongles = NULL;
static hashbin_t *tasks = NULL;
#ifdef CONFIG_IRDA_DEBUG
static const char *task_state[] = {
"IRDA_TASK_INIT",
"IRDA_TASK_DONE",
"IRDA_TASK_WAIT",
"IRDA_TASK_WAIT1",
"IRDA_TASK_WAIT2",
"IRDA_TASK_WAIT3",
"IRDA_TASK_CHILD_INIT",
"IRDA_TASK_CHILD_WAIT",
"IRDA_TASK_CHILD_DONE",
};
#endif /* CONFIG_IRDA_DEBUG */
static void irda_task_timer_expired(void *data);
int __init irda_device_init( void)
{
dongles = hashbin_new(HB_NOLOCK);
if (dongles == NULL) {
IRDA_WARNING("IrDA: Can't allocate dongles hashbin!\n");
return -ENOMEM;
}
spin_lock_init(&dongles->hb_spinlock);
tasks = hashbin_new(HB_LOCK);
if (tasks == NULL) {
IRDA_WARNING("IrDA: Can't allocate tasks hashbin!\n");
hashbin_delete(dongles, NULL);
return -ENOMEM;
}
/* We no longer initialise the driver ourselves here, we let
* the system do it for us... - Jean II */
return 0;
}
static void __exit leftover_dongle(void *arg)
{
struct dongle_reg *reg = arg;
IRDA_WARNING("IrDA: Dongle type %x not unregistered\n",
reg->type);
}
void __exit irda_device_cleanup(void)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
hashbin_delete(tasks, (FREE_FUNC) __irda_task_delete);
hashbin_delete(dongles, leftover_dongle);
}
/*
* Function irda_device_set_media_busy (self, status)
*
* Called when we have detected that another station is transmitting
* in contention mode.
*/
void irda_device_set_media_busy(struct net_device *dev, int status)
{
struct irlap_cb *self;
IRDA_DEBUG(4, "%s(%s)\n", __FUNCTION__, status ? "TRUE" : "FALSE");
self = (struct irlap_cb *) dev->atalk_ptr;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
if (status) {
self->media_busy = TRUE;
if (status == SMALL)
irlap_start_mbusy_timer(self, SMALLBUSY_TIMEOUT);
else
irlap_start_mbusy_timer(self, MEDIABUSY_TIMEOUT);
IRDA_DEBUG( 4, "Media busy!\n");
} else {
self->media_busy = FALSE;
irlap_stop_mbusy_timer(self);
}
}
EXPORT_SYMBOL(irda_device_set_media_busy);
/*
* Function irda_device_is_receiving (dev)
*
* Check if the device driver is currently receiving data
*
*/
int irda_device_is_receiving(struct net_device *dev)
{
struct if_irda_req req;
int ret;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
if (!dev->do_ioctl) {
IRDA_ERROR("%s: do_ioctl not impl. by device driver\n",
__FUNCTION__);
return -1;
}
ret = dev->do_ioctl(dev, (struct ifreq *) &req, SIOCGRECEIVING);
if (ret < 0)
return ret;
return req.ifr_receiving;
}
void irda_task_next_state(struct irda_task *task, IRDA_TASK_STATE state)
{
IRDA_DEBUG(2, "%s(), state = %s\n", __FUNCTION__, task_state[state]);
task->state = state;
}
EXPORT_SYMBOL(irda_task_next_state);
static void __irda_task_delete(struct irda_task *task)
{
del_timer(&task->timer);
kfree(task);
}
void irda_task_delete(struct irda_task *task)
{
/* Unregister task */
hashbin_remove(tasks, (long) task, NULL);
__irda_task_delete(task);
}
EXPORT_SYMBOL(irda_task_delete);
/*
* Function irda_task_kick (task)
*
* Tries to execute a task possible multiple times until the task is either
* finished, or askes for a timeout. When a task is finished, we do post
* processing, and notify the parent task, that is waiting for this task
* to complete.
*/
static int irda_task_kick(struct irda_task *task)
{
int finished = TRUE;
int count = 0;
int timeout;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
IRDA_ASSERT(task != NULL, return -1;);
IRDA_ASSERT(task->magic == IRDA_TASK_MAGIC, return -1;);
/* Execute task until it's finished, or askes for a timeout */
do {
timeout = task->function(task);
if (count++ > 100) {
IRDA_ERROR("%s: error in task handler!\n",
__FUNCTION__);
irda_task_delete(task);
return TRUE;
}
} while ((timeout == 0) && (task->state != IRDA_TASK_DONE));
if (timeout < 0) {
IRDA_ERROR("%s: Error executing task!\n", __FUNCTION__);
irda_task_delete(task);
return TRUE;
}
/* Check if we are finished */
if (task->state == IRDA_TASK_DONE) {
del_timer(&task->timer);
/* Do post processing */
if (task->finished)
task->finished(task);
/* Notify parent */
if (task->parent) {
/* Check if parent is waiting for us to complete */
if (task->parent->state == IRDA_TASK_CHILD_WAIT) {
task->parent->state = IRDA_TASK_CHILD_DONE;
/* Stop timer now that we are here */
del_timer(&task->parent->timer);
/* Kick parent task */
irda_task_kick(task->parent);
}
}
irda_task_delete(task);
} else if (timeout > 0) {
irda_start_timer(&task->timer, timeout, (void *) task,
irda_task_timer_expired);
finished = FALSE;
} else {
IRDA_DEBUG(0, "%s(), not finished, and no timeout!\n",
__FUNCTION__);
finished = FALSE;
}
return finished;
}
/*
* Function irda_task_execute (instance, function, finished)
*
* This function registers and tries to execute tasks that may take some
* time to complete. We do it this hairy way since we may have been
* called from interrupt context, so it's not possible to use
* schedule_timeout()
* Two important notes :
* o Make sure you irda_task_delete(task); in case you delete the
* calling instance.
* o No real need to lock when calling this function, but you may
* want to lock within the task handler.
* Jean II
*/
struct irda_task *irda_task_execute(void *instance,
IRDA_TASK_CALLBACK function,
IRDA_TASK_CALLBACK finished,
struct irda_task *parent, void *param)
{
struct irda_task *task;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
task = kmalloc(sizeof(struct irda_task), GFP_ATOMIC);
if (!task)
return NULL;
task->state = IRDA_TASK_INIT;
task->instance = instance;
task->function = function;
task->finished = finished;
task->parent = parent;
task->param = param;
task->magic = IRDA_TASK_MAGIC;
init_timer(&task->timer);
/* Register task */
hashbin_insert(tasks, (irda_queue_t *) task, (long) task, NULL);
/* No time to waste, so lets get going! */
return irda_task_kick(task) ? NULL : task;
}
EXPORT_SYMBOL(irda_task_execute);
/*
* Function irda_task_timer_expired (data)
*
* Task time has expired. We now try to execute task (again), and restart
* the timer if the task has not finished yet
*/
static void irda_task_timer_expired(void *data)
{
struct irda_task *task;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
task = (struct irda_task *) data;
irda_task_kick(task);
}
/*
* Function irda_device_setup (dev)
*
* This function should be used by low level device drivers in a similar way
* as ether_setup() is used by normal network device drivers
*/
static void irda_device_setup(struct net_device *dev)
{
dev->hard_header_len = 0;
dev->addr_len = 0;
dev->type = ARPHRD_IRDA;
dev->tx_queue_len = 8; /* Window size + 1 s-frame */
memset(dev->broadcast, 0xff, 4);
dev->mtu = 2048;
dev->flags = IFF_NOARP;
}
/*
* Funciton alloc_irdadev
* Allocates and sets up an IRDA device in a manner similar to
* alloc_etherdev.
*/
struct net_device *alloc_irdadev(int sizeof_priv)
{
return alloc_netdev(sizeof_priv, "irda%d", irda_device_setup);
}
EXPORT_SYMBOL(alloc_irdadev);
/*
* Function irda_device_init_dongle (self, type, qos)
*
* Initialize attached dongle.
*
* Important : request_module require us to call this function with
* a process context and irq enabled. - Jean II
*/
dongle_t *irda_device_dongle_init(struct net_device *dev, int type)
{
struct dongle_reg *reg;
dongle_t *dongle = NULL;
might_sleep();
spin_lock(&dongles->hb_spinlock);
reg = hashbin_find(dongles, type, NULL);
#ifdef CONFIG_KMOD
/* Try to load the module needed */
if (!reg && capable(CAP_SYS_MODULE)) {
spin_unlock(&dongles->hb_spinlock);
request_module("irda-dongle-%d", type);
spin_lock(&dongles->hb_spinlock);
reg = hashbin_find(dongles, type, NULL);
}
#endif
if (!reg || !try_module_get(reg->owner) ) {
IRDA_ERROR("IrDA: Unable to find requested dongle type %x\n",
type);
goto out;
}
/* Allocate dongle info for this instance */
dongle = kmalloc(sizeof(dongle_t), GFP_KERNEL);
if (!dongle)
goto out;
memset(dongle, 0, sizeof(dongle_t));
/* Bind the registration info to this particular instance */
dongle->issue = reg;
dongle->dev = dev;
out:
spin_unlock(&dongles->hb_spinlock);
return dongle;
}
EXPORT_SYMBOL(irda_device_dongle_init);
/*
* Function irda_device_dongle_cleanup (dongle)
*/
int irda_device_dongle_cleanup(dongle_t *dongle)
{
IRDA_ASSERT(dongle != NULL, return -1;);
dongle->issue->close(dongle);
module_put(dongle->issue->owner);
kfree(dongle);
return 0;
}
EXPORT_SYMBOL(irda_device_dongle_cleanup);
/*
* Function irda_device_register_dongle (dongle)
*/
int irda_device_register_dongle(struct dongle_reg *new)
{
spin_lock(&dongles->hb_spinlock);
/* Check if this dongle has been registered before */
if (hashbin_find(dongles, new->type, NULL)) {
IRDA_MESSAGE("%s: Dongle type %x already registered\n",
__FUNCTION__, new->type);
} else {
/* Insert IrDA dongle into hashbin */
hashbin_insert(dongles, (irda_queue_t *) new, new->type, NULL);
}
spin_unlock(&dongles->hb_spinlock);
return 0;
}
EXPORT_SYMBOL(irda_device_register_dongle);
/*
* Function irda_device_unregister_dongle (dongle)
*
* Unregister dongle, and remove dongle from list of registered dongles
*
*/
void irda_device_unregister_dongle(struct dongle_reg *dongle)
{
struct dongle *node;
spin_lock(&dongles->hb_spinlock);
node = hashbin_remove(dongles, dongle->type, NULL);
if (!node)
IRDA_ERROR("%s: dongle not found!\n", __FUNCTION__);
spin_unlock(&dongles->hb_spinlock);
}
EXPORT_SYMBOL(irda_device_unregister_dongle);
#ifdef CONFIG_ISA
/*
* Function setup_dma (idev, buffer, count, mode)
*
* Setup the DMA channel. Commonly used by ISA FIR drivers
*
*/
void irda_setup_dma(int channel, dma_addr_t buffer, int count, int mode)
{
unsigned long flags;
flags = claim_dma_lock();
disable_dma(channel);
clear_dma_ff(channel);
set_dma_mode(channel, mode);
set_dma_addr(channel, buffer);
set_dma_count(channel, count);
enable_dma(channel);
release_dma_lock(flags);
}
EXPORT_SYMBOL(irda_setup_dma);
#endif

1089
net/irda/iriap.c Normal file

File diff suppressed because it is too large Load Diff

502
net/irda/iriap_event.c Normal file
View File

@@ -0,0 +1,502 @@
/*********************************************************************
*
* Filename: iriap_event.c
* Version: 0.1
* Description: IAP Finite State Machine
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Thu Aug 21 00:02:07 1997
* Modified at: Wed Mar 1 11:28:34 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1997, 1999-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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <net/irda/irda.h>
#include <net/irda/irlmp.h>
#include <net/irda/iriap.h>
#include <net/irda/iriap_event.h>
static void state_s_disconnect (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_s_connecting (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_s_call (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_s_make_call (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_s_calling (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_s_outstanding (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_s_replying (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_s_wait_active (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_r_disconnect (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_r_call (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_r_waiting (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_r_wait_active (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_r_receiving (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_r_execute (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void state_r_returning (struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb);
static void (*iriap_state[])(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb) = {
/* Client FSM */
state_s_disconnect,
state_s_connecting,
state_s_call,
/* S-Call FSM */
state_s_make_call,
state_s_calling,
state_s_outstanding,
state_s_replying,
state_s_wait_for_call,
state_s_wait_active,
/* Server FSM */
state_r_disconnect,
state_r_call,
/* R-Connect FSM */
state_r_waiting,
state_r_wait_active,
state_r_receiving,
state_r_execute,
state_r_returning,
};
void iriap_next_client_state(struct iriap_cb *self, IRIAP_STATE state)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
self->client_state = state;
}
void iriap_next_call_state(struct iriap_cb *self, IRIAP_STATE state)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
self->call_state = state;
}
void iriap_next_server_state(struct iriap_cb *self, IRIAP_STATE state)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
self->server_state = state;
}
void iriap_next_r_connect_state(struct iriap_cb *self, IRIAP_STATE state)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
self->r_connect_state = state;
}
void iriap_do_client_event(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
(*iriap_state[ self->client_state]) (self, event, skb);
}
void iriap_do_call_event(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
(*iriap_state[ self->call_state]) (self, event, skb);
}
void iriap_do_server_event(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
(*iriap_state[ self->server_state]) (self, event, skb);
}
void iriap_do_r_connect_event(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
(*iriap_state[ self->r_connect_state]) (self, event, skb);
}
/*
* Function state_s_disconnect (event, skb)
*
* S-Disconnect, The device has no LSAP connection to a particular
* remote device.
*/
static void state_s_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
switch (event) {
case IAP_CALL_REQUEST_GVBC:
iriap_next_client_state(self, S_CONNECTING);
IRDA_ASSERT(self->request_skb == NULL, return;);
/* Don't forget to refcount it -
* see iriap_getvaluebyclass_request(). */
skb_get(skb);
self->request_skb = skb;
iriap_connect_request(self);
break;
case IAP_LM_DISCONNECT_INDICATION:
break;
default:
IRDA_DEBUG(0, "%s(), Unknown event %d\n", __FUNCTION__, event);
break;
}
}
/*
* Function state_s_connecting (self, event, skb)
*
* S-Connecting
*
*/
static void state_s_connecting(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
switch (event) {
case IAP_LM_CONNECT_CONFIRM:
/*
* Jump to S-Call FSM
*/
iriap_do_call_event(self, IAP_CALL_REQUEST, skb);
/* iriap_call_request(self, 0,0,0); */
iriap_next_client_state(self, S_CALL);
break;
case IAP_LM_DISCONNECT_INDICATION:
/* Abort calls */
iriap_next_call_state(self, S_MAKE_CALL);
iriap_next_client_state(self, S_DISCONNECT);
break;
default:
IRDA_DEBUG(0, "%s(), Unknown event %d\n", __FUNCTION__, event);
break;
}
}
/*
* Function state_s_call (self, event, skb)
*
* S-Call, The device can process calls to a specific remote
* device. Whenever the LSAP connection is disconnected, this state
* catches that event and clears up
*/
static void state_s_call(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_ASSERT(self != NULL, return;);
switch (event) {
case IAP_LM_DISCONNECT_INDICATION:
/* Abort calls */
iriap_next_call_state(self, S_MAKE_CALL);
iriap_next_client_state(self, S_DISCONNECT);
break;
default:
IRDA_DEBUG(0, "state_s_call: Unknown event %d\n", event);
break;
}
}
/*
* Function state_s_make_call (event, skb)
*
* S-Make-Call
*
*/
static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
struct sk_buff *tx_skb;
IRDA_ASSERT(self != NULL, return;);
switch (event) {
case IAP_CALL_REQUEST:
/* Already refcounted - see state_s_disconnect() */
tx_skb = self->request_skb;
self->request_skb = NULL;
irlmp_data_request(self->lsap, tx_skb);
iriap_next_call_state(self, S_OUTSTANDING);
break;
default:
IRDA_DEBUG(0, "%s(), Unknown event %d\n", __FUNCTION__, event);
break;
}
}
/*
* Function state_s_calling (event, skb)
*
* S-Calling
*
*/
static void state_s_calling(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(0, "%s(), Not implemented\n", __FUNCTION__);
}
/*
* Function state_s_outstanding (event, skb)
*
* S-Outstanding, The device is waiting for a response to a command
*
*/
static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_ASSERT(self != NULL, return;);
switch (event) {
case IAP_RECV_F_LST:
/*iriap_send_ack(self);*/
/*LM_Idle_request(idle); */
iriap_next_call_state(self, S_WAIT_FOR_CALL);
break;
default:
IRDA_DEBUG(0, "%s(), Unknown event %d\n", __FUNCTION__, event);
break;
}
}
/*
* Function state_s_replying (event, skb)
*
* S-Replying, The device is collecting a multiple part response
*/
static void state_s_replying(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(0, "%s(), Not implemented\n", __FUNCTION__);
}
/*
* Function state_s_wait_for_call (event, skb)
*
* S-Wait-for-Call
*
*/
static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(0, "%s(), Not implemented\n", __FUNCTION__);
}
/*
* Function state_s_wait_active (event, skb)
*
* S-Wait-Active
*
*/
static void state_s_wait_active(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(0, "%s(), Not implemented\n", __FUNCTION__);
}
/**************************************************************************
*
* Server FSM
*
**************************************************************************/
/*
* Function state_r_disconnect (self, event, skb)
*
* LM-IAS server is disconnected (not processing any requests!)
*
*/
static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
struct sk_buff *tx_skb;
switch (event) {
case IAP_LM_CONNECT_INDICATION:
tx_skb = dev_alloc_skb(64);
if (tx_skb == NULL) {
IRDA_WARNING("%s: unable to malloc!\n", __FUNCTION__);
return;
}
/* Reserve space for MUX_CONTROL and LAP header */
skb_reserve(tx_skb, LMP_MAX_HEADER);
irlmp_connect_response(self->lsap, tx_skb);
/*LM_Idle_request(idle); */
iriap_next_server_state(self, R_CALL);
/*
* Jump to R-Connect FSM, we skip R-Waiting since we do not
* care about LM_Idle_request()!
*/
iriap_next_r_connect_state(self, R_RECEIVING);
break;
default:
IRDA_DEBUG(0, "%s(), unknown event %d\n", __FUNCTION__, event);
break;
}
}
/*
* Function state_r_call (self, event, skb)
*/
static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
switch (event) {
case IAP_LM_DISCONNECT_INDICATION:
/* Abort call */
iriap_next_server_state(self, R_DISCONNECT);
iriap_next_r_connect_state(self, R_WAITING);
break;
default:
IRDA_DEBUG(0, "%s(), unknown event!\n", __FUNCTION__);
break;
}
}
/*
* R-Connect FSM
*/
/*
* Function state_r_waiting (self, event, skb)
*/
static void state_r_waiting(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(0, "%s(), Not implemented\n", __FUNCTION__);
}
static void state_r_wait_active(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(0, "%s(), Not implemented\n", __FUNCTION__);
}
/*
* Function state_r_receiving (self, event, skb)
*
* We are receiving a command
*
*/
static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
switch (event) {
case IAP_RECV_F_LST:
iriap_next_r_connect_state(self, R_EXECUTE);
iriap_call_indication(self, skb);
break;
default:
IRDA_DEBUG(0, "%s(), unknown event!\n", __FUNCTION__);
break;
}
}
/*
* Function state_r_execute (self, event, skb)
*
* The server is processing the request
*
*/
static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
IRDA_ASSERT(skb != NULL, return;);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
switch (event) {
case IAP_CALL_RESPONSE:
/*
* Since we don't implement the Waiting state, we return
* to state Receiving instead, DB.
*/
iriap_next_r_connect_state(self, R_RECEIVING);
/* Don't forget to refcount it - see
* iriap_getvaluebyclass_response(). */
skb_get(skb);
irlmp_data_request(self->lsap, skb);
break;
default:
IRDA_DEBUG(0, "%s(), unknown event!\n", __FUNCTION__);
break;
}
}
static void state_r_returning(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(0, "%s(), event=%d\n", __FUNCTION__, event);
switch (event) {
case IAP_RECV_F_LST:
break;
default:
break;
}
}

580
net/irda/irias_object.c Normal file
View File

@@ -0,0 +1,580 @@
/*********************************************************************
*
* Filename: irias_object.c
* Version: 0.3
* Description: IAS object database and functions
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Thu Oct 1 22:50:04 1998
* Modified at: Wed Dec 15 11:23:16 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
*
* 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/module.h>
#include <net/irda/irda.h>
#include <net/irda/irias_object.h>
hashbin_t *irias_objects;
/*
* Used when a missing value needs to be returned
*/
struct ias_value irias_missing = { IAS_MISSING, 0, 0, 0, {0}};
/*
* Function strndup (str, max)
*
* My own kernel version of strndup!
*
* Faster, check boundary... Jean II
*/
static char *strndup(char *str, int max)
{
char *new_str;
int len;
/* Check string */
if (str == NULL)
return NULL;
/* Check length, truncate */
len = strlen(str);
if(len > max)
len = max;
/* Allocate new string */
new_str = kmalloc(len + 1, GFP_ATOMIC);
if (new_str == NULL) {
IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__);
return NULL;
}
/* Copy and truncate */
memcpy(new_str, str, len);
new_str[len] = '\0';
return new_str;
}
/*
* Function ias_new_object (name, id)
*
* Create a new IAS object
*
*/
struct ias_object *irias_new_object( char *name, int id)
{
struct ias_object *obj;
IRDA_DEBUG( 4, "%s()\n", __FUNCTION__);
obj = (struct ias_object *) kmalloc(sizeof(struct ias_object),
GFP_ATOMIC);
if (obj == NULL) {
IRDA_WARNING("%s(), Unable to allocate object!\n",
__FUNCTION__);
return NULL;
}
memset(obj, 0, sizeof( struct ias_object));
obj->magic = IAS_OBJECT_MAGIC;
obj->name = strndup(name, IAS_MAX_CLASSNAME);
obj->id = id;
/* Locking notes : the attrib spinlock has lower precendence
* than the objects spinlock. Never grap the objects spinlock
* while holding any attrib spinlock (risk of deadlock). Jean II */
obj->attribs = hashbin_new(HB_LOCK);
if (obj->attribs == NULL) {
IRDA_WARNING("%s(), Unable to allocate attribs!\n",
__FUNCTION__);
kfree(obj);
return NULL;
}
return obj;
}
EXPORT_SYMBOL(irias_new_object);
/*
* Function irias_delete_attrib (attrib)
*
* Delete given attribute and deallocate all its memory
*
*/
static void __irias_delete_attrib(struct ias_attrib *attrib)
{
IRDA_ASSERT(attrib != NULL, return;);
IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;);
if (attrib->name)
kfree(attrib->name);
irias_delete_value(attrib->value);
attrib->magic = ~IAS_ATTRIB_MAGIC;
kfree(attrib);
}
void __irias_delete_object(struct ias_object *obj)
{
IRDA_ASSERT(obj != NULL, return;);
IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
if (obj->name)
kfree(obj->name);
hashbin_delete(obj->attribs, (FREE_FUNC) __irias_delete_attrib);
obj->magic = ~IAS_OBJECT_MAGIC;
kfree(obj);
}
/*
* Function irias_delete_object (obj)
*
* Remove object from hashbin and deallocate all attributes associated with
* with this object and the object itself
*
*/
int irias_delete_object(struct ias_object *obj)
{
struct ias_object *node;
IRDA_ASSERT(obj != NULL, return -1;);
IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;);
/* Remove from list */
node = hashbin_remove_this(irias_objects, (irda_queue_t *) obj);
if (!node)
IRDA_DEBUG( 0, "%s(), object already removed!\n",
__FUNCTION__);
/* Destroy */
__irias_delete_object(obj);
return 0;
}
EXPORT_SYMBOL(irias_delete_object);
/*
* Function irias_delete_attrib (obj)
*
* Remove attribute from hashbin and, if it was the last attribute of
* the object, remove the object as well.
*
*/
int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib,
int cleanobject)
{
struct ias_attrib *node;
IRDA_ASSERT(obj != NULL, return -1;);
IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;);
IRDA_ASSERT(attrib != NULL, return -1;);
/* Remove attribute from object */
node = hashbin_remove_this(obj->attribs, (irda_queue_t *) attrib);
if (!node)
return 0; /* Already removed or non-existent */
/* Deallocate attribute */
__irias_delete_attrib(node);
/* Check if object has still some attributes, destroy it if none.
* At first glance, this look dangerous, as the kernel reference
* various IAS objects. However, we only use this function on
* user attributes, not kernel attributes, so there is no risk
* of deleting a kernel object this way. Jean II */
node = (struct ias_attrib *) hashbin_get_first(obj->attribs);
if (cleanobject && !node)
irias_delete_object(obj);
return 0;
}
/*
* Function irias_insert_object (obj)
*
* Insert an object into the LM-IAS database
*
*/
void irias_insert_object(struct ias_object *obj)
{
IRDA_ASSERT(obj != NULL, return;);
IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
hashbin_insert(irias_objects, (irda_queue_t *) obj, 0, obj->name);
}
EXPORT_SYMBOL(irias_insert_object);
/*
* Function irias_find_object (name)
*
* Find object with given name
*
*/
struct ias_object *irias_find_object(char *name)
{
IRDA_ASSERT(name != NULL, return NULL;);
/* Unsafe (locking), object might change */
return hashbin_lock_find(irias_objects, 0, name);
}
EXPORT_SYMBOL(irias_find_object);
/*
* Function irias_find_attrib (obj, name)
*
* Find named attribute in object
*
*/
struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name)
{
struct ias_attrib *attrib;
IRDA_ASSERT(obj != NULL, return NULL;);
IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return NULL;);
IRDA_ASSERT(name != NULL, return NULL;);
attrib = hashbin_lock_find(obj->attribs, 0, name);
if (attrib == NULL)
return NULL;
/* Unsafe (locking), attrib might change */
return attrib;
}
EXPORT_SYMBOL(irias_find_attrib);
/*
* Function irias_add_attribute (obj, attrib)
*
* Add attribute to object
*
*/
static void irias_add_attrib(struct ias_object *obj, struct ias_attrib *attrib,
int owner)
{
IRDA_ASSERT(obj != NULL, return;);
IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
IRDA_ASSERT(attrib != NULL, return;);
IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;);
/* Set if attrib is owned by kernel or user space */
attrib->value->owner = owner;
hashbin_insert(obj->attribs, (irda_queue_t *) attrib, 0, attrib->name);
}
/*
* Function irias_object_change_attribute (obj_name, attrib_name, new_value)
*
* Change the value of an objects attribute.
*
*/
int irias_object_change_attribute(char *obj_name, char *attrib_name,
struct ias_value *new_value)
{
struct ias_object *obj;
struct ias_attrib *attrib;
unsigned long flags;
/* Find object */
obj = hashbin_lock_find(irias_objects, 0, obj_name);
if (obj == NULL) {
IRDA_WARNING("%s: Unable to find object: %s\n", __FUNCTION__,
obj_name);
return -1;
}
/* Slightly unsafe (obj might get removed under us) */
spin_lock_irqsave(&obj->attribs->hb_spinlock, flags);
/* Find attribute */
attrib = hashbin_find(obj->attribs, 0, attrib_name);
if (attrib == NULL) {
IRDA_WARNING("%s: Unable to find attribute: %s\n",
__FUNCTION__, attrib_name);
spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
return -1;
}
if ( attrib->value->type != new_value->type) {
IRDA_DEBUG( 0, "%s(), changing value type not allowed!\n",
__FUNCTION__);
spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
return -1;
}
/* Delete old value */
irias_delete_value(attrib->value);
/* Insert new value */
attrib->value = new_value;
/* Success */
spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
return 0;
}
EXPORT_SYMBOL(irias_object_change_attribute);
/*
* Function irias_object_add_integer_attrib (obj, name, value)
*
* Add an integer attribute to an LM-IAS object
*
*/
void irias_add_integer_attrib(struct ias_object *obj, char *name, int value,
int owner)
{
struct ias_attrib *attrib;
IRDA_ASSERT(obj != NULL, return;);
IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
IRDA_ASSERT(name != NULL, return;);
attrib = (struct ias_attrib *) kmalloc(sizeof(struct ias_attrib),
GFP_ATOMIC);
if (attrib == NULL) {
IRDA_WARNING("%s: Unable to allocate attribute!\n",
__FUNCTION__);
return;
}
memset(attrib, 0, sizeof( struct ias_attrib));
attrib->magic = IAS_ATTRIB_MAGIC;
attrib->name = strndup(name, IAS_MAX_ATTRIBNAME);
/* Insert value */
attrib->value = irias_new_integer_value(value);
irias_add_attrib(obj, attrib, owner);
}
EXPORT_SYMBOL(irias_add_integer_attrib);
/*
* Function irias_add_octseq_attrib (obj, name, octet_seq, len)
*
* Add a octet sequence attribute to an LM-IAS object
*
*/
void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets,
int len, int owner)
{
struct ias_attrib *attrib;
IRDA_ASSERT(obj != NULL, return;);
IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
IRDA_ASSERT(name != NULL, return;);
IRDA_ASSERT(octets != NULL, return;);
attrib = (struct ias_attrib *) kmalloc(sizeof(struct ias_attrib),
GFP_ATOMIC);
if (attrib == NULL) {
IRDA_WARNING("%s: Unable to allocate attribute!\n",
__FUNCTION__);
return;
}
memset(attrib, 0, sizeof( struct ias_attrib));
attrib->magic = IAS_ATTRIB_MAGIC;
attrib->name = strndup(name, IAS_MAX_ATTRIBNAME);
attrib->value = irias_new_octseq_value( octets, len);
irias_add_attrib(obj, attrib, owner);
}
EXPORT_SYMBOL(irias_add_octseq_attrib);
/*
* Function irias_object_add_string_attrib (obj, string)
*
* Add a string attribute to an LM-IAS object
*
*/
void irias_add_string_attrib(struct ias_object *obj, char *name, char *value,
int owner)
{
struct ias_attrib *attrib;
IRDA_ASSERT(obj != NULL, return;);
IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;);
IRDA_ASSERT(name != NULL, return;);
IRDA_ASSERT(value != NULL, return;);
attrib = (struct ias_attrib *) kmalloc(sizeof( struct ias_attrib),
GFP_ATOMIC);
if (attrib == NULL) {
IRDA_WARNING("%s: Unable to allocate attribute!\n",
__FUNCTION__);
return;
}
memset(attrib, 0, sizeof( struct ias_attrib));
attrib->magic = IAS_ATTRIB_MAGIC;
attrib->name = strndup(name, IAS_MAX_ATTRIBNAME);
attrib->value = irias_new_string_value(value);
irias_add_attrib(obj, attrib, owner);
}
EXPORT_SYMBOL(irias_add_string_attrib);
/*
* Function irias_new_integer_value (integer)
*
* Create new IAS integer value
*
*/
struct ias_value *irias_new_integer_value(int integer)
{
struct ias_value *value;
value = kmalloc(sizeof(struct ias_value), GFP_ATOMIC);
if (value == NULL) {
IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__);
return NULL;
}
memset(value, 0, sizeof(struct ias_value));
value->type = IAS_INTEGER;
value->len = 4;
value->t.integer = integer;
return value;
}
EXPORT_SYMBOL(irias_new_integer_value);
/*
* Function irias_new_string_value (string)
*
* Create new IAS string value
*
* Per IrLMP 1.1, 4.3.3.2, strings are up to 256 chars - Jean II
*/
struct ias_value *irias_new_string_value(char *string)
{
struct ias_value *value;
value = kmalloc(sizeof(struct ias_value), GFP_ATOMIC);
if (value == NULL) {
IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__);
return NULL;
}
memset( value, 0, sizeof( struct ias_value));
value->type = IAS_STRING;
value->charset = CS_ASCII;
value->t.string = strndup(string, IAS_MAX_STRING);
value->len = strlen(value->t.string);
return value;
}
EXPORT_SYMBOL(irias_new_string_value);
/*
* Function irias_new_octseq_value (octets, len)
*
* Create new IAS octet-sequence value
*
* Per IrLMP 1.1, 4.3.3.2, octet-sequence are up to 1024 bytes - Jean II
*/
struct ias_value *irias_new_octseq_value(__u8 *octseq , int len)
{
struct ias_value *value;
value = kmalloc(sizeof(struct ias_value), GFP_ATOMIC);
if (value == NULL) {
IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__);
return NULL;
}
memset(value, 0, sizeof(struct ias_value));
value->type = IAS_OCT_SEQ;
/* Check length */
if(len > IAS_MAX_OCTET_STRING)
len = IAS_MAX_OCTET_STRING;
value->len = len;
value->t.oct_seq = kmalloc(len, GFP_ATOMIC);
if (value->t.oct_seq == NULL){
IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__);
kfree(value);
return NULL;
}
memcpy(value->t.oct_seq, octseq , len);
return value;
}
EXPORT_SYMBOL(irias_new_octseq_value);
struct ias_value *irias_new_missing_value(void)
{
struct ias_value *value;
value = kmalloc(sizeof(struct ias_value), GFP_ATOMIC);
if (value == NULL) {
IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__);
return NULL;
}
memset(value, 0, sizeof(struct ias_value));
value->type = IAS_MISSING;
value->len = 0;
return value;
}
/*
* Function irias_delete_value (value)
*
* Delete IAS value
*
*/
void irias_delete_value(struct ias_value *value)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
IRDA_ASSERT(value != NULL, return;);
switch (value->type) {
case IAS_INTEGER: /* Fallthrough */
case IAS_MISSING:
/* No need to deallocate */
break;
case IAS_STRING:
/* If string, deallocate string */
if (value->t.string != NULL)
kfree(value->t.string);
break;
case IAS_OCT_SEQ:
/* If byte stream, deallocate byte stream */
if (value->t.oct_seq != NULL)
kfree(value->t.oct_seq);
break;
default:
IRDA_DEBUG(0, "%s(), Unknown value type!\n", __FUNCTION__);
break;
}
kfree(value);
}
EXPORT_SYMBOL(irias_delete_value);

14
net/irda/irlan/Kconfig Normal file
View File

@@ -0,0 +1,14 @@
config IRLAN
tristate "IrLAN protocol"
depends on IRDA
help
Say Y here if you want to build support for the IrLAN protocol.
To compile it as a module, choose M here: the module will be called
irlan. IrLAN emulates an Ethernet and makes it possible to put up
a wireless LAN using infrared beams.
The IrLAN protocol can be used to talk with infrared access points
like the HP NetbeamIR, or the ESI JetEye NET. You can also connect
to another Linux machine running the IrLAN protocol for ad-hoc
networking!

7
net/irda/irlan/Makefile Normal file
View File

@@ -0,0 +1,7 @@
#
# Makefile for the Linux IrDA IrLAN protocol layer.
#
obj-$(CONFIG_IRLAN) += irlan.o
irlan-objs := irlan_common.o irlan_eth.o irlan_event.o irlan_client.o irlan_provider.o irlan_filter.o irlan_provider_event.o irlan_client_event.o

View File

@@ -0,0 +1,576 @@
/*********************************************************************
*
* Filename: irlan_client.c
* Version: 0.9
* Description: IrDA LAN Access Protocol (IrLAN) Client
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:37 1997
* Modified at: Tue Dec 14 15:47:02 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
* slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
* Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
*
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
* All Rights Reserved.
*
* 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/bitops.h>
#include <net/arp.h>
#include <asm/system.h>
#include <asm/byteorder.h>
#include <net/irda/irda.h>
#include <net/irda/irttp.h>
#include <net/irda/irlmp.h>
#include <net/irda/irias_object.h>
#include <net/irda/iriap.h>
#include <net/irda/timer.h>
#include <net/irda/irlan_common.h>
#include <net/irda/irlan_event.h>
#include <net/irda/irlan_eth.h>
#include <net/irda/irlan_provider.h>
#include <net/irda/irlan_client.h>
#undef CONFIG_IRLAN_GRATUITOUS_ARP
static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap,
LM_REASON reason,
struct sk_buff *);
static int irlan_client_ctrl_data_indication(void *instance, void *sap,
struct sk_buff *skb);
static void irlan_client_ctrl_connect_confirm(void *instance, void *sap,
struct qos_info *qos,
__u32 max_sdu_size,
__u8 max_header_size,
struct sk_buff *);
static void irlan_check_response_param(struct irlan_cb *self, char *param,
char *value, int val_len);
static void irlan_client_open_ctrl_tsap(struct irlan_cb *self);
static void irlan_client_kick_timer_expired(void *data)
{
struct irlan_cb *self = (struct irlan_cb *) data;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
/*
* If we are in peer mode, the client may not have got the discovery
* indication it needs to make progress. If the client is still in
* IDLE state, we must kick it to, but only if the provider is not IDLE
*/
if ((self->provider.access_type == ACCESS_PEER) &&
(self->client.state == IRLAN_IDLE) &&
(self->provider.state != IRLAN_IDLE)) {
irlan_client_wakeup(self, self->saddr, self->daddr);
}
}
static void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
irda_start_timer(&self->client.kick_timer, timeout, (void *) self,
irlan_client_kick_timer_expired);
}
/*
* Function irlan_client_wakeup (self, saddr, daddr)
*
* Wake up client
*
*/
void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr)
{
IRDA_DEBUG(1, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
/*
* Check if we are already awake, or if we are a provider in direct
* mode (in that case we must leave the client idle
*/
if ((self->client.state != IRLAN_IDLE) ||
(self->provider.access_type == ACCESS_DIRECT))
{
IRDA_DEBUG(0, "%s(), already awake!\n", __FUNCTION__ );
return;
}
/* Addresses may have changed! */
self->saddr = saddr;
self->daddr = daddr;
if (self->disconnect_reason == LM_USER_REQUEST) {
IRDA_DEBUG(0, "%s(), still stopped by user\n", __FUNCTION__ );
return;
}
/* Open TSAPs */
irlan_client_open_ctrl_tsap(self);
irlan_open_data_tsap(self);
irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL);
/* Start kick timer */
irlan_client_start_kick_timer(self, 2*HZ);
}
/*
* Function irlan_discovery_indication (daddr)
*
* Remote device with IrLAN server support discovered
*
*/
void irlan_client_discovery_indication(discinfo_t *discovery,
DISCOVERY_MODE mode,
void *priv)
{
struct irlan_cb *self;
__u32 saddr, daddr;
IRDA_DEBUG(1, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(discovery != NULL, return;);
/*
* I didn't check it, but I bet that IrLAN suffer from the same
* deficiency as IrComm and doesn't handle two instances
* simultaneously connecting to each other.
* Same workaround, drop passive discoveries.
* Jean II */
if(mode == DISCOVERY_PASSIVE)
return;
saddr = discovery->saddr;
daddr = discovery->daddr;
/* Find instance */
rcu_read_lock();
self = irlan_get_any();
if (self) {
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
IRDA_DEBUG(1, "%s(), Found instance (%08x)!\n", __FUNCTION__ ,
daddr);
irlan_client_wakeup(self, saddr, daddr);
}
rcu_read_unlock();
}
/*
* Function irlan_client_data_indication (handle, skb)
*
* This function gets the data that is received on the control channel
*
*/
static int irlan_client_ctrl_data_indication(void *instance, void *sap,
struct sk_buff *skb)
{
struct irlan_cb *self;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
self = (struct irlan_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;);
irlan_do_client_event(self, IRLAN_DATA_INDICATION, skb);
/* Ready for a new command */
IRDA_DEBUG(2, "%s(), clearing tx_busy\n", __FUNCTION__ );
self->client.tx_busy = FALSE;
/* Check if we have some queued commands waiting to be sent */
irlan_run_ctrl_tx_queue(self);
return 0;
}
static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap,
LM_REASON reason,
struct sk_buff *userdata)
{
struct irlan_cb *self;
struct tsap_cb *tsap;
struct sk_buff *skb;
IRDA_DEBUG(4, "%s(), reason=%d\n", __FUNCTION__ , reason);
self = (struct irlan_cb *) instance;
tsap = (struct tsap_cb *) sap;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
IRDA_ASSERT(tsap != NULL, return;);
IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;);
IRDA_ASSERT(tsap == self->client.tsap_ctrl, return;);
/* Remove frames queued on the control channel */
while ((skb = skb_dequeue(&self->client.txq)) != NULL) {
dev_kfree_skb(skb);
}
self->client.tx_busy = FALSE;
irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
}
/*
* Function irlan_client_open_tsaps (self)
*
* Initialize callbacks and open IrTTP TSAPs
*
*/
static void irlan_client_open_ctrl_tsap(struct irlan_cb *self)
{
struct tsap_cb *tsap;
notify_t notify;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
/* Check if already open */
if (self->client.tsap_ctrl)
return;
irda_notify_init(&notify);
/* Set up callbacks */
notify.data_indication = irlan_client_ctrl_data_indication;
notify.connect_confirm = irlan_client_ctrl_connect_confirm;
notify.disconnect_indication = irlan_client_ctrl_disconnect_indication;
notify.instance = self;
strlcpy(notify.name, "IrLAN ctrl (c)", sizeof(notify.name));
tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, &notify);
if (!tsap) {
IRDA_DEBUG(2, "%s(), Got no tsap!\n", __FUNCTION__ );
return;
}
self->client.tsap_ctrl = tsap;
}
/*
* Function irlan_client_connect_confirm (handle, skb)
*
* Connection to peer IrLAN laye confirmed
*
*/
static void irlan_client_ctrl_connect_confirm(void *instance, void *sap,
struct qos_info *qos,
__u32 max_sdu_size,
__u8 max_header_size,
struct sk_buff *skb)
{
struct irlan_cb *self;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
self = (struct irlan_cb *) instance;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
self->client.max_sdu_size = max_sdu_size;
self->client.max_header_size = max_header_size;
/* TODO: we could set the MTU depending on the max_sdu_size */
irlan_do_client_event(self, IRLAN_CONNECT_COMPLETE, NULL);
}
/*
* Function print_ret_code (code)
*
* Print return code of request to peer IrLAN layer.
*
*/
static void print_ret_code(__u8 code)
{
switch(code) {
case 0:
printk(KERN_INFO "Success\n");
break;
case 1:
IRDA_WARNING("IrLAN: Insufficient resources\n");
break;
case 2:
IRDA_WARNING("IrLAN: Invalid command format\n");
break;
case 3:
IRDA_WARNING("IrLAN: Command not supported\n");
break;
case 4:
IRDA_WARNING("IrLAN: Parameter not supported\n");
break;
case 5:
IRDA_WARNING("IrLAN: Value not supported\n");
break;
case 6:
IRDA_WARNING("IrLAN: Not open\n");
break;
case 7:
IRDA_WARNING("IrLAN: Authentication required\n");
break;
case 8:
IRDA_WARNING("IrLAN: Invalid password\n");
break;
case 9:
IRDA_WARNING("IrLAN: Protocol error\n");
break;
case 255:
IRDA_WARNING("IrLAN: Asynchronous status\n");
break;
}
}
/*
* Function irlan_client_parse_response (self, skb)
*
* Extract all parameters from received buffer, then feed them to
* check_params for parsing
*/
void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb)
{
__u8 *frame;
__u8 *ptr;
int count;
int ret;
__u16 val_len;
int i;
char *name;
char *value;
IRDA_ASSERT(skb != NULL, return;);
IRDA_DEBUG(4, "%s() skb->len=%d\n", __FUNCTION__ , (int) skb->len);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
if (!skb) {
IRDA_ERROR("%s(), Got NULL skb!\n", __FUNCTION__);
return;
}
frame = skb->data;
/*
* Check return code and print it if not success
*/
if (frame[0]) {
print_ret_code(frame[0]);
return;
}
name = kmalloc(255, GFP_ATOMIC);
if (!name)
return;
value = kmalloc(1016, GFP_ATOMIC);
if (!value) {
kfree(name);
return;
}
/* How many parameters? */
count = frame[1];
IRDA_DEBUG(4, "%s(), got %d parameters\n", __FUNCTION__ , count);
ptr = frame+2;
/* For all parameters */
for (i=0; i<count;i++) {
ret = irlan_extract_param(ptr, name, value, &val_len);
if (ret < 0) {
IRDA_DEBUG(2, "%s(), IrLAN, Error!\n", __FUNCTION__ );
break;
}
ptr += ret;
irlan_check_response_param(self, name, value, val_len);
}
/* Cleanup */
kfree(name);
kfree(value);
}
/*
* Function irlan_check_response_param (self, param, value, val_len)
*
* Check which parameter is received and update local variables
*
*/
static void irlan_check_response_param(struct irlan_cb *self, char *param,
char *value, int val_len)
{
__u16 tmp_cpu; /* Temporary value in host order */
__u8 *bytes;
int i;
IRDA_DEBUG(4, "%s(), parm=%s\n", __FUNCTION__ , param);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
/* Media type */
if (strcmp(param, "MEDIA") == 0) {
if (strcmp(value, "802.3") == 0)
self->media = MEDIA_802_3;
else
self->media = MEDIA_802_5;
return;
}
if (strcmp(param, "FILTER_TYPE") == 0) {
if (strcmp(value, "DIRECTED") == 0)
self->client.filter_type |= IRLAN_DIRECTED;
else if (strcmp(value, "FUNCTIONAL") == 0)
self->client.filter_type |= IRLAN_FUNCTIONAL;
else if (strcmp(value, "GROUP") == 0)
self->client.filter_type |= IRLAN_GROUP;
else if (strcmp(value, "MAC_FRAME") == 0)
self->client.filter_type |= IRLAN_MAC_FRAME;
else if (strcmp(value, "MULTICAST") == 0)
self->client.filter_type |= IRLAN_MULTICAST;
else if (strcmp(value, "BROADCAST") == 0)
self->client.filter_type |= IRLAN_BROADCAST;
else if (strcmp(value, "IPX_SOCKET") == 0)
self->client.filter_type |= IRLAN_IPX_SOCKET;
}
if (strcmp(param, "ACCESS_TYPE") == 0) {
if (strcmp(value, "DIRECT") == 0)
self->client.access_type = ACCESS_DIRECT;
else if (strcmp(value, "PEER") == 0)
self->client.access_type = ACCESS_PEER;
else if (strcmp(value, "HOSTED") == 0)
self->client.access_type = ACCESS_HOSTED;
else {
IRDA_DEBUG(2, "%s(), unknown access type!\n", __FUNCTION__ );
}
}
/* IRLAN version */
if (strcmp(param, "IRLAN_VER") == 0) {
IRDA_DEBUG(4, "IrLAN version %d.%d\n", (__u8) value[0],
(__u8) value[1]);
self->version[0] = value[0];
self->version[1] = value[1];
return;
}
/* Which remote TSAP to use for data channel */
if (strcmp(param, "DATA_CHAN") == 0) {
self->dtsap_sel_data = value[0];
IRDA_DEBUG(4, "Data TSAP = %02x\n", self->dtsap_sel_data);
return;
}
if (strcmp(param, "CON_ARB") == 0) {
memcpy(&tmp_cpu, value, 2); /* Align value */
le16_to_cpus(&tmp_cpu); /* Convert to host order */
self->client.recv_arb_val = tmp_cpu;
IRDA_DEBUG(2, "%s(), receive arb val=%d\n", __FUNCTION__ ,
self->client.recv_arb_val);
}
if (strcmp(param, "MAX_FRAME") == 0) {
memcpy(&tmp_cpu, value, 2); /* Align value */
le16_to_cpus(&tmp_cpu); /* Convert to host order */
self->client.max_frame = tmp_cpu;
IRDA_DEBUG(4, "%s(), max frame=%d\n", __FUNCTION__ ,
self->client.max_frame);
}
/* RECONNECT_KEY, in case the link goes down! */
if (strcmp(param, "RECONNECT_KEY") == 0) {
IRDA_DEBUG(4, "Got reconnect key: ");
/* for (i = 0; i < val_len; i++) */
/* printk("%02x", value[i]); */
memcpy(self->client.reconnect_key, value, val_len);
self->client.key_len = val_len;
IRDA_DEBUG(4, "\n");
}
/* FILTER_ENTRY, have we got an ethernet address? */
if (strcmp(param, "FILTER_ENTRY") == 0) {
bytes = value;
IRDA_DEBUG(4, "Ethernet address = %02x:%02x:%02x:%02x:%02x:%02x\n",
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4],
bytes[5]);
for (i = 0; i < 6; i++)
self->dev->dev_addr[i] = bytes[i];
}
}
/*
* Function irlan_client_get_value_confirm (obj_id, value)
*
* Got results from remote LM-IAS
*
*/
void irlan_client_get_value_confirm(int result, __u16 obj_id,
struct ias_value *value, void *priv)
{
struct irlan_cb *self;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(priv != NULL, return;);
self = (struct irlan_cb *) priv;
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
/* We probably don't need to make any more queries */
iriap_close(self->client.iriap);
self->client.iriap = NULL;
/* Check if request succeeded */
if (result != IAS_SUCCESS) {
IRDA_DEBUG(2, "%s(), got NULL value!\n", __FUNCTION__ );
irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL,
NULL);
return;
}
switch (value->type) {
case IAS_INTEGER:
self->dtsap_sel_ctrl = value->t.integer;
if (value->t.integer != -1) {
irlan_do_client_event(self, IRLAN_IAS_PROVIDER_AVAIL,
NULL);
return;
}
irias_delete_value(value);
break;
default:
IRDA_DEBUG(2, "%s(), unknown type!\n", __FUNCTION__ );
break;
}
irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, NULL);
}

View File

@@ -0,0 +1,533 @@
/*********************************************************************
*
* Filename: irlan_client_event.c
* Version: 0.9
* Description: IrLAN client state machine
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:37 1997
* Modified at: Sun Dec 26 21:52:24 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
* All Rights Reserved.
*
* 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <linux/skbuff.h>
#include <net/irda/irda.h>
#include <net/irda/timer.h>
#include <net/irda/irmod.h>
#include <net/irda/iriap.h>
#include <net/irda/irlmp.h>
#include <net/irda/irttp.h>
#include <net/irda/irlan_common.h>
#include <net/irda/irlan_client.h>
#include <net/irda/irlan_event.h>
static int irlan_client_state_idle (struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_client_state_conn (struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_client_state_info (struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_client_state_open (struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_client_state_wait (struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_client_state_arb (struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_client_state_data (struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_client_state_sync (struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int (*state[])(struct irlan_cb *, IRLAN_EVENT event, struct sk_buff *) =
{
irlan_client_state_idle,
irlan_client_state_query,
irlan_client_state_conn,
irlan_client_state_info,
irlan_client_state_media,
irlan_client_state_open,
irlan_client_state_wait,
irlan_client_state_arb,
irlan_client_state_data,
irlan_client_state_close,
irlan_client_state_sync
};
void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
(*state[ self->client.state]) (self, event, skb);
}
/*
* Function irlan_client_state_idle (event, skb, info)
*
* IDLE, We are waiting for an indication that there is a provider
* available.
*/
static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
switch (event) {
case IRLAN_DISCOVERY_INDICATION:
if (self->client.iriap) {
IRDA_WARNING("%s(), busy with a previous query\n",
__FUNCTION__);
return -EBUSY;
}
self->client.iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
irlan_client_get_value_confirm);
/* Get some values from peer IAS */
irlan_next_client_state(self, IRLAN_QUERY);
iriap_getvaluebyclass_request(self->client.iriap,
self->saddr, self->daddr,
"IrLAN", "IrDA:TinyTP:LsapSel");
break;
case IRLAN_WATCHDOG_TIMEOUT:
IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
break;
default:
IRDA_DEBUG(4, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
/*
* Function irlan_client_state_query (event, skb, info)
*
* QUERY, We have queryed the remote IAS and is ready to connect
* to provider, just waiting for the confirm.
*
*/
static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
switch(event) {
case IRLAN_IAS_PROVIDER_AVAIL:
IRDA_ASSERT(self->dtsap_sel_ctrl != 0, return -1;);
self->client.open_retries = 0;
irttp_connect_request(self->client.tsap_ctrl,
self->dtsap_sel_ctrl,
self->saddr, self->daddr, NULL,
IRLAN_MTU, NULL);
irlan_next_client_state(self, IRLAN_CONN);
break;
case IRLAN_IAS_PROVIDER_NOT_AVAIL:
IRDA_DEBUG(2, "%s(), IAS_PROVIDER_NOT_AVAIL\n", __FUNCTION__ );
irlan_next_client_state(self, IRLAN_IDLE);
/* Give the client a kick! */
if ((self->provider.access_type == ACCESS_PEER) &&
(self->provider.state != IRLAN_IDLE))
irlan_client_wakeup(self, self->saddr, self->daddr);
break;
case IRLAN_LMP_DISCONNECT:
case IRLAN_LAP_DISCONNECT:
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
break;
default:
IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
/*
* Function irlan_client_state_conn (event, skb, info)
*
* CONN, We have connected to a provider but has not issued any
* commands yet.
*
*/
static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
switch (event) {
case IRLAN_CONNECT_COMPLETE:
/* Send getinfo cmd */
irlan_get_provider_info(self);
irlan_next_client_state(self, IRLAN_INFO);
break;
case IRLAN_LMP_DISCONNECT:
case IRLAN_LAP_DISCONNECT:
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
break;
default:
IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
/*
* Function irlan_client_state_info (self, event, skb, info)
*
* INFO, We have issued a GetInfo command and is awaiting a reply.
*/
static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
switch (event) {
case IRLAN_DATA_INDICATION:
IRDA_ASSERT(skb != NULL, return -1;);
irlan_client_parse_response(self, skb);
irlan_next_client_state(self, IRLAN_MEDIA);
irlan_get_media_char(self);
break;
case IRLAN_LMP_DISCONNECT:
case IRLAN_LAP_DISCONNECT:
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
break;
default:
IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
/*
* Function irlan_client_state_media (self, event, skb, info)
*
* MEDIA, The irlan_client has issued a GetMedia command and is awaiting a
* reply.
*
*/
static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
case IRLAN_DATA_INDICATION:
irlan_client_parse_response(self, skb);
irlan_open_data_channel(self);
irlan_next_client_state(self, IRLAN_OPEN);
break;
case IRLAN_LMP_DISCONNECT:
case IRLAN_LAP_DISCONNECT:
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
break;
default:
IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
/*
* Function irlan_client_state_open (self, event, skb, info)
*
* OPEN, The irlan_client has issued a OpenData command and is awaiting a
* reply
*
*/
static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
struct qos_info qos;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
case IRLAN_DATA_INDICATION:
irlan_client_parse_response(self, skb);
/*
* Check if we have got the remote TSAP for data
* communications
*/
IRDA_ASSERT(self->dtsap_sel_data != 0, return -1;);
/* Check which access type we are dealing with */
switch (self->client.access_type) {
case ACCESS_PEER:
if (self->provider.state == IRLAN_OPEN) {
irlan_next_client_state(self, IRLAN_ARB);
irlan_do_client_event(self, IRLAN_CHECK_CON_ARB,
NULL);
} else {
irlan_next_client_state(self, IRLAN_WAIT);
}
break;
case ACCESS_DIRECT:
case ACCESS_HOSTED:
qos.link_disc_time.bits = 0x01; /* 3 secs */
irttp_connect_request(self->tsap_data,
self->dtsap_sel_data,
self->saddr, self->daddr, &qos,
IRLAN_MTU, NULL);
irlan_next_client_state(self, IRLAN_DATA);
break;
default:
IRDA_DEBUG(2, "%s(), unknown access type!\n", __FUNCTION__ );
break;
}
break;
case IRLAN_LMP_DISCONNECT:
case IRLAN_LAP_DISCONNECT:
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
break;
default:
IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
/*
* Function irlan_client_state_wait (self, event, skb, info)
*
* WAIT, The irlan_client is waiting for the local provider to enter the
* provider OPEN state.
*
*/
static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
case IRLAN_PROVIDER_SIGNAL:
irlan_next_client_state(self, IRLAN_ARB);
irlan_do_client_event(self, IRLAN_CHECK_CON_ARB, NULL);
break;
case IRLAN_LMP_DISCONNECT:
case IRLAN_LAP_DISCONNECT:
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
break;
default:
IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
struct qos_info qos;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
case IRLAN_CHECK_CON_ARB:
if (self->client.recv_arb_val == self->provider.send_arb_val) {
irlan_next_client_state(self, IRLAN_CLOSE);
irlan_close_data_channel(self);
} else if (self->client.recv_arb_val <
self->provider.send_arb_val)
{
qos.link_disc_time.bits = 0x01; /* 3 secs */
irlan_next_client_state(self, IRLAN_DATA);
irttp_connect_request(self->tsap_data,
self->dtsap_sel_data,
self->saddr, self->daddr, &qos,
IRLAN_MTU, NULL);
} else if (self->client.recv_arb_val >
self->provider.send_arb_val)
{
IRDA_DEBUG(2, "%s(), lost the battle :-(\n", __FUNCTION__ );
}
break;
case IRLAN_DATA_CONNECT_INDICATION:
irlan_next_client_state(self, IRLAN_DATA);
break;
case IRLAN_LMP_DISCONNECT:
case IRLAN_LAP_DISCONNECT:
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __FUNCTION__ );
break;
default:
IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
/*
* Function irlan_client_state_data (self, event, skb, info)
*
* DATA, The data channel is connected, allowing data transfers between
* the local and remote machines.
*
*/
static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
switch(event) {
case IRLAN_DATA_INDICATION:
irlan_client_parse_response(self, skb);
break;
case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
case IRLAN_LAP_DISCONNECT:
irlan_next_client_state(self, IRLAN_IDLE);
break;
default:
IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
/*
* Function irlan_client_state_close (self, event, skb, info)
*
*
*
*/
static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
if (skb)
dev_kfree_skb(skb);
return 0;
}
/*
* Function irlan_client_state_sync (self, event, skb, info)
*
*
*
*/
static int irlan_client_state_sync(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
if (skb)
dev_kfree_skb(skb);
return 0;
}

File diff suppressed because it is too large Load Diff

387
net/irda/irlan/irlan_eth.c Normal file
View File

@@ -0,0 +1,387 @@
/*********************************************************************
*
* Filename: irlan_eth.c
* Version:
* Description:
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Thu Oct 15 08:37:58 1998
* Modified at: Tue Mar 21 09:06:41 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
* slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
* Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
*
* Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
*
* 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <linux/config.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/inetdevice.h>
#include <linux/if_arp.h>
#include <linux/module.h>
#include <net/arp.h>
#include <net/irda/irda.h>
#include <net/irda/irmod.h>
#include <net/irda/irlan_common.h>
#include <net/irda/irlan_client.h>
#include <net/irda/irlan_event.h>
#include <net/irda/irlan_eth.h>
static int irlan_eth_open(struct net_device *dev);
static int irlan_eth_close(struct net_device *dev);
static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev);
static void irlan_eth_set_multicast_list( struct net_device *dev);
static struct net_device_stats *irlan_eth_get_stats(struct net_device *dev);
/*
* Function irlan_eth_setup (dev)
*
* The network device initialization function.
*
*/
static void irlan_eth_setup(struct net_device *dev)
{
dev->open = irlan_eth_open;
dev->stop = irlan_eth_close;
dev->hard_start_xmit = irlan_eth_xmit;
dev->get_stats = irlan_eth_get_stats;
dev->set_multicast_list = irlan_eth_set_multicast_list;
dev->destructor = free_netdev;
SET_MODULE_OWNER(dev);
ether_setup(dev);
/*
* Lets do all queueing in IrTTP instead of this device driver.
* Queueing here as well can introduce some strange latency
* problems, which we will avoid by setting the queue size to 0.
*/
/*
* The bugs in IrTTP and IrLAN that created this latency issue
* have now been fixed, and we can propagate flow control properly
* to the network layer. However, this requires a minimal queue of
* packets for the device.
* Without flow control, the Tx Queue is 14 (ttp) + 0 (dev) = 14
* With flow control, the Tx Queue is 7 (ttp) + 4 (dev) = 11
* See irlan_eth_flow_indication()...
* Note : this number was randomly selected and would need to
* be adjusted.
* Jean II */
dev->tx_queue_len = 4;
}
/*
* Function alloc_irlandev
*
* Allocate network device and control block
*
*/
struct net_device *alloc_irlandev(const char *name)
{
return alloc_netdev(sizeof(struct irlan_cb), name,
irlan_eth_setup);
}
/*
* Function irlan_eth_open (dev)
*
* Network device has been opened by user
*
*/
static int irlan_eth_open(struct net_device *dev)
{
struct irlan_cb *self = netdev_priv(dev);
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
/* Ready to play! */
netif_stop_queue(dev); /* Wait until data link is ready */
/* We are now open, so time to do some work */
self->disconnect_reason = 0;
irlan_client_wakeup(self, self->saddr, self->daddr);
/* Make sure we have a hardware address before we return,
so DHCP clients gets happy */
return wait_event_interruptible(self->open_wait,
!self->tsap_data->connected);
}
/*
* Function irlan_eth_close (dev)
*
* Stop the ether network device, his function will usually be called by
* ifconfig down. We should now disconnect the link, We start the
* close timer, so that the instance will be removed if we are unable
* to discover the remote device after the disconnect.
*/
static int irlan_eth_close(struct net_device *dev)
{
struct irlan_cb *self = netdev_priv(dev);
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
/* Stop device */
netif_stop_queue(dev);
irlan_close_data_channel(self);
irlan_close_tsaps(self);
irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
/* Remove frames queued on the control channel */
skb_queue_purge(&self->client.txq);
self->client.tx_busy = 0;
return 0;
}
/*
* Function irlan_eth_tx (skb)
*
* Transmits ethernet frames over IrDA link.
*
*/
static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct irlan_cb *self = netdev_priv(dev);
int ret;
/* skb headroom large enough to contain all IrDA-headers? */
if ((skb_headroom(skb) < self->max_header_size) || (skb_shared(skb))) {
struct sk_buff *new_skb =
skb_realloc_headroom(skb, self->max_header_size);
/* We have to free the original skb anyway */
dev_kfree_skb(skb);
/* Did the realloc succeed? */
if (new_skb == NULL)
return 0;
/* Use the new skb instead */
skb = new_skb;
}
dev->trans_start = jiffies;
/* Now queue the packet in the transport layer */
if (self->use_udata)
ret = irttp_udata_request(self->tsap_data, skb);
else
ret = irttp_data_request(self->tsap_data, skb);
if (ret < 0) {
/*
* IrTTPs tx queue is full, so we just have to
* drop the frame! You might think that we should
* just return -1 and don't deallocate the frame,
* but that is dangerous since it's possible that
* we have replaced the original skb with a new
* one with larger headroom, and that would really
* confuse do_dev_queue_xmit() in dev.c! I have
* tried :-) DB
*/
/* irttp_data_request already free the packet */
self->stats.tx_dropped++;
} else {
self->stats.tx_packets++;
self->stats.tx_bytes += skb->len;
}
return 0;
}
/*
* Function irlan_eth_receive (handle, skb)
*
* This function gets the data that is received on the data channel
*
*/
int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb)
{
struct irlan_cb *self = instance;
if (skb == NULL) {
++self->stats.rx_dropped;
return 0;
}
if (skb->len < ETH_HLEN) {
IRDA_DEBUG(0, "%s() : IrLAN frame too short (%d)\n",
__FUNCTION__, skb->len);
++self->stats.rx_dropped;
dev_kfree_skb(skb);
return 0;
}
/*
* Adopt this frame! Important to set all these fields since they
* might have been previously set by the low level IrDA network
* device driver
*/
skb->dev = self->dev;
skb->protocol=eth_type_trans(skb, skb->dev); /* Remove eth header */
self->stats.rx_packets++;
self->stats.rx_bytes += skb->len;
netif_rx(skb); /* Eat it! */
return 0;
}
/*
* Function irlan_eth_flow (status)
*
* Do flow control between IP/Ethernet and IrLAN/IrTTP. This is done by
* controlling the queue stop/start.
*
* The IrDA link layer has the advantage to have flow control, and
* IrTTP now properly handles that. Flow controlling the higher layers
* prevent us to drop Tx packets in here (up to 15% for a TCP socket,
* more for UDP socket).
* Also, this allow us to reduce the overall transmit queue, which means
* less latency in case of mixed traffic.
* Jean II
*/
void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
{
struct irlan_cb *self;
struct net_device *dev;
self = (struct irlan_cb *) instance;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
dev = self->dev;
IRDA_ASSERT(dev != NULL, return;);
IRDA_DEBUG(0, "%s() : flow %s ; running %d\n", __FUNCTION__,
flow == FLOW_STOP ? "FLOW_STOP" : "FLOW_START",
netif_running(dev));
switch (flow) {
case FLOW_STOP:
/* IrTTP is full, stop higher layers */
netif_stop_queue(dev);
break;
case FLOW_START:
default:
/* Tell upper layers that its time to transmit frames again */
/* Schedule network layer */
netif_wake_queue(dev);
break;
}
}
/*
* Function irlan_etc_send_gratuitous_arp (dev)
*
* Send gratuitous ARP to announce that we have changed
* hardware address, so that all peers updates their ARP tables
*/
void irlan_eth_send_gratuitous_arp(struct net_device *dev)
{
struct in_device *in_dev;
/*
* When we get a new MAC address do a gratuitous ARP. This
* is useful if we have changed access points on the same
* subnet.
*/
#ifdef CONFIG_INET
IRDA_DEBUG(4, "IrLAN: Sending gratuitous ARP\n");
rcu_read_lock();
in_dev = __in_dev_get(dev);
if (in_dev == NULL)
goto out;
if (in_dev->ifa_list)
arp_send(ARPOP_REQUEST, ETH_P_ARP,
in_dev->ifa_list->ifa_address,
dev,
in_dev->ifa_list->ifa_address,
NULL, dev->dev_addr, NULL);
out:
rcu_read_unlock();
#endif /* CONFIG_INET */
}
/*
* Function set_multicast_list (dev)
*
* Configure the filtering of the device
*
*/
#define HW_MAX_ADDRS 4 /* Must query to get it! */
static void irlan_eth_set_multicast_list(struct net_device *dev)
{
struct irlan_cb *self = netdev_priv(dev);
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
/* Check if data channel has been connected yet */
if (self->client.state != IRLAN_DATA) {
IRDA_DEBUG(1, "%s(), delaying!\n", __FUNCTION__ );
return;
}
if (dev->flags & IFF_PROMISC) {
/* Enable promiscuous mode */
IRDA_WARNING("Promiscous mode not implemented by IrLAN!\n");
}
else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > HW_MAX_ADDRS) {
/* Disable promiscuous mode, use normal mode. */
IRDA_DEBUG(4, "%s(), Setting multicast filter\n", __FUNCTION__ );
/* hardware_set_filter(NULL); */
irlan_set_multicast_filter(self, TRUE);
}
else if (dev->mc_count) {
IRDA_DEBUG(4, "%s(), Setting multicast filter\n", __FUNCTION__ );
/* Walk the address list, and load the filter */
/* hardware_set_filter(dev->mc_list); */
irlan_set_multicast_filter(self, TRUE);
}
else {
IRDA_DEBUG(4, "%s(), Clearing multicast filter\n", __FUNCTION__ );
irlan_set_multicast_filter(self, FALSE);
}
if (dev->flags & IFF_BROADCAST)
irlan_set_broadcast_filter(self, TRUE);
else
irlan_set_broadcast_filter(self, FALSE);
}
/*
* Function irlan_get_stats (dev)
*
* Get the current statistics for this device
*
*/
static struct net_device_stats *irlan_eth_get_stats(struct net_device *dev)
{
struct irlan_cb *self = netdev_priv(dev);
return &self->stats;
}

View File

@@ -0,0 +1,60 @@
/*********************************************************************
*
* Filename: irlan_event.c
* Version:
* Description:
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Oct 20 09:10:16 1998
* Modified at: Sat Oct 30 12:59:01 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
*
* 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <net/irda/irlan_event.h>
char *irlan_state[] = {
"IRLAN_IDLE",
"IRLAN_QUERY",
"IRLAN_CONN",
"IRLAN_INFO",
"IRLAN_MEDIA",
"IRLAN_OPEN",
"IRLAN_WAIT",
"IRLAN_ARB",
"IRLAN_DATA",
"IRLAN_CLOSE",
"IRLAN_SYNC",
};
void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state)
{
IRDA_DEBUG(2, "%s(), %s\n", __FUNCTION__ , irlan_state[state]);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
self->client.state = state;
}
void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state)
{
IRDA_DEBUG(2, "%s(), %s\n", __FUNCTION__ , irlan_state[state]);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
self->provider.state = state;
}

View File

@@ -0,0 +1,246 @@
/*********************************************************************
*
* Filename: irlan_filter.c
* Version:
* Description:
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Fri Jan 29 11:16:38 1999
* Modified at: Sat Oct 30 12:58:45 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
*
* 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <linux/skbuff.h>
#include <linux/random.h>
#include <linux/seq_file.h>
#include <net/irda/irlan_common.h>
/*
* Function irlan_filter_request (self, skb)
*
* Handle filter request from client peer device
*
*/
void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
if ((self->provider.filter_type == IRLAN_DIRECTED) &&
(self->provider.filter_operation == DYNAMIC))
{
IRDA_DEBUG(0, "Giving peer a dynamic Ethernet address\n");
self->provider.mac_address[0] = 0x40;
self->provider.mac_address[1] = 0x00;
self->provider.mac_address[2] = 0x00;
self->provider.mac_address[3] = 0x00;
/* Use arbitration value to generate MAC address */
if (self->provider.access_type == ACCESS_PEER) {
self->provider.mac_address[4] =
self->provider.send_arb_val & 0xff;
self->provider.mac_address[5] =
(self->provider.send_arb_val >> 8) & 0xff;
} else {
/* Just generate something for now */
get_random_bytes(self->provider.mac_address+4, 1);
get_random_bytes(self->provider.mac_address+5, 1);
}
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x03;
irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
irlan_insert_short_param(skb, "MAX_ENTRY", 0x0001);
irlan_insert_array_param(skb, "FILTER_ENTRY",
self->provider.mac_address, 6);
return;
}
if ((self->provider.filter_type == IRLAN_DIRECTED) &&
(self->provider.filter_mode == FILTER))
{
IRDA_DEBUG(0, "Directed filter on\n");
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x00;
return;
}
if ((self->provider.filter_type == IRLAN_DIRECTED) &&
(self->provider.filter_mode == NONE))
{
IRDA_DEBUG(0, "Directed filter off\n");
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x00;
return;
}
if ((self->provider.filter_type == IRLAN_BROADCAST) &&
(self->provider.filter_mode == FILTER))
{
IRDA_DEBUG(0, "Broadcast filter on\n");
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x00;
return;
}
if ((self->provider.filter_type == IRLAN_BROADCAST) &&
(self->provider.filter_mode == NONE))
{
IRDA_DEBUG(0, "Broadcast filter off\n");
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x00;
return;
}
if ((self->provider.filter_type == IRLAN_MULTICAST) &&
(self->provider.filter_mode == FILTER))
{
IRDA_DEBUG(0, "Multicast filter on\n");
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x00;
return;
}
if ((self->provider.filter_type == IRLAN_MULTICAST) &&
(self->provider.filter_mode == NONE))
{
IRDA_DEBUG(0, "Multicast filter off\n");
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x00;
return;
}
if ((self->provider.filter_type == IRLAN_MULTICAST) &&
(self->provider.filter_operation == GET))
{
IRDA_DEBUG(0, "Multicast filter get\n");
skb->data[0] = 0x00; /* Success? */
skb->data[1] = 0x02;
irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
irlan_insert_short_param(skb, "MAX_ENTRY", 16);
return;
}
skb->data[0] = 0x00; /* Command not supported */
skb->data[1] = 0x00;
IRDA_DEBUG(0, "Not implemented!\n");
}
/*
* Function check_request_param (self, param, value)
*
* Check parameters in request from peer device
*
*/
void irlan_check_command_param(struct irlan_cb *self, char *param, char *value)
{
__u8 *bytes;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
bytes = value;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
IRDA_DEBUG(4, "%s, %s\n", param, value);
/*
* This is experimental!! DB.
*/
if (strcmp(param, "MODE") == 0) {
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
self->use_udata = TRUE;
return;
}
/*
* FILTER_TYPE
*/
if (strcmp(param, "FILTER_TYPE") == 0) {
if (strcmp(value, "DIRECTED") == 0) {
self->provider.filter_type = IRLAN_DIRECTED;
return;
}
if (strcmp(value, "MULTICAST") == 0) {
self->provider.filter_type = IRLAN_MULTICAST;
return;
}
if (strcmp(value, "BROADCAST") == 0) {
self->provider.filter_type = IRLAN_BROADCAST;
return;
}
}
/*
* FILTER_MODE
*/
if (strcmp(param, "FILTER_MODE") == 0) {
if (strcmp(value, "ALL") == 0) {
self->provider.filter_mode = ALL;
return;
}
if (strcmp(value, "FILTER") == 0) {
self->provider.filter_mode = FILTER;
return;
}
if (strcmp(value, "NONE") == 0) {
self->provider.filter_mode = FILTER;
return;
}
}
/*
* FILTER_OPERATION
*/
if (strcmp(param, "FILTER_OPERATION") == 0) {
if (strcmp(value, "DYNAMIC") == 0) {
self->provider.filter_operation = DYNAMIC;
return;
}
if (strcmp(value, "GET") == 0) {
self->provider.filter_operation = GET;
return;
}
}
}
/*
* Function irlan_print_filter (filter_type, buf)
*
* Print status of filter. Used by /proc file system
*
*/
#ifdef CONFIG_PROC_FS
#define MASK2STR(m,s) { .mask = m, .str = s }
void irlan_print_filter(struct seq_file *seq, int filter_type)
{
static struct {
int mask;
const char *str;
} filter_mask2str[] = {
MASK2STR(IRLAN_DIRECTED, "DIRECTED"),
MASK2STR(IRLAN_FUNCTIONAL, "FUNCTIONAL"),
MASK2STR(IRLAN_GROUP, "GROUP"),
MASK2STR(IRLAN_MAC_FRAME, "MAC_FRAME"),
MASK2STR(IRLAN_MULTICAST, "MULTICAST"),
MASK2STR(IRLAN_BROADCAST, "BROADCAST"),
MASK2STR(IRLAN_IPX_SOCKET, "IPX_SOCKET"),
MASK2STR(0, NULL)
}, *p;
for (p = filter_mask2str; p->str; p++) {
if (filter_type & p->mask)
seq_printf(seq, "%s ", p->str);
}
seq_putc(seq, '\n');
}
#undef MASK2STR
#endif

View File

@@ -0,0 +1,413 @@
/*********************************************************************
*
* Filename: irlan_provider.c
* Version: 0.9
* Description: IrDA LAN Access Protocol Implementation
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:37 1997
* Modified at: Sat Oct 30 12:52:10 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
* slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
* Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
*
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
* All Rights Reserved.
*
* 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/random.h>
#include <linux/bitops.h>
#include <asm/system.h>
#include <asm/byteorder.h>
#include <net/irda/irda.h>
#include <net/irda/irttp.h>
#include <net/irda/irlmp.h>
#include <net/irda/irias_object.h>
#include <net/irda/iriap.h>
#include <net/irda/timer.h>
#include <net/irda/irlan_common.h>
#include <net/irda/irlan_eth.h>
#include <net/irda/irlan_event.h>
#include <net/irda/irlan_provider.h>
#include <net/irda/irlan_filter.h>
#include <net/irda/irlan_client.h>
static void irlan_provider_connect_indication(void *instance, void *sap,
struct qos_info *qos,
__u32 max_sdu_size,
__u8 max_header_size,
struct sk_buff *skb);
/*
* Function irlan_provider_control_data_indication (handle, skb)
*
* This function gets the data that is received on the control channel
*
*/
static int irlan_provider_data_indication(void *instance, void *sap,
struct sk_buff *skb)
{
struct irlan_cb *self;
__u8 code;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
self = (struct irlan_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;);
code = skb->data[0];
switch(code) {
case CMD_GET_PROVIDER_INFO:
IRDA_DEBUG(4, "Got GET_PROVIDER_INFO command!\n");
irlan_do_provider_event(self, IRLAN_GET_INFO_CMD, skb);
break;
case CMD_GET_MEDIA_CHAR:
IRDA_DEBUG(4, "Got GET_MEDIA_CHAR command!\n");
irlan_do_provider_event(self, IRLAN_GET_MEDIA_CMD, skb);
break;
case CMD_OPEN_DATA_CHANNEL:
IRDA_DEBUG(4, "Got OPEN_DATA_CHANNEL command!\n");
irlan_do_provider_event(self, IRLAN_OPEN_DATA_CMD, skb);
break;
case CMD_FILTER_OPERATION:
IRDA_DEBUG(4, "Got FILTER_OPERATION command!\n");
irlan_do_provider_event(self, IRLAN_FILTER_CONFIG_CMD, skb);
break;
case CMD_RECONNECT_DATA_CHAN:
IRDA_DEBUG(2, "%s(), Got RECONNECT_DATA_CHAN command\n", __FUNCTION__ );
IRDA_DEBUG(2, "%s(), NOT IMPLEMENTED\n", __FUNCTION__ );
break;
case CMD_CLOSE_DATA_CHAN:
IRDA_DEBUG(2, "Got CLOSE_DATA_CHAN command!\n");
IRDA_DEBUG(2, "%s(), NOT IMPLEMENTED\n", __FUNCTION__ );
break;
default:
IRDA_DEBUG(2, "%s(), Unknown command!\n", __FUNCTION__ );
break;
}
return 0;
}
/*
* Function irlan_provider_connect_indication (handle, skb, priv)
*
* Got connection from peer IrLAN client
*
*/
static void irlan_provider_connect_indication(void *instance, void *sap,
struct qos_info *qos,
__u32 max_sdu_size,
__u8 max_header_size,
struct sk_buff *skb)
{
struct irlan_cb *self;
struct tsap_cb *tsap;
__u32 saddr, daddr;
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
self = (struct irlan_cb *) instance;
tsap = (struct tsap_cb *) sap;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
IRDA_ASSERT(tsap == self->provider.tsap_ctrl,return;);
IRDA_ASSERT(self->provider.state == IRLAN_IDLE, return;);
daddr = irttp_get_daddr(tsap);
saddr = irttp_get_saddr(tsap);
self->provider.max_sdu_size = max_sdu_size;
self->provider.max_header_size = max_header_size;
irlan_do_provider_event(self, IRLAN_CONNECT_INDICATION, NULL);
/*
* If we are in peer mode, the client may not have got the discovery
* indication it needs to make progress. If the client is still in
* IDLE state, we must kick it.
*/
if ((self->provider.access_type == ACCESS_PEER) &&
(self->client.state == IRLAN_IDLE))
{
irlan_client_wakeup(self, self->saddr, self->daddr);
}
}
/*
* Function irlan_provider_connect_response (handle)
*
* Accept incoming connection
*
*/
void irlan_provider_connect_response(struct irlan_cb *self,
struct tsap_cb *tsap)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
/* Just accept */
irttp_connect_response(tsap, IRLAN_MTU, NULL);
}
static void irlan_provider_disconnect_indication(void *instance, void *sap,
LM_REASON reason,
struct sk_buff *userdata)
{
struct irlan_cb *self;
struct tsap_cb *tsap;
IRDA_DEBUG(4, "%s(), reason=%d\n", __FUNCTION__ , reason);
self = (struct irlan_cb *) instance;
tsap = (struct tsap_cb *) sap;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
IRDA_ASSERT(tsap != NULL, return;);
IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;);
IRDA_ASSERT(tsap == self->provider.tsap_ctrl, return;);
irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
}
/*
* Function irlan_parse_open_data_cmd (self, skb)
*
*
*
*/
int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb)
{
int ret;
ret = irlan_provider_parse_command(self, CMD_OPEN_DATA_CHANNEL, skb);
/* Open data channel */
irlan_open_data_tsap(self);
return ret;
}
/*
* Function parse_command (skb)
*
* Extract all parameters from received buffer, then feed them to
* check_params for parsing
*
*/
int irlan_provider_parse_command(struct irlan_cb *self, int cmd,
struct sk_buff *skb)
{
__u8 *frame;
__u8 *ptr;
int count;
__u16 val_len;
int i;
char *name;
char *value;
int ret = RSP_SUCCESS;
IRDA_ASSERT(skb != NULL, return -RSP_PROTOCOL_ERROR;);
IRDA_DEBUG(4, "%s(), skb->len=%d\n", __FUNCTION__ , (int)skb->len);
IRDA_ASSERT(self != NULL, return -RSP_PROTOCOL_ERROR;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -RSP_PROTOCOL_ERROR;);
if (!skb)
return -RSP_PROTOCOL_ERROR;
frame = skb->data;
name = kmalloc(255, GFP_ATOMIC);
if (!name)
return -RSP_INSUFFICIENT_RESOURCES;
value = kmalloc(1016, GFP_ATOMIC);
if (!value) {
kfree(name);
return -RSP_INSUFFICIENT_RESOURCES;
}
/* How many parameters? */
count = frame[1];
IRDA_DEBUG(4, "Got %d parameters\n", count);
ptr = frame+2;
/* For all parameters */
for (i=0; i<count;i++) {
ret = irlan_extract_param(ptr, name, value, &val_len);
if (ret < 0) {
IRDA_DEBUG(2, "%s(), IrLAN, Error!\n", __FUNCTION__ );
break;
}
ptr+=ret;
ret = RSP_SUCCESS;
irlan_check_command_param(self, name, value);
}
/* Cleanup */
kfree(name);
kfree(value);
return ret;
}
/*
* Function irlan_provider_send_reply (self, info)
*
* Send reply to query to peer IrLAN layer
*
*/
void irlan_provider_send_reply(struct irlan_cb *self, int command,
int ret_code)
{
struct sk_buff *skb;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
skb = dev_alloc_skb(128);
if (!skb)
return;
/* Reserve space for TTP, LMP, and LAP header */
skb_reserve(skb, self->provider.max_header_size);
skb_put(skb, 2);
switch (command) {
case CMD_GET_PROVIDER_INFO:
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x02; /* 2 parameters */
switch (self->media) {
case MEDIA_802_3:
irlan_insert_string_param(skb, "MEDIA", "802.3");
break;
case MEDIA_802_5:
irlan_insert_string_param(skb, "MEDIA", "802.5");
break;
default:
IRDA_DEBUG(2, "%s(), unknown media type!\n", __FUNCTION__ );
break;
}
irlan_insert_short_param(skb, "IRLAN_VER", 0x0101);
break;
case CMD_GET_MEDIA_CHAR:
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x05; /* 5 parameters */
irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED");
irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST");
irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST");
switch (self->provider.access_type) {
case ACCESS_DIRECT:
irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT");
break;
case ACCESS_PEER:
irlan_insert_string_param(skb, "ACCESS_TYPE", "PEER");
break;
case ACCESS_HOSTED:
irlan_insert_string_param(skb, "ACCESS_TYPE", "HOSTED");
break;
default:
IRDA_DEBUG(2, "%s(), Unknown access type\n", __FUNCTION__ );
break;
}
irlan_insert_short_param(skb, "MAX_FRAME", 0x05ee);
break;
case CMD_OPEN_DATA_CHANNEL:
skb->data[0] = 0x00; /* Success */
if (self->provider.send_arb_val) {
skb->data[1] = 0x03; /* 3 parameters */
irlan_insert_short_param(skb, "CON_ARB",
self->provider.send_arb_val);
} else
skb->data[1] = 0x02; /* 2 parameters */
irlan_insert_byte_param(skb, "DATA_CHAN", self->stsap_sel_data);
irlan_insert_array_param(skb, "RECONNECT_KEY", "LINUX RULES!",
12);
break;
case CMD_FILTER_OPERATION:
irlan_filter_request(self, skb);
break;
default:
IRDA_DEBUG(2, "%s(), Unknown command!\n", __FUNCTION__ );
break;
}
irttp_data_request(self->provider.tsap_ctrl, skb);
}
/*
* Function irlan_provider_register(void)
*
* Register provider support so we can accept incoming connections.
*
*/
int irlan_provider_open_ctrl_tsap(struct irlan_cb *self)
{
struct tsap_cb *tsap;
notify_t notify;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
/* Check if already open */
if (self->provider.tsap_ctrl)
return -1;
/*
* First register well known control TSAP
*/
irda_notify_init(&notify);
notify.data_indication = irlan_provider_data_indication;
notify.connect_indication = irlan_provider_connect_indication;
notify.disconnect_indication = irlan_provider_disconnect_indication;
notify.instance = self;
strlcpy(notify.name, "IrLAN ctrl (p)", sizeof(notify.name));
tsap = irttp_open_tsap(LSAP_ANY, 1, &notify);
if (!tsap) {
IRDA_DEBUG(2, "%s(), Got no tsap!\n", __FUNCTION__ );
return -1;
}
self->provider.tsap_ctrl = tsap;
/* Register with LM-IAS */
irlan_ias_register(self, tsap->stsap_sel);
return 0;
}

View File

@@ -0,0 +1,241 @@
/*********************************************************************
*
* Filename: irlan_provider_event.c
* Version: 0.9
* Description: IrLAN provider state machine)
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:37 1997
* Modified at: Sat Oct 30 12:52:41 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, All Rights Reserved.
*
* 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <net/irda/irda.h>
#include <net/irda/iriap.h>
#include <net/irda/irlmp.h>
#include <net/irda/irttp.h>
#include <net/irda/irlan_provider.h>
#include <net/irda/irlan_event.h>
static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb);
static int (*state[])(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb) =
{
irlan_provider_state_idle,
NULL, /* Query */
NULL, /* Info */
irlan_provider_state_info,
NULL, /* Media */
irlan_provider_state_open,
NULL, /* Wait */
NULL, /* Arb */
irlan_provider_state_data,
NULL, /* Close */
NULL, /* Sync */
};
void irlan_do_provider_event(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_ASSERT(*state[ self->provider.state] != NULL, return;);
(*state[self->provider.state]) (self, event, skb);
}
/*
* Function irlan_provider_state_idle (event, skb, info)
*
* IDLE, We are waiting for an indication that there is a provider
* available.
*/
static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
case IRLAN_CONNECT_INDICATION:
irlan_provider_connect_response( self, self->provider.tsap_ctrl);
irlan_next_provider_state( self, IRLAN_INFO);
break;
default:
IRDA_DEBUG(4, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
/*
* Function irlan_provider_state_info (self, event, skb, info)
*
* INFO, We have issued a GetInfo command and is awaiting a reply.
*/
static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
int ret;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
case IRLAN_GET_INFO_CMD:
/* Be sure to use 802.3 in case of peer mode */
if (self->provider.access_type == ACCESS_PEER) {
self->media = MEDIA_802_3;
/* Check if client has started yet */
if (self->client.state == IRLAN_IDLE) {
/* This should get the client going */
irlmp_discovery_request(8);
}
}
irlan_provider_send_reply(self, CMD_GET_PROVIDER_INFO,
RSP_SUCCESS);
/* Keep state */
break;
case IRLAN_GET_MEDIA_CMD:
irlan_provider_send_reply(self, CMD_GET_MEDIA_CHAR,
RSP_SUCCESS);
/* Keep state */
break;
case IRLAN_OPEN_DATA_CMD:
ret = irlan_parse_open_data_cmd(self, skb);
if (self->provider.access_type == ACCESS_PEER) {
/* FIXME: make use of random functions! */
self->provider.send_arb_val = (jiffies & 0xffff);
}
irlan_provider_send_reply(self, CMD_OPEN_DATA_CHANNEL, ret);
if (ret == RSP_SUCCESS) {
irlan_next_provider_state(self, IRLAN_OPEN);
/* Signal client that we are now open */
irlan_do_client_event(self, IRLAN_PROVIDER_SIGNAL, NULL);
}
break;
case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
case IRLAN_LAP_DISCONNECT:
irlan_next_provider_state(self, IRLAN_IDLE);
break;
default:
IRDA_DEBUG( 0, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
/*
* Function irlan_provider_state_open (self, event, skb, info)
*
* OPEN, The client has issued a OpenData command and is awaiting a
* reply
*
*/
static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
case IRLAN_FILTER_CONFIG_CMD:
irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb);
irlan_provider_send_reply(self, CMD_FILTER_OPERATION,
RSP_SUCCESS);
/* Keep state */
break;
case IRLAN_DATA_CONNECT_INDICATION:
irlan_next_provider_state(self, IRLAN_DATA);
irlan_provider_connect_response(self, self->tsap_data);
break;
case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
case IRLAN_LAP_DISCONNECT:
irlan_next_provider_state(self, IRLAN_IDLE);
break;
default:
IRDA_DEBUG(2, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}
/*
* Function irlan_provider_state_data (self, event, skb, info)
*
* DATA, The data channel is connected, allowing data transfers between
* the local and remote machines.
*
*/
static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
switch(event) {
case IRLAN_FILTER_CONFIG_CMD:
irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb);
irlan_provider_send_reply(self, CMD_FILTER_OPERATION,
RSP_SUCCESS);
break;
case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */
case IRLAN_LAP_DISCONNECT:
irlan_next_provider_state(self, IRLAN_IDLE);
break;
default:
IRDA_DEBUG( 0, "%s(), Unknown event %d\n", __FUNCTION__ , event);
break;
}
if (skb)
dev_kfree_skb(skb);
return 0;
}

1258
net/irda/irlap.c Normal file

File diff suppressed because it is too large Load Diff

2334
net/irda/irlap_event.c Normal file

File diff suppressed because it is too large Load Diff

1437
net/irda/irlap_frame.c Normal file

File diff suppressed because it is too large Load Diff

2041
net/irda/irlmp.c Normal file

File diff suppressed because it is too large Load Diff

912
net/irda/irlmp_event.c Normal file
View File

@@ -0,0 +1,912 @@
/*********************************************************************
*
* Filename: irlmp_event.c
* Version: 0.8
* Description: An IrDA LMP event driver for Linux
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Aug 4 20:40:53 1997
* Modified at: Tue Dec 14 23:04:16 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998-1999 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <linux/config.h>
#include <linux/kernel.h>
#include <net/irda/irda.h>
#include <net/irda/timer.h>
#include <net/irda/irlap.h>
#include <net/irda/irlmp.h>
#include <net/irda/irlmp_frame.h>
#include <net/irda/irlmp_event.h>
const char *irlmp_state[] = {
"LAP_STANDBY",
"LAP_U_CONNECT",
"LAP_ACTIVE",
};
const char *irlsap_state[] = {
"LSAP_DISCONNECTED",
"LSAP_CONNECT",
"LSAP_CONNECT_PEND",
"LSAP_DATA_TRANSFER_READY",
"LSAP_SETUP",
"LSAP_SETUP_PEND",
};
#ifdef CONFIG_IRDA_DEBUG
static const char *irlmp_event[] = {
"LM_CONNECT_REQUEST",
"LM_CONNECT_CONFIRM",
"LM_CONNECT_RESPONSE",
"LM_CONNECT_INDICATION",
"LM_DISCONNECT_INDICATION",
"LM_DISCONNECT_REQUEST",
"LM_DATA_REQUEST",
"LM_UDATA_REQUEST",
"LM_DATA_INDICATION",
"LM_UDATA_INDICATION",
"LM_WATCHDOG_TIMEOUT",
/* IrLAP events */
"LM_LAP_CONNECT_REQUEST",
"LM_LAP_CONNECT_INDICATION",
"LM_LAP_CONNECT_CONFIRM",
"LM_LAP_DISCONNECT_INDICATION",
"LM_LAP_DISCONNECT_REQUEST",
"LM_LAP_DISCOVERY_REQUEST",
"LM_LAP_DISCOVERY_CONFIRM",
"LM_LAP_IDLE_TIMEOUT",
};
#endif /* CONFIG_IRDA_DEBUG */
/* LAP Connection control proto declarations */
static void irlmp_state_standby (struct lap_cb *, IRLMP_EVENT,
struct sk_buff *);
static void irlmp_state_u_connect(struct lap_cb *, IRLMP_EVENT,
struct sk_buff *);
static void irlmp_state_active (struct lap_cb *, IRLMP_EVENT,
struct sk_buff *);
/* LSAP Connection control proto declarations */
static int irlmp_state_disconnected(struct lsap_cb *, IRLMP_EVENT,
struct sk_buff *);
static int irlmp_state_connect (struct lsap_cb *, IRLMP_EVENT,
struct sk_buff *);
static int irlmp_state_connect_pend(struct lsap_cb *, IRLMP_EVENT,
struct sk_buff *);
static int irlmp_state_dtr (struct lsap_cb *, IRLMP_EVENT,
struct sk_buff *);
static int irlmp_state_setup (struct lsap_cb *, IRLMP_EVENT,
struct sk_buff *);
static int irlmp_state_setup_pend (struct lsap_cb *, IRLMP_EVENT,
struct sk_buff *);
static void (*lap_state[]) (struct lap_cb *, IRLMP_EVENT, struct sk_buff *) =
{
irlmp_state_standby,
irlmp_state_u_connect,
irlmp_state_active,
};
static int (*lsap_state[])( struct lsap_cb *, IRLMP_EVENT, struct sk_buff *) =
{
irlmp_state_disconnected,
irlmp_state_connect,
irlmp_state_connect_pend,
irlmp_state_dtr,
irlmp_state_setup,
irlmp_state_setup_pend
};
static inline void irlmp_next_lap_state(struct lap_cb *self,
IRLMP_STATE state)
{
/*
IRDA_DEBUG(4, "%s(), LMP LAP = %s\n", __FUNCTION__, irlmp_state[state]);
*/
self->lap_state = state;
}
static inline void irlmp_next_lsap_state(struct lsap_cb *self,
LSAP_STATE state)
{
/*
IRDA_ASSERT(self != NULL, return;);
IRDA_DEBUG(4, "%s(), LMP LSAP = %s\n", __FUNCTION__, irlsap_state[state]);
*/
self->lsap_state = state;
}
/* Do connection control events */
int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
IRDA_DEBUG(4, "%s(), EVENT = %s, STATE = %s\n",
__FUNCTION__, irlmp_event[event], irlsap_state[ self->lsap_state]);
return (*lsap_state[self->lsap_state]) (self, event, skb);
}
/*
* Function do_lap_event (event, skb, info)
*
* Do IrLAP control events
*
*/
void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
IRDA_DEBUG(4, "%s(), EVENT = %s, STATE = %s\n", __FUNCTION__,
irlmp_event[event],
irlmp_state[self->lap_state]);
(*lap_state[self->lap_state]) (self, event, skb);
}
void irlmp_discovery_timer_expired(void *data)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
/* We always cleanup the log (active & passive discovery) */
irlmp_do_expiry();
/* Active discovery is conditional */
if (sysctl_discovery)
irlmp_do_discovery(sysctl_discovery_slots);
/* Restart timer */
irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout * HZ);
}
void irlmp_watchdog_timer_expired(void *data)
{
struct lsap_cb *self = (struct lsap_cb *) data;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
irlmp_do_lsap_event(self, LM_WATCHDOG_TIMEOUT, NULL);
}
void irlmp_idle_timer_expired(void *data)
{
struct lap_cb *self = (struct lap_cb *) data;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
irlmp_do_lap_event(self, LM_LAP_IDLE_TIMEOUT, NULL);
}
/*
* Send an event on all LSAPs attached to this LAP.
*/
static inline void
irlmp_do_all_lsap_event(hashbin_t * lsap_hashbin,
IRLMP_EVENT event)
{
struct lsap_cb *lsap;
struct lsap_cb *lsap_next;
/* Note : this function use the new hashbin_find_next()
* function, instead of the old hashbin_get_next().
* This make sure that we are always pointing one lsap
* ahead, so that if the current lsap is removed as the
* result of sending the event, we don't care.
* Also, as we store the context ourselves, if an enumeration
* of the same lsap hashbin happens as the result of sending the
* event, we don't care.
* The only problem is if the next lsap is removed. In that case,
* hashbin_find_next() will return NULL and we will abort the
* enumeration. - Jean II */
/* Also : we don't accept any skb in input. We can *NOT* pass
* the same skb to multiple clients safely, we would need to
* skb_clone() it. - Jean II */
lsap = (struct lsap_cb *) hashbin_get_first(lsap_hashbin);
while (NULL != hashbin_find_next(lsap_hashbin,
(long) lsap,
NULL,
(void *) &lsap_next) ) {
irlmp_do_lsap_event(lsap, event, NULL);
lsap = lsap_next;
}
}
/*********************************************************************
*
* LAP connection control states
*
********************************************************************/
/*
* Function irlmp_state_standby (event, skb, info)
*
* STANDBY, The IrLAP connection does not exist.
*
*/
static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self->irlap != NULL, return;);
switch (event) {
case LM_LAP_DISCOVERY_REQUEST:
/* irlmp_next_station_state( LMP_DISCOVER); */
irlap_discovery_request(self->irlap, &irlmp->discovery_cmd);
break;
case LM_LAP_CONNECT_INDICATION:
/* It's important to switch state first, to avoid IrLMP to
* think that the link is free since IrLMP may then start
* discovery before the connection is properly set up. DB.
*/
irlmp_next_lap_state(self, LAP_ACTIVE);
/* Just accept connection TODO, this should be fixed */
irlap_connect_response(self->irlap, skb);
break;
case LM_LAP_CONNECT_REQUEST:
IRDA_DEBUG(4, "%s() LS_CONNECT_REQUEST\n", __FUNCTION__);
irlmp_next_lap_state(self, LAP_U_CONNECT);
/* FIXME: need to set users requested QoS */
irlap_connect_request(self->irlap, self->daddr, NULL, 0);
break;
case LM_LAP_DISCONNECT_INDICATION:
IRDA_DEBUG(4, "%s(), Error LM_LAP_DISCONNECT_INDICATION\n",
__FUNCTION__);
irlmp_next_lap_state(self, LAP_STANDBY);
break;
default:
IRDA_DEBUG(0, "%s(), Unknown event %s\n",
__FUNCTION__, irlmp_event[event]);
break;
}
}
/*
* Function irlmp_state_u_connect (event, skb, info)
*
* U_CONNECT, The layer above has tried to open an LSAP connection but
* since the IrLAP connection does not exist, we must first start an
* IrLAP connection. We are now waiting response from IrLAP.
* */
static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(2, "%s(), event=%s\n", __FUNCTION__, irlmp_event[event]);
switch (event) {
case LM_LAP_CONNECT_INDICATION:
/* It's important to switch state first, to avoid IrLMP to
* think that the link is free since IrLMP may then start
* discovery before the connection is properly set up. DB.
*/
irlmp_next_lap_state(self, LAP_ACTIVE);
/* Just accept connection TODO, this should be fixed */
irlap_connect_response(self->irlap, skb);
/* Tell LSAPs that they can start sending data */
irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
/* Note : by the time we get there (LAP retries and co),
* the lsaps may already have gone. This avoid getting stuck
* forever in LAP_ACTIVE state - Jean II */
if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
IRDA_DEBUG(0, "%s() NO LSAPs !\n", __FUNCTION__);
irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
}
break;
case LM_LAP_CONNECT_REQUEST:
/* Already trying to connect */
break;
case LM_LAP_CONNECT_CONFIRM:
/* For all lsap_ce E Associated do LS_Connect_confirm */
irlmp_next_lap_state(self, LAP_ACTIVE);
/* Tell LSAPs that they can start sending data */
irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
/* Note : by the time we get there (LAP retries and co),
* the lsaps may already have gone. This avoid getting stuck
* forever in LAP_ACTIVE state - Jean II */
if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
IRDA_DEBUG(0, "%s() NO LSAPs !\n", __FUNCTION__);
irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
}
break;
case LM_LAP_DISCONNECT_INDICATION:
IRDA_DEBUG(4, "%s(), LM_LAP_DISCONNECT_INDICATION\n", __FUNCTION__);
irlmp_next_lap_state(self, LAP_STANDBY);
/* Send disconnect event to all LSAPs using this link */
irlmp_do_all_lsap_event(self->lsaps,
LM_LAP_DISCONNECT_INDICATION);
break;
case LM_LAP_DISCONNECT_REQUEST:
IRDA_DEBUG(4, "%s(), LM_LAP_DISCONNECT_REQUEST\n", __FUNCTION__);
/* One of the LSAP did timeout or was closed, if it was
* the last one, try to get out of here - Jean II */
if (HASHBIN_GET_SIZE(self->lsaps) <= 1) {
irlap_disconnect_request(self->irlap);
}
break;
default:
IRDA_DEBUG(0, "%s(), Unknown event %s\n",
__FUNCTION__, irlmp_event[event]);
break;
}
}
/*
* Function irlmp_state_active (event, skb, info)
*
* ACTIVE, IrLAP connection is active
*
*/
static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
switch (event) {
case LM_LAP_CONNECT_REQUEST:
IRDA_DEBUG(4, "%s(), LS_CONNECT_REQUEST\n", __FUNCTION__);
/*
* IrLAP may have a pending disconnect. We tried to close
* IrLAP, but it was postponed because the link was
* busy or we were still sending packets. As we now
* need it, make sure it stays on. Jean II
*/
irlap_clear_disconnect(self->irlap);
/*
* LAP connection already active, just bounce back! Since we
* don't know which LSAP that tried to do this, we have to
* notify all LSAPs using this LAP, but that should be safe to
* do anyway.
*/
irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM);
/* Needed by connect indication */
irlmp_do_all_lsap_event(irlmp->unconnected_lsaps,
LM_LAP_CONNECT_CONFIRM);
/* Keep state */
break;
case LM_LAP_DISCONNECT_REQUEST:
/*
* Need to find out if we should close IrLAP or not. If there
* is only one LSAP connection left on this link, that LSAP
* must be the one that tries to close IrLAP. It will be
* removed later and moved to the list of unconnected LSAPs
*/
if (HASHBIN_GET_SIZE(self->lsaps) > 0) {
/* Timer value is checked in irsysctl - Jean II */
irlmp_start_idle_timer(self, sysctl_lap_keepalive_time * HZ / 1000);
} else {
/* No more connections, so close IrLAP */
/* We don't want to change state just yet, because
* we want to reflect accurately the real state of
* the LAP, not the state we wish it was in,
* so that we don't lose LM_LAP_CONNECT_REQUEST.
* In some cases, IrLAP won't close the LAP
* immediately. For example, it might still be
* retrying packets or waiting for the pf bit.
* As the LAP always send a DISCONNECT_INDICATION
* in PCLOSE or SCLOSE, just change state on that.
* Jean II */
irlap_disconnect_request(self->irlap);
}
break;
case LM_LAP_IDLE_TIMEOUT:
if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
/* Same reasoning as above - keep state */
irlap_disconnect_request(self->irlap);
}
break;
case LM_LAP_DISCONNECT_INDICATION:
irlmp_next_lap_state(self, LAP_STANDBY);
/* In some case, at this point our side has already closed
* all lsaps, and we are waiting for the idle_timer to
* expire. If another device reconnect immediately, the
* idle timer will expire in the midle of the connection
* initialisation, screwing up things a lot...
* Therefore, we must stop the timer... */
irlmp_stop_idle_timer(self);
/*
* Inform all connected LSAP's using this link
*/
irlmp_do_all_lsap_event(self->lsaps,
LM_LAP_DISCONNECT_INDICATION);
/* Force an expiry of the discovery log.
* Now that the LAP is free, the system may attempt to
* connect to another device. Unfortunately, our entries
* are stale. There is a small window (<3s) before the
* normal discovery will run and where irlmp_connect_request()
* can get the wrong info, so make sure things get
* cleaned *NOW* ;-) - Jean II */
irlmp_do_expiry();
break;
default:
IRDA_DEBUG(0, "%s(), Unknown event %s\n",
__FUNCTION__, irlmp_event[event]);
break;
}
}
/*********************************************************************
*
* LSAP connection control states
*
********************************************************************/
/*
* Function irlmp_state_disconnected (event, skb, info)
*
* DISCONNECTED
*
*/
static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
int ret = 0;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
switch (event) {
#ifdef CONFIG_IRDA_ULTRA
case LM_UDATA_INDICATION:
/* This is most bizzare. Those packets are aka unreliable
* connected, aka IrLPT or SOCK_DGRAM/IRDAPROTO_UNITDATA.
* Why do we pass them as Ultra ??? Jean II */
irlmp_connless_data_indication(self, skb);
break;
#endif /* CONFIG_IRDA_ULTRA */
case LM_CONNECT_REQUEST:
IRDA_DEBUG(4, "%s(), LM_CONNECT_REQUEST\n", __FUNCTION__);
if (self->conn_skb) {
IRDA_WARNING("%s: busy with another request!\n",
__FUNCTION__);
return -EBUSY;
}
/* Don't forget to refcount it (see irlmp_connect_request()) */
skb_get(skb);
self->conn_skb = skb;
irlmp_next_lsap_state(self, LSAP_SETUP_PEND);
/* Start watchdog timer (5 secs for now) */
irlmp_start_watchdog_timer(self, 5*HZ);
irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL);
break;
case LM_CONNECT_INDICATION:
if (self->conn_skb) {
IRDA_WARNING("%s: busy with another request!\n",
__FUNCTION__);
return -EBUSY;
}
/* Don't forget to refcount it (see irlap_driver_rcv()) */
skb_get(skb);
self->conn_skb = skb;
irlmp_next_lsap_state(self, LSAP_CONNECT_PEND);
/* Start watchdog timer
* This is not mentionned in the spec, but there is a rare
* race condition that can get the socket stuck.
* If we receive this event while our LAP is closing down,
* the LM_LAP_CONNECT_REQUEST get lost and we get stuck in
* CONNECT_PEND state forever.
* The other cause of getting stuck down there is if the
* higher layer never reply to the CONNECT_INDICATION.
* Anyway, it make sense to make sure that we always have
* a backup plan. 1 second is plenty (should be immediate).
* Jean II */
irlmp_start_watchdog_timer(self, 1*HZ);
irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL);
break;
default:
IRDA_DEBUG(1, "%s(), Unknown event %s on LSAP %#02x\n",
__FUNCTION__, irlmp_event[event], self->slsap_sel);
break;
}
return ret;
}
/*
* Function irlmp_state_connect (self, event, skb)
*
* CONNECT
*
*/
static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
struct lsap_cb *lsap;
int ret = 0;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
switch (event) {
case LM_CONNECT_RESPONSE:
/*
* Bind this LSAP to the IrLAP link where the connect was
* received
*/
lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self,
NULL);
IRDA_ASSERT(lsap == self, return -1;);
IRDA_ASSERT(self->lap != NULL, return -1;);
IRDA_ASSERT(self->lap->lsaps != NULL, return -1;);
hashbin_insert(self->lap->lsaps, (irda_queue_t *) self,
(long) self, NULL);
set_bit(0, &self->connected); /* TRUE */
irlmp_send_lcf_pdu(self->lap, self->dlsap_sel,
self->slsap_sel, CONNECT_CNF, skb);
del_timer(&self->watchdog_timer);
irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY);
break;
case LM_WATCHDOG_TIMEOUT:
/* May happen, who knows...
* Jean II */
IRDA_DEBUG(0, "%s() WATCHDOG_TIMEOUT!\n", __FUNCTION__);
/* Disconnect, get out... - Jean II */
self->lap = NULL;
self->dlsap_sel = LSAP_ANY;
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
break;
default:
/* LM_LAP_DISCONNECT_INDICATION : Should never happen, we
* are *not* yet bound to the IrLAP link. Jean II */
IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
__FUNCTION__, irlmp_event[event], self->slsap_sel);
break;
}
return ret;
}
/*
* Function irlmp_state_connect_pend (event, skb, info)
*
* CONNECT_PEND
*
*/
static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
struct sk_buff *tx_skb;
int ret = 0;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
switch (event) {
case LM_CONNECT_REQUEST:
/* Keep state */
break;
case LM_CONNECT_RESPONSE:
IRDA_DEBUG(0, "%s(), LM_CONNECT_RESPONSE, "
"no indication issued yet\n", __FUNCTION__);
/* Keep state */
break;
case LM_DISCONNECT_REQUEST:
IRDA_DEBUG(0, "%s(), LM_DISCONNECT_REQUEST, "
"not yet bound to IrLAP connection\n", __FUNCTION__);
/* Keep state */
break;
case LM_LAP_CONNECT_CONFIRM:
IRDA_DEBUG(4, "%s(), LS_CONNECT_CONFIRM\n", __FUNCTION__);
irlmp_next_lsap_state(self, LSAP_CONNECT);
tx_skb = self->conn_skb;
self->conn_skb = NULL;
irlmp_connect_indication(self, tx_skb);
/* Drop reference count - see irlmp_connect_indication(). */
dev_kfree_skb(tx_skb);
break;
case LM_WATCHDOG_TIMEOUT:
/* Will happen in some rare cases because of a race condition.
* Just make sure we don't stay there forever...
* Jean II */
IRDA_DEBUG(0, "%s() WATCHDOG_TIMEOUT!\n", __FUNCTION__);
/* Go back to disconnected mode, keep the socket waiting */
self->lap = NULL;
self->dlsap_sel = LSAP_ANY;
if(self->conn_skb)
dev_kfree_skb(self->conn_skb);
self->conn_skb = NULL;
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
break;
default:
/* LM_LAP_DISCONNECT_INDICATION : Should never happen, we
* are *not* yet bound to the IrLAP link. Jean II */
IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
__FUNCTION__, irlmp_event[event], self->slsap_sel);
break;
}
return ret;
}
/*
* Function irlmp_state_dtr (self, event, skb)
*
* DATA_TRANSFER_READY
*
*/
static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
LM_REASON reason;
int ret = 0;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
IRDA_ASSERT(self->lap != NULL, return -1;);
switch (event) {
case LM_DATA_REQUEST: /* Optimize for the common case */
irlmp_send_data_pdu(self->lap, self->dlsap_sel,
self->slsap_sel, FALSE, skb);
break;
case LM_DATA_INDICATION: /* Optimize for the common case */
irlmp_data_indication(self, skb);
break;
case LM_UDATA_REQUEST:
IRDA_ASSERT(skb != NULL, return -1;);
irlmp_send_data_pdu(self->lap, self->dlsap_sel,
self->slsap_sel, TRUE, skb);
break;
case LM_UDATA_INDICATION:
irlmp_udata_indication(self, skb);
break;
case LM_CONNECT_REQUEST:
IRDA_DEBUG(0, "%s(), LM_CONNECT_REQUEST, "
"error, LSAP already connected\n", __FUNCTION__);
/* Keep state */
break;
case LM_CONNECT_RESPONSE:
IRDA_DEBUG(0, "%s(), LM_CONNECT_RESPONSE, "
"error, LSAP already connected\n", __FUNCTION__);
/* Keep state */
break;
case LM_DISCONNECT_REQUEST:
irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, self->slsap_sel,
DISCONNECT, skb);
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
/* Called only from irlmp_disconnect_request(), will
* unbind from LAP over there. Jean II */
/* Try to close the LAP connection if its still there */
if (self->lap) {
IRDA_DEBUG(4, "%s(), trying to close IrLAP\n",
__FUNCTION__);
irlmp_do_lap_event(self->lap,
LM_LAP_DISCONNECT_REQUEST,
NULL);
}
break;
case LM_LAP_DISCONNECT_INDICATION:
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
reason = irlmp_convert_lap_reason(self->lap->reason);
irlmp_disconnect_indication(self, reason, NULL);
break;
case LM_DISCONNECT_INDICATION:
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
IRDA_ASSERT(self->lap != NULL, return -1;);
IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;);
IRDA_ASSERT(skb->len > 3, return -1;);
reason = skb->data[3];
/* Try to close the LAP connection */
IRDA_DEBUG(4, "%s(), trying to close IrLAP\n", __FUNCTION__);
irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
irlmp_disconnect_indication(self, reason, skb);
break;
default:
IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
__FUNCTION__, irlmp_event[event], self->slsap_sel);
break;
}
return ret;
}
/*
* Function irlmp_state_setup (event, skb, info)
*
* SETUP, Station Control has set up the underlying IrLAP connection.
* An LSAP connection request has been transmitted to the peer
* LSAP-Connection Control FSM and we are awaiting reply.
*/
static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
LM_REASON reason;
int ret = 0;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
switch (event) {
case LM_CONNECT_CONFIRM:
irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY);
del_timer(&self->watchdog_timer);
irlmp_connect_confirm(self, skb);
break;
case LM_DISCONNECT_INDICATION:
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
IRDA_ASSERT(self->lap != NULL, return -1;);
IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;);
IRDA_ASSERT(skb->len > 3, return -1;);
reason = skb->data[3];
/* Try to close the LAP connection */
IRDA_DEBUG(4, "%s(), trying to close IrLAP\n", __FUNCTION__);
irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
irlmp_disconnect_indication(self, reason, skb);
break;
case LM_LAP_DISCONNECT_INDICATION:
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
del_timer(&self->watchdog_timer);
IRDA_ASSERT(self->lap != NULL, return -1;);
IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;);
reason = irlmp_convert_lap_reason(self->lap->reason);
irlmp_disconnect_indication(self, reason, skb);
break;
case LM_WATCHDOG_TIMEOUT:
IRDA_DEBUG(0, "%s() WATCHDOG_TIMEOUT!\n", __FUNCTION__);
IRDA_ASSERT(self->lap != NULL, return -1;);
irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL);
break;
default:
IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
__FUNCTION__, irlmp_event[event], self->slsap_sel);
break;
}
return ret;
}
/*
* Function irlmp_state_setup_pend (event, skb, info)
*
* SETUP_PEND, An LM_CONNECT_REQUEST has been received from the service
* user to set up an LSAP connection. A request has been sent to the
* LAP FSM to set up the underlying IrLAP connection, and we
* are awaiting confirm.
*/
static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
struct sk_buff *tx_skb;
LM_REASON reason;
int ret = 0;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(irlmp != NULL, return -1;);
switch (event) {
case LM_LAP_CONNECT_CONFIRM:
IRDA_ASSERT(self->conn_skb != NULL, return -1;);
tx_skb = self->conn_skb;
self->conn_skb = NULL;
irlmp_send_lcf_pdu(self->lap, self->dlsap_sel,
self->slsap_sel, CONNECT_CMD, tx_skb);
/* Drop reference count - see irlap_data_request(). */
dev_kfree_skb(tx_skb);
irlmp_next_lsap_state(self, LSAP_SETUP);
break;
case LM_WATCHDOG_TIMEOUT:
IRDA_DEBUG(0, "%s() : WATCHDOG_TIMEOUT !\n", __FUNCTION__);
IRDA_ASSERT(self->lap != NULL, return -1;);
irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL);
break;
case LM_LAP_DISCONNECT_INDICATION: /* LS_Disconnect.indication */
del_timer( &self->watchdog_timer);
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
reason = irlmp_convert_lap_reason(self->lap->reason);
irlmp_disconnect_indication(self, reason, NULL);
break;
default:
IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
__FUNCTION__, irlmp_event[event], self->slsap_sel);
break;
}
return ret;
}

491
net/irda/irlmp_frame.c Normal file
View File

@@ -0,0 +1,491 @@
/*********************************************************************
*
* Filename: irlmp_frame.c
* Version: 0.9
* Description: IrLMP frame implementation
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Aug 19 02:09:59 1997
* Modified at: Mon Dec 13 13:41:12 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998-1999 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <linux/config.h>
#include <linux/skbuff.h>
#include <linux/kernel.h>
#include <net/irda/irda.h>
#include <net/irda/irlap.h>
#include <net/irda/timer.h>
#include <net/irda/irlmp.h>
#include <net/irda/irlmp_frame.h>
#include <net/irda/discovery.h>
static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap,
__u8 slsap, int status, hashbin_t *);
inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
int expedited, struct sk_buff *skb)
{
skb->data[0] = dlsap;
skb->data[1] = slsap;
if (expedited) {
IRDA_DEBUG(4, "%s(), sending expedited data\n", __FUNCTION__);
irlap_data_request(self->irlap, skb, TRUE);
} else
irlap_data_request(self->irlap, skb, FALSE);
}
/*
* Function irlmp_send_lcf_pdu (dlsap, slsap, opcode,skb)
*
* Send Link Control Frame to IrLAP
*/
void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
__u8 opcode, struct sk_buff *skb)
{
__u8 *frame;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
frame = skb->data;
frame[0] = dlsap | CONTROL_BIT;
frame[1] = slsap;
frame[2] = opcode;
if (opcode == DISCONNECT)
frame[3] = 0x01; /* Service user request */
else
frame[3] = 0x00; /* rsvd */
irlap_data_request(self->irlap, skb, FALSE);
}
/*
* Function irlmp_input (skb)
*
* Used by IrLAP to pass received data frames to IrLMP layer
*
*/
void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
int unreliable)
{
struct lsap_cb *lsap;
__u8 slsap_sel; /* Source (this) LSAP address */
__u8 dlsap_sel; /* Destination LSAP address */
__u8 *fp;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
IRDA_ASSERT(skb->len > 2, return;);
fp = skb->data;
/*
* The next statements may be confusing, but we do this so that
* destination LSAP of received frame is source LSAP in our view
*/
slsap_sel = fp[0] & LSAP_MASK;
dlsap_sel = fp[1];
/*
* Check if this is an incoming connection, since we must deal with
* it in a different way than other established connections.
*/
if ((fp[0] & CONTROL_BIT) && (fp[2] == CONNECT_CMD)) {
IRDA_DEBUG(3, "%s(), incoming connection, "
"source LSAP=%d, dest LSAP=%d\n",
__FUNCTION__, slsap_sel, dlsap_sel);
/* Try to find LSAP among the unconnected LSAPs */
lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, CONNECT_CMD,
irlmp->unconnected_lsaps);
/* Maybe LSAP was already connected, so try one more time */
if (!lsap) {
IRDA_DEBUG(1, "%s(), incoming connection for LSAP already connected\n", __FUNCTION__);
lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
self->lsaps);
}
} else
lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
self->lsaps);
if (lsap == NULL) {
IRDA_DEBUG(2, "IrLMP, Sorry, no LSAP for received frame!\n");
IRDA_DEBUG(2, "%s(), slsap_sel = %02x, dlsap_sel = %02x\n",
__FUNCTION__, slsap_sel, dlsap_sel);
if (fp[0] & CONTROL_BIT) {
IRDA_DEBUG(2, "%s(), received control frame %02x\n",
__FUNCTION__, fp[2]);
} else {
IRDA_DEBUG(2, "%s(), received data frame\n", __FUNCTION__);
}
return;
}
/*
* Check if we received a control frame?
*/
if (fp[0] & CONTROL_BIT) {
switch (fp[2]) {
case CONNECT_CMD:
lsap->lap = self;
irlmp_do_lsap_event(lsap, LM_CONNECT_INDICATION, skb);
break;
case CONNECT_CNF:
irlmp_do_lsap_event(lsap, LM_CONNECT_CONFIRM, skb);
break;
case DISCONNECT:
IRDA_DEBUG(4, "%s(), Disconnect indication!\n",
__FUNCTION__);
irlmp_do_lsap_event(lsap, LM_DISCONNECT_INDICATION,
skb);
break;
case ACCESSMODE_CMD:
IRDA_DEBUG(0, "Access mode cmd not implemented!\n");
break;
case ACCESSMODE_CNF:
IRDA_DEBUG(0, "Access mode cnf not implemented!\n");
break;
default:
IRDA_DEBUG(0, "%s(), Unknown control frame %02x\n",
__FUNCTION__, fp[2]);
break;
}
} else if (unreliable) {
/* Optimize and bypass the state machine if possible */
if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
irlmp_udata_indication(lsap, skb);
else
irlmp_do_lsap_event(lsap, LM_UDATA_INDICATION, skb);
} else {
/* Optimize and bypass the state machine if possible */
if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY)
irlmp_data_indication(lsap, skb);
else
irlmp_do_lsap_event(lsap, LM_DATA_INDICATION, skb);
}
}
/*
* Function irlmp_link_unitdata_indication (self, skb)
*
*
*
*/
#ifdef CONFIG_IRDA_ULTRA
void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
{
struct lsap_cb *lsap;
__u8 slsap_sel; /* Source (this) LSAP address */
__u8 dlsap_sel; /* Destination LSAP address */
__u8 pid; /* Protocol identifier */
__u8 *fp;
unsigned long flags;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
IRDA_ASSERT(skb->len > 2, return;);
fp = skb->data;
/*
* The next statements may be confusing, but we do this so that
* destination LSAP of received frame is source LSAP in our view
*/
slsap_sel = fp[0] & LSAP_MASK;
dlsap_sel = fp[1];
pid = fp[2];
if (pid & 0x80) {
IRDA_DEBUG(0, "%s(), extension in PID not supp!\n",
__FUNCTION__);
return;
}
/* Check if frame is addressed to the connectionless LSAP */
if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) {
IRDA_DEBUG(0, "%s(), dropping frame!\n", __FUNCTION__);
return;
}
/* Search the connectionless LSAP */
spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps);
while (lsap != NULL) {
/*
* Check if source LSAP and dest LSAP selectors and PID match.
*/
if ((lsap->slsap_sel == slsap_sel) &&
(lsap->dlsap_sel == dlsap_sel) &&
(lsap->pid == pid))
{
break;
}
lsap = (struct lsap_cb *) hashbin_get_next(irlmp->unconnected_lsaps);
}
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
if (lsap)
irlmp_connless_data_indication(lsap, skb);
else {
IRDA_DEBUG(0, "%s(), found no matching LSAP!\n", __FUNCTION__);
}
}
#endif /* CONFIG_IRDA_ULTRA */
/*
* Function irlmp_link_disconnect_indication (reason, userdata)
*
* IrLAP has disconnected
*
*/
void irlmp_link_disconnect_indication(struct lap_cb *lap,
struct irlap_cb *irlap,
LAP_REASON reason,
struct sk_buff *skb)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
IRDA_ASSERT(lap != NULL, return;);
IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
lap->reason = reason;
lap->daddr = DEV_ADDR_ANY;
/* FIXME: must do something with the skb if any */
/*
* Inform station state machine
*/
irlmp_do_lap_event(lap, LM_LAP_DISCONNECT_INDICATION, NULL);
}
/*
* Function irlmp_link_connect_indication (qos)
*
* Incoming LAP connection!
*
*/
void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr,
__u32 daddr, struct qos_info *qos,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
/* Copy QoS settings for this session */
self->qos = qos;
/* Update destination device address */
self->daddr = daddr;
IRDA_ASSERT(self->saddr == saddr, return;);
irlmp_do_lap_event(self, LM_LAP_CONNECT_INDICATION, skb);
}
/*
* Function irlmp_link_connect_confirm (qos)
*
* LAP connection confirmed!
*
*/
void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos,
struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
IRDA_ASSERT(qos != NULL, return;);
/* Don't need use the skb for now */
/* Copy QoS settings for this session */
self->qos = qos;
irlmp_do_lap_event(self, LM_LAP_CONNECT_CONFIRM, NULL);
}
/*
* Function irlmp_link_discovery_indication (self, log)
*
* Device is discovering us
*
* It's not an answer to our own discoveries, just another device trying
* to perform discovery, but we don't want to miss the opportunity
* to exploit this information, because :
* o We may not actively perform discovery (just passive discovery)
* o This type of discovery is much more reliable. In some cases, it
* seem that less than 50% of our discoveries get an answer, while
* we always get ~100% of these.
* o Make faster discovery, statistically divide time of discovery
* events by 2 (important for the latency aspect and user feel)
* o Even is we do active discovery, the other node might not
* answer our discoveries (ex: Palm). The Palm will just perform
* one active discovery and connect directly to us.
*
* However, when both devices discover each other, they might attempt to
* connect to each other following the discovery event, and it would create
* collisions on the medium (SNRM battle).
* The "fix" for that is to disable all connection requests in IrLAP
* for 100ms after a discovery indication by setting the media_busy flag.
* Previously, we used to postpone the event which was quite ugly. Now
* that IrLAP takes care of this problem, just pass the event up...
*
* Jean II
*/
void irlmp_link_discovery_indication(struct lap_cb *self,
discovery_t *discovery)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
/* Add to main log, cleanup */
irlmp_add_discovery(irlmp->cachelog, discovery);
/* Just handle it the same way as a discovery confirm,
* bypass the LM_LAP state machine (see below) */
irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_PASSIVE);
}
/*
* Function irlmp_link_discovery_confirm (self, log)
*
* Called by IrLAP with a list of discoveries after the discovery
* request has been carried out. A NULL log is received if IrLAP
* was unable to carry out the discovery request
*
*/
void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
/* Add to main log, cleanup */
irlmp_add_discovery_log(irlmp->cachelog, log);
/* Propagate event to various LSAPs registered for it.
* We bypass the LM_LAP state machine because
* 1) We do it regardless of the LM_LAP state
* 2) It doesn't affect the LM_LAP state
* 3) Faster, slimer, simpler, ...
* Jean II */
irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_ACTIVE);
}
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
static inline void irlmp_update_cache(struct lap_cb *lap,
struct lsap_cb *lsap)
{
/* Prevent concurrent read to get garbage */
lap->cache.valid = FALSE;
/* Update cache entry */
lap->cache.dlsap_sel = lsap->dlsap_sel;
lap->cache.slsap_sel = lsap->slsap_sel;
lap->cache.lsap = lsap;
lap->cache.valid = TRUE;
}
#endif
/*
* Function irlmp_find_handle (self, dlsap_sel, slsap_sel, status, queue)
*
* Find handle associated with destination and source LSAP
*
* Any IrDA connection (LSAP/TSAP) is uniquely identified by
* 3 parameters, the local lsap, the remote lsap and the remote address.
* We may initiate multiple connections to the same remote service
* (they will have different local lsap), a remote device may initiate
* multiple connections to the same local service (they will have
* different remote lsap), or multiple devices may connect to the same
* service and may use the same remote lsap (and they will have
* different remote address).
* So, where is the remote address ? Each LAP connection is made with
* a single remote device, so imply a specific remote address.
* Jean II
*/
static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel,
__u8 slsap_sel, int status,
hashbin_t *queue)
{
struct lsap_cb *lsap;
unsigned long flags;
/*
* Optimize for the common case. We assume that the last frame
* received is in the same connection as the last one, so check in
* cache first to avoid the linear search
*/
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
if ((self->cache.valid) &&
(self->cache.slsap_sel == slsap_sel) &&
(self->cache.dlsap_sel == dlsap_sel))
{
return (self->cache.lsap);
}
#endif
spin_lock_irqsave(&queue->hb_spinlock, flags);
lsap = (struct lsap_cb *) hashbin_get_first(queue);
while (lsap != NULL) {
/*
* If this is an incoming connection, then the destination
* LSAP selector may have been specified as LM_ANY so that
* any client can connect. In that case we only need to check
* if the source LSAP (in our view!) match!
*/
if ((status == CONNECT_CMD) &&
(lsap->slsap_sel == slsap_sel) &&
(lsap->dlsap_sel == LSAP_ANY)) {
/* This is where the dest lsap sel is set on incoming
* lsaps */
lsap->dlsap_sel = dlsap_sel;
break;
}
/*
* Check if source LSAP and dest LSAP selectors match.
*/
if ((lsap->slsap_sel == slsap_sel) &&
(lsap->dlsap_sel == dlsap_sel))
break;
lsap = (struct lsap_cb *) hashbin_get_next(queue);
}
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
if(lsap)
irlmp_update_cache(self, lsap);
#endif
spin_unlock_irqrestore(&queue->hb_spinlock, flags);
/* Return what we've found or NULL */
return lsap;
}

185
net/irda/irmod.c Normal file
View File

@@ -0,0 +1,185 @@
/*********************************************************************
*
* Filename: irmod.c
* Version: 0.9
* Description: IrDA stack main entry points
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Dec 15 13:55:39 1997
* Modified at: Wed Jan 5 15:12:41 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1997, 1999-2000 Dag Brattli, All Rights Reserved.
* Copyright (c) 2000-2004 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
/*
* This file contains the main entry points of the IrDA stack.
* They are in this file and not af_irda.c because some developpers
* are using the IrDA stack without the socket API (compiling out
* af_irda.c).
* Jean II
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <net/irda/irda.h>
#include <net/irda/irmod.h> /* notify_t */
#include <net/irda/irlap.h> /* irlap_init */
#include <net/irda/irlmp.h> /* irlmp_init */
#include <net/irda/iriap.h> /* iriap_init */
#include <net/irda/irttp.h> /* irttp_init */
#include <net/irda/irda_device.h> /* irda_device_init */
/* irproc.c */
extern void irda_proc_register(void);
extern void irda_proc_unregister(void);
/* irsysctl.c */
extern int irda_sysctl_register(void);
extern void irda_sysctl_unregister(void);
/* af_irda.c */
extern int irsock_init(void);
extern void irsock_cleanup(void);
/* irlap_frame.c */
extern int irlap_driver_rcv(struct sk_buff *, struct net_device *,
struct packet_type *);
/*
* Module parameters
*/
#ifdef CONFIG_IRDA_DEBUG
unsigned int irda_debug = IRDA_DEBUG_LEVEL;
module_param_named(debug, irda_debug, uint, 0);
MODULE_PARM_DESC(debug, "IRDA debugging level");
EXPORT_SYMBOL(irda_debug);
#endif
/* Packet type handler.
* Tell the kernel how IrDA packets should be handled.
*/
static struct packet_type irda_packet_type = {
.type = __constant_htons(ETH_P_IRDA),
.func = irlap_driver_rcv, /* Packet type handler irlap_frame.c */
};
/*
* Function irda_notify_init (notify)
*
* Used for initializing the notify structure
*
*/
void irda_notify_init(notify_t *notify)
{
notify->data_indication = NULL;
notify->udata_indication = NULL;
notify->connect_confirm = NULL;
notify->connect_indication = NULL;
notify->disconnect_indication = NULL;
notify->flow_indication = NULL;
notify->status_indication = NULL;
notify->instance = NULL;
strlcpy(notify->name, "Unknown", sizeof(notify->name));
}
EXPORT_SYMBOL(irda_notify_init);
/*
* Function irda_init (void)
*
* Protocol stack initialisation entry point.
* Initialise the various components of the IrDA stack
*/
static int __init irda_init(void)
{
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
/* Lower layer of the stack */
irlmp_init();
irlap_init();
/* Higher layers of the stack */
iriap_init();
irttp_init();
irsock_init();
/* Add IrDA packet type (Start receiving packets) */
dev_add_pack(&irda_packet_type);
/* External APIs */
#ifdef CONFIG_PROC_FS
irda_proc_register();
#endif
#ifdef CONFIG_SYSCTL
irda_sysctl_register();
#endif
/* Driver/dongle support */
irda_device_init();
return 0;
}
/*
* Function irda_cleanup (void)
*
* Protocol stack cleanup/removal entry point.
* Cleanup the various components of the IrDA stack
*/
static void __exit irda_cleanup(void)
{
/* Remove External APIs */
#ifdef CONFIG_SYSCTL
irda_sysctl_unregister();
#endif
#ifdef CONFIG_PROC_FS
irda_proc_unregister();
#endif
/* Remove IrDA packet type (stop receiving packets) */
dev_remove_pack(&irda_packet_type);
/* Remove higher layers */
irsock_cleanup();
irttp_cleanup();
iriap_cleanup();
/* Remove lower layers */
irda_device_cleanup();
irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */
/* Remove middle layer */
irlmp_cleanup();
}
/*
* The IrDA stack must be initialised *before* drivers get initialised,
* and *before* higher protocols (IrLAN/IrCOMM/IrNET) get initialised,
* otherwise bad things will happen (hashbins will be NULL for example).
* Those modules are at module_init()/device_initcall() level.
*
* On the other hand, it needs to be initialised *after* the basic
* networking, the /proc/net filesystem and sysctl module. Those are
* currently initialised in .../init/main.c (before initcalls).
* Also, IrDA drivers needs to be initialised *after* the random number
* generator (main stack and higher layer init don't need it anymore).
*
* Jean II
*/
subsys_initcall(irda_init);
module_exit(irda_cleanup);
MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no> & Jean Tourrilhes <jt@hpl.hp.com>");
MODULE_DESCRIPTION("The Linux IrDA Protocol Stack");
MODULE_LICENSE("GPL");
MODULE_ALIAS_NETPROTO(PF_IRDA);

13
net/irda/irnet/Kconfig Normal file
View File

@@ -0,0 +1,13 @@
config IRNET
tristate "IrNET protocol"
depends on IRDA && PPP
help
Say Y here if you want to build support for the IrNET protocol.
To compile it as a module, choose M here: the module will be
called irnet. IrNET is a PPP driver, so you will also need a
working PPP subsystem (driver, daemon and config)...
IrNET is an alternate way to transfer TCP/IP traffic over IrDA. It
uses synchronous PPP over a set of point to point IrDA sockets. You
can use it between Linux machine or with W2k.

7
net/irda/irnet/Makefile Normal file
View File

@@ -0,0 +1,7 @@
#
# Makefile for the Linux IrDA IrNET protocol layer.
#
obj-$(CONFIG_IRNET) += irnet.o
irnet-objs := irnet_ppp.o irnet_irda.o

529
net/irda/irnet/irnet.h Normal file
View File

@@ -0,0 +1,529 @@
/*
* IrNET protocol module : Synchronous PPP over an IrDA socket.
*
* Jean II - HPL `00 - <jt@hpl.hp.com>
*
* This file contains definitions and declarations global to the IrNET module,
* all grouped in one place...
* This file is a *private* header, so other modules don't want to know
* what's in there...
*
* Note : as most part of the Linux kernel, this module is available
* under the GNU General Public License (GPL).
*/
#ifndef IRNET_H
#define IRNET_H
/************************** DOCUMENTATION ***************************/
/*
* What is IrNET
* -------------
* IrNET is a protocol allowing to carry TCP/IP traffic between two
* IrDA peers in an efficient fashion. It is a thin layer, passing PPP
* packets to IrTTP and vice versa. It uses PPP in synchronous mode,
* because IrTTP offer a reliable sequenced packet service (as opposed
* to a byte stream). In fact, you could see IrNET as carrying TCP/IP
* in a IrDA socket, using PPP to provide the glue.
*
* The main difference with traditional PPP over IrCOMM is that we
* avoid the framing and serial emulation which are a performance
* bottleneck. It also allows multipoint communications in a sensible
* fashion.
*
* The main difference with IrLAN is that we use PPP for the link
* management, which is more standard, interoperable and flexible than
* the IrLAN protocol. For example, PPP adds authentication,
* encryption, compression, header compression and automated routing
* setup. And, as IrNET let PPP do the hard work, the implementation
* is much simpler than IrLAN.
*
* The Linux implementation
* ------------------------
* IrNET is written on top of the Linux-IrDA stack, and interface with
* the generic Linux PPP driver. Because IrNET depend on recent
* changes of the PPP driver interface, IrNET will work only with very
* recent kernel (2.3.99-pre6 and up).
*
* The present implementation offer the following features :
* o simple user interface using pppd
* o efficient implementation (interface directly to PPP and IrTTP)
* o addressing (you can specify the name of the IrNET recipient)
* o multipoint operation (limited by IrLAP specification)
* o information in /proc/net/irda/irnet
* o IrNET events on /dev/irnet (for user space daemon)
* o IrNET daemon (irnetd) to automatically handle incoming requests
* o Windows 2000 compatibility (tested, but need more work)
* Currently missing :
* o Lot's of testing (that's your job)
* o Connection retries (may be too hard to do)
* o Check pppd persist mode
* o User space daemon (to automatically handle incoming requests)
*
* The setup is not currently the most easy, but this should get much
* better when everything will get integrated...
*
* Acknowledgements
* ----------------
* This module is based on :
* o The PPP driver (ppp_synctty/ppp_generic) by Paul Mackerras
* o The IrLAN protocol (irlan_common/XXX) by Dag Brattli
* o The IrSock interface (af_irda) by Dag Brattli
* o Some other bits from the kernel and my drivers...
* Infinite thanks to those brave souls for providing the infrastructure
* upon which IrNET is built.
*
* Thanks to all my collegues in HP for helping me. In particular,
* thanks to Salil Pradhan and Bill Serra for W2k testing...
* Thanks to Luiz Magalhaes for irnetd and much testing...
*
* Thanks to Alan Cox for answering lot's of my stupid questions, and
* to Paul Mackerras answering my questions on how to best integrate
* IrNET and pppd.
*
* Jean II
*
* Note on some implementations choices...
* ------------------------------------
* 1) Direct interface vs tty/socket
* I could have used a tty interface to hook to ppp and use the full
* socket API to connect to IrDA. The code would have been easier to
* maintain, and maybe the code would have been smaller...
* Instead, we hook directly to ppp_generic and to IrTTP, which make
* things more complicated...
*
* The first reason is flexibility : this allow us to create IrNET
* instances on demand (no /dev/ircommX crap) and to allow linkname
* specification on pppd command line...
*
* Second reason is speed optimisation. If you look closely at the
* transmit and receive paths, you will notice that they are "super lean"
* (that's why they look ugly), with no function calls and as little data
* copy and modification as I could...
*
* 2) irnetd in user space
* irnetd is implemented in user space, which is necessary to call pppd.
* This also give maximum benefits in term of flexibility and customability,
* and allow to offer the event channel, useful for other stuff like debug.
*
* On the other hand, this require a loose coordination between the
* present module and irnetd. One critical area is how incoming request
* are handled.
* When irnet receive an incoming request, it send an event to irnetd and
* drop the incoming IrNET socket.
* irnetd start a pppd instance, which create a new IrNET socket. This new
* socket is then connected in the originating node to the pppd instance.
* At this point, in the originating node, the first socket is closed.
*
* I admit, this is a bit messy and waste some resources. The alternative
* is caching incoming socket, and that's also quite messy and waste
* resources.
* We also make connection time slower. For example, on a 115 kb/s link it
* adds 60ms to the connection time (770 ms). However, this is slower than
* the time it takes to fire up pppd on my P133...
*
*
* History :
* -------
*
* v1 - 15.5.00 - Jean II
* o Basic IrNET (hook to ppp_generic & IrTTP - incl. multipoint)
* o control channel on /dev/irnet (set name/address)
* o event channel on /dev/irnet (for user space daemon)
*
* v2 - 5.6.00 - Jean II
* o Enable DROP_NOT_READY to avoid PPP timeouts & other weirdness...
* o Add DISCONNECT_TO event and rename DISCONNECT_FROM.
* o Set official device number alloaction on /dev/irnet
*
* v3 - 30.8.00 - Jean II
* o Update to latest Linux-IrDA changes :
* - queue_t => irda_queue_t
* o Update to ppp-2.4.0 :
* - move irda_irnet_connect from PPPIOCATTACH to TIOCSETD
* o Add EXPIRE event (depend on new IrDA-Linux patch)
* o Switch from `hashbin_remove' to `hashbin_remove_this' to fix
* a multilink bug... (depend on new IrDA-Linux patch)
* o fix a self->daddr to self->raddr in irda_irnet_connect to fix
* another multilink bug (darn !)
* o Remove LINKNAME_IOCTL cruft
*
* v3b - 31.8.00 - Jean II
* o Dump discovery log at event channel startup
*
* v4 - 28.9.00 - Jean II
* o Fix interaction between poll/select and dump discovery log
* o Add IRNET_BLOCKED_LINK event (depend on new IrDA-Linux patch)
* o Add IRNET_NOANSWER_FROM event (mostly to help support)
* o Release flow control in disconnect_indication
* o Block packets while connecting (speed up connections)
*
* v5 - 11.01.01 - Jean II
* o Init self->max_header_size, just in case...
* o Set up ap->chan.hdrlen, to get zero copy on tx side working.
* o avoid tx->ttp->flow->ppp->tx->... loop, by checking flow state
* Thanks to Christian Gennerat for finding this bug !
* ---
* o Declare the proper MTU/MRU that we can support
* (but PPP doesn't read the MTU value :-()
* o Declare hashbin HB_NOLOCK instead of HB_LOCAL to avoid
* disabling and enabling irq twice
*
* v6 - 31.05.01 - Jean II
* o Print source address in Found, Discovery, Expiry & Request events
* o Print requested source address in /proc/net/irnet
* o Change control channel input. Allow multiple commands in one line.
* o Add saddr command to change ap->rsaddr (and use that in IrDA)
* ---
* o Make the IrDA connection procedure totally asynchronous.
* Heavy rewrite of the IAS query code and the whole connection
* procedure. Now, irnet_connect() no longer need to be called from
* a process context...
* o Enable IrDA connect retries in ppp_irnet_send(). The good thing
* is that IrDA connect retries are directly driven by PPP LCP
* retries (we retry for each LCP packet), so that everything
* is transparently controlled from pppd lcp-max-configure.
* o Add ttp_connect flag to prevent rentry on the connect procedure
* o Test and fixups to eliminate side effects of retries
*
* v7 - 22.08.01 - Jean II
* o Cleanup : Change "saddr = 0x0" to "saddr = DEV_ADDR_ANY"
* o Fix bug in BLOCK_WHEN_CONNECT introduced in v6 : due to the
* asynchronous IAS query, self->tsap is NULL when PPP send the
* first packet. This was preventing "connect-delay 0" to work.
* Change the test in ppp_irnet_send() to self->ttp_connect.
*
* v8 - 1.11.01 - Jean II
* o Tighten the use of self->ttp_connect and self->ttp_open to
* prevent various race conditions.
* o Avoid leaking discovery log and skb
* o Replace "self" with "server" in irnet_connect_indication() to
* better detect cut'n'paste error ;-)
*
* v9 - 29.11.01 - Jean II
* o Fix event generation in disconnect indication that I broke in v8
* It was always generation "No-Answer" because I was testing ttp_open
* just after clearing it. *blush*.
* o Use newly created irttp_listen() to fix potential crash when LAP
* destroyed before irnet module removed.
*
* v10 - 4.3.2 - Jean II
* o When receiving a disconnect indication, don't reenable the
* PPP Tx queue, this will trigger a reconnect. Instead, close
* the channel, which will kill pppd...
*
* v11 - 20.3.02 - Jean II
* o Oops ! v10 fix disabled IrNET retries and passive behaviour.
* Better fix in irnet_disconnect_indication() :
* - if connected, kill pppd via hangup.
* - if not connected, reenable ppp Tx, which trigger IrNET retry.
*
* v12 - 10.4.02 - Jean II
* o Fix race condition in irnet_connect_indication().
* If the socket was already trying to connect, drop old connection
* and use new one only if acting as primary. See comments.
*
* v13 - 30.5.02 - Jean II
* o Update module init code
*
* v14 - 20.2.03 - Jean II
* o Add discovery hint bits in the control channel.
* o Remove obsolete MOD_INC/DEC_USE_COUNT in favor of .owner
*
* v15 - 7.4.03 - Jean II
* o Replace spin_lock_irqsave() with spin_lock_bh() so that we can
* use ppp_unit_number(). It's probably also better overall...
* o Disable call to ppp_unregister_channel(), because we can't do it.
*/
/***************************** INCLUDES *****************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/tty.h>
#include <linux/proc_fs.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/netdevice.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#include <linux/config.h>
#include <linux/ctype.h> /* isspace() */
#include <asm/uaccess.h>
#include <linux/init.h>
#include <linux/ppp_defs.h>
#include <linux/if_ppp.h>
#include <linux/ppp_channel.h>
#include <net/irda/irda.h>
#include <net/irda/iriap.h>
#include <net/irda/irias_object.h>
#include <net/irda/irlmp.h>
#include <net/irda/irttp.h>
#include <net/irda/discovery.h>
/***************************** OPTIONS *****************************/
/*
* Define or undefine to compile or not some optional part of the
* IrNET driver...
* Note : the present defaults make sense, play with that at your
* own risk...
*/
/* IrDA side of the business... */
#define DISCOVERY_NOMASK /* To enable W2k compatibility... */
#define ADVERTISE_HINT /* Advertise IrLAN hint bit */
#define ALLOW_SIMULT_CONNECT /* This seem to work, cross fingers... */
#define DISCOVERY_EVENTS /* Query the discovery log to post events */
#define INITIAL_DISCOVERY /* Dump current discovery log as events */
#undef STREAM_COMPAT /* Not needed - potentially messy */
#undef CONNECT_INDIC_KICK /* Might mess IrDA, not needed */
#undef FAIL_SEND_DISCONNECT /* Might mess IrDA, not needed */
#undef PASS_CONNECT_PACKETS /* Not needed ? Safe */
#undef MISSING_PPP_API /* Stuff I wish I could do */
/* PPP side of the business */
#define BLOCK_WHEN_CONNECT /* Block packets when connecting */
#define CONNECT_IN_SEND /* Retry IrDA connection procedure */
#undef FLUSH_TO_PPP /* Not sure about this one, let's play safe */
#undef SECURE_DEVIRNET /* Bah... */
/****************************** DEBUG ******************************/
/*
* This set of flags enable and disable all the various warning,
* error and debug message of this driver.
* Each section can be enabled and disabled independently
*/
/* In the PPP part */
#define DEBUG_CTRL_TRACE 0 /* Control channel */
#define DEBUG_CTRL_INFO 0 /* various info */
#define DEBUG_CTRL_ERROR 1 /* problems */
#define DEBUG_FS_TRACE 0 /* filesystem callbacks */
#define DEBUG_FS_INFO 0 /* various info */
#define DEBUG_FS_ERROR 1 /* problems */
#define DEBUG_PPP_TRACE 0 /* PPP related functions */
#define DEBUG_PPP_INFO 0 /* various info */
#define DEBUG_PPP_ERROR 1 /* problems */
#define DEBUG_MODULE_TRACE 0 /* module insertion/removal */
#define DEBUG_MODULE_ERROR 1 /* problems */
/* In the IrDA part */
#define DEBUG_IRDA_SR_TRACE 0 /* IRDA subroutines */
#define DEBUG_IRDA_SR_INFO 0 /* various info */
#define DEBUG_IRDA_SR_ERROR 1 /* problems */
#define DEBUG_IRDA_SOCK_TRACE 0 /* IRDA main socket functions */
#define DEBUG_IRDA_SOCK_INFO 0 /* various info */
#define DEBUG_IRDA_SOCK_ERROR 1 /* problems */
#define DEBUG_IRDA_SERV_TRACE 0 /* The IrNET server */
#define DEBUG_IRDA_SERV_INFO 0 /* various info */
#define DEBUG_IRDA_SERV_ERROR 1 /* problems */
#define DEBUG_IRDA_TCB_TRACE 0 /* IRDA IrTTP callbacks */
#define DEBUG_IRDA_CB_INFO 0 /* various info */
#define DEBUG_IRDA_CB_ERROR 1 /* problems */
#define DEBUG_IRDA_OCB_TRACE 0 /* IRDA other callbacks */
#define DEBUG_IRDA_OCB_INFO 0 /* various info */
#define DEBUG_IRDA_OCB_ERROR 1 /* problems */
#define DEBUG_ASSERT 0 /* Verify all assertions */
/*
* These are the macros we are using to actually print the debug
* statements. Don't look at it, it's ugly...
*
* One of the trick is that, as the DEBUG_XXX are constant, the
* compiler will optimise away the if() in all cases.
*/
/* All error messages (will show up in the normal logs) */
#define DERROR(dbg, format, args...) \
{if(DEBUG_##dbg) \
printk(KERN_INFO "irnet: %s(): " format, __FUNCTION__ , ##args);}
/* Normal debug message (will show up in /var/log/debug) */
#define DEBUG(dbg, format, args...) \
{if(DEBUG_##dbg) \
printk(KERN_DEBUG "irnet: %s(): " format, __FUNCTION__ , ##args);}
/* Entering a function (trace) */
#define DENTER(dbg, format, args...) \
{if(DEBUG_##dbg) \
printk(KERN_DEBUG "irnet: -> %s" format, __FUNCTION__ , ##args);}
/* Entering and exiting a function in one go (trace) */
#define DPASS(dbg, format, args...) \
{if(DEBUG_##dbg) \
printk(KERN_DEBUG "irnet: <>%s" format, __FUNCTION__ , ##args);}
/* Exiting a function (trace) */
#define DEXIT(dbg, format, args...) \
{if(DEBUG_##dbg) \
printk(KERN_DEBUG "irnet: <-%s()" format, __FUNCTION__ , ##args);}
/* Exit a function with debug */
#define DRETURN(ret, dbg, args...) \
{DEXIT(dbg, ": " args);\
return ret; }
/* Exit a function on failed condition */
#define DABORT(cond, ret, dbg, args...) \
{if(cond) {\
DERROR(dbg, args);\
return ret; }}
/* Invalid assertion, print out an error and exit... */
#define DASSERT(cond, ret, dbg, args...) \
{if((DEBUG_ASSERT) && !(cond)) {\
DERROR(dbg, "Invalid assertion: " args);\
return ret; }}
/************************ CONSTANTS & MACROS ************************/
/* Paranoia */
#define IRNET_MAGIC 0xB00754
/* Number of control events in the control channel buffer... */
#define IRNET_MAX_EVENTS 8 /* Should be more than enough... */
/****************************** TYPES ******************************/
/*
* This is the main structure where we store all the data pertaining to
* one instance of irnet.
* Note : in irnet functions, a pointer this structure is usually called
* "ap" or "self". If the code is borrowed from the IrDA stack, it tend
* to be called "self", and if it is borrowed from the PPP driver it is
* "ap". Apart from that, it's exactly the same structure ;-)
*/
typedef struct irnet_socket
{
/* ------------------- Instance management ------------------- */
/* We manage a linked list of IrNET socket instances */
irda_queue_t q; /* Must be first - for hasbin */
int magic; /* Paranoia */
/* --------------------- FileSystem part --------------------- */
/* "pppd" interact directly with us on a /dev/ file */
struct file * file; /* File descriptor of this instance */
/* TTY stuff - to keep "pppd" happy */
struct termios termios; /* Various tty flags */
/* Stuff for the control channel */
int event_index; /* Last read in the event log */
/* ------------------------- PPP part ------------------------- */
/* We interface directly to the ppp_generic driver in the kernel */
int ppp_open; /* registered with ppp_generic */
struct ppp_channel chan; /* Interface to generic ppp layer */
int mru; /* Max size of PPP payload */
u32 xaccm[8]; /* Asynchronous character map (just */
u32 raccm; /* to please pppd - dummy) */
unsigned int flags; /* PPP flags (compression, ...) */
unsigned int rbits; /* Unused receive flags ??? */
/* ------------------------ IrTTP part ------------------------ */
/* We create a pseudo "socket" over the IrDA tranport */
unsigned long ttp_open; /* Set when IrTTP is ready */
unsigned long ttp_connect; /* Set when IrTTP is connecting */
struct tsap_cb * tsap; /* IrTTP instance (the connection) */
char rname[NICKNAME_MAX_LEN + 1];
/* IrDA nickname of destination */
__u32 rdaddr; /* Requested peer IrDA address */
__u32 rsaddr; /* Requested local IrDA address */
__u32 daddr; /* actual peer IrDA address */
__u32 saddr; /* my local IrDA address */
__u8 dtsap_sel; /* Remote TSAP selector */
__u8 stsap_sel; /* Local TSAP selector */
__u32 max_sdu_size_rx;/* Socket parameters used for IrTTP */
__u32 max_sdu_size_tx;
__u32 max_data_size;
__u8 max_header_size;
LOCAL_FLOW tx_flow; /* State of the Tx path in IrTTP */
/* ------------------- IrLMP and IrIAS part ------------------- */
/* Used for IrDA Discovery and socket name resolution */
void * ckey; /* IrLMP client handle */
__u16 mask; /* Hint bits mask (filter discov.)*/
int nslots; /* Number of slots for discovery */
struct iriap_cb * iriap; /* Used to query remote IAS */
int errno; /* status of the IAS query */
/* -------------------- Discovery log part -------------------- */
/* Used by initial discovery on the control channel
* and by irnet_discover_daddr_and_lsap_sel() */
struct irda_device_info *discoveries; /* Copy of the discovery log */
int disco_index; /* Last read in the discovery log */
int disco_number; /* Size of the discovery log */
} irnet_socket;
/*
* This is the various event that we will generate on the control channel
*/
typedef enum irnet_event
{
IRNET_DISCOVER, /* New IrNET node discovered */
IRNET_EXPIRE, /* IrNET node expired */
IRNET_CONNECT_TO, /* IrNET socket has connected to other node */
IRNET_CONNECT_FROM, /* Other node has connected to IrNET socket */
IRNET_REQUEST_FROM, /* Non satisfied connection request */
IRNET_NOANSWER_FROM, /* Failed connection request */
IRNET_BLOCKED_LINK, /* Link (IrLAP) is blocked for > 3s */
IRNET_DISCONNECT_FROM, /* IrNET socket has disconnected */
IRNET_DISCONNECT_TO /* Closing IrNET socket */
} irnet_event;
/*
* This is the storage for an event and its arguments
*/
typedef struct irnet_log
{
irnet_event event;
int unit;
__u32 saddr;
__u32 daddr;
char name[NICKNAME_MAX_LEN + 1]; /* 21 + 1 */
__u16_host_order hints; /* Discovery hint bits */
} irnet_log;
/*
* This is the storage for all events and related stuff...
*/
typedef struct irnet_ctrl_channel
{
irnet_log log[IRNET_MAX_EVENTS]; /* Event log */
int index; /* Current index in log */
spinlock_t spinlock; /* Serialize access to the event log */
wait_queue_head_t rwait; /* processes blocked on read (or poll) */
} irnet_ctrl_channel;
/**************************** PROTOTYPES ****************************/
/*
* Global functions of the IrNET module
* Note : we list here also functions called from one file to the other.
*/
/* -------------------------- IRDA PART -------------------------- */
extern int
irda_irnet_create(irnet_socket *); /* Initialise a IrNET socket */
extern int
irda_irnet_connect(irnet_socket *); /* Try to connect over IrDA */
extern void
irda_irnet_destroy(irnet_socket *); /* Teardown a IrNET socket */
extern int
irda_irnet_init(void); /* Initialise IrDA part of IrNET */
extern void
irda_irnet_cleanup(void); /* Teardown IrDA part of IrNET */
/* ---------------------------- MODULE ---------------------------- */
extern int
irnet_init(void); /* Initialise IrNET module */
/**************************** VARIABLES ****************************/
/* Control channel stuff - allocated in irnet_irda.h */
extern struct irnet_ctrl_channel irnet_events;
#endif /* IRNET_H */

1866
net/irda/irnet/irnet_irda.c Normal file

File diff suppressed because it is too large Load Diff

186
net/irda/irnet/irnet_irda.h Normal file
View File

@@ -0,0 +1,186 @@
/*
* IrNET protocol module : Synchronous PPP over an IrDA socket.
*
* Jean II - HPL `00 - <jt@hpl.hp.com>
*
* This file contains all definitions and declarations necessary for the
* IRDA part of the IrNET module (dealing with IrTTP, IrIAS and co).
* This file is a private header, so other modules don't want to know
* what's in there...
*/
#ifndef IRNET_IRDA_H
#define IRNET_IRDA_H
/***************************** INCLUDES *****************************/
/* Please add other headers in irnet.h */
#include "irnet.h" /* Module global include */
/************************ CONSTANTS & MACROS ************************/
/*
* Name of the service (socket name) used by IrNET
*/
/* IAS object name (or part of it) */
#define IRNET_SERVICE_NAME "IrNetv1"
/* IAS attribute */
#define IRNET_IAS_VALUE "IrDA:TinyTP:LsapSel"
/* LMP notify name for client (only for /proc/net/irda/irlmp) */
#define IRNET_NOTIFY_NAME "IrNET socket"
/* LMP notify name for server (only for /proc/net/irda/irlmp) */
#define IRNET_NOTIFY_NAME_SERV "IrNET server"
/****************************** TYPES ******************************/
/*
* This is the main structure where we store all the data pertaining to
* the IrNET server (listen for connection requests) and the root
* of the IrNET socket list
*/
typedef struct irnet_root
{
irnet_socket s; /* To pretend we are a client... */
/* Generic stuff */
int magic; /* Paranoia */
int running; /* Are we operational ? */
/* Link list of all IrNET instances opened */
hashbin_t * list;
spinlock_t spinlock; /* Serialize access to the list */
/* Note : the way hashbin has been designed is absolutely not
* reentrant, beware... So, we blindly protect all with spinlock */
/* Handle for the hint bit advertised in IrLMP */
void * skey;
/* Server socket part */
struct ias_object * ias_obj; /* Our service name + lsap in IAS */
} irnet_root;
/**************************** PROTOTYPES ****************************/
/* ----------------------- CONTROL CHANNEL ----------------------- */
static void
irnet_post_event(irnet_socket *,
irnet_event,
__u32,
__u32,
char *,
__u16);
/* ----------------------- IRDA SUBROUTINES ----------------------- */
static inline int
irnet_open_tsap(irnet_socket *);
static inline __u8
irnet_ias_to_tsap(irnet_socket *,
int,
struct ias_value *);
static inline int
irnet_find_lsap_sel(irnet_socket *);
static inline int
irnet_connect_tsap(irnet_socket *);
static inline int
irnet_discover_next_daddr(irnet_socket *);
static inline int
irnet_discover_daddr_and_lsap_sel(irnet_socket *);
static inline int
irnet_dname_to_daddr(irnet_socket *);
/* ------------------------ SERVER SOCKET ------------------------ */
static inline int
irnet_daddr_to_dname(irnet_socket *);
static inline irnet_socket *
irnet_find_socket(irnet_socket *);
static inline int
irnet_connect_socket(irnet_socket *,
irnet_socket *,
struct qos_info *,
__u32,
__u8);
static inline void
irnet_disconnect_server(irnet_socket *,
struct sk_buff *);
static inline int
irnet_setup_server(void);
static inline void
irnet_destroy_server(void);
/* ---------------------- IRDA-TTP CALLBACKS ---------------------- */
static int
irnet_data_indication(void *, /* instance */
void *, /* sap */
struct sk_buff *);
static void
irnet_disconnect_indication(void *,
void *,
LM_REASON,
struct sk_buff *);
static void
irnet_connect_confirm(void *,
void *,
struct qos_info *,
__u32,
__u8,
struct sk_buff *);
static void
irnet_flow_indication(void *,
void *,
LOCAL_FLOW);
static void
irnet_status_indication(void *,
LINK_STATUS,
LOCK_STATUS);
static void
irnet_connect_indication(void *,
void *,
struct qos_info *,
__u32,
__u8,
struct sk_buff *);
/* -------------------- IRDA-IAS/LMP CALLBACKS -------------------- */
static void
irnet_getvalue_confirm(int,
__u16,
struct ias_value *,
void *);
static void
irnet_discovervalue_confirm(int,
__u16,
struct ias_value *,
void *);
#ifdef DISCOVERY_EVENTS
static void
irnet_discovery_indication(discinfo_t *,
DISCOVERY_MODE,
void *);
static void
irnet_expiry_indication(discinfo_t *,
DISCOVERY_MODE,
void *);
#endif
/* -------------------------- PROC ENTRY -------------------------- */
#ifdef CONFIG_PROC_FS
static int
irnet_proc_read(char *,
char **,
off_t,
int);
#endif /* CONFIG_PROC_FS */
/**************************** VARIABLES ****************************/
/*
* The IrNET server. Listen to connection requests and co...
*/
static struct irnet_root irnet_server;
/* Control channel stuff (note : extern) */
struct irnet_ctrl_channel irnet_events;
/* The /proc/net/irda directory, defined elsewhere... */
#ifdef CONFIG_PROC_FS
extern struct proc_dir_entry *proc_irda;
#endif /* CONFIG_PROC_FS */
#endif /* IRNET_IRDA_H */

1142
net/irda/irnet/irnet_ppp.c Normal file

File diff suppressed because it is too large Load Diff

119
net/irda/irnet/irnet_ppp.h Normal file
View File

@@ -0,0 +1,119 @@
/*
* IrNET protocol module : Synchronous PPP over an IrDA socket.
*
* Jean II - HPL `00 - <jt@hpl.hp.com>
*
* This file contains all definitions and declarations necessary for the
* PPP part of the IrNET module.
* This file is a private header, so other modules don't want to know
* what's in there...
*/
#ifndef IRNET_PPP_H
#define IRNET_PPP_H
/***************************** INCLUDES *****************************/
#include "irnet.h" /* Module global include */
/************************ CONSTANTS & MACROS ************************/
/* /dev/irnet file constants */
#define IRNET_MAJOR 10 /* Misc range */
#define IRNET_MINOR 187 /* Official allocation */
/* IrNET control channel stuff */
#define IRNET_MAX_COMMAND 256 /* Max length of a command line */
/* PPP hardcore stuff */
/* Bits in rbits (PPP flags in irnet struct) */
#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
/* Bit numbers in busy */
#define XMIT_BUSY 0
#define RECV_BUSY 1
#define XMIT_WAKEUP 2
#define XMIT_FULL 3
/* Queue management */
#define PPPSYNC_MAX_RQLEN 32 /* arbitrary */
/****************************** TYPES ******************************/
/**************************** PROTOTYPES ****************************/
/* ----------------------- CONTROL CHANNEL ----------------------- */
static inline ssize_t
irnet_ctrl_write(irnet_socket *,
const char *,
size_t);
static inline ssize_t
irnet_ctrl_read(irnet_socket *,
struct file *,
char *,
size_t);
static inline unsigned int
irnet_ctrl_poll(irnet_socket *,
struct file *,
poll_table *);
/* ----------------------- CHARACTER DEVICE ----------------------- */
static int
dev_irnet_open(struct inode *, /* fs callback : open */
struct file *),
dev_irnet_close(struct inode *,
struct file *);
static ssize_t
dev_irnet_write(struct file *,
const char __user *,
size_t,
loff_t *),
dev_irnet_read(struct file *,
char __user *,
size_t,
loff_t *);
static unsigned int
dev_irnet_poll(struct file *,
poll_table *);
static int
dev_irnet_ioctl(struct inode *,
struct file *,
unsigned int,
unsigned long);
/* ------------------------ PPP INTERFACE ------------------------ */
static inline struct sk_buff *
irnet_prepare_skb(irnet_socket *,
struct sk_buff *);
static int
ppp_irnet_send(struct ppp_channel *,
struct sk_buff *);
static int
ppp_irnet_ioctl(struct ppp_channel *,
unsigned int,
unsigned long);
/**************************** VARIABLES ****************************/
/* Filesystem callbacks (to call us) */
static struct file_operations irnet_device_fops =
{
.owner = THIS_MODULE,
.read = dev_irnet_read,
.write = dev_irnet_write,
.poll = dev_irnet_poll,
.ioctl = dev_irnet_ioctl,
.open = dev_irnet_open,
.release = dev_irnet_close
/* Also : llseek, readdir, mmap, flush, fsync, fasync, lock, readv, writev */
};
/* Structure so that the misc major (drivers/char/misc.c) take care of us... */
static struct miscdevice irnet_misc_device =
{
IRNET_MINOR,
"irnet",
&irnet_device_fops
};
#endif /* IRNET_PPP_H */

100
net/irda/irproc.c Normal file
View File

@@ -0,0 +1,100 @@
/*********************************************************************
*
* Filename: irproc.c
* Version: 1.0
* Description: Various entries in the /proc file system
* Status: Experimental.
* Author: Thomas Davis, <ratbert@radiks.net>
* Created at: Sat Feb 21 21:33:24 1998
* Modified at: Sun Nov 14 08:54:54 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998-1999, Dag Brattli <dagb@cs.uit.no>
* Copyright (c) 1998, Thomas Davis, <ratbert@radiks.net>,
* All Rights Reserved.
*
* 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.
*
* I, Thomas Davis, provide no warranty for any of this software.
* This material is provided "AS-IS" and at no charge.
*
********************************************************************/
#include <linux/miscdevice.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/init.h>
#include <net/irda/irda.h>
#include <net/irda/irlap.h>
#include <net/irda/irlmp.h>
extern struct file_operations discovery_seq_fops;
extern struct file_operations irlap_seq_fops;
extern struct file_operations irlmp_seq_fops;
extern struct file_operations irttp_seq_fops;
extern struct file_operations irias_seq_fops;
struct irda_entry {
const char *name;
struct file_operations *fops;
};
struct proc_dir_entry *proc_irda;
EXPORT_SYMBOL(proc_irda);
static struct irda_entry irda_dirs[] = {
{"discovery", &discovery_seq_fops},
{"irttp", &irttp_seq_fops},
{"irlmp", &irlmp_seq_fops},
{"irlap", &irlap_seq_fops},
{"irias", &irias_seq_fops},
};
/*
* Function irda_proc_register (void)
*
* Register irda entry in /proc file system
*
*/
void __init irda_proc_register(void)
{
int i;
struct proc_dir_entry *d;
proc_irda = proc_mkdir("irda", proc_net);
if (proc_irda == NULL)
return;
proc_irda->owner = THIS_MODULE;
for (i=0; i<ARRAY_SIZE(irda_dirs); i++) {
d = create_proc_entry(irda_dirs[i].name, 0, proc_irda);
if (d)
d->proc_fops = irda_dirs[i].fops;
}
}
/*
* Function irda_proc_unregister (void)
*
* Unregister irda entry in /proc file system
*
*/
void __exit irda_proc_unregister(void)
{
int i;
if (proc_irda) {
for (i=0; i<ARRAY_SIZE(irda_dirs); i++)
remove_proc_entry(irda_dirs[i].name, proc_irda);
remove_proc_entry("irda", proc_net);
proc_irda = NULL;
}
}

915
net/irda/irqueue.c Normal file
View File

@@ -0,0 +1,915 @@
/*********************************************************************
*
* Filename: irqueue.c
* Version: 0.3
* Description: General queue implementation
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Jun 9 13:29:31 1998
* Modified at: Sun Dec 12 13:48:22 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Modified at: Thu Jan 4 14:29:10 CET 2001
* Modified by: Marc Zyngier <mzyngier@freesurf.fr>
*
* Copyright (C) 1998-1999, Aage Kvalnes <aage@cs.uit.no>
* Copyright (C) 1998, Dag Brattli,
* All Rights Reserved.
*
* This code is taken from the Vortex Operating System written by Aage
* Kvalnes. Aage has agreed that this code can use the GPL licence,
* although he does not use that licence in his own code.
*
* This copyright does however _not_ include the ELF hash() function
* which I currently don't know which licence or copyright it
* has. Please inform me if you know.
*
* 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
/*
* NOTE :
* There are various problems with this package :
* o the hash function for ints is pathetic (but could be changed)
* o locking is sometime suspicious (especially during enumeration)
* o most users have only a few elements (== overhead)
* o most users never use seach, so don't benefit from hashing
* Problem already fixed :
* o not 64 bit compliant (most users do hashv = (int) self)
* o hashbin_remove() is broken => use hashbin_remove_this()
* I think most users would be better served by a simple linked list
* (like include/linux/list.h) with a global spinlock per list.
* Jean II
*/
/*
* Notes on the concurrent access to hashbin and other SMP issues
* -------------------------------------------------------------
* Hashbins are very often in the IrDA stack a global repository of
* information, and therefore used in a very asynchronous manner following
* various events (driver calls, timers, user calls...).
* Therefore, very often it is highly important to consider the
* management of concurrent access to the hashbin and how to guarantee the
* consistency of the operations on it.
*
* First, we need to define the objective of locking :
* 1) Protect user data (content pointed by the hashbin)
* 2) Protect hashbin structure itself (linked list in each bin)
*
* OLD LOCKING
* -----------
*
* The previous locking strategy, either HB_LOCAL or HB_GLOBAL were
* both inadequate in *both* aspect.
* o HB_GLOBAL was using a spinlock for each bin (local locking).
* o HB_LOCAL was disabling irq on *all* CPUs, so use a single
* global semaphore.
* The problems were :
* A) Global irq disabling is no longer supported by the kernel
* B) No protection for the hashbin struct global data
* o hashbin_delete()
* o hb_current
* C) No protection for user data in some cases
*
* A) HB_LOCAL use global irq disabling, so doesn't work on kernel
* 2.5.X. Even when it is supported (kernel 2.4.X and earlier), its
* performance is not satisfactory on SMP setups. Most hashbins were
* HB_LOCAL, so (A) definitely need fixing.
* B) HB_LOCAL could be modified to fix (B). However, because HB_GLOBAL
* lock only the individual bins, it will never be able to lock the
* global data, so can't do (B).
* C) Some functions return pointer to data that is still in the
* hashbin :
* o hashbin_find()
* o hashbin_get_first()
* o hashbin_get_next()
* As the data is still in the hashbin, it may be changed or free'd
* while the caller is examinimg the data. In those case, locking can't
* be done within the hashbin, but must include use of the data within
* the caller.
* The caller can easily do this with HB_LOCAL (just disable irqs).
* However, this is impossible with HB_GLOBAL because the caller has no
* way to know the proper bin, so don't know which spinlock to use.
*
* Quick summary : can no longer use HB_LOCAL, and HB_GLOBAL is
* fundamentally broken and will never work.
*
* NEW LOCKING
* -----------
*
* To fix those problems, I've introduce a few changes in the
* hashbin locking :
* 1) New HB_LOCK scheme
* 2) hashbin->hb_spinlock
* 3) New hashbin usage policy
*
* HB_LOCK :
* -------
* HB_LOCK is a locking scheme intermediate between the old HB_LOCAL
* and HB_GLOBAL. It uses a single spinlock to protect the whole content
* of the hashbin. As it is a single spinlock, it can protect the global
* data of the hashbin and not only the bins themselves.
* HB_LOCK can only protect some of the hashbin calls, so it only lock
* call that can be made 100% safe and leave other call unprotected.
* HB_LOCK in theory is slower than HB_GLOBAL, but as the hashbin
* content is always small contention is not high, so it doesn't matter
* much. HB_LOCK is probably faster than HB_LOCAL.
*
* hashbin->hb_spinlock :
* --------------------
* The spinlock that HB_LOCK uses is available for caller, so that
* the caller can protect unprotected calls (see below).
* If the caller want to do entirely its own locking (HB_NOLOCK), he
* can do so and may use safely this spinlock.
* Locking is done like this :
* spin_lock_irqsave(&hashbin->hb_spinlock, flags);
* Releasing the lock :
* spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
*
* Safe & Protected calls :
* ----------------------
* The following calls are safe or protected via HB_LOCK :
* o hashbin_new() -> safe
* o hashbin_delete()
* o hashbin_insert()
* o hashbin_remove_first()
* o hashbin_remove()
* o hashbin_remove_this()
* o HASHBIN_GET_SIZE() -> atomic
*
* The following calls only protect the hashbin itself :
* o hashbin_lock_find()
* o hashbin_find_next()
*
* Unprotected calls :
* -----------------
* The following calls need to be protected by the caller :
* o hashbin_find()
* o hashbin_get_first()
* o hashbin_get_next()
*
* Locking Policy :
* --------------
* If the hashbin is used only in a single thread of execution
* (explicitly or implicitely), you can use HB_NOLOCK
* If the calling module already provide concurrent access protection,
* you may use HB_NOLOCK.
*
* In all other cases, you need to use HB_LOCK and lock the hashbin
* every time before calling one of the unprotected calls. You also must
* use the pointer returned by the unprotected call within the locked
* region.
*
* Extra care for enumeration :
* --------------------------
* hashbin_get_first() and hashbin_get_next() use the hashbin to
* store the current position, in hb_current.
* As long as the hashbin remains locked, this is safe. If you unlock
* the hashbin, the current position may change if anybody else modify
* or enumerate the hashbin.
* Summary : do the full enumeration while locked.
*
* Alternatively, you may use hashbin_find_next(). But, this will
* be slower, is more complex to use and doesn't protect the hashbin
* content. So, care is needed here as well.
*
* Other issues :
* ------------
* I believe that we are overdoing it by using spin_lock_irqsave()
* and we should use only spin_lock_bh() or similar. But, I don't have
* the balls to try it out.
* Don't believe that because hashbin are now (somewhat) SMP safe
* that the rest of the code is. Higher layers tend to be safest,
* but LAP and LMP would need some serious dedicated love.
*
* Jean II
*/
#include <linux/module.h>
#include <net/irda/irda.h>
#include <net/irda/irqueue.h>
/************************ QUEUE SUBROUTINES ************************/
/*
* Hashbin
*/
#define GET_HASHBIN(x) ( x & HASHBIN_MASK )
/*
* Function hash (name)
*
* This function hash the input string 'name' using the ELF hash
* function for strings.
*/
static __u32 hash( const char* name)
{
__u32 h = 0;
__u32 g;
while(*name) {
h = (h<<4) + *name++;
if ((g = (h & 0xf0000000)))
h ^=g>>24;
h &=~g;
}
return h;
}
/*
* Function enqueue_first (queue, proc)
*
* Insert item first in queue.
*
*/
static void enqueue_first(irda_queue_t **queue, irda_queue_t* element)
{
IRDA_DEBUG( 4, "%s()\n", __FUNCTION__);
/*
* Check if queue is empty.
*/
if ( *queue == NULL ) {
/*
* Queue is empty. Insert one element into the queue.
*/
element->q_next = element->q_prev = *queue = element;
} else {
/*
* Queue is not empty. Insert element into front of queue.
*/
element->q_next = (*queue);
(*queue)->q_prev->q_next = element;
element->q_prev = (*queue)->q_prev;
(*queue)->q_prev = element;
(*queue) = element;
}
}
/*
* Function dequeue (queue)
*
* Remove first entry in queue
*
*/
static irda_queue_t *dequeue_first(irda_queue_t **queue)
{
irda_queue_t *ret;
IRDA_DEBUG( 4, "dequeue_first()\n");
/*
* Set return value
*/
ret = *queue;
if ( *queue == NULL ) {
/*
* Queue was empty.
*/
} else if ( (*queue)->q_next == *queue ) {
/*
* Queue only contained a single element. It will now be
* empty.
*/
*queue = NULL;
} else {
/*
* Queue contained several element. Remove the first one.
*/
(*queue)->q_prev->q_next = (*queue)->q_next;
(*queue)->q_next->q_prev = (*queue)->q_prev;
*queue = (*queue)->q_next;
}
/*
* Return the removed entry (or NULL of queue was empty).
*/
return ret;
}
/*
* Function dequeue_general (queue, element)
*
*
*/
static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element)
{
irda_queue_t *ret;
IRDA_DEBUG( 4, "dequeue_general()\n");
/*
* Set return value
*/
ret = *queue;
if ( *queue == NULL ) {
/*
* Queue was empty.
*/
} else if ( (*queue)->q_next == *queue ) {
/*
* Queue only contained a single element. It will now be
* empty.
*/
*queue = NULL;
} else {
/*
* Remove specific element.
*/
element->q_prev->q_next = element->q_next;
element->q_next->q_prev = element->q_prev;
if ( (*queue) == element)
(*queue) = element->q_next;
}
/*
* Return the removed entry (or NULL of queue was empty).
*/
return ret;
}
/************************ HASHBIN MANAGEMENT ************************/
/*
* Function hashbin_create ( type, name )
*
* Create hashbin!
*
*/
hashbin_t *hashbin_new(int type)
{
hashbin_t* hashbin;
/*
* Allocate new hashbin
*/
hashbin = kmalloc( sizeof(hashbin_t), GFP_ATOMIC);
if (!hashbin)
return NULL;
/*
* Initialize structure
*/
memset(hashbin, 0, sizeof(hashbin_t));
hashbin->hb_type = type;
hashbin->magic = HB_MAGIC;
//hashbin->hb_current = NULL;
/* Make sure all spinlock's are unlocked */
if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_init(&hashbin->hb_spinlock);
}
return hashbin;
}
EXPORT_SYMBOL(hashbin_new);
/*
* Function hashbin_delete (hashbin, free_func)
*
* Destroy hashbin, the free_func can be a user supplied special routine
* for deallocating this structure if it's complex. If not the user can
* just supply kfree, which should take care of the job.
*/
int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
{
irda_queue_t* queue;
unsigned long flags = 0;
int i;
IRDA_ASSERT(hashbin != NULL, return -1;);
IRDA_ASSERT(hashbin->magic == HB_MAGIC, return -1;);
/* Synchronize */
if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
}
/*
* Free the entries in the hashbin, TODO: use hashbin_clear when
* it has been shown to work
*/
for (i = 0; i < HASHBIN_SIZE; i ++ ) {
queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]);
while (queue ) {
if (free_func)
(*free_func)(queue);
queue = dequeue_first(
(irda_queue_t**) &hashbin->hb_queue[i]);
}
}
/* Cleanup local data */
hashbin->hb_current = NULL;
hashbin->magic = ~HB_MAGIC;
/* Release lock */
if ( hashbin->hb_type & HB_LOCK) {
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
}
/*
* Free the hashbin structure
*/
kfree(hashbin);
return 0;
}
EXPORT_SYMBOL(hashbin_delete);
/********************* HASHBIN LIST OPERATIONS *********************/
/*
* Function hashbin_insert (hashbin, entry, name)
*
* Insert an entry into the hashbin
*
*/
void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv,
const char* name)
{
unsigned long flags = 0;
int bin;
IRDA_DEBUG( 4, "%s()\n", __FUNCTION__);
IRDA_ASSERT( hashbin != NULL, return;);
IRDA_ASSERT( hashbin->magic == HB_MAGIC, return;);
/*
* Locate hashbin
*/
if ( name )
hashv = hash( name );
bin = GET_HASHBIN( hashv );
/* Synchronize */
if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
/*
* Store name and key
*/
entry->q_hash = hashv;
if ( name )
strlcpy( entry->q_name, name, sizeof(entry->q_name));
/*
* Insert new entry first
*/
enqueue_first( (irda_queue_t**) &hashbin->hb_queue[ bin ],
entry);
hashbin->hb_size++;
/* Release lock */
if ( hashbin->hb_type & HB_LOCK ) {
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
}
EXPORT_SYMBOL(hashbin_insert);
/*
* Function hashbin_remove_first (hashbin)
*
* Remove first entry of the hashbin
*
* Note : this function no longer use hashbin_remove(), but does things
* similar to hashbin_remove_this(), so can be considered safe.
* Jean II
*/
void *hashbin_remove_first( hashbin_t *hashbin)
{
unsigned long flags = 0;
irda_queue_t *entry = NULL;
/* Synchronize */
if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
entry = hashbin_get_first( hashbin);
if ( entry != NULL) {
int bin;
long hashv;
/*
* Locate hashbin
*/
hashv = entry->q_hash;
bin = GET_HASHBIN( hashv );
/*
* Dequeue the entry...
*/
dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
(irda_queue_t*) entry );
hashbin->hb_size--;
entry->q_next = NULL;
entry->q_prev = NULL;
/*
* Check if this item is the currently selected item, and in
* that case we must reset hb_current
*/
if ( entry == hashbin->hb_current)
hashbin->hb_current = NULL;
}
/* Release lock */
if ( hashbin->hb_type & HB_LOCK ) {
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
return entry;
}
/*
* Function hashbin_remove (hashbin, hashv, name)
*
* Remove entry with the given name
*
* The use of this function is highly discouraged, because the whole
* concept behind hashbin_remove() is broken. In many cases, it's not
* possible to guarantee the unicity of the index (either hashv or name),
* leading to removing the WRONG entry.
* The only simple safe use is :
* hashbin_remove(hasbin, (int) self, NULL);
* In other case, you must think hard to guarantee unicity of the index.
* Jean II
*/
void* hashbin_remove( hashbin_t* hashbin, long hashv, const char* name)
{
int bin, found = FALSE;
unsigned long flags = 0;
irda_queue_t* entry;
IRDA_DEBUG( 4, "%s()\n", __FUNCTION__);
IRDA_ASSERT( hashbin != NULL, return NULL;);
IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
/*
* Locate hashbin
*/
if ( name )
hashv = hash( name );
bin = GET_HASHBIN( hashv );
/* Synchronize */
if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
/*
* Search for entry
*/
entry = hashbin->hb_queue[ bin ];
if ( entry ) {
do {
/*
* Check for key
*/
if ( entry->q_hash == hashv ) {
/*
* Name compare too?
*/
if ( name ) {
if ( strcmp( entry->q_name, name) == 0)
{
found = TRUE;
break;
}
} else {
found = TRUE;
break;
}
}
entry = entry->q_next;
} while ( entry != hashbin->hb_queue[ bin ] );
}
/*
* If entry was found, dequeue it
*/
if ( found ) {
dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
(irda_queue_t*) entry );
hashbin->hb_size--;
/*
* Check if this item is the currently selected item, and in
* that case we must reset hb_current
*/
if ( entry == hashbin->hb_current)
hashbin->hb_current = NULL;
}
/* Release lock */
if ( hashbin->hb_type & HB_LOCK ) {
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
/* Return */
if ( found )
return entry;
else
return NULL;
}
EXPORT_SYMBOL(hashbin_remove);
/*
* Function hashbin_remove_this (hashbin, entry)
*
* Remove entry with the given name
*
* In some cases, the user of hashbin can't guarantee the unicity
* of either the hashv or name.
* In those cases, using the above function is guaranteed to cause troubles,
* so we use this one instead...
* And by the way, it's also faster, because we skip the search phase ;-)
*/
void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry)
{
unsigned long flags = 0;
int bin;
long hashv;
IRDA_DEBUG( 4, "%s()\n", __FUNCTION__);
IRDA_ASSERT( hashbin != NULL, return NULL;);
IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
IRDA_ASSERT( entry != NULL, return NULL;);
/* Synchronize */
if ( hashbin->hb_type & HB_LOCK ) {
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
/* Check if valid and not already removed... */
if((entry->q_next == NULL) || (entry->q_prev == NULL)) {
entry = NULL;
goto out;
}
/*
* Locate hashbin
*/
hashv = entry->q_hash;
bin = GET_HASHBIN( hashv );
/*
* Dequeue the entry...
*/
dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ],
(irda_queue_t*) entry );
hashbin->hb_size--;
entry->q_next = NULL;
entry->q_prev = NULL;
/*
* Check if this item is the currently selected item, and in
* that case we must reset hb_current
*/
if ( entry == hashbin->hb_current)
hashbin->hb_current = NULL;
out:
/* Release lock */
if ( hashbin->hb_type & HB_LOCK ) {
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
} /* Default is no-lock */
return entry;
}
EXPORT_SYMBOL(hashbin_remove_this);
/*********************** HASHBIN ENUMERATION ***********************/
/*
* Function hashbin_common_find (hashbin, hashv, name)
*
* Find item with the given hashv or name
*
*/
void* hashbin_find( hashbin_t* hashbin, long hashv, const char* name )
{
int bin;
irda_queue_t* entry;
IRDA_DEBUG( 4, "hashbin_find()\n");
IRDA_ASSERT( hashbin != NULL, return NULL;);
IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
/*
* Locate hashbin
*/
if ( name )
hashv = hash( name );
bin = GET_HASHBIN( hashv );
/*
* Search for entry
*/
entry = hashbin->hb_queue[ bin];
if ( entry ) {
do {
/*
* Check for key
*/
if ( entry->q_hash == hashv ) {
/*
* Name compare too?
*/
if ( name ) {
if ( strcmp( entry->q_name, name ) == 0 ) {
return entry;
}
} else {
return entry;
}
}
entry = entry->q_next;
} while ( entry != hashbin->hb_queue[ bin ] );
}
return NULL;
}
EXPORT_SYMBOL(hashbin_find);
/*
* Function hashbin_lock_find (hashbin, hashv, name)
*
* Find item with the given hashv or name
*
* Same, but with spinlock protection...
* I call it safe, but it's only safe with respect to the hashbin, not its
* content. - Jean II
*/
void* hashbin_lock_find( hashbin_t* hashbin, long hashv, const char* name )
{
unsigned long flags = 0;
irda_queue_t* entry;
/* Synchronize */
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
/*
* Search for entry
*/
entry = (irda_queue_t* ) hashbin_find( hashbin, hashv, name );
/* Release lock */
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
return entry;
}
EXPORT_SYMBOL(hashbin_lock_find);
/*
* Function hashbin_find (hashbin, hashv, name, pnext)
*
* Find an item with the given hashv or name, and its successor
*
* This function allow to do concurrent enumerations without the
* need to lock over the whole session, because the caller keep the
* context of the search. On the other hand, it might fail and return
* NULL if the entry is removed. - Jean II
*/
void* hashbin_find_next( hashbin_t* hashbin, long hashv, const char* name,
void ** pnext)
{
unsigned long flags = 0;
irda_queue_t* entry;
/* Synchronize */
spin_lock_irqsave(&hashbin->hb_spinlock, flags);
/*
* Search for current entry
* This allow to check if the current item is still in the
* hashbin or has been removed.
*/
entry = (irda_queue_t* ) hashbin_find( hashbin, hashv, name );
/*
* Trick hashbin_get_next() to return what we want
*/
if(entry) {
hashbin->hb_current = entry;
*pnext = hashbin_get_next( hashbin );
} else
*pnext = NULL;
/* Release lock */
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
return entry;
}
EXPORT_SYMBOL(hashbin_find_next);
/*
* Function hashbin_get_first (hashbin)
*
* Get a pointer to first element in hashbin, this function must be
* called before any calls to hashbin_get_next()!
*
*/
irda_queue_t *hashbin_get_first( hashbin_t* hashbin)
{
irda_queue_t *entry;
int i;
IRDA_ASSERT( hashbin != NULL, return NULL;);
IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
if ( hashbin == NULL)
return NULL;
for ( i = 0; i < HASHBIN_SIZE; i ++ ) {
entry = hashbin->hb_queue[ i];
if ( entry) {
hashbin->hb_current = entry;
return entry;
}
}
/*
* Did not find any item in hashbin
*/
return NULL;
}
EXPORT_SYMBOL(hashbin_get_first);
/*
* Function hashbin_get_next (hashbin)
*
* Get next item in hashbin. A series of hashbin_get_next() calls must
* be started by a call to hashbin_get_first(). The function returns
* NULL when all items have been traversed
*
* The context of the search is stored within the hashbin, so you must
* protect yourself from concurrent enumerations. - Jean II
*/
irda_queue_t *hashbin_get_next( hashbin_t *hashbin)
{
irda_queue_t* entry;
int bin;
int i;
IRDA_ASSERT( hashbin != NULL, return NULL;);
IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
if ( hashbin->hb_current == NULL) {
IRDA_ASSERT( hashbin->hb_current != NULL, return NULL;);
return NULL;
}
entry = hashbin->hb_current->q_next;
bin = GET_HASHBIN( entry->q_hash);
/*
* Make sure that we are not back at the beginning of the queue
* again
*/
if ( entry != hashbin->hb_queue[ bin ]) {
hashbin->hb_current = entry;
return entry;
}
/*
* Check that this is not the last queue in hashbin
*/
if ( bin >= HASHBIN_SIZE)
return NULL;
/*
* Move to next queue in hashbin
*/
bin++;
for ( i = bin; i < HASHBIN_SIZE; i++ ) {
entry = hashbin->hb_queue[ i];
if ( entry) {
hashbin->hb_current = entry;
return entry;
}
}
return NULL;
}
EXPORT_SYMBOL(hashbin_get_next);

297
net/irda/irsysctl.c Normal file
View File

@@ -0,0 +1,297 @@
/*********************************************************************
*
* Filename: irsysctl.c
* Version: 1.0
* Description: Sysctl interface for IrDA
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun May 24 22:12:06 1998
* Modified at: Fri Jun 4 02:50:15 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved.
* Copyright (c) 2000-2001 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/ctype.h>
#include <linux/sysctl.h>
#include <linux/init.h>
#include <net/irda/irda.h> /* irda_debug */
#include <net/irda/irias_object.h>
#define NET_IRDA 412 /* Random number */
enum { DISCOVERY=1, DEVNAME, DEBUG, FAST_POLL, DISCOVERY_SLOTS,
DISCOVERY_TIMEOUT, SLOT_TIMEOUT, MAX_BAUD_RATE, MIN_TX_TURN_TIME,
MAX_TX_DATA_SIZE, MAX_TX_WINDOW, MAX_NOREPLY_TIME, WARN_NOREPLY_TIME,
LAP_KEEPALIVE_TIME };
extern int sysctl_discovery;
extern int sysctl_discovery_slots;
extern int sysctl_discovery_timeout;
extern int sysctl_slot_timeout;
extern int sysctl_fast_poll_increase;
extern char sysctl_devname[];
extern int sysctl_max_baud_rate;
extern int sysctl_min_tx_turn_time;
extern int sysctl_max_tx_data_size;
extern int sysctl_max_tx_window;
extern int sysctl_max_noreply_time;
extern int sysctl_warn_noreply_time;
extern int sysctl_lap_keepalive_time;
/* this is needed for the proc_dointvec_minmax - Jean II */
static int max_discovery_slots = 16; /* ??? */
static int min_discovery_slots = 1;
/* IrLAP 6.13.2 says 25ms to 10+70ms - allow higher since some devices
* seems to require it. (from Dag's comment) */
static int max_slot_timeout = 160;
static int min_slot_timeout = 20;
static int max_max_baud_rate = 16000000; /* See qos.c - IrLAP spec */
static int min_max_baud_rate = 2400;
static int max_min_tx_turn_time = 10000; /* See qos.c - IrLAP spec */
static int min_min_tx_turn_time;
static int max_max_tx_data_size = 2048; /* See qos.c - IrLAP spec */
static int min_max_tx_data_size = 64;
static int max_max_tx_window = 7; /* See qos.c - IrLAP spec */
static int min_max_tx_window = 1;
static int max_max_noreply_time = 40; /* See qos.c - IrLAP spec */
static int min_max_noreply_time = 3;
static int max_warn_noreply_time = 3; /* 3s == standard */
static int min_warn_noreply_time = 1; /* 1s == min WD_TIMER */
static int max_lap_keepalive_time = 10000; /* 10s */
static int min_lap_keepalive_time = 100; /* 100us */
/* For other sysctl, I've no idea of the range. Maybe Dag could help
* us on that - Jean II */
static int do_devname(ctl_table *table, int write, struct file *filp,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
int ret;
ret = proc_dostring(table, write, filp, buffer, lenp, ppos);
if (ret == 0 && write) {
struct ias_value *val;
val = irias_new_string_value(sysctl_devname);
if (val)
irias_object_change_attribute("Device", "DeviceName", val);
}
return ret;
}
/* One file */
static ctl_table irda_table[] = {
{
.ctl_name = DISCOVERY,
.procname = "discovery",
.data = &sysctl_discovery,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = DEVNAME,
.procname = "devname",
.data = sysctl_devname,
.maxlen = 65,
.mode = 0644,
.proc_handler = &do_devname,
.strategy = &sysctl_string
},
#ifdef CONFIG_IRDA_DEBUG
{
.ctl_name = DEBUG,
.procname = "debug",
.data = &irda_debug,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
#endif
#ifdef CONFIG_IRDA_FAST_RR
{
.ctl_name = FAST_POLL,
.procname = "fast_poll_increase",
.data = &sysctl_fast_poll_increase,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
#endif
{
.ctl_name = DISCOVERY_SLOTS,
.procname = "discovery_slots",
.data = &sysctl_discovery_slots,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_discovery_slots,
.extra2 = &max_discovery_slots
},
{
.ctl_name = DISCOVERY_TIMEOUT,
.procname = "discovery_timeout",
.data = &sysctl_discovery_timeout,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = SLOT_TIMEOUT,
.procname = "slot_timeout",
.data = &sysctl_slot_timeout,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_slot_timeout,
.extra2 = &max_slot_timeout
},
{
.ctl_name = MAX_BAUD_RATE,
.procname = "max_baud_rate",
.data = &sysctl_max_baud_rate,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_max_baud_rate,
.extra2 = &max_max_baud_rate
},
{
.ctl_name = MIN_TX_TURN_TIME,
.procname = "min_tx_turn_time",
.data = &sysctl_min_tx_turn_time,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_min_tx_turn_time,
.extra2 = &max_min_tx_turn_time
},
{
.ctl_name = MAX_TX_DATA_SIZE,
.procname = "max_tx_data_size",
.data = &sysctl_max_tx_data_size,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_max_tx_data_size,
.extra2 = &max_max_tx_data_size
},
{
.ctl_name = MAX_TX_WINDOW,
.procname = "max_tx_window",
.data = &sysctl_max_tx_window,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_max_tx_window,
.extra2 = &max_max_tx_window
},
{
.ctl_name = MAX_NOREPLY_TIME,
.procname = "max_noreply_time",
.data = &sysctl_max_noreply_time,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_max_noreply_time,
.extra2 = &max_max_noreply_time
},
{
.ctl_name = WARN_NOREPLY_TIME,
.procname = "warn_noreply_time",
.data = &sysctl_warn_noreply_time,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_warn_noreply_time,
.extra2 = &max_warn_noreply_time
},
{
.ctl_name = LAP_KEEPALIVE_TIME,
.procname = "lap_keepalive_time",
.data = &sysctl_lap_keepalive_time,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_lap_keepalive_time,
.extra2 = &max_lap_keepalive_time
},
{ .ctl_name = 0 }
};
/* One directory */
static ctl_table irda_net_table[] = {
{
.ctl_name = NET_IRDA,
.procname = "irda",
.maxlen = 0,
.mode = 0555,
.child = irda_table
},
{ .ctl_name = 0 }
};
/* The parent directory */
static ctl_table irda_root_table[] = {
{
.ctl_name = CTL_NET,
.procname = "net",
.maxlen = 0,
.mode = 0555,
.child = irda_net_table
},
{ .ctl_name = 0 }
};
static struct ctl_table_header *irda_table_header;
/*
* Function irda_sysctl_register (void)
*
* Register our sysctl interface
*
*/
int __init irda_sysctl_register(void)
{
irda_table_header = register_sysctl_table(irda_root_table, 0);
if (!irda_table_header)
return -ENOMEM;
return 0;
}
/*
* Function irda_sysctl_unregister (void)
*
* Unregister our sysctl interface
*
*/
void __exit irda_sysctl_unregister(void)
{
unregister_sysctl_table(irda_table_header);
}

1912
net/irda/irttp.c Normal file

File diff suppressed because it is too large Load Diff

589
net/irda/parameters.c Normal file
View File

@@ -0,0 +1,589 @@
/*********************************************************************
*
* Filename: parameters.c
* Version: 1.0
* Description: A more general way to handle (pi,pl,pv) parameters
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Jun 7 10:25:11 1999
* Modified at: Sun Jan 30 14:08:39 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
*
* 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/types.h>
#include <linux/module.h>
#include <asm/unaligned.h>
#include <asm/byteorder.h>
#include <net/irda/irda.h>
#include <net/irda/parameters.h>
static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi,
PV_TYPE type, PI_HANDLER func);
static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi,
PV_TYPE type, PI_HANDLER func);
static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi,
PV_TYPE type, PI_HANDLER func);
static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi,
PV_TYPE type, PI_HANDLER func);
static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi,
PV_TYPE type, PI_HANDLER func);
static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi,
PV_TYPE type, PI_HANDLER func);
static int irda_param_unpack(__u8 *buf, char *fmt, ...);
/* Parameter value call table. Must match PV_TYPE */
static PV_HANDLER pv_extract_table[] = {
irda_extract_integer, /* Handler for any length integers */
irda_extract_integer, /* Handler for 8 bits integers */
irda_extract_integer, /* Handler for 16 bits integers */
irda_extract_string, /* Handler for strings */
irda_extract_integer, /* Handler for 32 bits integers */
irda_extract_octseq, /* Handler for octet sequences */
irda_extract_no_value /* Handler for no value parameters */
};
static PV_HANDLER pv_insert_table[] = {
irda_insert_integer, /* Handler for any length integers */
irda_insert_integer, /* Handler for 8 bits integers */
irda_insert_integer, /* Handler for 16 bits integers */
NULL, /* Handler for strings */
irda_insert_integer, /* Handler for 32 bits integers */
NULL, /* Handler for octet sequences */
irda_insert_no_value /* Handler for no value parameters */
};
/*
* Function irda_insert_no_value (self, buf, len, pi, type, func)
*/
static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi,
PV_TYPE type, PI_HANDLER func)
{
irda_param_t p;
int ret;
p.pi = pi;
p.pl = 0;
/* Call handler for this parameter */
ret = (*func)(self, &p, PV_GET);
/* Extract values anyway, since handler may need them */
irda_param_pack(buf, "bb", p.pi, p.pl);
if (ret < 0)
return ret;
return 2; /* Inserted pl+2 bytes */
}
/*
* Function irda_extract_no_value (self, buf, len, type, func)
*
* Extracts a parameter without a pv field (pl=0)
*
*/
static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi,
PV_TYPE type, PI_HANDLER func)
{
irda_param_t p;
int ret;
/* Extract values anyway, since handler may need them */
irda_param_unpack(buf, "bb", &p.pi, &p.pl);
/* Call handler for this parameter */
ret = (*func)(self, &p, PV_PUT);
if (ret < 0)
return ret;
return 2; /* Extracted pl+2 bytes */
}
/*
* Function irda_insert_integer (self, buf, len, pi, type, func)
*/
static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi,
PV_TYPE type, PI_HANDLER func)
{
irda_param_t p;
int n = 0;
int err;
p.pi = pi; /* In case handler needs to know */
p.pl = type & PV_MASK; /* The integer type codes the lenght as well */
p.pv.i = 0; /* Clear value */
/* Call handler for this parameter */
err = (*func)(self, &p, PV_GET);
if (err < 0)
return err;
/*
* If parameter lenght is still 0, then (1) this is an any length
* integer, and (2) the handler function does not care which length
* we choose to use, so we pick the one the gives the fewest bytes.
*/
if (p.pl == 0) {
if (p.pv.i < 0xff) {
IRDA_DEBUG(2, "%s(), using 1 byte\n", __FUNCTION__);
p.pl = 1;
} else if (p.pv.i < 0xffff) {
IRDA_DEBUG(2, "%s(), using 2 bytes\n", __FUNCTION__);
p.pl = 2;
} else {
IRDA_DEBUG(2, "%s(), using 4 bytes\n", __FUNCTION__);
p.pl = 4; /* Default length */
}
}
/* Check if buffer is long enough for insertion */
if (len < (2+p.pl)) {
IRDA_WARNING("%s: buffer to short for insertion!\n",
__FUNCTION__);
return -1;
}
IRDA_DEBUG(2, "%s(), pi=%#x, pl=%d, pi=%d\n", __FUNCTION__,
p.pi, p.pl, p.pv.i);
switch (p.pl) {
case 1:
n += irda_param_pack(buf, "bbb", p.pi, p.pl, (__u8) p.pv.i);
break;
case 2:
if (type & PV_BIG_ENDIAN)
p.pv.i = cpu_to_be16((__u16) p.pv.i);
else
p.pv.i = cpu_to_le16((__u16) p.pv.i);
n += irda_param_pack(buf, "bbs", p.pi, p.pl, (__u16) p.pv.i);
break;
case 4:
if (type & PV_BIG_ENDIAN)
cpu_to_be32s(&p.pv.i);
else
cpu_to_le32s(&p.pv.i);
n += irda_param_pack(buf, "bbi", p.pi, p.pl, p.pv.i);
break;
default:
IRDA_WARNING("%s: length %d not supported\n",
__FUNCTION__, p.pl);
/* Skip parameter */
return -1;
}
return p.pl+2; /* Inserted pl+2 bytes */
}
/*
* Function irda_extract integer (self, buf, len, pi, type, func)
*
* Extract a possibly variable length integer from buffer, and call
* handler for processing of the parameter
*/
static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi,
PV_TYPE type, PI_HANDLER func)
{
irda_param_t p;
int n = 0;
int extract_len; /* Real lenght we extract */
int err;
p.pi = pi; /* In case handler needs to know */
p.pl = buf[1]; /* Extract lenght of value */
p.pv.i = 0; /* Clear value */
extract_len = p.pl; /* Default : extract all */
/* Check if buffer is long enough for parsing */
if (len < (2+p.pl)) {
IRDA_WARNING("%s: buffer to short for parsing! "
"Need %d bytes, but len is only %d\n",
__FUNCTION__, p.pl, len);
return -1;
}
/*
* Check that the integer length is what we expect it to be. If the
* handler want a 16 bits integer then a 32 bits is not good enough
* PV_INTEGER means that the handler is flexible.
*/
if (((type & PV_MASK) != PV_INTEGER) && ((type & PV_MASK) != p.pl)) {
IRDA_ERROR("%s: invalid parameter length! "
"Expected %d bytes, but value had %d bytes!\n",
__FUNCTION__, type & PV_MASK, p.pl);
/* Most parameters are bit/byte fields or little endian,
* so it's ok to only extract a subset of it (the subset
* that the handler expect). This is necessary, as some
* broken implementations seems to add extra undefined bits.
* If the parameter is shorter than we expect or is big
* endian, we can't play those tricks. Jean II */
if((p.pl < (type & PV_MASK)) || (type & PV_BIG_ENDIAN)) {
/* Skip parameter */
return p.pl+2;
} else {
/* Extract subset of it, fallthrough */
extract_len = type & PV_MASK;
}
}
switch (extract_len) {
case 1:
n += irda_param_unpack(buf+2, "b", &p.pv.i);
break;
case 2:
n += irda_param_unpack(buf+2, "s", &p.pv.i);
if (type & PV_BIG_ENDIAN)
p.pv.i = be16_to_cpu((__u16) p.pv.i);
else
p.pv.i = le16_to_cpu((__u16) p.pv.i);
break;
case 4:
n += irda_param_unpack(buf+2, "i", &p.pv.i);
if (type & PV_BIG_ENDIAN)
be32_to_cpus(&p.pv.i);
else
le32_to_cpus(&p.pv.i);
break;
default:
IRDA_WARNING("%s: length %d not supported\n",
__FUNCTION__, p.pl);
/* Skip parameter */
return p.pl+2;
}
IRDA_DEBUG(2, "%s(), pi=%#x, pl=%d, pi=%d\n", __FUNCTION__,
p.pi, p.pl, p.pv.i);
/* Call handler for this parameter */
err = (*func)(self, &p, PV_PUT);
if (err < 0)
return err;
return p.pl+2; /* Extracted pl+2 bytes */
}
/*
* Function irda_extract_string (self, buf, len, type, func)
*/
static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi,
PV_TYPE type, PI_HANDLER func)
{
char str[33];
irda_param_t p;
int err;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
p.pi = pi; /* In case handler needs to know */
p.pl = buf[1]; /* Extract lenght of value */
IRDA_DEBUG(2, "%s(), pi=%#x, pl=%d\n", __FUNCTION__,
p.pi, p.pl);
/* Check if buffer is long enough for parsing */
if (len < (2+p.pl)) {
IRDA_WARNING("%s: buffer to short for parsing! "
"Need %d bytes, but len is only %d\n",
__FUNCTION__, p.pl, len);
return -1;
}
/* Should be safe to copy string like this since we have already
* checked that the buffer is long enough */
strncpy(str, buf+2, p.pl);
IRDA_DEBUG(2, "%s(), str=0x%02x 0x%02x\n", __FUNCTION__,
(__u8) str[0], (__u8) str[1]);
/* Null terminate string */
str[p.pl+1] = '\0';
p.pv.c = str; /* Handler will need to take a copy */
/* Call handler for this parameter */
err = (*func)(self, &p, PV_PUT);
if (err < 0)
return err;
return p.pl+2; /* Extracted pl+2 bytes */
}
/*
* Function irda_extract_octseq (self, buf, len, type, func)
*/
static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi,
PV_TYPE type, PI_HANDLER func)
{
irda_param_t p;
p.pi = pi; /* In case handler needs to know */
p.pl = buf[1]; /* Extract lenght of value */
/* Check if buffer is long enough for parsing */
if (len < (2+p.pl)) {
IRDA_WARNING("%s: buffer to short for parsing! "
"Need %d bytes, but len is only %d\n",
__FUNCTION__, p.pl, len);
return -1;
}
IRDA_DEBUG(0, "%s(), not impl\n", __FUNCTION__);
return p.pl+2; /* Extracted pl+2 bytes */
}
/*
* Function irda_param_pack (skb, fmt, ...)
*
* Format:
* 'i' = 32 bits integer
* 's' = string
*
*/
int irda_param_pack(__u8 *buf, char *fmt, ...)
{
irda_pv_t arg;
va_list args;
char *p;
int n = 0;
va_start(args, fmt);
for (p = fmt; *p != '\0'; p++) {
switch (*p) {
case 'b': /* 8 bits unsigned byte */
buf[n++] = (__u8)va_arg(args, int);
break;
case 's': /* 16 bits unsigned short */
arg.i = (__u16)va_arg(args, int);
put_unaligned((__u16)arg.i, (__u16 *)(buf+n)); n+=2;
break;
case 'i': /* 32 bits unsigned integer */
arg.i = va_arg(args, __u32);
put_unaligned(arg.i, (__u32 *)(buf+n)); n+=4;
break;
#if 0
case 'c': /* \0 terminated string */
arg.c = va_arg(args, char *);
strcpy(buf+n, arg.c);
n += strlen(arg.c) + 1;
break;
#endif
default:
va_end(args);
return -1;
}
}
va_end(args);
return 0;
}
EXPORT_SYMBOL(irda_param_pack);
/*
* Function irda_param_unpack (skb, fmt, ...)
*/
static int irda_param_unpack(__u8 *buf, char *fmt, ...)
{
irda_pv_t arg;
va_list args;
char *p;
int n = 0;
va_start(args, fmt);
for (p = fmt; *p != '\0'; p++) {
switch (*p) {
case 'b': /* 8 bits byte */
arg.ip = va_arg(args, __u32 *);
*arg.ip = buf[n++];
break;
case 's': /* 16 bits short */
arg.ip = va_arg(args, __u32 *);
*arg.ip = get_unaligned((__u16 *)(buf+n)); n+=2;
break;
case 'i': /* 32 bits unsigned integer */
arg.ip = va_arg(args, __u32 *);
*arg.ip = get_unaligned((__u32 *)(buf+n)); n+=4;
break;
#if 0
case 'c': /* \0 terminated string */
arg.c = va_arg(args, char *);
strcpy(arg.c, buf+n);
n += strlen(arg.c) + 1;
break;
#endif
default:
va_end(args);
return -1;
}
}
va_end(args);
return 0;
}
/*
* Function irda_param_insert (self, pi, buf, len, info)
*
* Insert the specified parameter (pi) into buffer. Returns number of
* bytes inserted
*/
int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len,
pi_param_info_t *info)
{
pi_minor_info_t *pi_minor_info;
__u8 pi_minor;
__u8 pi_major;
int type;
int ret = -1;
int n = 0;
IRDA_ASSERT(buf != NULL, return ret;);
IRDA_ASSERT(info != 0, return ret;);
pi_minor = pi & info->pi_mask;
pi_major = pi >> info->pi_major_offset;
/* Check if the identifier value (pi) is valid */
if ((pi_major > info->len-1) ||
(pi_minor > info->tables[pi_major].len-1))
{
IRDA_DEBUG(0, "%s(), no handler for parameter=0x%02x\n",
__FUNCTION__, pi);
/* Skip this parameter */
return -1;
}
/* Lookup the info on how to parse this parameter */
pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor];
/* Find expected data type for this parameter identifier (pi)*/
type = pi_minor_info->type;
/* Check if handler has been implemented */
if (!pi_minor_info->func) {
IRDA_MESSAGE("%s: no handler for pi=%#x\n", __FUNCTION__, pi);
/* Skip this parameter */
return -1;
}
/* Insert parameter value */
ret = (*pv_insert_table[type & PV_MASK])(self, buf+n, len, pi, type,
pi_minor_info->func);
return ret;
}
EXPORT_SYMBOL(irda_param_insert);
/*
* Function irda_param_extract (self, buf, len, info)
*
* Parse all parameters. If len is correct, then everything should be
* safe. Returns the number of bytes that was parsed
*
*/
static int irda_param_extract(void *self, __u8 *buf, int len,
pi_param_info_t *info)
{
pi_minor_info_t *pi_minor_info;
__u8 pi_minor;
__u8 pi_major;
int type;
int ret = -1;
int n = 0;
IRDA_ASSERT(buf != NULL, return ret;);
IRDA_ASSERT(info != 0, return ret;);
pi_minor = buf[n] & info->pi_mask;
pi_major = buf[n] >> info->pi_major_offset;
/* Check if the identifier value (pi) is valid */
if ((pi_major > info->len-1) ||
(pi_minor > info->tables[pi_major].len-1))
{
IRDA_DEBUG(0, "%s(), no handler for parameter=0x%02x\n",
__FUNCTION__, buf[0]);
/* Skip this parameter */
return 2 + buf[n + 1]; /* Continue */
}
/* Lookup the info on how to parse this parameter */
pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor];
/* Find expected data type for this parameter identifier (pi)*/
type = pi_minor_info->type;
IRDA_DEBUG(3, "%s(), pi=[%d,%d], type=%d\n", __FUNCTION__,
pi_major, pi_minor, type);
/* Check if handler has been implemented */
if (!pi_minor_info->func) {
IRDA_MESSAGE("%s: no handler for pi=%#x\n",
__FUNCTION__, buf[n]);
/* Skip this parameter */
return 2 + buf[n + 1]; /* Continue */
}
/* Parse parameter value */
ret = (*pv_extract_table[type & PV_MASK])(self, buf+n, len, buf[n],
type, pi_minor_info->func);
return ret;
}
/*
* Function irda_param_extract_all (self, buf, len, info)
*
* Parse all parameters. If len is correct, then everything should be
* safe. Returns the number of bytes that was parsed
*
*/
int irda_param_extract_all(void *self, __u8 *buf, int len,
pi_param_info_t *info)
{
int ret = -1;
int n = 0;
IRDA_ASSERT(buf != NULL, return ret;);
IRDA_ASSERT(info != 0, return ret;);
/*
* Parse all parameters. Each parameter must be at least two bytes
* long or else there is no point in trying to parse it
*/
while (len > 2) {
ret = irda_param_extract(self, buf+n, len, info);
if (ret < 0)
return ret;
n += ret;
len -= ret;
}
return n;
}
EXPORT_SYMBOL(irda_param_extract_all);

774
net/irda/qos.c Normal file
View File

@@ -0,0 +1,774 @@
/*********************************************************************
*
* Filename: qos.c
* Version: 1.0
* Description: IrLAP QoS parameter negotiation
* Status: Stable
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Tue Sep 9 00:00:26 1997
* Modified at: Sun Jan 30 14:29:16 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-2001 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/config.h>
#include <asm/byteorder.h>
#include <net/irda/irda.h>
#include <net/irda/parameters.h>
#include <net/irda/qos.h>
#include <net/irda/irlap.h>
/*
* Maximum values of the baud rate we negociate with the other end.
* Most often, you don't have to change that, because Linux-IrDA will
* use the maximum offered by the link layer, which usually works fine.
* In some very rare cases, you may want to limit it to lower speeds...
*/
int sysctl_max_baud_rate = 16000000;
/*
* Maximum value of the lap disconnect timer we negociate with the other end.
* Most often, the value below represent the best compromise, but some user
* may want to keep the LAP alive longuer or shorter in case of link failure.
* Remember that the threshold time (early warning) is fixed to 3s...
*/
int sysctl_max_noreply_time = 12;
/*
* Minimum turn time to be applied before transmitting to the peer.
* Nonzero values (usec) are used as lower limit to the per-connection
* mtt value which was announced by the other end during negotiation.
* Might be helpful if the peer device provides too short mtt.
* Default is 10us which means using the unmodified value given by the
* peer except if it's 0 (0 is likely a bug in the other stack).
*/
unsigned sysctl_min_tx_turn_time = 10;
/*
* Maximum data size to be used in transmission in payload of LAP frame.
* There is a bit of confusion in the IrDA spec :
* The LAP spec defines the payload of a LAP frame (I field) to be
* 2048 bytes max (IrLAP 1.1, chapt 6.6.5, p40).
* On the other hand, the PHY mention frames of 2048 bytes max (IrPHY
* 1.2, chapt 5.3.2.1, p41). But, this number includes the LAP header
* (2 bytes), and CRC (32 bits at 4 Mb/s). So, for the I field (LAP
* payload), that's only 2042 bytes. Oups !
* My nsc-ircc hardware has troubles receiving 2048 bytes frames at 4 Mb/s,
* so adjust to 2042... I don't know if this bug applies only for 2048
* bytes frames or all negotiated frame sizes, but you can use the sysctl
* to play with this value anyway.
* Jean II */
unsigned sysctl_max_tx_data_size = 2042;
/*
* Maximum transmit window, i.e. number of LAP frames between turn-around.
* This allow to override what the peer told us. Some peers are buggy and
* don't always support what they tell us.
* Jean II */
unsigned sysctl_max_tx_window = 7;
static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get);
static int irlap_param_link_disconnect(void *instance, irda_param_t *parm,
int get);
static int irlap_param_max_turn_time(void *instance, irda_param_t *param,
int get);
static int irlap_param_data_size(void *instance, irda_param_t *param, int get);
static int irlap_param_window_size(void *instance, irda_param_t *param,
int get);
static int irlap_param_additional_bofs(void *instance, irda_param_t *parm,
int get);
static int irlap_param_min_turn_time(void *instance, irda_param_t *param,
int get);
#ifndef CONFIG_IRDA_DYNAMIC_WINDOW
static __u32 irlap_requested_line_capacity(struct qos_info *qos);
#endif
static __u32 min_turn_times[] = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */
static __u32 baud_rates[] = { 2400, 9600, 19200, 38400, 57600, 115200, 576000,
1152000, 4000000, 16000000 }; /* bps */
static __u32 data_sizes[] = { 64, 128, 256, 512, 1024, 2048 }; /* bytes */
static __u32 add_bofs[] = { 48, 24, 12, 5, 3, 2, 1, 0 }; /* bytes */
static __u32 max_turn_times[] = { 500, 250, 100, 50 }; /* ms */
static __u32 link_disc_times[] = { 3, 8, 12, 16, 20, 25, 30, 40 }; /* secs */
static __u32 max_line_capacities[10][4] = {
/* 500 ms 250 ms 100 ms 50 ms (max turn time) */
{ 100, 0, 0, 0 }, /* 2400 bps */
{ 400, 0, 0, 0 }, /* 9600 bps */
{ 800, 0, 0, 0 }, /* 19200 bps */
{ 1600, 0, 0, 0 }, /* 38400 bps */
{ 2360, 0, 0, 0 }, /* 57600 bps */
{ 4800, 2400, 960, 480 }, /* 115200 bps */
{ 28800, 11520, 5760, 2880 }, /* 576000 bps */
{ 57600, 28800, 11520, 5760 }, /* 1152000 bps */
{ 200000, 100000, 40000, 20000 }, /* 4000000 bps */
{ 800000, 400000, 160000, 80000 }, /* 16000000 bps */
};
static pi_minor_info_t pi_minor_call_table_type_0[] = {
{ NULL, 0 },
/* 01 */{ irlap_param_baud_rate, PV_INTEGER | PV_LITTLE_ENDIAN },
{ NULL, 0 },
{ NULL, 0 },
{ NULL, 0 },
{ NULL, 0 },
{ NULL, 0 },
{ NULL, 0 },
/* 08 */{ irlap_param_link_disconnect, PV_INT_8_BITS }
};
static pi_minor_info_t pi_minor_call_table_type_1[] = {
{ NULL, 0 },
{ NULL, 0 },
/* 82 */{ irlap_param_max_turn_time, PV_INT_8_BITS },
/* 83 */{ irlap_param_data_size, PV_INT_8_BITS },
/* 84 */{ irlap_param_window_size, PV_INT_8_BITS },
/* 85 */{ irlap_param_additional_bofs, PV_INT_8_BITS },
/* 86 */{ irlap_param_min_turn_time, PV_INT_8_BITS },
};
static pi_major_info_t pi_major_call_table[] = {
{ pi_minor_call_table_type_0, 9 },
{ pi_minor_call_table_type_1, 7 },
};
static pi_param_info_t irlap_param_info = { pi_major_call_table, 2, 0x7f, 7 };
/* ---------------------- LOCAL SUBROUTINES ---------------------- */
/* Note : we start with a bunch of local subroutines.
* As the compiler is "one pass", this is the only way to get them to
* inline properly...
* Jean II
*/
/*
* Function value_index (value, array, size)
*
* Returns the index to the value in the specified array
*/
static inline int value_index(__u32 value, __u32 *array, int size)
{
int i;
for (i=0; i < size; i++)
if (array[i] == value)
break;
return i;
}
/*
* Function index_value (index, array)
*
* Returns value to index in array, easy!
*
*/
static inline __u32 index_value(int index, __u32 *array)
{
return array[index];
}
/*
* Function msb_index (word)
*
* Returns index to most significant bit (MSB) in word
*
*/
static int msb_index (__u16 word)
{
__u16 msb = 0x8000;
int index = 15; /* Current MSB */
/* Check for buggy peers.
* Note : there is a small probability that it could be us, but I
* would expect driver authors to catch that pretty early and be
* able to check precisely what's going on. If a end user sees this,
* it's very likely the peer. - Jean II */
if (word == 0) {
IRDA_WARNING("%s(), Detected buggy peer, adjust null PV to 0x1!\n",
__FUNCTION__);
/* The only safe choice (we don't know the array size) */
word = 0x1;
}
while (msb) {
if (word & msb)
break; /* Found it! */
msb >>=1;
index--;
}
return index;
}
/*
* Function value_lower_bits (value, array)
*
* Returns a bit field marking all possibility lower than value.
*/
static inline int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field)
{
int i;
__u16 mask = 0x1;
__u16 result = 0x0;
for (i=0; i < size; i++) {
/* Add the current value to the bit field, shift mask */
result |= mask;
mask <<= 1;
/* Finished ? */
if (array[i] >= value)
break;
}
/* Send back a valid index */
if(i >= size)
i = size - 1; /* Last item */
*field = result;
return i;
}
/*
* Function value_highest_bit (value, array)
*
* Returns a bit field marking the highest possibility lower than value.
*/
static inline int value_highest_bit(__u32 value, __u32 *array, int size, __u16 *field)
{
int i;
__u16 mask = 0x1;
__u16 result = 0x0;
for (i=0; i < size; i++) {
/* Finished ? */
if (array[i] <= value)
break;
/* Shift mask */
mask <<= 1;
}
/* Set the current value to the bit field */
result |= mask;
/* Send back a valid index */
if(i >= size)
i = size - 1; /* Last item */
*field = result;
return i;
}
/* -------------------------- MAIN CALLS -------------------------- */
/*
* Function irda_qos_compute_intersection (qos, new)
*
* Compute the intersection of the old QoS capabilities with new ones
*
*/
void irda_qos_compute_intersection(struct qos_info *qos, struct qos_info *new)
{
IRDA_ASSERT(qos != NULL, return;);
IRDA_ASSERT(new != NULL, return;);
/* Apply */
qos->baud_rate.bits &= new->baud_rate.bits;
qos->window_size.bits &= new->window_size.bits;
qos->min_turn_time.bits &= new->min_turn_time.bits;
qos->max_turn_time.bits &= new->max_turn_time.bits;
qos->data_size.bits &= new->data_size.bits;
qos->link_disc_time.bits &= new->link_disc_time.bits;
qos->additional_bofs.bits &= new->additional_bofs.bits;
irda_qos_bits_to_value(qos);
}
/*
* Function irda_init_max_qos_capabilies (qos)
*
* The purpose of this function is for layers and drivers to be able to
* set the maximum QoS possible and then "and in" their own limitations
*
*/
void irda_init_max_qos_capabilies(struct qos_info *qos)
{
int i;
/*
* These are the maximum supported values as specified on pages
* 39-43 in IrLAP
*/
/* Use sysctl to set some configurable values... */
/* Set configured max speed */
i = value_lower_bits(sysctl_max_baud_rate, baud_rates, 10,
&qos->baud_rate.bits);
sysctl_max_baud_rate = index_value(i, baud_rates);
/* Set configured max disc time */
i = value_lower_bits(sysctl_max_noreply_time, link_disc_times, 8,
&qos->link_disc_time.bits);
sysctl_max_noreply_time = index_value(i, link_disc_times);
/* LSB is first byte, MSB is second byte */
qos->baud_rate.bits &= 0x03ff;
qos->window_size.bits = 0x7f;
qos->min_turn_time.bits = 0xff;
qos->max_turn_time.bits = 0x0f;
qos->data_size.bits = 0x3f;
qos->link_disc_time.bits &= 0xff;
qos->additional_bofs.bits = 0xff;
}
EXPORT_SYMBOL(irda_init_max_qos_capabilies);
/*
* Function irlap_adjust_qos_settings (qos)
*
* Adjust QoS settings in case some values are not possible to use because
* of other settings
*/
static void irlap_adjust_qos_settings(struct qos_info *qos)
{
__u32 line_capacity;
int index;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
/*
* Make sure the mintt is sensible.
* Main culprit : Ericsson T39. - Jean II
*/
if (sysctl_min_tx_turn_time > qos->min_turn_time.value) {
int i;
IRDA_WARNING("%s(), Detected buggy peer, adjust mtt to %dus!\n",
__FUNCTION__, sysctl_min_tx_turn_time);
/* We don't really need bits, but easier this way */
i = value_highest_bit(sysctl_min_tx_turn_time, min_turn_times,
8, &qos->min_turn_time.bits);
sysctl_min_tx_turn_time = index_value(i, min_turn_times);
qos->min_turn_time.value = sysctl_min_tx_turn_time;
}
/*
* Not allowed to use a max turn time less than 500 ms if the baudrate
* is less than 115200
*/
if ((qos->baud_rate.value < 115200) &&
(qos->max_turn_time.value < 500))
{
IRDA_DEBUG(0,
"%s(), adjusting max turn time from %d to 500 ms\n",
__FUNCTION__, qos->max_turn_time.value);
qos->max_turn_time.value = 500;
}
/*
* The data size must be adjusted according to the baud rate and max
* turn time
*/
index = value_index(qos->data_size.value, data_sizes, 6);
line_capacity = irlap_max_line_capacity(qos->baud_rate.value,
qos->max_turn_time.value);
#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
while ((qos->data_size.value > line_capacity) && (index > 0)) {
qos->data_size.value = data_sizes[index--];
IRDA_DEBUG(2, "%s(), reducing data size to %d\n",
__FUNCTION__, qos->data_size.value);
}
#else /* Use method described in section 6.6.11 of IrLAP */
while (irlap_requested_line_capacity(qos) > line_capacity) {
IRDA_ASSERT(index != 0, return;);
/* Must be able to send at least one frame */
if (qos->window_size.value > 1) {
qos->window_size.value--;
IRDA_DEBUG(2, "%s(), reducing window size to %d\n",
__FUNCTION__, qos->window_size.value);
} else if (index > 1) {
qos->data_size.value = data_sizes[index--];
IRDA_DEBUG(2, "%s(), reducing data size to %d\n",
__FUNCTION__, qos->data_size.value);
} else {
IRDA_WARNING("%s(), nothing more we can do!\n",
__FUNCTION__);
}
}
#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
/*
* Fix tx data size according to user limits - Jean II
*/
if (qos->data_size.value > sysctl_max_tx_data_size)
/* Allow non discrete adjustement to avoid loosing capacity */
qos->data_size.value = sysctl_max_tx_data_size;
/*
* Override Tx window if user request it. - Jean II
*/
if (qos->window_size.value > sysctl_max_tx_window)
qos->window_size.value = sysctl_max_tx_window;
}
/*
* Function irlap_negotiate (qos_device, qos_session, skb)
*
* Negotiate QoS values, not really that much negotiation :-)
* We just set the QoS capabilities for the peer station
*
*/
int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb)
{
int ret;
ret = irda_param_extract_all(self, skb->data, skb->len,
&irlap_param_info);
/* Convert the negotiated bits to values */
irda_qos_bits_to_value(&self->qos_tx);
irda_qos_bits_to_value(&self->qos_rx);
irlap_adjust_qos_settings(&self->qos_tx);
IRDA_DEBUG(2, "Setting BAUD_RATE to %d bps.\n",
self->qos_tx.baud_rate.value);
IRDA_DEBUG(2, "Setting DATA_SIZE to %d bytes\n",
self->qos_tx.data_size.value);
IRDA_DEBUG(2, "Setting WINDOW_SIZE to %d\n",
self->qos_tx.window_size.value);
IRDA_DEBUG(2, "Setting XBOFS to %d\n",
self->qos_tx.additional_bofs.value);
IRDA_DEBUG(2, "Setting MAX_TURN_TIME to %d ms.\n",
self->qos_tx.max_turn_time.value);
IRDA_DEBUG(2, "Setting MIN_TURN_TIME to %d usecs.\n",
self->qos_tx.min_turn_time.value);
IRDA_DEBUG(2, "Setting LINK_DISC to %d secs.\n",
self->qos_tx.link_disc_time.value);
return ret;
}
/*
* Function irlap_insert_negotiation_params (qos, fp)
*
* Insert QoS negotiaion pararameters into frame
*
*/
int irlap_insert_qos_negotiation_params(struct irlap_cb *self,
struct sk_buff *skb)
{
int ret;
/* Insert data rate */
ret = irda_param_insert(self, PI_BAUD_RATE, skb->tail,
skb_tailroom(skb), &irlap_param_info);
if (ret < 0)
return ret;
skb_put(skb, ret);
/* Insert max turnaround time */
ret = irda_param_insert(self, PI_MAX_TURN_TIME, skb->tail,
skb_tailroom(skb), &irlap_param_info);
if (ret < 0)
return ret;
skb_put(skb, ret);
/* Insert data size */
ret = irda_param_insert(self, PI_DATA_SIZE, skb->tail,
skb_tailroom(skb), &irlap_param_info);
if (ret < 0)
return ret;
skb_put(skb, ret);
/* Insert window size */
ret = irda_param_insert(self, PI_WINDOW_SIZE, skb->tail,
skb_tailroom(skb), &irlap_param_info);
if (ret < 0)
return ret;
skb_put(skb, ret);
/* Insert additional BOFs */
ret = irda_param_insert(self, PI_ADD_BOFS, skb->tail,
skb_tailroom(skb), &irlap_param_info);
if (ret < 0)
return ret;
skb_put(skb, ret);
/* Insert minimum turnaround time */
ret = irda_param_insert(self, PI_MIN_TURN_TIME, skb->tail,
skb_tailroom(skb), &irlap_param_info);
if (ret < 0)
return ret;
skb_put(skb, ret);
/* Insert link disconnect/threshold time */
ret = irda_param_insert(self, PI_LINK_DISC, skb->tail,
skb_tailroom(skb), &irlap_param_info);
if (ret < 0)
return ret;
skb_put(skb, ret);
return 0;
}
/*
* Function irlap_param_baud_rate (instance, param, get)
*
* Negotiate data-rate
*
*/
static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get)
{
__u16 final;
struct irlap_cb *self = (struct irlap_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
if (get) {
param->pv.i = self->qos_rx.baud_rate.bits;
IRDA_DEBUG(2, "%s(), baud rate = 0x%02x\n",
__FUNCTION__, param->pv.i);
} else {
/*
* Stations must agree on baud rate, so calculate
* intersection
*/
IRDA_DEBUG(2, "Requested BAUD_RATE: 0x%04x\n", (__u16) param->pv.i);
final = (__u16) param->pv.i & self->qos_rx.baud_rate.bits;
IRDA_DEBUG(2, "Final BAUD_RATE: 0x%04x\n", final);
self->qos_tx.baud_rate.bits = final;
self->qos_rx.baud_rate.bits = final;
}
return 0;
}
/*
* Function irlap_param_link_disconnect (instance, param, get)
*
* Negotiate link disconnect/threshold time.
*
*/
static int irlap_param_link_disconnect(void *instance, irda_param_t *param,
int get)
{
__u16 final;
struct irlap_cb *self = (struct irlap_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
if (get)
param->pv.i = self->qos_rx.link_disc_time.bits;
else {
/*
* Stations must agree on link disconnect/threshold
* time.
*/
IRDA_DEBUG(2, "LINK_DISC: %02x\n", (__u8) param->pv.i);
final = (__u8) param->pv.i & self->qos_rx.link_disc_time.bits;
IRDA_DEBUG(2, "Final LINK_DISC: %02x\n", final);
self->qos_tx.link_disc_time.bits = final;
self->qos_rx.link_disc_time.bits = final;
}
return 0;
}
/*
* Function irlap_param_max_turn_time (instance, param, get)
*
* Negotiate the maximum turnaround time. This is a type 1 parameter and
* will be negotiated independently for each station
*
*/
static int irlap_param_max_turn_time(void *instance, irda_param_t *param,
int get)
{
struct irlap_cb *self = (struct irlap_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
if (get)
param->pv.i = self->qos_rx.max_turn_time.bits;
else
self->qos_tx.max_turn_time.bits = (__u8) param->pv.i;
return 0;
}
/*
* Function irlap_param_data_size (instance, param, get)
*
* Negotiate the data size. This is a type 1 parameter and
* will be negotiated independently for each station
*
*/
static int irlap_param_data_size(void *instance, irda_param_t *param, int get)
{
struct irlap_cb *self = (struct irlap_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
if (get)
param->pv.i = self->qos_rx.data_size.bits;
else
self->qos_tx.data_size.bits = (__u8) param->pv.i;
return 0;
}
/*
* Function irlap_param_window_size (instance, param, get)
*
* Negotiate the window size. This is a type 1 parameter and
* will be negotiated independently for each station
*
*/
static int irlap_param_window_size(void *instance, irda_param_t *param,
int get)
{
struct irlap_cb *self = (struct irlap_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
if (get)
param->pv.i = self->qos_rx.window_size.bits;
else
self->qos_tx.window_size.bits = (__u8) param->pv.i;
return 0;
}
/*
* Function irlap_param_additional_bofs (instance, param, get)
*
* Negotiate additional BOF characters. This is a type 1 parameter and
* will be negotiated independently for each station.
*/
static int irlap_param_additional_bofs(void *instance, irda_param_t *param, int get)
{
struct irlap_cb *self = (struct irlap_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
if (get)
param->pv.i = self->qos_rx.additional_bofs.bits;
else
self->qos_tx.additional_bofs.bits = (__u8) param->pv.i;
return 0;
}
/*
* Function irlap_param_min_turn_time (instance, param, get)
*
* Negotiate the minimum turn around time. This is a type 1 parameter and
* will be negotiated independently for each station
*/
static int irlap_param_min_turn_time(void *instance, irda_param_t *param,
int get)
{
struct irlap_cb *self = (struct irlap_cb *) instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
if (get)
param->pv.i = self->qos_rx.min_turn_time.bits;
else
self->qos_tx.min_turn_time.bits = (__u8) param->pv.i;
return 0;
}
/*
* Function irlap_max_line_capacity (speed, max_turn_time, min_turn_time)
*
* Calculate the maximum line capacity
*
*/
__u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time)
{
__u32 line_capacity;
int i,j;
IRDA_DEBUG(2, "%s(), speed=%d, max_turn_time=%d\n",
__FUNCTION__, speed, max_turn_time);
i = value_index(speed, baud_rates, 10);
j = value_index(max_turn_time, max_turn_times, 4);
IRDA_ASSERT(((i >=0) && (i <10)), return 0;);
IRDA_ASSERT(((j >=0) && (j <4)), return 0;);
line_capacity = max_line_capacities[i][j];
IRDA_DEBUG(2, "%s(), line capacity=%d bytes\n",
__FUNCTION__, line_capacity);
return line_capacity;
}
#ifndef CONFIG_IRDA_DYNAMIC_WINDOW
static __u32 irlap_requested_line_capacity(struct qos_info *qos)
{
__u32 line_capacity;
line_capacity = qos->window_size.value *
(qos->data_size.value + 6 + qos->additional_bofs.value) +
irlap_min_turn_time_in_bytes(qos->baud_rate.value,
qos->min_turn_time.value);
IRDA_DEBUG(2, "%s(), requested line capacity=%d\n",
__FUNCTION__, line_capacity);
return line_capacity;
}
#endif
void irda_qos_bits_to_value(struct qos_info *qos)
{
int index;
IRDA_ASSERT(qos != NULL, return;);
index = msb_index(qos->baud_rate.bits);
qos->baud_rate.value = baud_rates[index];
index = msb_index(qos->data_size.bits);
qos->data_size.value = data_sizes[index];
index = msb_index(qos->window_size.bits);
qos->window_size.value = index+1;
index = msb_index(qos->min_turn_time.bits);
qos->min_turn_time.value = min_turn_times[index];
index = msb_index(qos->max_turn_time.bits);
qos->max_turn_time.value = max_turn_times[index];
index = msb_index(qos->link_disc_time.bits);
qos->link_disc_time.value = link_disc_times[index];
index = msb_index(qos->additional_bofs.bits);
qos->additional_bofs.value = add_bofs[index];
}
EXPORT_SYMBOL(irda_qos_bits_to_value);

233
net/irda/timer.c Normal file
View File

@@ -0,0 +1,233 @@
/*********************************************************************
*
* Filename: timer.c
* Version:
* Description:
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sat Aug 16 00:59:29 1997
* Modified at: Wed Dec 8 12:50:34 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>,
* All Rights Reserved.
* Copyright (c) 2000-2002 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <asm/system.h>
#include <linux/config.h>
#include <linux/delay.h>
#include <net/irda/timer.h>
#include <net/irda/irda.h>
#include <net/irda/irda_device.h>
#include <net/irda/irlap.h>
#include <net/irda/irlmp.h>
extern int sysctl_slot_timeout;
static void irlap_slot_timer_expired(void* data);
static void irlap_query_timer_expired(void* data);
static void irlap_final_timer_expired(void* data);
static void irlap_wd_timer_expired(void* data);
static void irlap_backoff_timer_expired(void* data);
static void irlap_media_busy_expired(void* data);
void irlap_start_slot_timer(struct irlap_cb *self, int timeout)
{
irda_start_timer(&self->slot_timer, timeout, (void *) self,
irlap_slot_timer_expired);
}
void irlap_start_query_timer(struct irlap_cb *self, int S, int s)
{
int timeout;
/* Calculate when the peer discovery should end. Normally, we
* get the end-of-discovery frame, so this is just in case
* we miss it.
* Basically, we multiply the number of remaining slots by our
* slot time, plus add some extra time to properly receive the last
* discovery packet (which is longer due to extra discovery info),
* to avoid messing with for incomming connections requests and
* to accomodate devices that perform discovery slower than us.
* Jean II */
timeout = ((sysctl_slot_timeout * HZ / 1000) * (S - s)
+ XIDEXTRA_TIMEOUT + SMALLBUSY_TIMEOUT);
/* Set or re-set the timer. We reset the timer for each received
* discovery query, which allow us to automatically adjust to
* the speed of the peer discovery (faster or slower). Jean II */
irda_start_timer( &self->query_timer, timeout, (void *) self,
irlap_query_timer_expired);
}
void irlap_start_final_timer(struct irlap_cb *self, int timeout)
{
irda_start_timer(&self->final_timer, timeout, (void *) self,
irlap_final_timer_expired);
}
void irlap_start_wd_timer(struct irlap_cb *self, int timeout)
{
irda_start_timer(&self->wd_timer, timeout, (void *) self,
irlap_wd_timer_expired);
}
void irlap_start_backoff_timer(struct irlap_cb *self, int timeout)
{
irda_start_timer(&self->backoff_timer, timeout, (void *) self,
irlap_backoff_timer_expired);
}
void irlap_start_mbusy_timer(struct irlap_cb *self, int timeout)
{
irda_start_timer(&self->media_busy_timer, timeout,
(void *) self, irlap_media_busy_expired);
}
void irlap_stop_mbusy_timer(struct irlap_cb *self)
{
/* If timer is activated, kill it! */
del_timer(&self->media_busy_timer);
/* If we are in NDM, there is a bunch of events in LAP that
* that be pending due to the media_busy condition, such as
* CONNECT_REQUEST and SEND_UI_FRAME. If we don't generate
* an event, they will wait forever...
* Jean II */
if (self->state == LAP_NDM)
irlap_do_event(self, MEDIA_BUSY_TIMER_EXPIRED, NULL, NULL);
}
void irlmp_start_watchdog_timer(struct lsap_cb *self, int timeout)
{
irda_start_timer(&self->watchdog_timer, timeout, (void *) self,
irlmp_watchdog_timer_expired);
}
void irlmp_start_discovery_timer(struct irlmp_cb *self, int timeout)
{
irda_start_timer(&self->discovery_timer, timeout, (void *) self,
irlmp_discovery_timer_expired);
}
void irlmp_start_idle_timer(struct lap_cb *self, int timeout)
{
irda_start_timer(&self->idle_timer, timeout, (void *) self,
irlmp_idle_timer_expired);
}
void irlmp_stop_idle_timer(struct lap_cb *self)
{
/* If timer is activated, kill it! */
del_timer(&self->idle_timer);
}
/*
* Function irlap_slot_timer_expired (data)
*
* IrLAP slot timer has expired
*
*/
static void irlap_slot_timer_expired(void *data)
{
struct irlap_cb *self = (struct irlap_cb *) data;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
irlap_do_event(self, SLOT_TIMER_EXPIRED, NULL, NULL);
}
/*
* Function irlap_query_timer_expired (data)
*
* IrLAP query timer has expired
*
*/
static void irlap_query_timer_expired(void *data)
{
struct irlap_cb *self = (struct irlap_cb *) data;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
irlap_do_event(self, QUERY_TIMER_EXPIRED, NULL, NULL);
}
/*
* Function irda_final_timer_expired (data)
*
*
*
*/
static void irlap_final_timer_expired(void *data)
{
struct irlap_cb *self = (struct irlap_cb *) data;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
irlap_do_event(self, FINAL_TIMER_EXPIRED, NULL, NULL);
}
/*
* Function irda_wd_timer_expired (data)
*
*
*
*/
static void irlap_wd_timer_expired(void *data)
{
struct irlap_cb *self = (struct irlap_cb *) data;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
irlap_do_event(self, WD_TIMER_EXPIRED, NULL, NULL);
}
/*
* Function irda_backoff_timer_expired (data)
*
*
*
*/
static void irlap_backoff_timer_expired(void *data)
{
struct irlap_cb *self = (struct irlap_cb *) data;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
irlap_do_event(self, BACKOFF_TIMER_EXPIRED, NULL, NULL);
}
/*
* Function irtty_media_busy_expired (data)
*
*
*/
void irlap_media_busy_expired(void* data)
{
struct irlap_cb *self = (struct irlap_cb *) data;
IRDA_ASSERT(self != NULL, return;);
irda_device_set_media_busy(self->netdev, FALSE);
/* Note : the LAP event will be send in irlap_stop_mbusy_timer(),
* to catch other cases where the flag is cleared (for example
* after a discovery) - Jean II */
}

491
net/irda/wrapper.c Normal file
View File

@@ -0,0 +1,491 @@
/*********************************************************************
*
* Filename: wrapper.c
* Version: 1.2
* Description: IrDA SIR async wrapper layer
* Status: Stable
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Aug 4 20:40:53 1997
* Modified at: Fri Jan 28 13:21:09 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Modified at: Fri May 28 3:11 CST 1999
* Modified by: Horst von Brand <vonbrand@sleipnir.valparaiso.cl>
*
* Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
* All Rights Reserved.
* Copyright (c) 2000-2002 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<6D> admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <linux/skbuff.h>
#include <linux/string.h>
#include <linux/module.h>
#include <asm/byteorder.h>
#include <net/irda/irda.h>
#include <net/irda/wrapper.h>
#include <net/irda/crc.h>
#include <net/irda/irlap.h>
#include <net/irda/irlap_frame.h>
#include <net/irda/irda_device.h>
/************************** FRAME WRAPPING **************************/
/*
* Unwrap and unstuff SIR frames
*
* Note : at FIR and MIR, HDLC framing is used and usually handled
* by the controller, so we come here only for SIR... Jean II
*/
/*
* Function stuff_byte (byte, buf)
*
* Byte stuff one single byte and put the result in buffer pointed to by
* buf. The buffer must at all times be able to have two bytes inserted.
*
* This is in a tight loop, better inline it, so need to be prior to callers.
* (2000 bytes on P6 200MHz, non-inlined ~370us, inline ~170us) - Jean II
*/
static inline int stuff_byte(__u8 byte, __u8 *buf)
{
switch (byte) {
case BOF: /* FALLTHROUGH */
case EOF: /* FALLTHROUGH */
case CE:
/* Insert transparently coded */
buf[0] = CE; /* Send link escape */
buf[1] = byte^IRDA_TRANS; /* Complement bit 5 */
return 2;
/* break; */
default:
/* Non-special value, no transparency required */
buf[0] = byte;
return 1;
/* break; */
}
}
/*
* Function async_wrap (skb, *tx_buff, buffsize)
*
* Makes a new buffer with wrapping and stuffing, should check that
* we don't get tx buffer overflow.
*/
int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize)
{
struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
int xbofs;
int i;
int n;
union {
__u16 value;
__u8 bytes[2];
} fcs;
/* Initialize variables */
fcs.value = INIT_FCS;
n = 0;
/*
* Send XBOF's for required min. turn time and for the negotiated
* additional XBOFS
*/
if (cb->magic != LAP_MAGIC) {
/*
* This will happen for all frames sent from user-space.
* Nothing to worry about, but we set the default number of
* BOF's
*/
IRDA_DEBUG(1, "%s(), wrong magic in skb!\n", __FUNCTION__);
xbofs = 10;
} else
xbofs = cb->xbofs + cb->xbofs_delay;
IRDA_DEBUG(4, "%s(), xbofs=%d\n", __FUNCTION__, xbofs);
/* Check that we never use more than 115 + 48 xbofs */
if (xbofs > 163) {
IRDA_DEBUG(0, "%s(), too many xbofs (%d)\n", __FUNCTION__,
xbofs);
xbofs = 163;
}
memset(tx_buff + n, XBOF, xbofs);
n += xbofs;
/* Start of packet character BOF */
tx_buff[n++] = BOF;
/* Insert frame and calc CRC */
for (i=0; i < skb->len; i++) {
/*
* Check for the possibility of tx buffer overflow. We use
* bufsize-5 since the maximum number of bytes that can be
* transmitted after this point is 5.
*/
if(n >= (buffsize-5)) {
IRDA_ERROR("%s(), tx buffer overflow (n=%d)\n",
__FUNCTION__, n);
return n;
}
n += stuff_byte(skb->data[i], tx_buff+n);
fcs.value = irda_fcs(fcs.value, skb->data[i]);
}
/* Insert CRC in little endian format (LSB first) */
fcs.value = ~fcs.value;
#ifdef __LITTLE_ENDIAN
n += stuff_byte(fcs.bytes[0], tx_buff+n);
n += stuff_byte(fcs.bytes[1], tx_buff+n);
#else /* ifdef __BIG_ENDIAN */
n += stuff_byte(fcs.bytes[1], tx_buff+n);
n += stuff_byte(fcs.bytes[0], tx_buff+n);
#endif
tx_buff[n++] = EOF;
return n;
}
EXPORT_SYMBOL(async_wrap_skb);
/************************* FRAME UNWRAPPING *************************/
/*
* Unwrap and unstuff SIR frames
*
* Complete rewrite by Jean II :
* More inline, faster, more compact, more logical. Jean II
* (16 bytes on P6 200MHz, old 5 to 7 us, new 4 to 6 us)
* (24 bytes on P6 200MHz, old 9 to 10 us, new 7 to 8 us)
* (for reference, 115200 b/s is 1 byte every 69 us)
* And reduce wrapper.o by ~900B in the process ;-)
*
* Then, we have the addition of ZeroCopy, which is optional
* (i.e. the driver must initiate it) and improve final processing.
* (2005 B frame + EOF on P6 200MHz, without 30 to 50 us, with 10 to 25 us)
*
* Note : at FIR and MIR, HDLC framing is used and usually handled
* by the controller, so we come here only for SIR... Jean II
*/
/*
* We can also choose where we want to do the CRC calculation. We can
* do it "inline", as we receive the bytes, or "postponed", when
* receiving the End-Of-Frame.
* (16 bytes on P6 200MHz, inlined 4 to 6 us, postponed 4 to 5 us)
* (24 bytes on P6 200MHz, inlined 7 to 8 us, postponed 5 to 7 us)
* With ZeroCopy :
* (2005 B frame on P6 200MHz, inlined 10 to 25 us, postponed 140 to 180 us)
* Without ZeroCopy :
* (2005 B frame on P6 200MHz, inlined 30 to 50 us, postponed 150 to 180 us)
* (Note : numbers taken with irq disabled)
*
* From those numbers, it's not clear which is the best strategy, because
* we end up running through a lot of data one way or another (i.e. cache
* misses). I personally prefer to avoid the huge latency spike of the
* "postponed" solution, because it come just at the time when we have
* lot's of protocol processing to do and it will hurt our ability to
* reach low link turnaround times... Jean II
*/
//#define POSTPONE_RX_CRC
/*
* Function async_bump (buf, len, stats)
*
* Got a frame, make a copy of it, and pass it up the stack! We can try
* to inline it since it's only called from state_inside_frame
*/
static inline void
async_bump(struct net_device *dev,
struct net_device_stats *stats,
iobuff_t *rx_buff)
{
struct sk_buff *newskb;
struct sk_buff *dataskb;
int docopy;
/* Check if we need to copy the data to a new skb or not.
* If the driver doesn't use ZeroCopy Rx, we have to do it.
* With ZeroCopy Rx, the rx_buff already point to a valid
* skb. But, if the frame is small, it is more efficient to
* copy it to save memory (copy will be fast anyway - that's
* called Rx-copy-break). Jean II */
docopy = ((rx_buff->skb == NULL) ||
(rx_buff->len < IRDA_RX_COPY_THRESHOLD));
/* Allocate a new skb */
newskb = dev_alloc_skb(docopy ? rx_buff->len + 1 : rx_buff->truesize);
if (!newskb) {
stats->rx_dropped++;
/* We could deliver the current skb if doing ZeroCopy Rx,
* but this would stall the Rx path. Better drop the
* packet... Jean II */
return;
}
/* Align IP header to 20 bytes (i.e. increase skb->data)
* Note this is only useful with IrLAN, as PPP has a variable
* header size (2 or 1 bytes) - Jean II */
skb_reserve(newskb, 1);
if(docopy) {
/* Copy data without CRC (lenght already checked) */
memcpy(newskb->data, rx_buff->data, rx_buff->len - 2);
/* Deliver this skb */
dataskb = newskb;
} else {
/* We are using ZeroCopy. Deliver old skb */
dataskb = rx_buff->skb;
/* And hook the new skb to the rx_buff */
rx_buff->skb = newskb;
rx_buff->head = newskb->data; /* NOT newskb->head */
//printk(KERN_DEBUG "ZeroCopy : len = %d, dataskb = %p, newskb = %p\n", rx_buff->len, dataskb, newskb);
}
/* Set proper length on skb (without CRC) */
skb_put(dataskb, rx_buff->len - 2);
/* Feed it to IrLAP layer */
dataskb->dev = dev;
dataskb->mac.raw = dataskb->data;
dataskb->protocol = htons(ETH_P_IRDA);
netif_rx(dataskb);
stats->rx_packets++;
stats->rx_bytes += rx_buff->len;
/* Clean up rx_buff (redundant with async_unwrap_bof() ???) */
rx_buff->data = rx_buff->head;
rx_buff->len = 0;
}
/*
* Function async_unwrap_bof(dev, byte)
*
* Handle Beginning Of Frame character received within a frame
*
*/
static inline void
async_unwrap_bof(struct net_device *dev,
struct net_device_stats *stats,
iobuff_t *rx_buff, __u8 byte)
{
switch(rx_buff->state) {
case LINK_ESCAPE:
case INSIDE_FRAME:
/* Not supposed to happen, the previous frame is not
* finished - Jean II */
IRDA_DEBUG(1, "%s(), Discarding incomplete frame\n",
__FUNCTION__);
stats->rx_errors++;
stats->rx_missed_errors++;
irda_device_set_media_busy(dev, TRUE);
break;
case OUTSIDE_FRAME:
case BEGIN_FRAME:
default:
/* We may receive multiple BOF at the start of frame */
break;
}
/* Now receiving frame */
rx_buff->state = BEGIN_FRAME;
rx_buff->in_frame = TRUE;
/* Time to initialize receive buffer */
rx_buff->data = rx_buff->head;
rx_buff->len = 0;
rx_buff->fcs = INIT_FCS;
}
/*
* Function async_unwrap_eof(dev, byte)
*
* Handle End Of Frame character received within a frame
*
*/
static inline void
async_unwrap_eof(struct net_device *dev,
struct net_device_stats *stats,
iobuff_t *rx_buff, __u8 byte)
{
#ifdef POSTPONE_RX_CRC
int i;
#endif
switch(rx_buff->state) {
case OUTSIDE_FRAME:
/* Probably missed the BOF */
stats->rx_errors++;
stats->rx_missed_errors++;
irda_device_set_media_busy(dev, TRUE);
break;
case BEGIN_FRAME:
case LINK_ESCAPE:
case INSIDE_FRAME:
default:
/* Note : in the case of BEGIN_FRAME and LINK_ESCAPE,
* the fcs will most likely not match and generate an
* error, as expected - Jean II */
rx_buff->state = OUTSIDE_FRAME;
rx_buff->in_frame = FALSE;
#ifdef POSTPONE_RX_CRC
/* If we haven't done the CRC as we receive bytes, we
* must do it now... Jean II */
for(i = 0; i < rx_buff->len; i++)
rx_buff->fcs = irda_fcs(rx_buff->fcs,
rx_buff->data[i]);
#endif
/* Test FCS and signal success if the frame is good */
if (rx_buff->fcs == GOOD_FCS) {
/* Deliver frame */
async_bump(dev, stats, rx_buff);
break;
} else {
/* Wrong CRC, discard frame! */
irda_device_set_media_busy(dev, TRUE);
IRDA_DEBUG(1, "%s(), crc error\n", __FUNCTION__);
stats->rx_errors++;
stats->rx_crc_errors++;
}
break;
}
}
/*
* Function async_unwrap_ce(dev, byte)
*
* Handle Character Escape character received within a frame
*
*/
static inline void
async_unwrap_ce(struct net_device *dev,
struct net_device_stats *stats,
iobuff_t *rx_buff, __u8 byte)
{
switch(rx_buff->state) {
case OUTSIDE_FRAME:
/* Activate carrier sense */
irda_device_set_media_busy(dev, TRUE);
break;
case LINK_ESCAPE:
IRDA_WARNING("%s: state not defined\n", __FUNCTION__);
break;
case BEGIN_FRAME:
case INSIDE_FRAME:
default:
/* Stuffed byte coming */
rx_buff->state = LINK_ESCAPE;
break;
}
}
/*
* Function async_unwrap_other(dev, byte)
*
* Handle other characters received within a frame
*
*/
static inline void
async_unwrap_other(struct net_device *dev,
struct net_device_stats *stats,
iobuff_t *rx_buff, __u8 byte)
{
switch(rx_buff->state) {
/* This is on the critical path, case are ordered by
* probability (most frequent first) - Jean II */
case INSIDE_FRAME:
/* Must be the next byte of the frame */
if (rx_buff->len < rx_buff->truesize) {
rx_buff->data[rx_buff->len++] = byte;
#ifndef POSTPONE_RX_CRC
rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
#endif
} else {
IRDA_DEBUG(1, "%s(), Rx buffer overflow, aborting\n",
__FUNCTION__);
rx_buff->state = OUTSIDE_FRAME;
}
break;
case LINK_ESCAPE:
/*
* Stuffed char, complement bit 5 of byte
* following CE, IrLAP p.114
*/
byte ^= IRDA_TRANS;
if (rx_buff->len < rx_buff->truesize) {
rx_buff->data[rx_buff->len++] = byte;
#ifndef POSTPONE_RX_CRC
rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
#endif
rx_buff->state = INSIDE_FRAME;
} else {
IRDA_DEBUG(1, "%s(), Rx buffer overflow, aborting\n",
__FUNCTION__);
rx_buff->state = OUTSIDE_FRAME;
}
break;
case OUTSIDE_FRAME:
/* Activate carrier sense */
if(byte != XBOF)
irda_device_set_media_busy(dev, TRUE);
break;
case BEGIN_FRAME:
default:
rx_buff->data[rx_buff->len++] = byte;
#ifndef POSTPONE_RX_CRC
rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
#endif
rx_buff->state = INSIDE_FRAME;
break;
}
}
/*
* Function async_unwrap_char (dev, rx_buff, byte)
*
* Parse and de-stuff frame received from the IrDA-port
*
* This is the main entry point for SIR drivers.
*/
void async_unwrap_char(struct net_device *dev,
struct net_device_stats *stats,
iobuff_t *rx_buff, __u8 byte)
{
switch(byte) {
case CE:
async_unwrap_ce(dev, stats, rx_buff, byte);
break;
case BOF:
async_unwrap_bof(dev, stats, rx_buff, byte);
break;
case EOF:
async_unwrap_eof(dev, stats, rx_buff, byte);
break;
default:
async_unwrap_other(dev, stats, rx_buff, byte);
break;
}
}
EXPORT_SYMBOL(async_unwrap_char);