M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
config IRCOMM
tristate "IrCOMM protocol"
depends on IRDA
help
Say Y here if you want to build support for the IrCOMM protocol.
To compile it as modules, choose M here: the modules will be
called ircomm and ircomm_tty.
IrCOMM implements serial port emulation, and makes it possible to
use all existing applications that understands TTY's with an
infrared link. Thus you should be able to use application like PPP,
minicom and others.
+8
View File
@@ -0,0 +1,8 @@
#
# Makefile for the Linux IrDA IrCOMM protocol layer.
#
obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o
ircomm-y := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o
ircomm-tty-y := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o
+592
View File
@@ -0,0 +1,592 @@
/*********************************************************************
*
* 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 <linux/slab.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", __func__);
return -ENOMEM;
}
#ifdef CONFIG_PROC_FS
{ struct proc_dir_entry *ent;
ent = proc_create("ircomm", 0, proc_irda, &ircomm_proc_fops);
if (!ent) {
printk(KERN_ERR "ircomm_init: can't create /proc entry!\n");
return -ENODEV;
}
}
#endif /* CONFIG_PROC_FS */
IRDA_MESSAGE("IrCOMM protocol (Dag Brattli)\n");
return 0;
}
static void __exit ircomm_cleanup(void)
{
IRDA_DEBUG(2, "%s()\n", __func__ );
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", __func__ ,
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", __func__ );
/* 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", __func__ );
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", __func__ );
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)
{
IRDA_DEBUG(2, "%s()\n", __func__ );
/*
* 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", __func__ );
}
}
/*
* 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", __func__ );
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", __func__ );
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", __func__ );
}
}
/*
* 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", __func__ );
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", __func__ );
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", __func__ );
}
}
/*
* 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];
/*
* Input validation check: a stir4200/mcp2150 combinations sometimes
* results in frames with clen > remaining packet size. These are
* illegal; if we throw away just this frame then it seems to carry on
* fine
*/
if (unlikely(skb->len < (clen + 1))) {
IRDA_DEBUG(2, "%s() throwing away illegal frame\n",
__func__ );
return;
}
/*
* 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",
__func__ );
}
}
/*
* 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", __func__ );
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", __func__ );
/* 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", __func__ );
}
}
/*
* 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", __func__ );
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", __func__ );
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", __func__ );
}
}
/*
* Function ircomm_flow_request (self, flow)
*
*
*
*/
void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow)
{
IRDA_DEBUG(2, "%s()\n", __func__ );
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 const 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
View 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);
const char *const ircomm_state[] = {
"IRCOMM_IDLE",
"IRCOMM_WAITI",
"IRCOMM_WAITR",
"IRCOMM_CONN",
};
#ifdef CONFIG_IRDA_DEBUG
static const char *const 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", __func__ ,
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", __func__ ,
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", __func__ ,
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", __func__ ,
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", __func__ ,
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", __func__ ,
ircomm_state[self->state], self->service_type);
}
+370
View File
@@ -0,0 +1,370 @@
/*********************************************************************
*
* 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 <linux/gfp.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", __func__ );
/* 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;
IRDA_DEBUG(0, "%s()\n", __func__ );
/* 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;
}
return irlmp_connect_response(self->lsap, tx_skb);
}
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", __func__ );
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", __func__ );
line = cb->line;
self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL);
if (!self) {
IRDA_DEBUG(2, "%s(), didn't find myself\n", __func__ );
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", __func__ );
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", __func__ );
/* Don't forget to refcount it - see ircomm_tty_do_softint() */
skb_get(skb);
skb_orphan(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", __func__ );
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", __func__);
/* 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", __func__ );
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", __func__ );
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", __func__ );
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", __func__ );
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", __func__ );
/* Register callbacks */
irda_notify_init(&notify);
notify.data_indication = ircomm_lmp_data_indication;
notify.connect_confirm = ircomm_lmp_connect_confirm;
notify.connect_indication = ircomm_lmp_connect_indication;
notify.disconnect_indication = ircomm_lmp_disconnect_indication;
notify.instance = self;
strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
self->lsap = irlmp_open_lsap(LSAP_ANY, &notify, 0);
if (!self->lsap) {
IRDA_DEBUG(0,"%sfailed to allocate tsap\n", __func__ );
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;
}
+511
View File
@@ -0,0 +1,511 @@
/*********************************************************************
*
* Filename: ircomm_param.c
* Version: 1.0
* Description: Parameter handling for the IrCOMM protocol
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Jun 7 10:25:11 1999
* Modified at: Sun Jan 30 14:32:03 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
********************************************************************/
#include <linux/gfp.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <net/irda/irda.h>
#include <net/irda/parameters.h>
#include <net/irda/ircomm_core.h>
#include <net/irda/ircomm_tty_attach.h>
#include <net/irda/ircomm_tty.h>
#include <net/irda/ircomm_param.h>
static int ircomm_param_service_type(void *instance, irda_param_t *param,
int get);
static int ircomm_param_port_type(void *instance, irda_param_t *param,
int get);
static int ircomm_param_port_name(void *instance, irda_param_t *param,
int get);
static int ircomm_param_service_type(void *instance, irda_param_t *param,
int get);
static int ircomm_param_data_rate(void *instance, irda_param_t *param,
int get);
static int ircomm_param_data_format(void *instance, irda_param_t *param,
int get);
static int ircomm_param_flow_control(void *instance, irda_param_t *param,
int get);
static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get);
static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get);
static int ircomm_param_line_status(void *instance, irda_param_t *param,
int get);
static int ircomm_param_dte(void *instance, irda_param_t *param, int get);
static int ircomm_param_dce(void *instance, irda_param_t *param, int get);
static int ircomm_param_poll(void *instance, irda_param_t *param, int get);
static pi_minor_info_t pi_minor_call_table_common[] = {
{ ircomm_param_service_type, PV_INT_8_BITS },
{ ircomm_param_port_type, PV_INT_8_BITS },
{ ircomm_param_port_name, PV_STRING }
};
static pi_minor_info_t pi_minor_call_table_non_raw[] = {
{ ircomm_param_data_rate, PV_INT_32_BITS | PV_BIG_ENDIAN },
{ ircomm_param_data_format, PV_INT_8_BITS },
{ ircomm_param_flow_control, PV_INT_8_BITS },
{ ircomm_param_xon_xoff, PV_INT_16_BITS },
{ ircomm_param_enq_ack, PV_INT_16_BITS },
{ ircomm_param_line_status, PV_INT_8_BITS }
};
static pi_minor_info_t pi_minor_call_table_9_wire[] = {
{ ircomm_param_dte, PV_INT_8_BITS },
{ ircomm_param_dce, PV_INT_8_BITS },
{ ircomm_param_poll, PV_NO_VALUE },
};
static pi_major_info_t pi_major_call_table[] = {
{ pi_minor_call_table_common, 3 },
{ pi_minor_call_table_non_raw, 6 },
{ pi_minor_call_table_9_wire, 3 }
/* { pi_minor_call_table_centronics } */
};
pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 };
/*
* Function ircomm_param_request (self, pi, flush)
*
* Queue a parameter for the control channel
*
*/
int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush)
{
struct tty_struct *tty;
unsigned long flags;
struct sk_buff *skb;
int count;
IRDA_DEBUG(2, "%s()\n", __func__ );
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_pointer(skb),
skb_tailroom(skb), &ircomm_param_info);
if (count < 0) {
IRDA_WARNING("%s(), no room for parameter!\n", __func__);
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", __func__ , 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", __func__ );
return -1;
}
IRDA_DEBUG(0, "%s(), services in common=%02x\n", __func__ ,
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", __func__ ,
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", __func__ ,
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", __func__ );
} else {
IRDA_DEBUG(0, "%s(), port-name=%s\n", __func__ , 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", __func__ , 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", __func__ , (__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", __func__ ,
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", __func__ ,
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", __func__ );
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", __func__ , (__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", __func__ );
}
}
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 length 0 (just a signal) */
if (!get) {
/* Respond with DTE line settings */
ircomm_param_request(self, IRCOMM_DTE, TRUE);
}
return 0;
}
+368
View 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", __func__ );
/* Register callbacks */
irda_notify_init(&notify);
notify.data_indication = ircomm_ttp_data_indication;
notify.connect_confirm = ircomm_ttp_connect_confirm;
notify.connect_indication = ircomm_ttp_connect_indication;
notify.flow_indication = ircomm_ttp_flow_indication;
notify.disconnect_indication = ircomm_ttp_disconnect_indication;
notify.instance = self;
strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
&notify);
if (!self->tsap) {
IRDA_DEBUG(0, "%sfailed to allocate tsap\n", __func__ );
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", __func__ );
/* 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", __func__ );
/* 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", __func__ , 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", __func__);
/* 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", __func__ );
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", __func__ );
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",
__func__);
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", __func__ );
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",
__func__);
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", __func__ );
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", __func__ );
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);
}
File diff suppressed because it is too large Load Diff
+997
View File
@@ -0,0 +1,997 @@
/*********************************************************************
*
* Filename: ircomm_tty_attach.c
* Version:
* Description: Code for attaching the serial driver to IrCOMM
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sat Jun 5 17:42:00 1999
* Modified at: Tue Jan 4 14:20:49 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999-2000 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 <linux/sched.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/parameters.h>
#include <net/irda/ircomm_core.h>
#include <net/irda/ircomm_param.h>
#include <net/irda/ircomm_event.h>
#include <net/irda/ircomm_tty.h>
#include <net/irda/ircomm_tty_attach.h>
static void ircomm_tty_ias_register(struct ircomm_tty_cb *self);
static void ircomm_tty_discovery_indication(discinfo_t *discovery,
DISCOVERY_MODE mode,
void *priv);
static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
struct ias_value *value, void *priv);
static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
int timeout);
static void ircomm_tty_watchdog_timer_expired(void *data);
static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
IRCOMM_TTY_EVENT event,
struct sk_buff *skb,
struct ircomm_tty_info *info);
static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
IRCOMM_TTY_EVENT event,
struct sk_buff *skb,
struct ircomm_tty_info *info);
static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
IRCOMM_TTY_EVENT event,
struct sk_buff *skb,
struct ircomm_tty_info *info);
static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
IRCOMM_TTY_EVENT event,
struct sk_buff *skb,
struct ircomm_tty_info *info);
static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
IRCOMM_TTY_EVENT event,
struct sk_buff *skb,
struct ircomm_tty_info *info);
static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
IRCOMM_TTY_EVENT event,
struct sk_buff *skb,
struct ircomm_tty_info *info);
const char *const ircomm_tty_state[] = {
"IRCOMM_TTY_IDLE",
"IRCOMM_TTY_SEARCH",
"IRCOMM_TTY_QUERY_PARAMETERS",
"IRCOMM_TTY_QUERY_LSAP_SEL",
"IRCOMM_TTY_SETUP",
"IRCOMM_TTY_READY",
"*** ERROR *** ",
};
#ifdef CONFIG_IRDA_DEBUG
static const char *const ircomm_tty_event[] = {
"IRCOMM_TTY_ATTACH_CABLE",
"IRCOMM_TTY_DETACH_CABLE",
"IRCOMM_TTY_DATA_REQUEST",
"IRCOMM_TTY_DATA_INDICATION",
"IRCOMM_TTY_DISCOVERY_REQUEST",
"IRCOMM_TTY_DISCOVERY_INDICATION",
"IRCOMM_TTY_CONNECT_CONFIRM",
"IRCOMM_TTY_CONNECT_INDICATION",
"IRCOMM_TTY_DISCONNECT_REQUEST",
"IRCOMM_TTY_DISCONNECT_INDICATION",
"IRCOMM_TTY_WD_TIMER_EXPIRED",
"IRCOMM_TTY_GOT_PARAMETERS",
"IRCOMM_TTY_GOT_LSAPSEL",
"*** ERROR ****",
};
#endif /* CONFIG_IRDA_DEBUG */
static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
struct sk_buff *skb, struct ircomm_tty_info *info) =
{
ircomm_tty_state_idle,
ircomm_tty_state_search,
ircomm_tty_state_query_parameters,
ircomm_tty_state_query_lsap_sel,
ircomm_tty_state_setup,
ircomm_tty_state_ready,
};
/*
* Function ircomm_tty_attach_cable (driver)
*
* Try to attach cable (IrCOMM link). This function will only return
* when the link has been connected, or if an error condition occurs.
* If success, the return value is the resulting service type.
*/
int ircomm_tty_attach_cable(struct ircomm_tty_cb *self)
{
IRDA_DEBUG(0, "%s()\n", __func__ );
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
/* Check if somebody has already connected to us */
if (ircomm_is_connected(self->ircomm)) {
IRDA_DEBUG(0, "%s(), already connected!\n", __func__ );
return 0;
}
/* Make sure nobody tries to write before the link is up */
self->tty->hw_stopped = 1;
ircomm_tty_ias_register(self);
ircomm_tty_do_event(self, IRCOMM_TTY_ATTACH_CABLE, NULL, NULL);
return 0;
}
/*
* Function ircomm_detach_cable (driver)
*
* Detach cable, or cable has been detached by peer
*
*/
void ircomm_tty_detach_cable(struct ircomm_tty_cb *self)
{
IRDA_DEBUG(0, "%s()\n", __func__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
del_timer(&self->watchdog_timer);
/* Remove discovery handler */
if (self->ckey) {
irlmp_unregister_client(self->ckey);
self->ckey = NULL;
}
/* Remove IrCOMM hint bits */
if (self->skey) {
irlmp_unregister_service(self->skey);
self->skey = NULL;
}
if (self->iriap) {
iriap_close(self->iriap);
self->iriap = NULL;
}
/* Remove LM-IAS object */
if (self->obj) {
irias_delete_object(self->obj);
self->obj = NULL;
}
ircomm_tty_do_event(self, IRCOMM_TTY_DETACH_CABLE, NULL, NULL);
/* Reset some values */
self->daddr = self->saddr = 0;
self->dlsap_sel = self->slsap_sel = 0;
memset(&self->settings, 0, sizeof(struct ircomm_params));
}
/*
* Function ircomm_tty_ias_register (self)
*
* Register with LM-IAS depending on which service type we are
*
*/
static void ircomm_tty_ias_register(struct ircomm_tty_cb *self)
{
__u8 oct_seq[6];
__u16 hints;
IRDA_DEBUG(0, "%s()\n", __func__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
/* Compute hint bits based on service */
hints = irlmp_service_to_hint(S_COMM);
if (self->service_type & IRCOMM_3_WIRE_RAW)
hints |= irlmp_service_to_hint(S_PRINTER);
/* Advertise IrCOMM hint bit in discovery */
if (!self->skey)
self->skey = irlmp_register_service(hints);
/* Set up a discovery handler */
if (!self->ckey)
self->ckey = irlmp_register_client(hints,
ircomm_tty_discovery_indication,
NULL, (void *) self);
/* If already done, no need to do it again */
if (self->obj)
return;
if (self->service_type & IRCOMM_3_WIRE_RAW) {
/* Register IrLPT with LM-IAS */
self->obj = irias_new_object("IrLPT", IAS_IRLPT_ID);
irias_add_integer_attrib(self->obj, "IrDA:IrLMP:LsapSel",
self->slsap_sel, IAS_KERNEL_ATTR);
} else {
/* Register IrCOMM with LM-IAS */
self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID);
irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel",
self->slsap_sel, IAS_KERNEL_ATTR);
/* Code the parameters into the buffer */
irda_param_pack(oct_seq, "bbbbbb",
IRCOMM_SERVICE_TYPE, 1, self->service_type,
IRCOMM_PORT_TYPE, 1, IRCOMM_SERIAL);
/* Register parameters with LM-IAS */
irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6,
IAS_KERNEL_ATTR);
}
irias_insert_object(self->obj);
}
/*
* Function ircomm_tty_ias_unregister (self)
*
* Remove our IAS object and client hook while connected.
*
*/
static void ircomm_tty_ias_unregister(struct ircomm_tty_cb *self)
{
/* Remove LM-IAS object now so it is not reused.
* IrCOMM deals very poorly with multiple incoming connections.
* It should looks a lot more like IrNET, and "dup" a server TSAP
* to the application TSAP (based on various rules).
* This is a cheap workaround allowing multiple clients to
* connect to us. It will not always work.
* Each IrCOMM socket has an IAS entry. Incoming connection will
* pick the first one found. So, when we are fully connected,
* we remove our IAS entries so that the next IAS entry is used.
* We do that for *both* client and server, because a server
* can also create client instances.
* Jean II */
if (self->obj) {
irias_delete_object(self->obj);
self->obj = NULL;
}
#if 0
/* Remove discovery handler.
* While we are connected, we no longer need to receive
* discovery events. This would be the case if there is
* multiple IrLAP interfaces. Jean II */
if (self->ckey) {
irlmp_unregister_client(self->ckey);
self->ckey = NULL;
}
#endif
}
/*
* Function ircomm_send_initial_parameters (self)
*
* Send initial parameters to the remote IrCOMM device. These parameters
* must be sent before any data.
*/
int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self)
{
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (self->service_type & IRCOMM_3_WIRE_RAW)
return 0;
/*
* Set default values, but only if the application for some reason
* haven't set them already
*/
IRDA_DEBUG(2, "%s(), data-rate = %d\n", __func__ ,
self->settings.data_rate);
if (!self->settings.data_rate)
self->settings.data_rate = 9600;
IRDA_DEBUG(2, "%s(), data-format = %d\n", __func__ ,
self->settings.data_format);
if (!self->settings.data_format)
self->settings.data_format = IRCOMM_WSIZE_8; /* 8N1 */
IRDA_DEBUG(2, "%s(), flow-control = %d\n", __func__ ,
self->settings.flow_control);
/*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/
/* Do not set delta values for the initial parameters */
self->settings.dte = IRCOMM_DTR | IRCOMM_RTS;
/* Only send service type parameter when we are the client */
if (self->client)
ircomm_param_request(self, IRCOMM_SERVICE_TYPE, FALSE);
ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
/* For a 3 wire service, we just flush the last parameter and return */
if (self->settings.service_type == IRCOMM_3_WIRE) {
ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
return 0;
}
/* Only 9-wire service types continue here */
ircomm_param_request(self, IRCOMM_FLOW_CONTROL, FALSE);
#if 0
ircomm_param_request(self, IRCOMM_XON_XOFF, FALSE);
ircomm_param_request(self, IRCOMM_ENQ_ACK, FALSE);
#endif
/* Notify peer that we are ready to receive data */
ircomm_param_request(self, IRCOMM_DTE, TRUE);
return 0;
}
/*
* Function ircomm_tty_discovery_indication (discovery)
*
* Remote device is discovered, try query the remote IAS to see which
* device it is, and which services it has.
*
*/
static void ircomm_tty_discovery_indication(discinfo_t *discovery,
DISCOVERY_MODE mode,
void *priv)
{
struct ircomm_tty_cb *self;
struct ircomm_tty_info info;
IRDA_DEBUG(2, "%s()\n", __func__ );
/* Important note :
* We need to drop all passive discoveries.
* The LSAP management of IrComm is deficient and doesn't deal
* with the case of two instance connecting to each other
* simultaneously (it will deadlock in LMP).
* The proper fix would be to use the same technique as in IrNET,
* to have one server socket and separate instances for the
* connecting/connected socket.
* The workaround is to drop passive discovery, which drastically
* reduce the probability of this happening.
* Jean II */
if(mode == DISCOVERY_PASSIVE)
return;
info.daddr = discovery->daddr;
info.saddr = discovery->saddr;
self = priv;
ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION,
NULL, &info);
}
/*
* Function ircomm_tty_disconnect_indication (instance, sap, reason, skb)
*
* Link disconnected
*
*/
void ircomm_tty_disconnect_indication(void *instance, void *sap,
LM_REASON reason,
struct sk_buff *skb)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
IRDA_DEBUG(2, "%s()\n", __func__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
if (!self->tty)
return;
/* This will stop control data transfers */
self->flow = FLOW_STOP;
/* Stop data transfers */
self->tty->hw_stopped = 1;
ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL,
NULL);
}
/*
* Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv)
*
* Got result from the IAS query we make
*
*/
static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
struct ias_value *value,
void *priv)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv;
IRDA_DEBUG(2, "%s()\n", __func__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
/* We probably don't need to make any more queries */
iriap_close(self->iriap);
self->iriap = NULL;
/* Check if request succeeded */
if (result != IAS_SUCCESS) {
IRDA_DEBUG(4, "%s(), got NULL value!\n", __func__ );
return;
}
switch (value->type) {
case IAS_OCT_SEQ:
IRDA_DEBUG(2, "%s(), got octet sequence\n", __func__ );
irda_param_extract_all(self, value->t.oct_seq, value->len,
&ircomm_param_info);
ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL,
NULL);
break;
case IAS_INTEGER:
/* Got LSAP selector */
IRDA_DEBUG(2, "%s(), got lsapsel = %d\n", __func__ ,
value->t.integer);
if (value->t.integer == -1) {
IRDA_DEBUG(0, "%s(), invalid value!\n", __func__ );
} else
self->dlsap_sel = value->t.integer;
ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL);
break;
case IAS_MISSING:
IRDA_DEBUG(0, "%s(), got IAS_MISSING\n", __func__ );
break;
default:
IRDA_DEBUG(0, "%s(), got unknown type!\n", __func__ );
break;
}
irias_delete_value(value);
}
/*
* Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb)
*
* Connection confirmed
*
*/
void ircomm_tty_connect_confirm(void *instance, void *sap,
struct qos_info *qos,
__u32 max_data_size,
__u8 max_header_size,
struct sk_buff *skb)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
IRDA_DEBUG(2, "%s()\n", __func__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
self->client = TRUE;
self->max_data_size = max_data_size;
self->max_header_size = max_header_size;
self->flow = FLOW_START;
ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL);
/* No need to kfree_skb - see ircomm_ttp_connect_confirm() */
}
/*
* Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size,
* skb)
*
* we are discovered and being requested to connect by remote device !
*
*/
void ircomm_tty_connect_indication(void *instance, void *sap,
struct qos_info *qos,
__u32 max_data_size,
__u8 max_header_size,
struct sk_buff *skb)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
int clen;
IRDA_DEBUG(2, "%s()\n", __func__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
self->client = FALSE;
self->max_data_size = max_data_size;
self->max_header_size = max_header_size;
self->flow = FLOW_START;
clen = skb->data[0];
if (clen)
irda_param_extract_all(self, skb->data+1,
IRDA_MIN(skb->len, clen),
&ircomm_param_info);
ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL);
/* No need to kfree_skb - see ircomm_ttp_connect_indication() */
}
/*
* Function ircomm_tty_link_established (self)
*
* Called when the IrCOMM link is established
*
*/
void ircomm_tty_link_established(struct ircomm_tty_cb *self)
{
IRDA_DEBUG(2, "%s()\n", __func__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
if (!self->tty)
return;
del_timer(&self->watchdog_timer);
/*
* IrCOMM link is now up, and if we are not using hardware
* flow-control, then declare the hardware as running. Otherwise we
* will have to wait for the peer device (DCE) to raise the CTS
* line.
*/
if ((self->flags & ASYNC_CTS_FLOW) && ((self->settings.dce & IRCOMM_CTS) == 0)) {
IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __func__ );
return;
} else {
IRDA_DEBUG(1, "%s(), starting hardware!\n", __func__ );
self->tty->hw_stopped = 0;
/* Wake up processes blocked on open */
wake_up_interruptible(&self->open_wait);
}
schedule_work(&self->tqueue);
}
/*
* Function ircomm_tty_start_watchdog_timer (self, timeout)
*
* Start the watchdog timer. This timer is used to make sure that any
* connection attempt is successful, and if not, we will retry after
* the timeout
*/
static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self,
int timeout)
{
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
irda_start_timer(&self->watchdog_timer, timeout, (void *) self,
ircomm_tty_watchdog_timer_expired);
}
/*
* Function ircomm_tty_watchdog_timer_expired (data)
*
* Called when the connect procedure have taken to much time.
*
*/
static void ircomm_tty_watchdog_timer_expired(void *data)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data;
IRDA_DEBUG(2, "%s()\n", __func__ );
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL);
}
/*
* Function ircomm_tty_do_event (self, event, skb)
*
* Process event
*
*/
int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
struct sk_buff *skb, struct ircomm_tty_info *info)
{
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
ircomm_tty_state[self->state], ircomm_tty_event[event]);
return (*state[self->state])(self, event, skb, info);
}
/*
* Function ircomm_tty_next_state (self, state)
*
* Switch state
*
*/
static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state)
{
/*
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
IRDA_DEBUG(2, "%s: next state=%s, service type=%d\n", __func__ ,
ircomm_tty_state[self->state], self->service_type);
*/
self->state = state;
}
/*
* Function ircomm_tty_state_idle (self, event, skb, info)
*
* Just hanging around
*
*/
static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
IRCOMM_TTY_EVENT event,
struct sk_buff *skb,
struct ircomm_tty_info *info)
{
int ret = 0;
IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
ircomm_tty_state[self->state], ircomm_tty_event[event]);
switch (event) {
case IRCOMM_TTY_ATTACH_CABLE:
/* Try to discover any remote devices */
ircomm_tty_start_watchdog_timer(self, 3*HZ);
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
break;
case IRCOMM_TTY_DISCOVERY_INDICATION:
self->daddr = info->daddr;
self->saddr = info->saddr;
if (self->iriap) {
IRDA_WARNING("%s(), busy with a previous query\n",
__func__);
return -EBUSY;
}
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
ircomm_tty_getvalue_confirm);
iriap_getvaluebyclass_request(self->iriap,
self->saddr, self->daddr,
"IrDA:IrCOMM", "Parameters");
ircomm_tty_start_watchdog_timer(self, 3*HZ);
ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
break;
case IRCOMM_TTY_CONNECT_INDICATION:
del_timer(&self->watchdog_timer);
/* Accept connection */
ircomm_connect_response(self->ircomm, NULL);
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
break;
case IRCOMM_TTY_WD_TIMER_EXPIRED:
/* Just stay idle */
break;
case IRCOMM_TTY_DETACH_CABLE:
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
ircomm_tty_event[event]);
ret = -EINVAL;
}
return ret;
}
/*
* Function ircomm_tty_state_search (self, event, skb, info)
*
* Trying to discover an IrCOMM device
*
*/
static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
IRCOMM_TTY_EVENT event,
struct sk_buff *skb,
struct ircomm_tty_info *info)
{
int ret = 0;
IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
ircomm_tty_state[self->state], ircomm_tty_event[event]);
switch (event) {
case IRCOMM_TTY_DISCOVERY_INDICATION:
self->daddr = info->daddr;
self->saddr = info->saddr;
if (self->iriap) {
IRDA_WARNING("%s(), busy with a previous query\n",
__func__);
return -EBUSY;
}
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
ircomm_tty_getvalue_confirm);
if (self->service_type == IRCOMM_3_WIRE_RAW) {
iriap_getvaluebyclass_request(self->iriap, self->saddr,
self->daddr, "IrLPT",
"IrDA:IrLMP:LsapSel");
ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
} else {
iriap_getvaluebyclass_request(self->iriap, self->saddr,
self->daddr,
"IrDA:IrCOMM",
"Parameters");
ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
}
ircomm_tty_start_watchdog_timer(self, 3*HZ);
break;
case IRCOMM_TTY_CONNECT_INDICATION:
del_timer(&self->watchdog_timer);
ircomm_tty_ias_unregister(self);
/* Accept connection */
ircomm_connect_response(self->ircomm, NULL);
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
break;
case IRCOMM_TTY_WD_TIMER_EXPIRED:
#if 1
/* Give up */
#else
/* Try to discover any remote devices */
ircomm_tty_start_watchdog_timer(self, 3*HZ);
irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
#endif
break;
case IRCOMM_TTY_DETACH_CABLE:
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
ircomm_tty_event[event]);
ret = -EINVAL;
}
return ret;
}
/*
* Function ircomm_tty_state_query (self, event, skb, info)
*
* Querying the remote LM-IAS for IrCOMM parameters
*
*/
static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
IRCOMM_TTY_EVENT event,
struct sk_buff *skb,
struct ircomm_tty_info *info)
{
int ret = 0;
IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
ircomm_tty_state[self->state], ircomm_tty_event[event]);
switch (event) {
case IRCOMM_TTY_GOT_PARAMETERS:
if (self->iriap) {
IRDA_WARNING("%s(), busy with a previous query\n",
__func__);
return -EBUSY;
}
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
ircomm_tty_getvalue_confirm);
iriap_getvaluebyclass_request(self->iriap, self->saddr,
self->daddr, "IrDA:IrCOMM",
"IrDA:TinyTP:LsapSel");
ircomm_tty_start_watchdog_timer(self, 3*HZ);
ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
break;
case IRCOMM_TTY_WD_TIMER_EXPIRED:
/* Go back to search mode */
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
ircomm_tty_start_watchdog_timer(self, 3*HZ);
break;
case IRCOMM_TTY_CONNECT_INDICATION:
del_timer(&self->watchdog_timer);
ircomm_tty_ias_unregister(self);
/* Accept connection */
ircomm_connect_response(self->ircomm, NULL);
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
break;
case IRCOMM_TTY_DETACH_CABLE:
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
ircomm_tty_event[event]);
ret = -EINVAL;
}
return ret;
}
/*
* Function ircomm_tty_state_query_lsap_sel (self, event, skb, info)
*
* Query remote LM-IAS for the LSAP selector which we can connect to
*
*/
static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
IRCOMM_TTY_EVENT event,
struct sk_buff *skb,
struct ircomm_tty_info *info)
{
int ret = 0;
IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
ircomm_tty_state[self->state], ircomm_tty_event[event]);
switch (event) {
case IRCOMM_TTY_GOT_LSAPSEL:
/* Connect to remote device */
ret = ircomm_connect_request(self->ircomm, self->dlsap_sel,
self->saddr, self->daddr,
NULL, self->service_type);
ircomm_tty_start_watchdog_timer(self, 3*HZ);
ircomm_tty_next_state(self, IRCOMM_TTY_SETUP);
break;
case IRCOMM_TTY_WD_TIMER_EXPIRED:
/* Go back to search mode */
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
ircomm_tty_start_watchdog_timer(self, 3*HZ);
break;
case IRCOMM_TTY_CONNECT_INDICATION:
del_timer(&self->watchdog_timer);
ircomm_tty_ias_unregister(self);
/* Accept connection */
ircomm_connect_response(self->ircomm, NULL);
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
break;
case IRCOMM_TTY_DETACH_CABLE:
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
ircomm_tty_event[event]);
ret = -EINVAL;
}
return ret;
}
/*
* Function ircomm_tty_state_setup (self, event, skb, info)
*
* Trying to connect
*
*/
static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
IRCOMM_TTY_EVENT event,
struct sk_buff *skb,
struct ircomm_tty_info *info)
{
int ret = 0;
IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
ircomm_tty_state[self->state], ircomm_tty_event[event]);
switch (event) {
case IRCOMM_TTY_CONNECT_CONFIRM:
del_timer(&self->watchdog_timer);
ircomm_tty_ias_unregister(self);
/*
* Send initial parameters. This will also send out queued
* parameters waiting for the connection to come up
*/
ircomm_tty_send_initial_parameters(self);
ircomm_tty_link_established(self);
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
break;
case IRCOMM_TTY_CONNECT_INDICATION:
del_timer(&self->watchdog_timer);
ircomm_tty_ias_unregister(self);
/* Accept connection */
ircomm_connect_response(self->ircomm, NULL);
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
break;
case IRCOMM_TTY_WD_TIMER_EXPIRED:
/* Go back to search mode */
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
ircomm_tty_start_watchdog_timer(self, 3*HZ);
break;
case IRCOMM_TTY_DETACH_CABLE:
/* ircomm_disconnect_request(self->ircomm, NULL); */
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
ircomm_tty_event[event]);
ret = -EINVAL;
}
return ret;
}
/*
* Function ircomm_tty_state_ready (self, event, skb, info)
*
* IrCOMM is now connected
*
*/
static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
IRCOMM_TTY_EVENT event,
struct sk_buff *skb,
struct ircomm_tty_info *info)
{
int ret = 0;
switch (event) {
case IRCOMM_TTY_DATA_REQUEST:
ret = ircomm_data_request(self->ircomm, skb);
break;
case IRCOMM_TTY_DETACH_CABLE:
ircomm_disconnect_request(self->ircomm, NULL);
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
case IRCOMM_TTY_DISCONNECT_INDICATION:
ircomm_tty_ias_register(self);
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
ircomm_tty_start_watchdog_timer(self, 3*HZ);
if (self->flags & ASYNC_CHECK_CD) {
/* Drop carrier */
self->settings.dce = IRCOMM_DELTA_CD;
ircomm_tty_check_modem_status(self);
} else {
IRDA_DEBUG(0, "%s(), hanging up!\n", __func__ );
if (self->tty)
tty_hangup(self->tty);
}
break;
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
ircomm_tty_event[event]);
ret = -EINVAL;
}
return ret;
}
+427
View 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", __func__ );
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", __func__);
} 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", __func__ );
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)
*
*
*
*/
int ircomm_tty_tiocmget(struct tty_struct *tty)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
unsigned int result;
IRDA_DEBUG(2, "%s()\n", __func__ );
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, set, clear)
*
*
*
*/
int ircomm_tty_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
IRDA_DEBUG(2, "%s()\n", __func__ );
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", __func__ );
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", __func__ );
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, cmd, arg)
*
*
*
*/
int ircomm_tty_ioctl(struct tty_struct *tty,
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", __func__ );
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", __func__ );
#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;
}