Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
96
net/irda/Kconfig
Normal file
96
net/irda/Kconfig
Normal 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
15
net/irda/Makefile
Normal 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
|
||||
2617
net/irda/af_irda.c
Normal file
2617
net/irda/af_irda.c
Normal file
File diff suppressed because it is too large
Load Diff
419
net/irda/discovery.c
Normal file
419
net/irda/discovery.c
Normal 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/fs.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);
|
||||
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);
|
||||
}
|
||||
|
||||
const 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
12
net/irda/ircomm/Kconfig
Normal 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
8
net/irda/ircomm/Makefile
Normal 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
|
||||
583
net/irda/ircomm/ircomm_core.c
Normal file
583
net/irda/ircomm/ircomm_core.c
Normal file
@@ -0,0 +1,583 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/module.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 const 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 = kzalloc(sizeof(struct ircomm_cb), GFP_ATOMIC);
|
||||
if (self == NULL)
|
||||
return NULL;
|
||||
|
||||
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);
|
||||
250
net/irda/ircomm/ircomm_event.c
Normal file
250
net/irda/ircomm/ircomm_event.c
Normal file
@@ -0,0 +1,250 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/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);
|
||||
}
|
||||
371
net/irda/ircomm/ircomm_lmp.c
Normal file
371
net/irda/ircomm/ircomm_lmp.c
Normal file
@@ -0,0 +1,371 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/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 = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
|
||||
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 = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
|
||||
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(¬ify);
|
||||
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, ¬ify, 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;
|
||||
}
|
||||
510
net/irda/ircomm/ircomm_param.c
Normal file
510
net/irda/ircomm/ircomm_param.c
Normal file
@@ -0,0 +1,510 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/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 = alloc_skb(256, GFP_ATOMIC);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
368
net/irda/ircomm/ircomm_ttp.c
Normal file
368
net/irda/ircomm/ircomm_ttp.c
Normal file
@@ -0,0 +1,368 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/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(¬ify);
|
||||
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,
|
||||
¬ify);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
1401
net/irda/ircomm/ircomm_tty.c
Normal file
1401
net/irda/ircomm/ircomm_tty.c
Normal file
File diff suppressed because it is too large
Load Diff
1005
net/irda/ircomm/ircomm_tty_attach.c
Normal file
1005
net/irda/ircomm/ircomm_tty_attach.c
Normal file
File diff suppressed because it is too large
Load Diff
427
net/irda/ircomm/ircomm_tty_ioctl.c
Normal file
427
net/irda/ircomm/ircomm_tty_ioctl.c
Normal file
@@ -0,0 +1,427 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/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 ktermios *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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
494
net/irda/irda_device.c
Normal file
494
net/irda/irda_device.c
Normal file
@@ -0,0 +1,494 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/string.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/capability.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;
|
||||
|
||||
/* Some drivers may enable the receive interrupt before calling
|
||||
* irlap_open(), or they may disable the receive interrupt
|
||||
* after calling irlap_close().
|
||||
* The IrDA stack is protected from this in irlap_driver_rcv().
|
||||
* However, the driver calls directly the wrapper, that calls
|
||||
* us directly. Make sure we protect ourselves.
|
||||
* Jean II */
|
||||
if (!self || 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 = LAP_ALEN;
|
||||
|
||||
dev->type = ARPHRD_IRDA;
|
||||
dev->tx_queue_len = 8; /* Window size + 1 s-frame */
|
||||
|
||||
memset(dev->broadcast, 0xff, LAP_ALEN);
|
||||
|
||||
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 = kzalloc(sizeof(dongle_t), GFP_KERNEL);
|
||||
if (!dongle)
|
||||
goto out;
|
||||
|
||||
/* 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_DMA_API
|
||||
/*
|
||||
* Function setup_dma (idev, buffer, count, mode)
|
||||
*
|
||||
* Setup the DMA channel. Commonly used by LPC 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
|
||||
1091
net/irda/iriap.c
Normal file
1091
net/irda/iriap.c
Normal file
File diff suppressed because it is too large
Load Diff
502
net/irda/iriap_event.c
Normal file
502
net/irda/iriap_event.c
Normal 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 = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
|
||||
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;
|
||||
}
|
||||
}
|
||||
599
net/irda/irias_object.c
Normal file
599
net/irda/irias_object.c
Normal file
@@ -0,0 +1,599 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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, size_t 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 = kzalloc(sizeof(struct ias_object), GFP_ATOMIC);
|
||||
if (obj == NULL) {
|
||||
IRDA_WARNING("%s(), Unable to allocate object!\n",
|
||||
__FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj->magic = IAS_OBJECT_MAGIC;
|
||||
obj->name = strndup(name, IAS_MAX_CLASSNAME);
|
||||
if (!obj->name) {
|
||||
IRDA_WARNING("%s(), Unable to allocate name!\n",
|
||||
__FUNCTION__);
|
||||
kfree(obj);
|
||||
return NULL;
|
||||
}
|
||||
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->name);
|
||||
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;);
|
||||
|
||||
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;);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC);
|
||||
if (attrib == NULL) {
|
||||
IRDA_WARNING("%s: Unable to allocate attribute!\n",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
attrib->magic = IAS_ATTRIB_MAGIC;
|
||||
attrib->name = strndup(name, IAS_MAX_ATTRIBNAME);
|
||||
|
||||
/* Insert value */
|
||||
attrib->value = irias_new_integer_value(value);
|
||||
if (!attrib->name || !attrib->value) {
|
||||
IRDA_WARNING("%s: Unable to allocate attribute!\n",
|
||||
__FUNCTION__);
|
||||
if (attrib->value)
|
||||
irias_delete_value(attrib->value);
|
||||
kfree(attrib->name);
|
||||
kfree(attrib);
|
||||
return;
|
||||
}
|
||||
|
||||
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 = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC);
|
||||
if (attrib == NULL) {
|
||||
IRDA_WARNING("%s: Unable to allocate attribute!\n",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
attrib->magic = IAS_ATTRIB_MAGIC;
|
||||
attrib->name = strndup(name, IAS_MAX_ATTRIBNAME);
|
||||
|
||||
attrib->value = irias_new_octseq_value( octets, len);
|
||||
if (!attrib->name || !attrib->value) {
|
||||
IRDA_WARNING("%s: Unable to allocate attribute!\n",
|
||||
__FUNCTION__);
|
||||
if (attrib->value)
|
||||
irias_delete_value(attrib->value);
|
||||
kfree(attrib->name);
|
||||
kfree(attrib);
|
||||
return;
|
||||
}
|
||||
|
||||
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 = kzalloc(sizeof( struct ias_attrib), GFP_ATOMIC);
|
||||
if (attrib == NULL) {
|
||||
IRDA_WARNING("%s: Unable to allocate attribute!\n",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
attrib->magic = IAS_ATTRIB_MAGIC;
|
||||
attrib->name = strndup(name, IAS_MAX_ATTRIBNAME);
|
||||
|
||||
attrib->value = irias_new_string_value(value);
|
||||
if (!attrib->name || !attrib->value) {
|
||||
IRDA_WARNING("%s: Unable to allocate attribute!\n",
|
||||
__FUNCTION__);
|
||||
if (attrib->value)
|
||||
irias_delete_value(attrib->value);
|
||||
kfree(attrib->name);
|
||||
kfree(attrib);
|
||||
return;
|
||||
}
|
||||
|
||||
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 = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
|
||||
if (value == NULL) {
|
||||
IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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 = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
|
||||
if (value == NULL) {
|
||||
IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
value->type = IAS_STRING;
|
||||
value->charset = CS_ASCII;
|
||||
value->t.string = strndup(string, IAS_MAX_STRING);
|
||||
if (!value->t.string) {
|
||||
IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__);
|
||||
kfree(value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
value->len = strlen(value->t.string);
|
||||
|
||||
return 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 = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
|
||||
if (value == NULL) {
|
||||
IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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 = kmemdup(octseq, len, GFP_ATOMIC);
|
||||
if (value->t.oct_seq == NULL){
|
||||
IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__);
|
||||
kfree(value);
|
||||
return NULL;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
struct ias_value *irias_new_missing_value(void)
|
||||
{
|
||||
struct ias_value *value;
|
||||
|
||||
value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
|
||||
if (value == NULL) {
|
||||
IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
value->type = IAS_MISSING;
|
||||
|
||||
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:
|
||||
/* Deallocate string */
|
||||
kfree(value->t.string);
|
||||
break;
|
||||
case IAS_OCT_SEQ:
|
||||
/* Deallocate byte stream */
|
||||
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
14
net/irda/irlan/Kconfig
Normal 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
7
net/irda/irlan/Makefile
Normal 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
|
||||
577
net/irda/irlan/irlan_client.c
Normal file
577
net/irda/irlan/irlan_client.c
Normal file
@@ -0,0 +1,577 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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, goto out;);
|
||||
|
||||
IRDA_DEBUG(1, "%s(), Found instance (%08x)!\n", __FUNCTION__ ,
|
||||
daddr);
|
||||
|
||||
irlan_client_wakeup(self, saddr, daddr);
|
||||
}
|
||||
IRDA_ASSERT_LABEL(out:)
|
||||
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(¬ify);
|
||||
|
||||
/* 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, ¬ify);
|
||||
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);
|
||||
}
|
||||
533
net/irda/irlan/irlan_client_event.c
Normal file
533
net/irda/irlan/irlan_client_event.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1244
net/irda/irlan/irlan_common.c
Normal file
1244
net/irda/irlan/irlan_common.c
Normal file
File diff suppressed because it is too large
Load Diff
386
net/irda/irlan/irlan_eth.c
Normal file
386
net/irda/irlan/irlan_eth.c
Normal file
@@ -0,0 +1,386 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/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_rcu(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;
|
||||
}
|
||||
60
net/irda/irlan/irlan_event.c
Normal file
60
net/irda/irlan/irlan_event.c
Normal 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;
|
||||
}
|
||||
|
||||
247
net/irda/irlan/irlan_filter.c
Normal file
247
net/irda/irlan/irlan_filter.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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>
|
||||
#include <net/irda/irlan_filter.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
|
||||
419
net/irda/irlan/irlan_provider.c
Normal file
419
net/irda/irlan/irlan_provider.c
Normal file
@@ -0,0 +1,419 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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 = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER +
|
||||
/* Bigger param length comes from CMD_GET_MEDIA_CHAR */
|
||||
IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") +
|
||||
IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BORADCAST") +
|
||||
IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") +
|
||||
IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "HOSTED"),
|
||||
GFP_ATOMIC);
|
||||
|
||||
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_string_param(skb, "RECONNECT_KEY", "LINUX RULES!");
|
||||
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(¬ify);
|
||||
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, ¬ify);
|
||||
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;
|
||||
}
|
||||
|
||||
241
net/irda/irlan/irlan_provider_event.c
Normal file
241
net/irda/irlan/irlan_provider_event.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1255
net/irda/irlap.c
Normal file
1255
net/irda/irlap.c
Normal file
File diff suppressed because it is too large
Load Diff
2333
net/irda/irlap_event.c
Normal file
2333
net/irda/irlap_event.c
Normal file
File diff suppressed because it is too large
Load Diff
1433
net/irda/irlap_frame.c
Normal file
1433
net/irda/irlap_frame.c
Normal file
File diff suppressed because it is too large
Load Diff
2037
net/irda/irlmp.c
Normal file
2037
net/irda/irlmp.c
Normal file
File diff suppressed because it is too large
Load Diff
911
net/irda/irlmp_event.c
Normal file
911
net/irda/irlmp_event.c
Normal file
@@ -0,0 +1,911 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/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;
|
||||
}
|
||||
490
net/irda/irlmp_frame.c
Normal file
490
net/irda/irlmp_frame.c
Normal file
@@ -0,0 +1,490 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/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;
|
||||
}
|
||||
171
net/irda/irmod.c
Normal file
171
net/irda/irmod.c
Normal file
@@ -0,0 +1,171 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/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 */
|
||||
|
||||
/*
|
||||
* 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
13
net/irda/irnet/Kconfig
Normal 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
7
net/irda/irnet/Makefile
Normal 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
|
||||
525
net/irda/irnet/irnet.h
Normal file
525
net/irda/irnet/irnet.h
Normal file
@@ -0,0 +1,525 @@
|
||||
/*
|
||||
* 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/netdevice.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/capability.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 ??? */
|
||||
struct work_struct disconnect_work; /* Process context disconnection */
|
||||
/* ------------------------ 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 */
|
||||
|
||||
/**************************** VARIABLES ****************************/
|
||||
|
||||
/* Control channel stuff - allocated in irnet_irda.h */
|
||||
extern struct irnet_ctrl_channel irnet_events;
|
||||
|
||||
#endif /* IRNET_H */
|
||||
1882
net/irda/irnet/irnet_irda.c
Normal file
1882
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
186
net/irda/irnet/irnet_irda.h
Normal 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 */
|
||||
1141
net/irda/irnet/irnet_ppp.c
Normal file
1141
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
119
net/irda/irnet/irnet_ppp.h
Normal 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
100
net/irda/irproc.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
913
net/irda/irqueue.c
Normal file
913
net/irda/irqueue.c
Normal file
@@ -0,0 +1,913 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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 = kzalloc(sizeof(*hashbin), GFP_ATOMIC);
|
||||
if (!hashbin)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Initialize structure
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
296
net/irda/irsysctl.c
Normal file
296
net/irda/irsysctl.c
Normal file
@@ -0,0 +1,296 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
1907
net/irda/irttp.c
Normal file
1907
net/irda/irttp.c
Normal file
File diff suppressed because it is too large
Load Diff
589
net/irda/parameters.c
Normal file
589
net/irda/parameters.c
Normal 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
774
net/irda/qos.c
Normal 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 <asm/byteorder.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/parameters.h>
|
||||
#include <net/irda/qos.h>
|
||||
#include <net/irda/irlap.h>
|
||||
#include <net/irda/irlap_frame.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);
|
||||
232
net/irda/timer.c
Normal file
232
net/irda/timer.c
Normal file
@@ -0,0 +1,232 @@
|
||||
/*********************************************************************
|
||||
*
|
||||
* 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/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
491
net/irda/wrapper.c
Normal 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);
|
||||
|
||||
Reference in New Issue
Block a user