summaryrefslogtreecommitdiffstats
path: root/net/irda/ircomm
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
commitd6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch)
treee2be02f33984c48ec019c654051d27964e42c441 /net/irda/ircomm
parent609d1e803baf519487233b765eb487f9ec227a18 (diff)
Merge with 2.3.19.
Diffstat (limited to 'net/irda/ircomm')
-rw-r--r--net/irda/ircomm/.cvsignore2
-rw-r--r--net/irda/ircomm/Config.in2
-rw-r--r--net/irda/ircomm/Makefile16
-rw-r--r--net/irda/ircomm/ircomm_common.c1921
-rw-r--r--net/irda/ircomm/ircomm_core.c508
-rw-r--r--net/irda/ircomm/ircomm_event.c251
-rw-r--r--net/irda/ircomm/ircomm_lmp.c327
-rw-r--r--net/irda/ircomm/ircomm_param.c465
-rw-r--r--net/irda/ircomm/ircomm_ttp.c302
-rw-r--r--net/irda/ircomm/ircomm_tty.c1281
-rw-r--r--net/irda/ircomm/ircomm_tty_attach.c857
-rw-r--r--net/irda/ircomm/ircomm_tty_ioctl.c456
-rw-r--r--net/irda/ircomm/irvtd_driver.c2071
13 files changed, 4457 insertions, 4002 deletions
diff --git a/net/irda/ircomm/.cvsignore b/net/irda/ircomm/.cvsignore
deleted file mode 100644
index 857dd22e9..000000000
--- a/net/irda/ircomm/.cvsignore
+++ /dev/null
@@ -1,2 +0,0 @@
-.depend
-.*.flags
diff --git a/net/irda/ircomm/Config.in b/net/irda/ircomm/Config.in
index f384eb8d7..67f967c5a 100644
--- a/net/irda/ircomm/Config.in
+++ b/net/irda/ircomm/Config.in
@@ -1,3 +1,3 @@
-dep_tristate 'IrCOMM protocol' CONFIG_IRCOMM $CONFIG_IRDA
+dep_tristate ' IrCOMM protocol' CONFIG_IRCOMM $CONFIG_IRDA
diff --git a/net/irda/ircomm/Makefile b/net/irda/ircomm/Makefile
index 7df055ea1..89e3294fd 100644
--- a/net/irda/ircomm/Makefile
+++ b/net/irda/ircomm/Makefile
@@ -7,10 +7,10 @@
#
# Note 2! The CFLAGS definition is now in the main makefile...
-O_TARGET := ircomm_n_vtd.o
-O_OBJS := ircomm_common.o irvtd_driver.o
-M_OBJS := ircomm.o ircomm_tty.o
-MI_OBJS := ircomm_common.o irvtd_driver.o
+O_TARGET := ircomm_and_tty.o
+O_OBJS := ircomm_param.o ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o
+M_OBJS := ircomm.o ircomm-tty.o
+MI_OBJS := ircomm_param.o ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o
OX_OBJS +=
@@ -18,10 +18,12 @@ OX_OBJS +=
ifeq ($(CONFIG_IRCOMM),m)
ircomm.o: $(MI_OBJS)
- $(LD) $(LD_RFLAG) -r -o $@ ircomm_common.o
+ $(LD) $(LD_RFLAG) -r -o $@ ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o
+
+ircomm-tty.o: $(MI_OBJS)
+ $(LD) $(LD_RFLAG) -r -o $@ ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o
+
-ircomm_tty.o: $(MI_OBJS)
- $(LD) $(LD_RFLAG) -r -o $@ irvtd_driver.o
endif
include $(TOPDIR)/Rules.make
diff --git a/net/irda/ircomm/ircomm_common.c b/net/irda/ircomm/ircomm_common.c
deleted file mode 100644
index 5300f5f3c..000000000
--- a/net/irda/ircomm/ircomm_common.c
+++ /dev/null
@@ -1,1921 +0,0 @@
-/*********************************************************************
- *
- * Filename: ircomm_common.c
- * Version:
- * Description: An implementation of IrCOMM service interface,
- * state machine, and incidental function(s).
- * Status: Experimental.
- * Author: Takahide Higuchi <thiguchi@pluto.dti.ne.jp>
- * Source: irlpt_event.c
- *
- * Copyright (c) 1998-1999, Takahide Higuchi, <thiguchi@pluto.dti.ne.jp>,
- * 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, Takahide Higuchi, provide no warranty for any of this software.
- * This material is provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-/*
- * Reference:
- * "'IrCOMM':Serial and Parallel Port Emulation Over IR(Wire Replacement)"
- * version 1.0, which is available at http://www.irda.org/.
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irlmp.h>
-#include <net/irda/iriap.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irias_object.h>
-
-#include <net/irda/ircomm_common.h>
-
-static char *revision_date = "Tue May 18 03:11:39 1999";
-
-
-static void ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb );
-static void ircomm_state_discoverywait(struct ircomm_cb *self,
- IRCOMM_EVENT event,
- struct sk_buff *skb );
-static void ircomm_state_queryparamwait(struct ircomm_cb *self,
- IRCOMM_EVENT event,
- struct sk_buff *skb );
-static void ircomm_state_querylsapwait(struct ircomm_cb *self,
- IRCOMM_EVENT event,
- struct sk_buff *skb );
-static void ircomm_state_waiti( struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb );
-static void ircomm_state_waitr( struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb );
-static void ircomm_state_conn( struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb );
-static void ircomm_do_event( struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb);
-static void ircomm_next_state( struct ircomm_cb *self, IRCOMM_STATE state);
-
-static void ircomm_discovery_indication(discovery_t *discovery);
-static void ircomm_tx_controlchannel(struct ircomm_cb *self );
-static int ircomm_proc_read(char *buf, char **start, off_t offset,
- int len, int unused);
-
-static void start_discovering(struct ircomm_cb *self);
-static void query_lsapsel(struct ircomm_cb * self);
-static void query_parameters(struct ircomm_cb *self);
-static void queryias_done(struct ircomm_cb *self);
-static void ircomm_getvalue_confirm(int result, __u16 obj_id,
- struct ias_value *value, void *priv);
-
-
-struct ircomm_cb *discovering_instance;
-
-/*
- * debug parameter ircomm_cs:
- * 0 = client/server, 1 = client only 2 = server only
- * usage for example:
- * insmod ircomm ircomm_cs=1
- * LILO boot : Linux ircomm_cs=2 etc.
- */
-
-static int ircomm_cs = 0;
-MODULE_PARM(ircomm_cs, "i");
-
-
-
-static char *ircommstate[] = {
- "IDLE",
-
- "DISCOVERY_WAIT",
- "QUERYPARAM_WAIT",
- "QUERYLSAP_WAIT",
-
- "WAITI",
- "WAITR",
- "CONN",
-};
-
-static char *ircommservicetype[] = {
- "N/A",
- "THREE_WIRE_RAW",
- "THREE_WIRE",
- "NINE_WIRE",
- "CENTRONICS",
-};
-static char *ircommporttype[] = {
- "Unknown",
- "SERIAL",
- "PARALLEL",
-};
-
-
-struct ircomm_cb **ircomm = NULL;
-
-static char *ircommevent[] = {
- "IRCOMM_CONNECT_REQUEST",
- "TTP_CONNECT_INDICATION",
- "LMP_CONNECT_INDICATION",
-
- "TTP_CONNECT_CONFIRM",
- "TTP_DISCONNECT_INDICATION",
- "LMP_CONNECT_CONFIRM",
- "LMP_DISCONNECT_INDICATION",
-
- "IRCOMM_CONNECT_RESPONSE",
- "IRCOMM_DISCONNECT_REQUEST",
-
- "TTP_DATA_INDICATION",
- "IRCOMM_DATA_REQUEST",
- "LMP_DATA_INDICATION",
- "IRCOMM_CONTROL_REQUEST",
-
- "DISCOVERY_INDICATION",
- "GOT_PARAMETERS",
- "GOT_LSAPSEL",
- "QUERYIAS_ERROR",
-};
-
-#ifdef CONFIG_PROC_FS
-extern struct proc_dir_entry *proc_irda;
-#endif
-
-static void (*state[])( struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb) =
-{
- ircomm_state_idle,
-
- ircomm_state_discoverywait,
- ircomm_state_queryparamwait,
- ircomm_state_querylsapwait,
-
- ircomm_state_waiti,
- ircomm_state_waitr,
- ircomm_state_conn,
-};
-
-__initfunc(int ircomm_init(void))
-{
- int i;
-
- printk( "Linux-IrDA: IrCOMM protocol ( revision:%s ) \n",
- revision_date);
- DEBUG( 4, __FUNCTION__"()\n");
-
- /* allocate master array */
-
- ircomm = (struct ircomm_cb **) kmalloc( sizeof(struct ircomm_cb *) *
- IRCOMM_MAX_CONNECTION,
- GFP_KERNEL);
- if ( ircomm == NULL) {
- printk( KERN_ERR __FUNCTION__"(): kmalloc failed!\n");
- return -ENOMEM;
- }
-
- memset( ircomm, 0, sizeof(struct ircomm_cb *) * IRCOMM_MAX_CONNECTION);
-
- /* initialize structures */
-
- for(i = 0;i < IRCOMM_MAX_CONNECTION; i++){
- ircomm[i] = kmalloc( sizeof(struct ircomm_cb), GFP_KERNEL );
-
- if(!ircomm[i]){
- printk( KERN_ERR __FUNCTION__"(): kmalloc failed!\n");
- return -ENOMEM;
- }
-
-
- memset( ircomm[i], 0, sizeof(struct ircomm_cb));
-
- ircomm[i]->magic = IRCOMM_MAGIC;
- /* default settings */
- ircomm[i]->data_format = CS8;
- ircomm[i]->flow_ctrl = USE_RTS|USE_DTR; /*TODO: is this OK? */
- ircomm[i]->xon_char = 0x11;
- ircomm[i]->xoff_char = 0x13;
- ircomm[i]->enq_char = 0x05;
- ircomm[i]->ack_char = 0x06;
-
- ircomm[i]->max_header_size = COMM_MAX_HEADER_SIZE;
- ircomm[i]->tx_max_sdu_size = COMM_DEFAULT_SDU_SIZE;
- ircomm[i]->rx_max_sdu_size = SAR_DISABLE;
- ircomm[i]->ctrl_skb = dev_alloc_skb(COMM_DEFAULT_SDU_SIZE
- + COMM_MAX_HEADER_SIZE);
- if (ircomm[i]->ctrl_skb == NULL){
- DEBUG(0,"ircomm:init_module:alloc_skb failed!\n");
- return -ENOMEM;
- }
-
- skb_reserve(ircomm[i]->ctrl_skb,COMM_MAX_HEADER_SIZE);
-
- }
-
- /*
- * we register /proc/irda/ircomm
- */
-
-#ifdef CONFIG_PROC_FS
- create_proc_entry("ircomm", 0, proc_irda)->get_info = ircomm_proc_read;
-#endif /* CONFIG_PROC_FS */
-
- discovering_instance = NULL;
- return 0;
-}
-
-#ifdef MODULE
-void ircomm_cleanup(void)
-{
- int i;
-
- DEBUG( 4, "ircomm:cleanup_module\n");
- /*
- * free some resources
- */
- if (ircomm) {
- for (i=0; i<IRCOMM_MAX_CONNECTION; i++) {
- if (ircomm[i]) {
-
- if(ircomm[i]->ctrl_skb){
- dev_kfree_skb(ircomm[i]->ctrl_skb);
-
- ircomm[i]->ctrl_skb = NULL;
- }
-
- DEBUG( 4, "freeing structures(%d)\n",i);
- kfree(ircomm[i]);
- ircomm[i] = NULL;
- }
- }
- DEBUG( 4, "freeing master array\n");
- kfree(ircomm);
- ircomm = NULL;
- }
-
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("ircomm", proc_irda);
-#endif /* CONFIG_PROC_FS */
-}
-#endif /* MODULE */
-
-/*
- * ----------------------------------------------------------------------
- * callbacks which accept incoming indication/confirm from IrTTP (or IrLMP)
- * ----------------------------------------------------------------------
- */
-
-static int ircomm_accept_data_indication(void *instance, void *sap,
- struct sk_buff *skb)
-{
- struct ircomm_cb *self = (struct ircomm_cb *) instance;
-
- ASSERT(self != NULL, return -1;);
- ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
- ASSERT(skb != NULL, return -1;);
-
- DEBUG(4,__FUNCTION__"():\n");
- ircomm_do_event(self, TTP_DATA_INDICATION, skb);
- self->rx_packets++;
-
- return 0;
-}
-
-static void ircomm_accept_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;
-
- ASSERT(self != NULL, return;);
- ASSERT(self->magic == IRCOMM_MAGIC, return;);
- ASSERT(skb != NULL, return;);
- ASSERT(qos != NULL, return;);
-
- DEBUG(0,__FUNCTION__"(): got connected!\n");
-
- if (max_sdu_size == SAR_DISABLE)
- self->tx_max_sdu_size =(qos->data_size.value - max_header_size
- - COMM_HEADER_SIZE);
- else {
- ASSERT(max_sdu_size >= COMM_DEFAULT_SDU_SIZE, return;);
- /* use fragmentation */
- self->tx_max_sdu_size = max_sdu_size - COMM_HEADER_SIZE;
- }
-
- self->qos = qos;
- self->max_header_size = max_header_size + COMM_HEADER_SIZE;
- self->null_modem_mode = 0; /* disable null modem emulation */
-
- ircomm_do_event(self, TTP_CONNECT_CONFIRM, skb);
-}
-
-static void ircomm_accept_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;
-
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRCOMM_MAGIC, return;);
- ASSERT( skb != NULL, return;);
- ASSERT( qos != NULL, return;);
-
- DEBUG(0,__FUNCTION__"()\n");
-
- if (max_sdu_size == SAR_DISABLE)
- self->tx_max_sdu_size =(qos->data_size.value - max_header_size
- - COMM_HEADER_SIZE);
- else
- self->tx_max_sdu_size = max_sdu_size - COMM_HEADER_SIZE;
-
- self->qos = qos;
- self->max_header_size = max_header_size + COMM_HEADER_SIZE;
-
- ircomm_do_event( self, TTP_CONNECT_INDICATION, skb);
-
- /* stop connecting */
- wake_up_interruptible( &self->discovery_wait);
- wake_up_interruptible( &self->ias_wait);
-
-}
-
-static void ircomm_accept_disconnect_indication(void *instance, void *sap,
- LM_REASON reason,
- struct sk_buff *skb)
-{
- struct ircomm_cb *self = (struct ircomm_cb *)instance;
-
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRCOMM_MAGIC, return;);
-
- DEBUG(0,__FUNCTION__"():\n");
- ircomm_do_event( self, TTP_DISCONNECT_INDICATION, skb);
-
-}
-
-static void ircomm_accept_flow_indication( void *instance, void *sap,
- LOCAL_FLOW cmd)
-{
- IRCOMM_CMD command;
- struct ircomm_cb *self = (struct ircomm_cb *)instance;
-
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRCOMM_MAGIC, return;);
-
- switch(cmd){
- case FLOW_START:
- DEBUG(4,__FUNCTION__"():START\n");
- command = TX_READY;
- self->ttp_stop = 0;
- if(self->notify.flow_indication)
- self->notify.flow_indication( self->notify.instance,
- self, command);
- break;
-
- case FLOW_STOP:
- DEBUG(4,__FUNCTION__":STOP\n");
- command = TX_BUSY;
- self->ttp_stop = 1;
- if(self->notify.flow_indication)
- self->notify.flow_indication( self->notify.instance,
- self, command);
- break;
-
- default:
- DEBUG(0,__FUNCTION__"();unknown status!\n");
- }
-
-}
-
-
-/*
- * ircomm_discovery_indication()
- * Remote device is discovered, try query the remote IAS to see which
- * device it is, and which services it has.
- */
-
-static void ircomm_discovery_indication(discovery_t *discovery)
-{
- struct ircomm_cb *self;
-
- self = discovering_instance;
- if(self == NULL)
- return;
- ASSERT(self->magic == IRCOMM_MAGIC, return;);
-
- self->daddr = discovery->daddr;
- self->saddr = discovery->saddr;
-
- DEBUG( 0, __FUNCTION__"():daddr=%08x\n", self->daddr);
-
- ircomm_do_event(self, DISCOVERY_INDICATION, NULL);
- return;
-}
-
-/*
- * ircomm_getvalue_confirm()
- * handler for iriap_getvaluebyclass_request()
- */
-static void ircomm_getvalue_confirm(int result, __u16 obj_id,
- struct ias_value *value, void *priv)
-{
- struct ircomm_cb *self = (struct ircomm_cb *) priv;
- struct sk_buff *skb= NULL;
- __u8 *frame;
- __u8 servicetype = 0 ;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRCOMM_MAGIC, return;);
-
- /* Check if request succeeded */
- if (result != IAS_SUCCESS) {
- DEBUG( 0, __FUNCTION__ "(), got NULL value!\n");
- ircomm_do_event(self, QUERYIAS_ERROR, NULL);
- return;
- }
-
- DEBUG(4, __FUNCTION__"():type(%d)\n", value->type);
-
- self->ias_type = value->type;
- switch(value->type){
- case IAS_OCT_SEQ:
-
- DEBUG(4, __FUNCTION__"():got octet sequence:\n");
-#if 0
- {
- int i;
- for ( i=0;i<value->len;i++)
- printk("%02x",
- (__u8)(*(value->t.oct_seq + i)));
- printk("\n");
- }
-#endif
- skb = dev_alloc_skb((value->len) + 2);
- ASSERT(skb != NULL, ircomm_do_event(self, QUERYIAS_ERROR, NULL);return;);
- frame = skb_put(skb,2);
- /* MSB first */
- frame[0] = ( value->len >> 8 ) & 0xff;
- frame[1] = value->len & 0xff;
-
- frame = skb_put(skb,value->len);
- memcpy(frame, value->t.oct_seq, value->len);
- ircomm_parse_tuples(self, skb, IAS_PARAM);
- kfree_skb(skb);
-
- /*
- * check if servicetype we want is available
- */
-
- DEBUG(0,__FUNCTION__"():peer capability is:\n");
- DEBUG(0,"3wire raw: %s\n",
- ((self->peer_servicetype & THREE_WIRE_RAW) ? "yes":"no"));
- DEBUG(0,"3wire : %s\n",
- ((self->peer_servicetype & THREE_WIRE) ? "yes":"no"));
- DEBUG(0,"9wire : %s\n",
- ((self->peer_servicetype & NINE_WIRE) ? "yes":"no"));
- DEBUG(0,"IEEE1284 : %s\n",
- ((self->peer_servicetype & CENTRONICS) ? "yes":"no"));
-
- self->servicetype &= self->peer_servicetype;
- if(!(self->servicetype)){
- DEBUG(0,__FUNCTION__"(): servicetype mismatch!\n");
- ircomm_do_event(self, QUERYIAS_ERROR, NULL);
- break;
- }
-
- /*
- * then choose better one
- */
- if(self->servicetype & THREE_WIRE_RAW)
- servicetype = THREE_WIRE_RAW;
- if(self->servicetype & THREE_WIRE)
- servicetype = THREE_WIRE;
- if(self->servicetype & NINE_WIRE)
- servicetype = NINE_WIRE;
- if(self->servicetype & CENTRONICS)
- servicetype = CENTRONICS;
-
- self->servicetype = servicetype;
-
- /* enter next state */
- ircomm_do_event(self, GOT_PARAMETERS, NULL);
- break;
-
- case IAS_INTEGER:
- /* LsapSel seems to be sent to me */
- DEBUG(0, __FUNCTION__"():got lsapsel = %d\n", value->t.integer);
-
- if ( value->t.integer == -1){
- DEBUG( 0, __FUNCTION__"():invalid value!\n");
- ircomm_do_event(self, QUERYIAS_ERROR, NULL);
- return;
- }
- self->dlsap = value->t.integer;
- ircomm_do_event(self, GOT_LSAPSEL, NULL);
- break;
-
- case IAS_MISSING:
- DEBUG( 0, __FUNCTION__":got IAS_MISSING\n");
- ircomm_do_event(self, QUERYIAS_ERROR, NULL);
- break;
-
- default:
- DEBUG( 0, __FUNCTION__":got unknown (strange?)type!\n");
- ircomm_do_event(self, QUERYIAS_ERROR, NULL);
- break;
- }
-}
-
-
-
-/*
- * ----------------------------------------------------------------------
- * Impl. of actions (descrived in section 7.4 of the reference)
- * ----------------------------------------------------------------------
- */
-
-static void issue_connect_request(struct ircomm_cb *self,
- struct sk_buff *userdata )
-{
- /* TODO: we have to send/build userdata field which contains
- InitialControlParameters */
-
- switch(self->servicetype){
- case THREE_WIRE_RAW:
- DEBUG(0, __FUNCTION__"():THREE_WIRE_RAW is not implemented\n");
- break;
-
- case DEFAULT:
- case THREE_WIRE:
- case NINE_WIRE:
- case CENTRONICS:
-
- irttp_connect_request(self->tsap, self->dlsap,
- self->saddr, self->daddr,
- NULL, self->rx_max_sdu_size, userdata);
- break;
-
- default:
- printk(KERN_ERR __FUNCTION__"():Illegal servicetype %d\n"
- ,self->servicetype);
- }
-}
-
-static void disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb)
-{
-
- /*
- * Not implemented parameter"Reason".That is optional.
- * What is reason? maybe discribed in irmod.h?
- */
-
- if(self->notify.disconnect_indication)
- self->notify.disconnect_indication( self->notify.instance,
- self,
- self->reason, skb);
-
-}
-
-static void connect_indication(struct ircomm_cb *self, struct qos_info *qos,
- struct sk_buff *skb)
-{
-
-/* If controlparameters don't exist, we use the servicetype"DEFAULT".*/
-/* if( !ircomm_parse_controlchannel( self, data)) */
-/* self->servicetype = DEFAULT; TODOD:fix this! TH */
-
- if (self->notify.connect_indication)
- self->notify.connect_indication(self->notify.instance, self,
- qos, self->tx_max_sdu_size,
- self->max_header_size, skb);
-}
-
-#if 0
-/* it's for THREE_WIRE_RAW.*/
-static void connect_indication_three_wire_raw(void)
-{
- DEBUG(0,"ircomm:connect_indication_threewire():not implemented!");
-}
-#endif
-
-
-static void connect_confirm(struct ircomm_cb *self, struct sk_buff *skb)
-{
- DEBUG(4 ,__FUNCTION__"()\n");
-
- /* give a connect_confirm to the client */
- if( self->notify.connect_confirm )
- self->notify.connect_confirm(self->notify.instance,
- self, NULL, self->tx_max_sdu_size,
- self->max_header_size, skb);
-}
-
-static void issue_connect_response(struct ircomm_cb *self,
- struct sk_buff *skb)
-{
- DEBUG(0,__FUNCTION__"()\n");
-
- if( self->servicetype == THREE_WIRE_RAW){
- DEBUG(0,__FUNCTION__"():THREE_WIRE_RAW is not implemented yet\n");
- /* irlmp_connect_rsp(); */
- } else
- irttp_connect_response(self->tsap, self->rx_max_sdu_size, skb);
-}
-
-static void issue_disconnect_request(struct ircomm_cb *self,
- struct sk_buff *userdata)
-{
- if(self->servicetype == THREE_WIRE_RAW){
- DEBUG(0,__FUNCTION__"():3wireraw is not implemented\n");
- }
- else
- irttp_disconnect_request(self->tsap, userdata,
- self->disconnect_priority);
-}
-
-static void issue_data_request(struct ircomm_cb *self,
- struct sk_buff *userdata )
-{
- int err;
-
- if (self->servicetype == THREE_WIRE_RAW){
- /* irlmp_data_request(self->lmhandle,userdata); */
- DEBUG(0,__FUNCTION__"():not implemented!");
- return;
- }
-
- DEBUG(4,__FUNCTION__"():sending frame\n");
- err = irttp_data_request(self->tsap, userdata);
- if (err){
- printk(KERN_ERR __FUNCTION__":ttp_data_request failed\n");
- if (userdata)
- dev_kfree_skb( userdata);
- }
- self->tx_packets++;
-}
-
-static void issue_control_request(struct ircomm_cb *self,
- struct sk_buff *userdata)
-{
- int err;
-
- DEBUG(4,__FUNCTION__"()\n");
- if (self->servicetype == THREE_WIRE_RAW) {
- DEBUG(0,__FUNCTION__"():THREE_WIRE_RAW is not implemented\n");
-
- }
- else
- {
- err = irttp_data_request(self->tsap,userdata);
- if(err)
- {
- printk( __FUNCTION__"():ttp_data_request failed\n");
- if(userdata)
- dev_kfree_skb(userdata);
- }
- else
- self->tx_controls++;
-
- self->pending_control_tuples = 0;
- }
-}
-
-static void process_data(struct ircomm_cb *self, struct sk_buff *skb )
-{
-
- DEBUG(4,__FUNCTION__":skb->len=%d, ircomm header = 1, clen=%d\n",
- (int)skb->len ,(int)skb->data[0]);
-
- /* we have to parse control channel when receiving. (see
- * page17 of IrCOMM standard) but it is not parsed here since
- * upper layer may have some receive buffer.
- *
- * hence upper layer have to parse it when it consumes a packet.
- * -- TH
- */
-
- /* ircomm_parse_control(self, skb, CONTROL_CHANNEL); */
-
- if (self->notify.data_indication && skb->len)
- self->notify.data_indication(self->notify.instance, self,
- skb);
-}
-
-int ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb)
-{
- /* Not implemented yet:THREE_WIRE_RAW service uses this function. */
- DEBUG(0,"ircomm_data_indication:not implemented yet!\n");
-
- return 0;
-}
-
-
-/*
- * ----------------------------------------------------------------------
- * Implementation of state chart,
- * descrived in section 7.1 of the specification.
- * ----------------------------------------------------------------------
- */
-
-static void ircomm_do_event( struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb)
-{
-
- DEBUG( 4, __FUNCTION__": STATE = %s, EVENT = %s\n",
- ircommstate[self->state], ircommevent[event]);
- (*state[self->state])(self, event, skb);
-}
-
-static void ircomm_next_state( struct ircomm_cb *self, IRCOMM_STATE state)
-{
- self->state = state;
- DEBUG( 0, __FUNCTION__": NEXT STATE=%d(%s), servicetype=(%d)\n",
- (int)state, ircommstate[self->state],self->servicetype);
-}
-
-
-
-/*
- * ircomm_state_idle
- */
-
-static void ircomm_state_idle( struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb )
-{
- switch (event){
- case IRCOMM_CONNECT_REQUEST:
-
- /* ircomm_next_state(self, COMM_WAITI); */
- /* issue_connect_request( self, skb ); */
-
- ircomm_next_state(self, COMM_DISCOVERY_WAIT);
- start_discovering(self);
- break;
-
- case TTP_CONNECT_INDICATION:
-
- ircomm_next_state(self, COMM_WAITR);
- connect_indication( self, self->qos, skb);
- break;
-
- case LMP_CONNECT_INDICATION:
-
- DEBUG(0,__FUNCTION__"():LMP_CONNECT_IND is notimplemented!");
- /* connect_indication_three_wire_raw(); */
- /* ircomm_next_state(self, COMM_WAITR); */
- break;
-
- default:
- DEBUG(0,__FUNCTION__"():unknown event =%d(%s)\n",
- event, ircommevent[event]);
- }
-}
-
-/*
- * ircomm_state_discoverywait
- */
-static void ircomm_state_discoverywait(struct ircomm_cb *self,
- IRCOMM_EVENT event,
- struct sk_buff *skb )
-{
- switch(event){
-
- case TTP_CONNECT_INDICATION:
-
- ircomm_next_state(self, COMM_WAITR);
- queryias_done(self);
- connect_indication( self, self->qos, skb);
- break;
-
- case DISCOVERY_INDICATION:
- ircomm_next_state(self, COMM_QUERYPARAM_WAIT);
- query_parameters(self);
- break;
-
- case IRCOMM_DISCONNECT_REQUEST:
- ircomm_next_state(self, COMM_IDLE);
- queryias_done(self);
- break;
-
- case QUERYIAS_ERROR:
- ircomm_next_state(self, COMM_IDLE);
- disconnect_indication(self, NULL);
- queryias_done(self);
- break;
-
- default:
- DEBUG(0,__FUNCTION__"():unknown event =%d(%s)\n",
- event, ircommevent[event]);
- }
-}
-
-/*
- * ircomm_state_queryparamwait
- */
-
-static void ircomm_state_queryparamwait(struct ircomm_cb *self,
- IRCOMM_EVENT event,
- struct sk_buff *skb)
-{
- switch (event) {
- case TTP_CONNECT_INDICATION:
-
- ircomm_next_state(self, COMM_WAITR);
- connect_indication( self, self->qos, skb);
- break;
-
- case GOT_PARAMETERS:
-
- ircomm_next_state(self, COMM_QUERYLSAP_WAIT);
- query_lsapsel( self );
- break;
-
- case IRCOMM_DISCONNECT_REQUEST:
- ircomm_next_state(self, COMM_IDLE);
- queryias_done(self);
- break;
-
- case QUERYIAS_ERROR:
- ircomm_next_state(self, COMM_IDLE);
- disconnect_indication(self, NULL);
- queryias_done(self);
- break;
-
- default:
- DEBUG(0,__FUNCTION__"():unknown event =%d(%s)\n",
- event, ircommevent[event]);
- }
-}
-
-/*
- * ircomm_state_querylsapwait
- */
-
-static void ircomm_state_querylsapwait(struct ircomm_cb *self,
- IRCOMM_EVENT event,
- struct sk_buff *skb )
-{
- switch (event) {
-
- case TTP_CONNECT_INDICATION:
-
- ircomm_next_state(self, COMM_WAITR);
- connect_indication( self, self->qos, skb);
- break;
-
- case GOT_LSAPSEL:
-
- ircomm_next_state(self, COMM_WAITI);
- queryias_done(self);
- issue_connect_request( self, skb );
- break;
-
- case IRCOMM_DISCONNECT_REQUEST:
- ircomm_next_state(self, COMM_IDLE);
- queryias_done(self);
- break;
-
- case QUERYIAS_ERROR:
- ircomm_next_state(self, COMM_IDLE);
- disconnect_indication(self, NULL);
- queryias_done(self);
- break;
-
-
- default:
- DEBUG(0,__FUNCTION__"():unknown event =%d(%s)\n",
- event, ircommevent[event]);
- }
-}
-
-/*
- * ircomm_state_waiti
- */
-
-static void ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb )
-{
- switch (event) {
- case TTP_CONNECT_CONFIRM:
- ircomm_next_state(self, COMM_CONN);
- connect_confirm(self, skb );
- break;
- case TTP_DISCONNECT_INDICATION:
- ircomm_next_state(self, COMM_IDLE);
- disconnect_indication(self, skb);
- break;
-/* case LMP_CONNECT_CONFIRM: */
-/* ircomm_connect_cnfirmation; */
-/* ircomm_next_state(self, COMM_CONN); */
-/* break; */
-/* case LMP_DISCONNECT_INDICATION: */
-/* ircomm_disconnect_ind; */
-/* ircomm_next_state(self, COMM_IDLE); */
-/* break; */
- default:
- DEBUG(0,__FUNCTION__"():unknown event =%d(%s)\n",
- event, ircommevent[event]);
- }
-}
-
-/*
- * ircomm_state_waitr
- */
-static void ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb )
-{
- switch (event) {
- case IRCOMM_CONNECT_RESPONSE:
-
- /* issue_connect_response */
-
- if (self->servicetype==THREE_WIRE_RAW) {
- DEBUG(0,__FUNCTION__"():3WIRE_RAW is not implemented\n");
- /* irlmp_connect_response(Vpeersap,
- * ACCEPT,null);
- */
- } else {
- ircomm_next_state(self, COMM_CONN);
- issue_connect_response(self, skb);
- }
- break;
-
- case IRCOMM_DISCONNECT_REQUEST:
- ircomm_next_state(self, COMM_IDLE);
- issue_disconnect_request(self, skb);
- queryias_done(self);
- break;
-
- case TTP_DISCONNECT_INDICATION:
- ircomm_next_state(self, COMM_IDLE);
- disconnect_indication(self, skb);
- break;
-
- case DISCOVERY_INDICATION:
- DEBUG(0, __FUNCTION__"():DISCOVERY_INDICATION\n");
- queryias_done(self);
- break;
- case GOT_PARAMETERS:
- DEBUG(0, __FUNCTION__"():GOT_PARAMETERS\n");
- queryias_done(self);
- break;
- case GOT_LSAPSEL:
- DEBUG(0, __FUNCTION__"():GOT_LSAPSEL\n");
- queryias_done(self);
- break;
-
-/* case LMP_DISCONNECT_INDICATION: */
-/* disconnect_indication(); */
-/* ircomm_next_state(self, COMM_IDLE); */
-/* break; */
- default:
- DEBUG(0,"ircomm_state_waitr:unknown event =%d(%s)\n",
- event, ircommevent[event]);
- }
-}
-
-/*
- * ircomm_state_conn
- */
-
-static void ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
- struct sk_buff *skb )
-{
- switch (event) {
- case TTP_DATA_INDICATION:
- process_data(self, skb);
- break;
- case IRCOMM_DATA_REQUEST:
- issue_data_request(self, skb);
- break;
-/* case LMP_DATA_INDICATION: */
-/* ircomm_data_indicated(); */
-/* break; */
- case IRCOMM_CONTROL_REQUEST:
- issue_control_request(self, skb);
- break;
- case TTP_DISCONNECT_INDICATION:
- ircomm_next_state(self, COMM_IDLE);
- disconnect_indication(self, skb);
- break;
- case IRCOMM_DISCONNECT_REQUEST:
- ircomm_next_state(self, COMM_IDLE);
- issue_disconnect_request(self, skb);
- queryias_done(self);
- break;
-/* case LM_DISCONNECT_INDICATION: */
-/* disconnect_indication(); */
-/* ircomm_next_state(self, COMM_IDLE); */
-/* break; */
-
- case DISCOVERY_INDICATION:
- DEBUG(0, __FUNCTION__"():DISCOVERY_INDICATION\n");
- queryias_done(self);
- break;
- case GOT_PARAMETERS:
- DEBUG(0, __FUNCTION__"():GOT_PARAMETERS\n");
- queryias_done(self);
- break;
- case GOT_LSAPSEL:
- DEBUG(0, __FUNCTION__"():GOT_LSAPSEL\n");
- queryias_done(self);
- break;
-
- default:
- DEBUG(0,"ircomm_state_conn:unknown event =%d(%s)\n",
- event, ircommevent[event]);
- }
-}
-
-/*
- * ----------------------------------------------------------------------
- * IrCOMM service interfaces and supporting functions
- *
- * ----------------------------------------------------------------------
- */
-
-/*
- * Function start_discovering (self)
- *
- * Start discovering and enter DISCOVERY_WAIT state
- *
- */
-static void start_discovering(struct ircomm_cb *self)
-{
- __u16 hints;
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRCOMM_MAGIC, return;);
- DEBUG(4,__FUNCTION__"():servicetype = %d\n",self->servicetype);
-
-
- hints = irlmp_service_to_hint(S_COMM);
-
- DEBUG(1,__FUNCTION__"():start discovering..\n");
- switch (ircomm_cs) {
- case 0:
- MOD_INC_USE_COUNT;
- self->queryias_lock = 1;
- discovering_instance = self;
- self->skey = irlmp_register_service(hints);
- self->ckey = irlmp_register_client(hints, ircomm_discovery_indication,
- NULL);
- break;
-
- case 1: /* client only */
- MOD_INC_USE_COUNT;
- self->queryias_lock = 1;
- discovering_instance = self;
- DEBUG( 0, __FUNCTION__"():client only mode\n");
- self->ckey = irlmp_register_client(hints, ircomm_discovery_indication,
- NULL);
- break;
-
- case 2: /* server only */
- default:
- DEBUG( 0, __FUNCTION__"():server only mode\n");
- self->skey = irlmp_register_service(hints);
- discovering_instance = NULL;
- break;
- }
-
- return;
-}
-
-/*
- * queryias_done(self)
- *
- *
- */
-
-/*
- * Function queryias_done (self)
- *
- * Called when discovery process got wrong results, completed, or
- * terminated.
- *
- */
-static void queryias_done(struct ircomm_cb *self)
-{
- DEBUG(0, __FUNCTION__"():\n");
- if (self->queryias_lock){
- self->queryias_lock = 0;
- discovering_instance = NULL;
- MOD_DEC_USE_COUNT;
- irlmp_unregister_client(self->ckey);
- }
- if (ircomm_cs != 1)
- irlmp_unregister_service(self->skey);
- return;
-}
-
-
-
-static void query_parameters(struct ircomm_cb *self)
-{
-
- DEBUG(0, __FUNCTION__"():querying IAS: Parameters..\n");
- iriap_getvaluebyclass_request( "IrDA:IrCOMM", "Parameters",
- self->saddr, self->daddr,
- ircomm_getvalue_confirm, self );
-}
-
-static void query_lsapsel(struct ircomm_cb * self)
-{
- DEBUG(0, __FUNCTION__"():querying IAS: Lsapsel...\n");
-
- if (!(self->servicetype & THREE_WIRE_RAW)) {
- iriap_getvaluebyclass_request(
- "IrDA:IrCOMM", "IrDA:TinyTP:LsapSel",
- self->saddr, self->daddr,
- ircomm_getvalue_confirm, self );
- } else {
- DEBUG(0, __FUNCTION__ "THREE_WIRE_RAW is not implemented!\n");
- }
-}
-
-/*
- * Function ircomm_connect_request (self, servicetype)
- *
- * Impl. of this function is differ from one of the reference. This
- * function does discovery as well as sending connect request
- *
- */
-void ircomm_connect_request(struct ircomm_cb *self, __u8 servicetype)
-{
- /*
- * TODO:build a packet which contains "initial control parameters"
- * and send it with connect_request
- */
-
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRCOMM_MAGIC, return;);
-
-
- DEBUG(1, __FUNCTION__"():sending connect_request...\n");
-
- self->servicetype= servicetype;
- /* ircomm_control_request(self, SERVICETYPE); */ /*servictype*/
-
- self->rx_max_sdu_size = SAR_DISABLE;
- ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, NULL);
-}
-
-void ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata,
- __u32 max_sdu_size)
-{
-
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRCOMM_MAGIC, return;);
- /* ASSERT( userdata != NULL, return;); */
-
- DEBUG(4,"ircomm_connect_response:\n");
-
- /*
- * TODO:build a packet which contains "initial control parameters"
- * and send it with connect_response
- */
-
- if (!userdata){
- /* FIXME: check for errors and initialize? DB */
- userdata = dev_alloc_skb(COMM_DEFAULT_SDU_SIZE + COMM_MAX_HEADER_SIZE);
- if (userdata == NULL)
- return;
-
- skb_reserve(userdata,COMM_MAX_HEADER_SIZE);
- }
-
- /* enable null-modem emulation (i.e. server mode )*/
- self->null_modem_mode = 1;
-
- self->rx_max_sdu_size = max_sdu_size;
-
- ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata);
-}
-
-void ircomm_disconnect_request(struct ircomm_cb *self,
- struct sk_buff *userdata,
- int priority)
-{
-
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRCOMM_MAGIC, return;);
- DEBUG(0,__FUNCTION__"()\n");
-
-#if 0
- /* unregister layer */
- switch (ircomm_cs) {
- case 1: /* client only */
- irlmp_unregister_client(ckey);
- break;
-
- case 2: /* server only */
- irlmp_unregister_service(skey);
- break;
- case 0:
- default:
- irlmp_unregister_client(ckey);
- irlmp_unregister_service(skey);
- break;
- }
-#endif
-
- self->disconnect_priority = priority;
- if(priority != P_HIGH)
- self->disconnect_priority = P_NORMAL;
-
- ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata);
-}
-
-
-int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *userdata)
-{
- __u8 * frame;
-
- DEBUG(4,__FUNCTION__"()\n");
- ASSERT( self != NULL, return -EFAULT;);
- ASSERT( self->magic == IRCOMM_MAGIC, return -EFAULT;);
- ASSERT( userdata != NULL, return -EFAULT;);
-
-
- if(self->state != COMM_CONN){
- DEBUG(4,__FUNCTION__"():not connected, data is ignored\n");
- return -EINVAL;
- }
-
- if(self->ttp_stop)
- return -EBUSY;
-
- if(self->control_ch_pending){
- /* send control_channel */
- ircomm_tx_controlchannel(self);
- }
-
- if(self->ttp_stop)
- return -EBUSY;
-
- /* add "clen" field */
- frame = skb_push(userdata,1);
- frame[0]=0; /* without control channel */
-
- ircomm_do_event(self, IRCOMM_DATA_REQUEST, userdata);
- return 0;
-}
-
-/*
- * ----------------------------------------------------------------------
- * IrCOMM_control.req and friends
- *
- * ----------------------------------------------------------------------
- */
-
-
-static void ircomm_tx_controlchannel(struct ircomm_cb *self )
-{
-
- __u8 clen;
- struct sk_buff *skb = self->ctrl_skb;
-
- DEBUG(4,__FUNCTION__"()\n");
- /* 'self' should have been checked */
- ASSERT(!self->ttp_stop, return ;);
- ASSERT(self->state == COMM_CONN, return ;);
-
- /* add "clen" field */
-
- clen=skb->len;
- ASSERT(clen != 0,return;);
-
- skb_push(skb,1);
- skb->data[0]=clen;
-
-#if 0
- printk("tx_ctrl:");
- {
- int i;
- for ( i=0;i<skb->len;i++)
- printk("%02x", skb->data[i]);
- printk("\n");
- }
-#endif
-
- ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb);
- self->control_ch_pending = 0;
-
- skb = dev_alloc_skb(COMM_DEFAULT_SDU_SIZE + COMM_MAX_HEADER_SIZE);
- ASSERT(skb != NULL, return ;);
-
- skb_reserve(skb,COMM_MAX_HEADER_SIZE);
- self->ctrl_skb = skb;
-}
-
-
-static void append_tuple(struct ircomm_cb *self, __u8 instruction, __u8 pl ,
- __u8 *value)
-{
- __u8 *frame;
- struct sk_buff *skb;
- int i,c = 0;
- unsigned long flags;
-
- save_flags(flags);cli();
-
- skb = self->ctrl_skb;
- ASSERT(skb != NULL, return;);
-
- if(skb_tailroom(skb) < (pl+2)){
- DEBUG(0, __FUNCTION__"there's no room.. ignore it\n");
- self->ignored_control_tuples++;
- restore_flags(flags);
- return;
- }
-
- frame = skb_put(skb,pl+2);
- frame[c++] = instruction; /* PI */
- frame[c++] = pl; /* PL */
- for(i=0; i < pl ; i++)
- frame[c++] = *value++; /* PV */
- restore_flags(flags);
- self->pending_control_tuples++;
- self->control_ch_pending = 1;
-}
-
-/*
- * Function ircomm_control_request (self, instruction)
- *
- * This function is exported as a request to send some control-channel
- * tuples * to peer device
- *
- */
-void ircomm_control_request(struct ircomm_cb *self, __u8 instruction)
-{
-
- __u8 pv[32]; /* 32 max, for PORT_NAME */
- __u8 *value = &pv[0];
- __u32 temp;
- int notsupp=0;
-
- ASSERT( self != NULL, return;);
- ASSERT( self->magic == IRCOMM_MAGIC, return;);
-
- if(self->servicetype == THREE_WIRE_RAW){
- DEBUG(0,__FUNCTION__"():THREE_WIRE_RAW shuold not use me!\n");
- return;
- }
-
- DEBUG(4,__FUNCTION__"()\n");
-
- /* find parameter and its length */
-
- if(self->servicetype == THREE_WIRE) goto threewire;
- if(self->servicetype == NINE_WIRE) goto ninewire;
-
-
- /* FIXME: centronics service is not fully implemented yet*/
- switch(instruction){
- case IEEE1284_MODE_SUPPORT:
- case IEEE1284_DEVICEID:
- append_tuple(self,instruction,0,NULL);
- break;
- case STATUS_QUERY:
- append_tuple(self,instruction,0,NULL);
- break;
- case SET_BUSY_TIMEOUT:
- value[0] = self->busy_timeout;
- append_tuple(self,instruction,1,value);
- break;
- case IEEE1284_ECP_EPP_DATA_TRANSFER:
- value[0] = self->ecp_epp_mode;
- value[1] = self->channel_or_addr;
- append_tuple(self,instruction,2,value);
- break;
- default:
- notsupp=1;
- }
-
- ninewire:
- switch(instruction){
- case POLL_FOR_LINE_SETTINGS:
- append_tuple(self,instruction,0,NULL);
- break;
- case DTELINE_STATE:
- if(self->null_modem_mode){
- /* null modem emulation */
-
- /* output RTS as CTS */
-
- if(self->dte & DELTA_RTS)
- value[0] = DELTA_CTS;
- if(self->dte & MCR_RTS)
- value[0] |= MSR_CTS;
-
- /* output DTR as {DSR & CD & RI} */
-
- if(self->dte & DELTA_DTR)
- value[0] |= (DELTA_DSR|DELTA_RI|DELTA_DCD);
- if(self->dte & MCR_DTR)
- value[0] |= (MSR_DSR|MSR_RI|MSR_DCD);
- append_tuple(self,DCELINE_STATE,1,value);
- }else{
- value[0] = self->dte;
- append_tuple(self,instruction,1,value);
- }
- self->dte &= ~(DELTA_RTS|DELTA_DTR);
- break;
-
- case DCELINE_STATE:
- value[0] = self->dce;
- append_tuple(self,instruction,1,value);
- break;
-
- default:
- notsupp=1;
- }
-
- threewire:
- switch(instruction){
-
- case SERVICETYPE:
- value[0] = self->servicetype;
- append_tuple(self,instruction,1,value);
- break;
-
- case DATA_FORMAT:
- value[0] = self->data_format;
- append_tuple(self,instruction,1,value);
- break;
-
- case FLOW_CONTROL:
- if(self->null_modem_mode){
- /* inside out */
- value[0] = (self->flow_ctrl & 0x55) << 1;
- value[0] |= (self->flow_ctrl & 0xAA) >> 1;
- }else{
- value[0] = self->flow_ctrl;
- }
- append_tuple(self,instruction,1,value);
- break;
-
- case LINESTATUS:
- value[0] = self->line_status;
- append_tuple(self,instruction,1,value);
- break;
-
- case BREAK_SIGNAL:
- value[0] = self->break_signal;
- append_tuple(self,instruction,1,value);
- break;
-
- case XON_XOFF_CHAR:
- value[0] = self->xon_char;
- value[1] = self->xoff_char;
- append_tuple(self,instruction,2,value);
- break;
-
- case ENQ_ACK_CHAR:
- value[0] = self->enq_char;
- value[1] = self->ack_char;
- append_tuple(self,instruction,2,value);
- break;
-
- case DATA_RATE:
- temp = self->data_rate;
- value[3] = (__u8)((temp >> 24) & 0x000000ff);
- value[2] = (__u8)((temp >> 16) & 0x000000ff);
- value[1] = (__u8)((temp >> 8) & 0x000000ff);
- value[0] = (__u8)(temp & 0x000000ff);
- append_tuple(self,instruction,4,value);
- break;
-#if 0
- case PORT_NAME:
- case FIXED_PORT_NAME:
- temp = strlen(&self->port_name);
- if(temp < 32){
- value = (__u8) (self->port_name);
- append_tuple(self,instruction,temp,value);
- }else
- DEBUG(0,__FUNCTION__"() PORT_NAME:too long\n");
-#endif
- break;
-
- default:
- if(notsupp)
- DEBUG(0,__FUNCTION__"():instruction(0x%02x)is not"
- "implemented\n",instruction);
- }
-
-
-}
-
-void ircomm_parse_tuples(struct ircomm_cb *self, struct sk_buff *skb, int type)
-{
-
- __u8 *data;
- __u8 pi,plen;
- int clen = 0;
- int indicate=0;
-
- ASSERT(skb != NULL, return;);
- ASSERT(self != NULL, return ;);
- ASSERT(self->magic == IRCOMM_MAGIC, return ;);
-
-
-#ifdef IRCOMM_DEBUG_TUPLE
- DEBUG(0, __FUNCTION__"():tuple sequence is:\n");
- {
- int i;
- for ( i=0;i< skb->len;i++)
- printk("%02x", (__u8)(skb->data[i]));
- printk("\n");
- }
-#endif
-
- data = skb->data;
- if(type == IAS_PARAM)
- {
- clen = (data[0] << 8) & 0xff00;
- clen |= data[1] & 0x00ff;
- ASSERT( clen <= (skb->len - 2) && clen <= 1024, goto corrupted;);
- DEBUG(4, __FUNCTION__"():IAS_PARAM len = %d\n",clen );
- skb_pull( skb, 2);
- }
- else
- {
- /* CONTROL_CHANNEL */
- clen = data[0];
- ASSERT( clen < skb->len, goto corrupted;);
- DEBUG(4, __FUNCTION__"():CONTROL_CHANNEL:len = %d\n",clen );
- skb_pull( skb, 1);
- }
-
- while( clen >= 2 ){
- data = skb->data;
- indicate = 0;
-
- /*
- * parse controlparameters and set value into structure
- */
- pi = data[0];
- plen = data[1];
-
- ASSERT( clen >= 2+plen, goto corrupted; );
- DEBUG(4, __FUNCTION__"():instruction=0x%02x,len=%d\n",
- pi, plen) ;
-
-
- switch(pi)
- {
- case POLL_FOR_LINE_SETTINGS:
- ircomm_control_request(self, DTELINE_STATE);
- break;
-
- case SERVICETYPE:
- self->peer_servicetype = data[2];
- break;
-
- case PORT_TYPE:
- self->peer_port_type = data[2];
- break;
-
- case DATA_FORMAT:
- self->peer_data_format = data[2];
- break;
-
- case FLOW_CONTROL:
- self->peer_flow_ctrl = data[2];
- indicate = 1;
- break;
-
- case LINESTATUS:
- self->peer_line_status = data[2];
- indicate = 1;
- break;
-
- case BREAK_SIGNAL:
- self->peer_break_signal = data[2];
- /* indicate = 1; */
- break;
-
- case DCELINE_STATE:
- self->peer_dce = data[2];
- indicate = 1;
- break;
-
- case DTELINE_STATE:
- if(self->null_modem_mode){
- /* input DTR as {DSR & CD & RI} */
- self->peer_dce = 0;
- if(data[2] & DELTA_DTR)
- self->peer_dce |= (DELTA_DSR|
- DELTA_RI|
- DELTA_DCD);
- if(data[2] & MCR_DTR)
- self->peer_dce |= (MSR_DSR|
- MSR_RI|
- MSR_DCD);
- /* rts as cts */
- if(data[2] & DELTA_RTS)
- self->peer_dce |= DELTA_CTS;
- if(data[2] & MCR_RTS)
- self->peer_dce |= MSR_CTS;
- }else{
- self->peer_dte = data[2];
- }
- indicate = 1;
- break;
-
- case XON_XOFF_CHAR:
- self->peer_xon_char = data[2];
- self->peer_xoff_char = data[3];
- indicate = 1;
- break;
-
- case ENQ_ACK_CHAR:
- self->peer_enq_char = data[2];
- self->peer_ack_char = data[3];
- indicate = 1;
- break;
-
- case DATA_RATE:
- self->peer_data_rate = ( data[5]<<24
- & data[4]<<16
- & data[3]<<8
- & data[2]);
- indicate = 1;
- break;
-
- case PORT_NAME:
- ASSERT(plen <= 32 , goto corrupted;);
- memcpy(self->port_name, data + 2, plen);
- *(__u8 *)(self->port_name+plen) = 0;
- break;
-
- case FIXED_PORT_NAME:
- ASSERT(plen <= 32 , goto corrupted;);
- memcpy(self->port_name, data + 2, plen);
- *(__u8 *)(self->port_name+plen) = 0;
- /*
- * We should not connect if user of IrCOMM can't
- * recognize the port name
- */
- self->port_name_critical = TRUE;
- break;
-
- default:
- DEBUG(0, __FUNCTION__
- "():not implemented (PI=%d)\n", pi);
- }
-
-
- if(indicate &&
- self->notify.flow_indication && type == CONTROL_CHANNEL)
- {
- DEBUG(4,__FUNCTION__":indicating..:\n");
- self->pi = pi;
- if(self->notify.flow_indication)
- self->notify.flow_indication(self->notify.instance,
- self,
- CONTROL_CHANNEL);
- }
- skb_pull(skb, 2+plen);
- clen -= (2+plen);
- }
-
- return;
-
- corrupted:
- skb_pull(skb, skb->len); /* remove suspicious data */
- return;
-}
-
-/*
- * ----------------------------------------------------------------------
- * Function ircomm_open_instance() ,ircomm_close_instance() and friends
- *
- * ircomm_open_instance discoveres the peer device and then issues a
- * connect request
- * ----------------------------------------------------------------------
- */
-
-
-
-struct ircomm_cb * ircomm_open_instance( struct notify_t client_notify)
-{
- int i;
- struct ircomm_cb *self = NULL;
- struct notify_t notify;
- unsigned long flags;
-
- ASSERT(ircomm != NULL,return NULL;);
- DEBUG(0,__FUNCTION__"():\n");
-
- /* find free handle */
-
- save_flags(flags);
- cli();
- for(i = 0; i < IRCOMM_MAX_CONNECTION; i++){
- ASSERT(ircomm[i] != NULL,return(NULL););
- if(!ircomm[i]->in_use){
- self = ircomm[i];
- break;
- }
- }
-
- if (!self){
- DEBUG(0,__FUNCTION__"():no free handle!\n");
- return (NULL);
- }
-
- self->in_use = 1;
- restore_flags(flags);
-
- self->notify = client_notify;
- self->ttp_stop = 0;
- self->control_ch_pending = 0;
-
- /* register callbacks */
-
- irda_notify_init(&notify);
- notify.data_indication = ircomm_accept_data_indication;
- notify.connect_confirm = ircomm_accept_connect_confirm;
- notify.connect_indication = ircomm_accept_connect_indication;
- notify.flow_indication = ircomm_accept_flow_indication;
- notify.disconnect_indication = ircomm_accept_disconnect_indication;
- notify.instance = self;
- strncpy( notify.name, "IrCOMM", NOTIFY_MAX_NAME);
-
- self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
- &notify);
- if(!self->tsap){
- DEBUG(0,__FUNCTION__"failed to allocate tsap\n");
- return NULL;
- }
-
- ircomm_next_state(self, COMM_IDLE);
- return (self);
-}
-
-int ircomm_close_instance(struct ircomm_cb *self)
-{
- ASSERT( self != NULL, return -EIO;);
- ASSERT( self->magic == IRCOMM_MAGIC, return -EIO;);
- ASSERT( self->ctrl_skb != NULL, return -EIO;);
-
- DEBUG(0,__FUNCTION__"()\n");
-
- /* shutdown ircomm layer */
- if(self->state != COMM_IDLE && self->state != COMM_WAITI)
- {
- DEBUG(0,__FUNCTION__"():force disconnecting..\n");
- ircomm_disconnect_request(self, NULL, P_HIGH);
- }
-
- skb_trim(self->ctrl_skb,0);
- /* remove a tsap */
- if(self->tsap)
- irttp_close_tsap(self->tsap);
- self->tsap = NULL;
- self->in_use = 0;
- return 0;
-}
-
-
-/*
- * ----------------------------------------------------------------------
- * Function init_module(void) ,cleanup_module()
- *
- * Initializes the ircomm control structure
- * These Function are called when you insmod / rmmod .
- * ----------------------------------------------------------------------
- */
-
-#ifdef MODULE
-int init_module(void)
-{
- int err;
-
- err = ircomm_init();
-
- DEBUG( 4, __FUNCTION__"():done.\n");
- return err;
-}
-
-void cleanup_module(void)
-{
- ircomm_cleanup();
- DEBUG( 4, __FUNCTION__"():done.\n");
-}
-#endif /* MODULE */
-
-/************************************************************
- * proc stuff
- ************************************************************/
-
-#ifdef CONFIG_PROC_FS
-
-/*
- * Function proc_ircomm_read (buf, start, offset, len, unused)
- *
- * this function is called if there is a access on /proc/irda/comm .
- *
- */
-int ircomm_proc_read(char *buf, char **start, off_t offset,
- int len, int unused)
-{
- int i, index;
-
- len = 0;
- for (i=0; i<IRCOMM_MAX_CONNECTION; i++) {
-
- len += sprintf(buf+len, "instance %d:\n",i);
- if(ircomm[i]->in_use == 0){
- len += sprintf(buf+len, "\tunused\n");
- continue;
- }
-
- if (ircomm[i] == NULL || ircomm[i]->magic != IRCOMM_MAGIC) {
- len += sprintf(buf+len, "\tbroken???\n");
- continue;
- }
-
- switch (ircomm[i]->servicetype) {
- case UNKNOWN:
- index = 0;
- break;
- case THREE_WIRE_RAW:
- index = 1;
- break;
- case THREE_WIRE:
- index = 2;
- break;
- case NINE_WIRE:
- index = 3;
- break;
- case CENTRONICS:
- index = 4;
- break;
- default:
- index = 0;
- break;
- }
- len += sprintf(buf+len, " service: %s ",
- ircommservicetype[index]);
- if(!index)
- continue;
-
- len += sprintf(buf+len, "porttype: %s ",
- ircommporttype[ircomm[i]->port_type]);
- len += sprintf(buf+len, "state: %s ",
- ircommstate[ircomm[i]->state]);
- len += sprintf(buf+len, "user: %s\n",
- ircomm[i]->notify.name);
-
- len += sprintf(buf+len, " tx packets: %d ",
- ircomm[i]->tx_packets);
- len += sprintf(buf+len, "rx packets: %d ",
- ircomm[i]->rx_packets);
- len += sprintf(buf+len, "tx controls: %d\n",
- ircomm[i]->tx_controls);
-
- len += sprintf(buf+len, " pending tuples: %d ",
- ircomm[i]->pending_control_tuples);
- len += sprintf(buf+len, " ignored tuples: %d\n",
- ircomm[i]->ignored_control_tuples);
-
- len += sprintf(buf+len, " nullmodem emulation: %s ",
- (ircomm[i]->null_modem_mode ? "yes":"no"));
- len += sprintf(buf+len, "IrTTP: %s\n",
- (ircomm[i]->ttp_stop ? "BUSY":"READY"));
-
- len += sprintf(buf+len, " Peer capability: ");
- if(ircomm[i]->peer_cap & THREE_WIRE_RAW)
- len += sprintf(buf+len, "3wire-raw ");
- if(ircomm[i]->peer_cap & THREE_WIRE)
- len += sprintf(buf+len, "3wire ");
- if(ircomm[i]->peer_cap & NINE_WIRE)
- len += sprintf(buf+len, "9wire ");
- if(ircomm[i]->peer_cap & CENTRONICS)
- len += sprintf(buf+len, "centronics");
-
- len += sprintf(buf+len, "\n Port name: %s\n",
- (ircomm[i]->port_name));
- }
-
- return len;
-}
-
-#endif /* CONFIG_PROC_FS */
-
-
-
-
diff --git a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c
new file mode 100644
index 000000000..a1fd0223a
--- /dev/null
+++ b/net/irda/ircomm/ircomm_core.c
@@ -0,0 +1,508 @@
+/*********************************************************************
+ *
+ * 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 Aug 17 11:05:41 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/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.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
+static int ircomm_proc_read(char *buf, char **start, off_t offset, int len,
+ int unused);
+
+extern struct proc_dir_entry *proc_irda;
+#endif /* CONFIG_PROC_FS */
+
+hashbin_t *ircomm = NULL;
+
+int __init ircomm_init(void)
+{
+ ircomm = hashbin_new(HB_LOCAL);
+ if (ircomm == NULL) {
+ ERROR(__FUNCTION__ "(), can't allocate hashbin!\n");
+ return -ENOMEM;
+ }
+
+#ifdef CONFIG_PROC_FS
+ create_proc_entry("ircomm", 0, proc_irda)->get_info = ircomm_proc_read;
+#endif /* CONFIG_PROC_FS */
+
+ MESSAGE("IrCOMM protocol (Dag Brattli)\n");
+
+ return 0;
+}
+
+#ifdef MODULE
+void ircomm_cleanup(void)
+{
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close);
+
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("ircomm", proc_irda);
+#endif /* CONFIG_PROC_FS */
+}
+#endif /* MODULE */
+
+/*
+ * 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;
+
+ DEBUG(2, __FUNCTION__ "(), service_type=0x%02x\n",
+ service_type);
+
+ ASSERT(ircomm != NULL, return NULL;);
+
+ self = kmalloc(sizeof(struct ircomm_cb), GFP_ATOMIC);
+ if (self == NULL)
+ return NULL;
+
+ memset(self, 0, sizeof(struct ircomm_cb));
+
+ self->notify = *notify;
+ self->magic = IRCOMM_MAGIC;
+
+ /* Check if we should use IrLMP or IrTTP */
+ if (service_type & IRCOMM_3_WIRE_RAW) {
+ self->flow_status = FLOW_START;
+ ret = ircomm_open_lsap(self);
+ } else
+ ret = ircomm_open_tsap(self);
+
+ if (ret < 0)
+ return NULL;
+
+ self->service_type = service_type;
+ self->line = line;
+
+ hashbin_insert(ircomm, (QUEUE *) self, line, NULL);
+
+ ircomm_next_state(self, IRCOMM_IDLE);
+
+ return self;
+}
+
+/*
+ * Function ircomm_close_instance (self)
+ *
+ * Remove IrCOMM instance
+ *
+ */
+static int __ircomm_close(struct ircomm_cb *self)
+{
+ DEBUG(2, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return -EIO;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;);
+
+ /* 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;
+
+ entry = hashbin_remove(ircomm, self->line, NULL);
+
+ ASSERT(entry == self, return -1;);
+
+ return __ircomm_close(self);
+}
+
+/*
+ * 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;
+
+ DEBUG(2 , __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return -1;);
+ 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;
+}
+
+/*
+ * Function ircomm_connect_indication (self, qos, skb)
+ *
+ * Notify user layer about the incomming connection
+ *
+ */
+void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
+ struct ircomm_info *info)
+{
+ int clen = 0;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ /* 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 0
+ if (clen > 0)
+ ircomm_control_indication(self, skb, clen);
+#endif
+ if (self->notify.connect_indication)
+ self->notify.connect_indication(self->notify.instance, self,
+ info->qos, info->max_data_size,
+ info->max_header_size, skb);
+}
+
+/*
+ * 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;
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
+
+ DEBUG(4, __FUNCTION__ "()\n");
+
+ ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL);
+
+ return ret;
+}
+
+/*
+ * 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)
+{
+ DEBUG(4, __FUNCTION__"()\n");
+
+ if (self->notify.connect_confirm )
+ self->notify.connect_confirm(self->notify.instance,
+ self, info->qos,
+ info->max_data_size,
+ info->max_header_size, skb);
+}
+
+/*
+ * 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;
+
+ DEBUG(4, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return -EFAULT;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
+ ASSERT(skb != NULL, return -EFAULT;);
+
+ ret = ircomm_do_event(self, IRCOMM_DATA_REQUEST, skb, NULL);
+
+ return ret;
+}
+
+/*
+ * 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)
+{
+ DEBUG(4, __FUNCTION__"()\n");
+
+ ASSERT(skb->len > 0, return;);
+
+ if (self->notify.data_indication)
+ self->notify.data_indication(self->notify.instance, self, skb);
+}
+
+/*
+ * 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;
+
+ 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 {
+ DEBUG(4, __FUNCTION__ "(), data was control info only!\n");
+ dev_kfree_skb(skb);
+ }
+}
+
+/*
+ * 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;
+
+ DEBUG(2, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return -EFAULT;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
+ ASSERT(skb != NULL, return -EFAULT;);
+
+ ret = ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb, NULL);
+
+ return ret;
+}
+
+/*
+ * 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)
+{
+ struct sk_buff *ctrl_skb;
+
+ DEBUG(2, __FUNCTION__"()\n");
+
+ ctrl_skb = skb_clone(skb, GFP_ATOMIC);
+ if (!ctrl_skb)
+ return;
+
+ /* Remove data channel from control channel */
+ skb_trim(ctrl_skb, clen+1);
+
+ /* Use udata for delivering data on the control channel */
+ if (self->notify.udata_indication)
+ self->notify.udata_indication(self->notify.instance, self,
+ ctrl_skb);
+}
+
+/*
+ * 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;
+
+ DEBUG(2, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
+
+ ret = ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata,
+ &info);
+ return ret;
+}
+
+/*
+ * 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)
+{
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(info != NULL, return;);
+
+ if (self->notify.disconnect_indication) {
+ self->notify.disconnect_indication(self->notify.instance, self,
+ info->reason, skb);
+ }
+}
+
+/*
+ * Function ircomm_flow_request (self, flow)
+ *
+ *
+ *
+ */
+void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow)
+{
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return;);
+
+ if (self->service_type == IRCOMM_3_WIRE_RAW)
+ return;
+
+ irttp_flow_request(self->tsap, flow);
+}
+
+#ifdef CONFIG_PROC_FS
+/*
+ * Function ircomm_proc_read (buf, start, offset, len, unused)
+ *
+ *
+ *
+ */
+int ircomm_proc_read(char *buf, char **start, off_t offset, int len,
+ int unused)
+{
+ struct ircomm_cb *self;
+ unsigned long flags;
+ int i=0;
+
+ save_flags(flags);
+ cli();
+
+ len = 0;
+
+ len += sprintf(buf+len, "Instance %d:\n", i++);
+
+ self = (struct ircomm_cb *) hashbin_get_first(ircomm);
+ while (self != NULL) {
+ ASSERT(self->magic == IRCOMM_MAGIC, return len;);
+
+ self = (struct ircomm_cb *) hashbin_get_next(ircomm);
+ }
+ restore_flags(flags);
+
+ return len;
+}
+#endif /* CONFIG_PROC_FS */
+
+#ifdef MODULE
+int init_module(void)
+{
+ return ircomm_init();
+}
+
+void cleanup_module(void)
+{
+ ircomm_cleanup();
+}
+#endif /* MODULE */
+
diff --git a/net/irda/ircomm/ircomm_event.c b/net/irda/ircomm/ircomm_event.c
new file mode 100644
index 000000000..18f0b24db
--- /dev/null
+++ b/net/irda/ircomm/ircomm_event.c
@@ -0,0 +1,251 @@
+/*********************************************************************
+ *
+ * Filename: ircomm_event.c
+ * Version: 1.0
+ * Description: IrCOMM layer state machine
+ * Status: Experimental.
+ * Author: Dag Brattli <dagb@cs.uit.no>
+ * Created at: Sun Jun 6 20:33:11 1999
+ * Modified at: Thu Sep 2 10:09:25 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ ********************************************************************/
+
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/iriap.h>
+#include <net/irda/irttp.h>
+#include <net/irda/irias_object.h>
+
+#include <net/irda/ircomm_core.h>
+#include <net/irda/ircomm_event.h>
+
+static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
+ struct sk_buff *skb, struct ircomm_info *info);
+static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
+ struct sk_buff *skb, struct ircomm_info *info);
+static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
+ struct sk_buff *skb, struct ircomm_info *info);
+static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
+ struct sk_buff *skb, struct ircomm_info *info);
+
+
+char *ircomm_state[] = {
+ "IRCOMM_IDLE",
+ "IRCOMM_WAITI",
+ "IRCOMM_WAITR",
+ "IRCOMM_CONN",
+};
+
+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",
+};
+
+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:
+ DEBUG(4, __FUNCTION__"(), unknown event: %s\n",
+ ircomm_event[event]);
+ return -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:
+ DEBUG(0, __FUNCTION__"(), unknown event: %s\n",
+ ircomm_event[event]);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+/*
+ * Function ircomm_state_waitr (self, event, skb)
+ *
+ * IrCOMM has received an incomming 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:
+ DEBUG(0, __FUNCTION__ "(), unknown event = %s\n",
+ 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:
+ DEBUG(0, __FUNCTION__ "(), unknown event = %s\n",
+ 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)
+{
+ DEBUG(4, __FUNCTION__": state=%s, event=%s\n",
+ 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;
+
+ DEBUG(4, __FUNCTION__": next state=%s, service type=%d\n",
+ ircomm_state[self->state], self->service_type);
+}
diff --git a/net/irda/ircomm/ircomm_lmp.c b/net/irda/ircomm/ircomm_lmp.c
new file mode 100644
index 000000000..64f3a449c
--- /dev/null
+++ b/net/irda/ircomm/ircomm_lmp.c
@@ -0,0 +1,327 @@
+/*********************************************************************
+ *
+ * Filename: ircomm_lmp.c
+ * Version:
+ * Description: Interface between IrCOMM and IrLMP
+ * Status: Experimental.
+ * Author: Dag Brattli <dagb@cs.uit.no>
+ * Created at: Sun Jun 6 20:48:27 1999
+ * Modified at: Fri Aug 13 07:21:29 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ * Sources: Previous IrLPT work by Thomas Davis
+ *
+ * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ ********************************************************************/
+
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/iriap.h>
+
+#include <net/irda/ircomm_event.h>
+#include <net/irda/ircomm_lmp.h>
+
+/*
+ * 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;
+
+ DEBUG(0, __FUNCTION__ "()\n");
+
+ /* 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;
+ strncpy(notify.name, "IrCOMM", NOTIFY_MAX_NAME);
+
+ self->lsap = irlmp_open_lsap(LSAP_ANY, &notify);
+ if (!self->lsap) {
+ DEBUG(0,__FUNCTION__"failed to allocate tsap\n");
+ 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;
+}
+
+/*
+ * Function ircomm_lmp_connect_request (self, userdata)
+ *
+ *
+ *
+ */
+int ircomm_lmp_connect_request(struct ircomm_cb *self,
+ struct sk_buff *userdata,
+ struct ircomm_info *info)
+{
+ int ret = 0;
+
+ DEBUG(0, __FUNCTION__ "()\n");
+
+ ret = irlmp_connect_request(self->lsap, info->dlsap_sel,
+ info->saddr, info->daddr, NULL, userdata);
+ return ret;
+}
+
+/*
+ * Function ircomm_lmp_connect_response (self, skb)
+ *
+ *
+ *
+ */
+int ircomm_lmp_connect_response(struct ircomm_cb *self, struct sk_buff *skb)
+{
+ int ret;
+
+ DEBUG(0, __FUNCTION__"()\n");
+
+ ret = irlmp_connect_response(self->lsap, skb);
+
+ return 0;
+}
+
+int ircomm_lmp_disconnect_request(struct ircomm_cb *self,
+ struct sk_buff *userdata,
+ struct ircomm_info *info)
+{
+ struct sk_buff *skb;
+ int ret;
+
+ DEBUG(0, __FUNCTION__ "()\n");
+
+ if (!userdata) {
+ skb = dev_alloc_skb(64);
+ if (!skb)
+ return -ENOMEM;
+
+ /*
+ * Reserve space for MUX and LAP header
+ */
+ skb_reserve(skb, LMP_MAX_HEADER);
+
+ userdata = skb;
+ }
+ 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
+ */
+void ircomm_lmp_flow_control(struct sk_buff *skb)
+{
+ struct irda_skb_cb *cb;
+ struct ircomm_cb *self;
+ int line;
+
+ ASSERT(skb != NULL, return;);
+
+ cb = (struct irda_skb_cb *) skb->cb;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ line = cb->line;
+
+ self = (struct ircomm_cb *) hashbin_find(ircomm, line, NULL);
+ if (!self) {
+ DEBUG(2, __FUNCTION__ "(), didn't find myself\n");
+ return;
+ }
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return;);
+
+ self->pkt_count--;
+
+ if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) {
+ DEBUG(2, __FUNCTION__ "(), asking TTY to start again!\n");
+ 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
+ *
+ */
+int ircomm_lmp_data_request(struct ircomm_cb *self, struct sk_buff *skb,
+ int not_used)
+{
+ struct irda_skb_cb *cb;
+ int ret;
+
+ ASSERT(skb != NULL, return -1;);
+
+ cb = (struct irda_skb_cb *) skb->cb;
+
+ cb->line = self->line;
+
+ DEBUG(4, __FUNCTION__"(), sending frame\n");
+
+ skb->destructor = ircomm_lmp_flow_control;
+
+ if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) {
+ DEBUG(2, __FUNCTION__ "(), asking TTY to slow down!\n");
+ 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) {
+ ERROR(__FUNCTION__ "(), failed\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
+}
+
+/*
+ * Function ircomm_lmp_data_indication (instance, sap, skb)
+ *
+ * Incomming data which we must deliver to the state machine, to check
+ * we are still connected.
+ */
+int ircomm_lmp_data_indication(void *instance, void *sap,
+ struct sk_buff *skb)
+{
+ struct ircomm_cb *self = (struct ircomm_cb *) instance;
+
+ DEBUG(4, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
+ ASSERT(skb != NULL, return -1;);
+
+ ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL);
+
+ return 0;
+}
+
+/*
+ * Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size,
+ * max_header_size, skb)
+ *
+ * Connection has been confirmed by peer device
+ *
+ */
+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;
+
+ DEBUG(0, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return;);
+ ASSERT(skb != NULL, return;);
+ 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);
+}
+
+/*
+ * Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size,
+ * max_header_size, skb)
+ *
+ * Peer device wants to make a connection with us
+ *
+ */
+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;
+
+ DEBUG(0, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return;);
+ ASSERT(skb != NULL, return;);
+ 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);
+}
+
+/*
+ * Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb)
+ *
+ * Peer device has closed the connection, or the link went down for some
+ * other reason
+ */
+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;
+
+ DEBUG(0, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return;);
+
+ info.reason = reason;
+
+ ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info);
+}
+
+
diff --git a/net/irda/ircomm/ircomm_param.c b/net/irda/ircomm/ircomm_param.c
new file mode 100644
index 000000000..4294dbdb2
--- /dev/null
+++ b/net/irda/ircomm/ircomm_param.c
@@ -0,0 +1,465 @@
+/*********************************************************************
+ *
+ * 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: Fri Sep 3 09:28:20 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 <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, param_t *param, int get);
+static int ircomm_param_port_type(void *instance, param_t *param, int get);
+static int ircomm_param_port_name(void *instance, param_t *param, int get);
+static int ircomm_param_service_type(void *instance, param_t *param, int get);
+static int ircomm_param_data_rate(void *instance, param_t *param, int get);
+static int ircomm_param_data_format(void *instance, param_t *param, int get);
+static int ircomm_param_flow_control(void *instance, param_t *param, int get);
+static int ircomm_param_xon_xoff(void *instance, param_t *param, int get);
+static int ircomm_param_enq_ack(void *instance, param_t *param, int get);
+static int ircomm_param_line_status(void *instance, param_t *param, int get);
+static int ircomm_param_dte(void *instance, param_t *param, int get);
+static int ircomm_param_dce(void *instance, param_t *param, int get);
+static int ircomm_param_poll(void *instance, 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_INT_8_BITS },
+};
+
+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_flush (self)
+ *
+ * Flush (send) out all queued parameters
+ *
+ */
+int ircomm_param_flush(struct ircomm_tty_cb *self)
+{
+ if (self->ctrl_skb) {
+ ircomm_control_request(self->ircomm, self->ctrl_skb);
+ self->ctrl_skb = NULL;
+ }
+ return 0;
+}
+
+/*
+ * 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)
+{
+ unsigned long flags;
+ struct sk_buff *skb;
+ int count;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ if (self->state != IRCOMM_TTY_READY) {
+ DEBUG(2, __FUNCTION__ "(), not ready yet!\n");
+ return 0;
+ }
+
+ /* Make sure we don't send parameters for raw mode */
+ if (self->service_type == IRCOMM_3_WIRE_RAW)
+ return 0;
+
+ save_flags(flags);
+ cli();
+
+ skb = self->ctrl_skb;
+ if (!skb) {
+ skb = dev_alloc_skb(256);
+ if (!skb) {
+ restore_flags(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) {
+ DEBUG(0, __FUNCTION__ "(), no room for parameter!\n");
+ restore_flags(flags);
+ return -1;
+ }
+ skb_put(skb, count);
+ restore_flags(flags);
+
+ if (flush) {
+ ircomm_control_request(self->ircomm, skb);
+ self->ctrl_skb = NULL;
+ }
+ 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 paramters
+ *
+ */
+static int ircomm_param_service_type(void *instance, param_t *param, int get)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+ __u8 service_type = param->pv.b; /* We know it's a one byte integer */
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ if (get) {
+ param->pv.b = self->session.service_type;
+ return 0;
+ }
+
+ /*
+ * Now choose a preferred service type of those available
+ */
+ if (service_type & IRCOMM_3_WIRE_RAW) {
+ DEBUG(2, __FUNCTION__ "(), peer supports 3 wire raw\n");
+ self->session.service_type |= IRCOMM_3_WIRE_RAW;
+ }
+ if (service_type & IRCOMM_3_WIRE) {
+ DEBUG(2, __FUNCTION__ "(), peer supports 3 wire\n");
+ self->session.service_type |= IRCOMM_3_WIRE;
+ }
+ if (service_type & IRCOMM_9_WIRE) {
+ DEBUG(2, __FUNCTION__ "(), peer supports 9 wire\n");
+ self->session.service_type |= IRCOMM_9_WIRE;
+ }
+ if (service_type & IRCOMM_CENTRONICS) {
+ DEBUG(2, __FUNCTION__ "(), peer supports Centronics\n");
+ self->session.service_type |= IRCOMM_CENTRONICS;
+ }
+
+ self->session.service_type &= self->service_type;
+ if (!self->session.service_type) {
+ DEBUG(2, __FUNCTION__"(), No common service type to use!\n");
+ return -1;
+ }
+
+ DEBUG(2, __FUNCTION__ "(), resulting service type=0x%02x\n",
+ self->session.service_type);
+
+ 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, param_t *param, int get)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ if (get)
+ param->pv.b = IRCOMM_SERIAL;
+ else {
+ self->session.port_type = param->pv.b;
+
+ DEBUG(0, __FUNCTION__ "(), port type=%d\n",
+ self->session.port_type);
+ }
+ return 0;
+}
+
+/*
+ * Function ircomm_param_port_name (self, param)
+ *
+ *
+ *
+ */
+static int ircomm_param_port_name(void *instance, param_t *param, int get)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ if (get) {
+ DEBUG(0, __FUNCTION__ "(), not imp!\n");
+ } else {
+ DEBUG(0, __FUNCTION__ "(), port-name=%s\n", param->pv.c);
+ strncpy(self->session.port_name, param->pv.c, 32);
+ }
+
+ return 0;
+}
+
+/*
+ * Function ircomm_param_data_rate (self, param)
+ *
+ *
+ *
+ */
+static int ircomm_param_data_rate(void *instance, param_t *param, int get)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ if (get)
+ param->pv.i = self->session.data_rate;
+ else
+ self->session.data_rate = param->pv.i;
+
+ DEBUG(2, __FUNCTION__ "(), data rate = %d\n", param->pv.i);
+
+ return 0;
+}
+
+/*
+ * Function ircomm_param_data_format (self, param)
+ *
+ *
+ *
+ */
+static int ircomm_param_data_format(void *instance, param_t *param, int get)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ if (get)
+ param->pv.b = self->session.data_format;
+ else
+ self->session.data_format = param->pv.b;
+
+ return 0;
+}
+
+/*
+ * Function ircomm_param_flow_control (self, param)
+ *
+ *
+ *
+ */
+static int ircomm_param_flow_control(void *instance, param_t *param, int get)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ if (get)
+ param->pv.b = self->session.flow_control;
+ else
+ self->session.flow_control = param->pv.b;
+
+ DEBUG(1, __FUNCTION__ "(), flow control = 0x%02x\n", param->pv.b);
+
+ return 0;
+}
+
+/*
+ * Function ircomm_param_xon_xoff (self, param)
+ *
+ *
+ *
+ */
+static int ircomm_param_xon_xoff(void *instance, param_t *param, int get)
+{
+ DEBUG(2, __FUNCTION__ "(), not impl.\n");
+
+ return 0;
+}
+
+/*
+ * Function ircomm_param_enq_ack (self, param)
+ *
+ *
+ *
+ */
+static int ircomm_param_enq_ack(void *instance, param_t *param, int get)
+{
+ DEBUG(2, __FUNCTION__ "(), not impl.\n");
+
+ return 0;
+}
+
+/*
+ * Function ircomm_param_line_status (self, param)
+ *
+ *
+ *
+ */
+static int ircomm_param_line_status(void *instance, param_t *param, int get)
+{
+ DEBUG(2, __FUNCTION__ "(), not impl.\n");
+
+ 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, param_t *param, int get)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+ __u8 dte;
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ if (get)
+ param->pv.b = self->session.dte;
+ else {
+ dte = param->pv.b;
+
+ if (dte & IRCOMM_DELTA_DTR)
+ self->session.dce |= (IRCOMM_DELTA_DSR|
+ IRCOMM_DELTA_RI |
+ IRCOMM_DELTA_CD);
+ if (dte & IRCOMM_DTR)
+ self->session.dce |= (IRCOMM_DSR|
+ IRCOMM_RI |
+ IRCOMM_CD);
+
+ if (dte & IRCOMM_DELTA_RTS)
+ self->session.dce |= IRCOMM_DELTA_CTS;
+ if (dte & IRCOMM_RTS)
+ self->session.dce |= IRCOMM_CTS;
+
+ /* Take appropriate actions */
+ ircomm_tty_check_modem_status(self);
+
+ /* Null modem cable emulator */
+ self->session.null_modem = TRUE;
+ }
+
+ return 0;
+}
+
+/*
+ * Function ircomm_param_dce (instance, param)
+ *
+ *
+ *
+ */
+static int ircomm_param_dce(void *instance, param_t *param, int get)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+ __u8 dce;
+
+ DEBUG(1, __FUNCTION__ "(), dce = 0x%02x\n", param->pv.b);
+
+ dce = param->pv.b;
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ self->session.dce = dce;
+
+ /* Check if any of the settings have changed */
+ if (dce & 0x0f) {
+ if (dce & IRCOMM_DELTA_CTS) {
+ DEBUG(2, __FUNCTION__ "(), CTS \n");
+ }
+ }
+
+ 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, param_t *param, int get)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+ ASSERT(self != NULL, return -1;);
+ 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;
+}
+
+
+
+
+
diff --git a/net/irda/ircomm/ircomm_ttp.c b/net/irda/ircomm/ircomm_ttp.c
new file mode 100644
index 000000000..9c188aa49
--- /dev/null
+++ b/net/irda/ircomm/ircomm_ttp.c
@@ -0,0 +1,302 @@
+/*********************************************************************
+ *
+ * Filename: ircomm_ttp.c
+ * Version:
+ * Description: Interface between IrCOMM and IrTTP
+ * Status: Experimental.
+ * Author: Dag Brattli <dagb@cs.uit.no>
+ * Created at: Sun Jun 6 20:48:27 1999
+ * Modified at: Mon Sep 27 11:17:23 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ ********************************************************************/
+
+#include <linux/sched.h>
+#include <linux/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>
+
+/*
+ * Function ircomm_open_tsap (self)
+ *
+ *
+ *
+ */
+int ircomm_open_tsap(struct ircomm_cb *self)
+{
+ notify_t notify;
+
+ DEBUG(4, __FUNCTION__ "()\n");
+
+ /* 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;
+ strncpy(notify.name, "IrCOMM", NOTIFY_MAX_NAME);
+
+ self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
+ &notify);
+ if (!self->tsap) {
+ DEBUG(0,__FUNCTION__"failed to allocate tsap\n");
+ 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)
+ *
+ *
+ *
+ */
+int ircomm_ttp_connect_request(struct ircomm_cb *self,
+ struct sk_buff *userdata,
+ struct ircomm_info *info)
+{
+ int ret = 0;
+
+ DEBUG(4, __FUNCTION__ "()\n");
+
+ ret = irttp_connect_request(self->tsap, info->dlsap_sel,
+ info->saddr, info->daddr,
+ NULL, SAR_DISABLE, userdata);
+ return ret;
+}
+
+/*
+ * Function ircomm_ttp_connect_response (self, skb)
+ *
+ *
+ *
+ */
+int ircomm_ttp_connect_response(struct ircomm_cb *self, struct sk_buff *skb)
+{
+ int ret;
+
+ DEBUG(4, __FUNCTION__"()\n");
+
+ ret = irttp_connect_response(self->tsap, SAR_DISABLE, skb);
+
+ 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.
+ */
+int ircomm_ttp_data_request(struct ircomm_cb *self, struct sk_buff *skb,
+ int clen)
+{
+ int ret;
+
+ ASSERT(skb != NULL, return -1;);
+
+ DEBUG(2, __FUNCTION__"(), clen=%d\n", clen);
+
+ /*
+ * Insert clen field, currently we either send data only, or control
+ * only frames, to make things easier and avoid queueing
+ */
+ ASSERT(skb_headroom(skb) >= 1, return -1;);
+ skb_push(skb, IRCOMM_HEADER_SIZE);
+
+ skb->data[0] = clen;
+
+ ret = irttp_data_request(self->tsap, skb);
+ if (ret) {
+ ERROR(__FUNCTION__ "(), failed\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
+}
+
+/*
+ * Function ircomm_ttp_data_indication (instance, sap, skb)
+ *
+ * Incomming data
+ *
+ */
+int ircomm_ttp_data_indication(void *instance, void *sap,
+ struct sk_buff *skb)
+{
+ struct ircomm_cb *self = (struct ircomm_cb *) instance;
+
+ DEBUG(4, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
+ ASSERT(skb != NULL, return -1;);
+
+ ircomm_do_event(self, IRCOMM_TTP_DATA_INDICATION, skb, NULL);
+
+ return 0;
+}
+
+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;
+
+ DEBUG(4, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return;);
+ ASSERT(skb != NULL, return;);
+ ASSERT(qos != NULL, return;);
+
+ if (max_sdu_size != SAR_DISABLE) {
+ ERROR(__FUNCTION__ "(), SAR not allowed for IrCOMM!\n");
+ return;
+ }
+
+ info.max_data_size = irttp_get_max_seq_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);
+}
+
+/*
+ * Function ircomm_ttp_connect_indication (instance, sap, qos, max_sdu_size,
+ * max_header_size, skb)
+ *
+ *
+ *
+ */
+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;
+
+ DEBUG(4, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return;);
+ ASSERT(skb != NULL, return;);
+ ASSERT(qos != NULL, return;);
+
+ if (max_sdu_size != SAR_DISABLE) {
+ ERROR(__FUNCTION__ "(), SAR not allowed for IrCOMM!\n");
+ return;
+ }
+
+ info.max_data_size = irttp_get_max_seq_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);
+}
+
+/*
+ * Function ircomm_ttp_disconnect_request (self, userdata, info)
+ *
+ *
+ *
+ */
+int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
+ struct sk_buff *userdata,
+ struct ircomm_info *info)
+{
+ int ret;
+
+ ret = irttp_disconnect_request(self->tsap, userdata, P_NORMAL);
+
+ return ret;
+}
+
+/*
+ * Function ircomm_ttp_disconnect_indication (instance, sap, reason, skb)
+ *
+ *
+ *
+ */
+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;
+
+ DEBUG(4, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return;);
+
+ info.reason = reason;
+
+ ircomm_do_event(self, IRCOMM_TTP_DISCONNECT_INDICATION, skb, &info);
+}
+
+/*
+ * Function ircomm_ttp_flow_indication (instance, sap, cmd)
+ *
+ * Layer below is telling us to start or stop the flow of data
+ *
+ */
+void ircomm_ttp_flow_indication(void *instance, void *sap, LOCAL_FLOW cmd)
+{
+ struct ircomm_cb *self = (struct ircomm_cb *) instance;
+
+ DEBUG(4, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_MAGIC, return;);
+
+ if (self->notify.flow_indication)
+ self->notify.flow_indication(self->notify.instance, self, cmd);
+}
+
+
diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c
new file mode 100644
index 000000000..8df8d876e
--- /dev/null
+++ b/net/irda/ircomm/ircomm_tty.c
@@ -0,0 +1,1281 @@
+/*********************************************************************
+ *
+ * Filename: ircomm_tty.c
+ * Version: 1.0
+ * Description: IrCOMM serial TTY driver
+ * Status: Experimental.
+ * Author: Dag Brattli <dagb@cs.uit.no>
+ * Created at: Sun Jun 6 21:00:56 1999
+ * Modified at: Tue Sep 21 11:46:59 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ * Sources: serial.c and previous IrCOMM work by Takahide Higuchi
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/interrupt.h>
+
+#include <asm/segment.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>
+
+static int ircomm_tty_open(struct tty_struct *tty, struct file *filp);
+static void ircomm_tty_close(struct tty_struct * tty, struct file *filp);
+static int ircomm_tty_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count);
+static int ircomm_tty_write_room(struct tty_struct *tty);
+static void ircomm_tty_throttle(struct tty_struct *tty);
+static void ircomm_tty_unthrottle(struct tty_struct *tty);
+static int ircomm_tty_chars_in_buffer(struct tty_struct *tty);
+static void ircomm_tty_flush_buffer(struct tty_struct *tty);
+static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch);
+static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout);
+static void ircomm_tty_hangup(struct tty_struct *tty);
+static void ircomm_tty_do_softint(void *private_);
+
+static int ircomm_tty_data_indication(void *instance, void *sap,
+ struct sk_buff *skb);
+static int ircomm_tty_control_indication(void *instance, void *sap,
+ struct sk_buff *skb);
+static void ircomm_tty_flow_indication(void *instance, void *sap,
+ LOCAL_FLOW cmd);
+static int ircomm_tty_read_proc(char *buf, char **start, off_t offset, int len,
+ int *eof, void *unused);
+
+static struct tty_driver driver;
+static int ircomm_tty_refcount; /* If we manage several devices */
+
+static struct tty_struct *ircomm_tty_table[NR_PTYS];
+static struct termios *ircomm_tty_termios[NR_PTYS];
+static struct termios *ircomm_tty_termios_locked[NR_PTYS];
+
+hashbin_t *ircomm_tty = NULL;
+
+/*
+ * Function ircomm_tty_init()
+ *
+ * Init IrCOMM TTY layer/driver
+ *
+ */
+int __init ircomm_tty_init(void)
+{
+ ircomm_tty = hashbin_new(HB_LOCAL);
+ if (ircomm_tty == NULL) {
+ ERROR(__FUNCTION__ "(), can't allocate hashbin!\n");
+ return -ENOMEM;
+ }
+
+ memset(&driver, 0, sizeof(struct tty_driver));
+ driver.magic = TTY_DRIVER_MAGIC;
+ driver.driver_name = "ircomm";
+ driver.name = "ircomm";
+ driver.major = IRCOMM_TTY_MAJOR;
+ driver.minor_start = IRCOMM_TTY_MINOR;
+ driver.num = IRCOMM_TTY_PORTS;
+ driver.type = TTY_DRIVER_TYPE_SERIAL;
+ driver.subtype = SERIAL_TYPE_NORMAL;
+ driver.init_termios = tty_std_termios;
+ driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ driver.flags = TTY_DRIVER_REAL_RAW;
+ driver.refcount = &ircomm_tty_refcount;
+ driver.table = ircomm_tty_table;
+ driver.termios = ircomm_tty_termios;
+ driver.termios_locked = ircomm_tty_termios_locked;
+ driver.open = ircomm_tty_open;
+ driver.close = ircomm_tty_close;
+ driver.write = ircomm_tty_write;
+ driver.write_room = ircomm_tty_write_room;
+ driver.chars_in_buffer = ircomm_tty_chars_in_buffer;
+ driver.flush_buffer = ircomm_tty_flush_buffer;
+ driver.ioctl = ircomm_tty_ioctl;
+ driver.throttle = ircomm_tty_throttle;
+ driver.unthrottle = ircomm_tty_unthrottle;
+ driver.send_xchar = ircomm_tty_send_xchar;
+ driver.set_termios = ircomm_tty_set_termios;
+ driver.stop = ircomm_tty_stop;
+ driver.start = ircomm_tty_start;
+ driver.hangup = ircomm_tty_hangup;
+ driver.wait_until_sent = ircomm_tty_wait_until_sent;
+ driver.read_proc = ircomm_tty_read_proc;
+
+ if (tty_register_driver(&driver)) {
+ ERROR(__FUNCTION__ "Couldn't register serial driver\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Function ircomm_tty_cleanup ()
+ *
+ * Remove IrCOMM TTY layer/driver
+ *
+ */
+void ircomm_tty_cleanup(void)
+{
+ int ret;
+
+ DEBUG(4, __FUNCTION__"()\n");
+
+ ret = tty_unregister_driver(&driver);
+ if (ret) {
+ ERROR(__FUNCTION__ "(), failed to unregister driver\n");
+ return;
+ }
+
+ hashbin_delete(ircomm_tty, (FREE_FUNC) kfree);
+}
+
+/*
+ * Function ircomm_startup (self)
+ *
+ *
+ *
+ */
+static int ircomm_tty_startup(struct ircomm_tty_cb *self)
+{
+ notify_t notify;
+ int ret;
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ /* Already open */
+ if (self->flags & ASYNC_INITIALIZED) {
+ DEBUG(2, __FUNCTION__ "(), already open so break out!\n");
+ return 0;
+ }
+
+ /* Register with IrCOMM */
+ irda_notify_init(&notify);
+ /* These callbacks we must handle ourselves */
+ notify.data_indication = ircomm_tty_data_indication;
+ notify.udata_indication = ircomm_tty_control_indication;
+ notify.flow_indication = ircomm_tty_flow_indication;
+
+ /* Use the ircomm_tty interface for these ones */
+ notify.disconnect_indication = ircomm_tty_disconnect_indication;
+ notify.connect_confirm = ircomm_tty_connect_confirm;
+ notify.connect_indication = ircomm_tty_connect_indication;
+ strncpy(notify.name, "ircomm_tty", NOTIFY_MAX_NAME);
+ notify.instance = self;
+
+ if (!self->ircomm)
+ self->ircomm = ircomm_open(&notify, self->service_type,
+ self->line);
+ if (!self->ircomm)
+ return -ENODEV;
+
+ self->slsap_sel = self->ircomm->slsap_sel;
+
+ /* Connect IrCOMM link with remote device */
+ ret = ircomm_tty_attach_cable(self);
+ if (ret < 0) {
+ ERROR(__FUNCTION__ "(), error attaching cable!\n");
+ return ret;
+ }
+
+ self->flags |= ASYNC_INITIALIZED;
+
+ return 0;
+}
+
+/*
+ * Function ircomm_block_til_ready (self, filp)
+ *
+ *
+ *
+ */
+static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
+ struct file *filp)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int retval;
+ int do_clocal = 0, extra_count = 0;
+ unsigned long flags;
+ struct tty_struct *tty;
+
+ tty = self->tty;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ /* this is a callout device */
+ /* just verify that normal device is not in use */
+ if (self->flags & ASYNC_NORMAL_ACTIVE)
+ return -EBUSY;
+#if 0
+ if ((self->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (self->flags & ASYNC_SESSION_LOCKOUT) &&
+ (self->session != current->session))
+ return -EBUSY;
+#endif
+ if ((self->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (self->flags & ASYNC_PGRP_LOCKOUT) &&
+ (self->pgrp != current->pgrp))
+ return -EBUSY;
+ self->flags |= ASYNC_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){
+ /* nonblock mode is set or port is not enabled */
+ /* just verify that callout device is not active */
+ if (self->flags & ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ self->flags |= ASYNC_NORMAL_ACTIVE;
+
+ DEBUG(1, __FUNCTION__ "(), O_NONBLOCK requested!\n");
+ return 0;
+ }
+
+ if (self->flags & ASYNC_CALLOUT_ACTIVE) {
+ if (self->normal_termios.c_cflag & CLOCAL)
+ DEBUG(1, __FUNCTION__ "(), doing CLOCAL!\n");
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ DEBUG(1, __FUNCTION__ "(), doing CLOCAL!\n");
+ do_clocal = 1;
+ }
+
+ /* Wait for carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, self->open_count is dropped by one, so that
+ * mgsl_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+
+ retval = 0;
+ add_wait_queue(&self->open_wait, &wait);
+
+ DEBUG(2, "%s(%d):block_til_ready before block on %s open_count=%d\n",
+ __FILE__,__LINE__, tty->driver.name, self->open_count );
+
+ save_flags(flags); cli();
+ if (!tty_hung_up_p(filp)) {
+ extra_count = 1;
+ self->open_count--;
+ }
+ restore_flags(flags);
+ self->blocked_open++;
+
+ while (1) {
+ if (!(self->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (tty->termios->c_cflag & CBAUD)) {
+ save_flags(flags); cli();
+ self->session.dte |= IRCOMM_RTS + IRCOMM_DTR;
+
+ ircomm_param_request(self, IRCOMM_DTE, TRUE);
+ restore_flags(flags);
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (tty_hung_up_p(filp) || !(self->flags & ASYNC_INITIALIZED)){
+ retval = (self->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS;
+ break;
+ }
+
+ /*
+ * Check if link is ready now. Even if CLOCAL is
+ * specified, we cannot return before the IrCOMM link is
+ * ready
+ */
+ if (!(self->flags & ASYNC_CALLOUT_ACTIVE) &&
+ !(self->flags & ASYNC_CLOSING) &&
+ (do_clocal || (self->session.dce & IRCOMM_CD)) &&
+ self->state == IRCOMM_TTY_READY)
+ {
+ break;
+ }
+
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+
+ DEBUG(1, "%s(%d):block_til_ready blocking on %s open_count=%d\n",
+ __FILE__,__LINE__, tty->driver.name, self->open_count );
+
+ schedule();
+ }
+
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&self->open_wait, &wait);
+
+ if (extra_count)
+ self->open_count++;
+ self->blocked_open--;
+
+ DEBUG(1, "%s(%d):block_til_ready after blocking on %s open_count=%d\n",
+ __FILE__,__LINE__, tty->driver.name, self->open_count);
+
+ if (!retval)
+ self->flags |= ASYNC_NORMAL_ACTIVE;
+
+ return retval;
+}
+
+/*
+ * Function ircomm_tty_open (tty, filp)
+ *
+ * This routine is called when a particular tty device is opened. This
+ * routine is mandatory; if this routine is not filled in, the attempted
+ * open will fail with ENODEV.
+ */
+static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ struct ircomm_tty_cb *self;
+ int line;
+ int ret;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ MOD_INC_USE_COUNT;
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ if ((line < 0) || (line >= IRCOMM_TTY_PORTS)) {
+ MOD_DEC_USE_COUNT;
+ return -ENODEV;
+ }
+
+ /* Check if instance already exists */
+ self = hashbin_find(ircomm_tty, line, NULL);
+ if (!self) {
+ /* No, so make new instance */
+ self = kmalloc(sizeof(struct ircomm_tty_cb), GFP_KERNEL);
+ if (self == NULL) {
+ ERROR(__FUNCTION__"(), kmalloc failed!\n");
+ MOD_DEC_USE_COUNT;
+ return -ENOMEM;
+ }
+ memset(self, 0, sizeof(struct ircomm_tty_cb));
+
+ self->magic = IRCOMM_TTY_MAGIC;
+ self->line = line;
+ self->tqueue.routine = ircomm_tty_do_softint;
+ self->tqueue.data = self;
+ self->max_header_size = 5;
+ self->max_data_size = 64-self->max_header_size;
+ self->close_delay = 5*HZ/10;
+ self->closing_wait = 30*HZ;
+
+ /* Init some important stuff */
+ init_timer(&self->watchdog_timer);
+ init_waitqueue_head(&self->open_wait);
+ init_waitqueue_head(&self->close_wait);
+
+ /*
+ * Force TTY into raw mode by default which is usually what
+ * we want for IrCOMM and IrLPT. This way applications will
+ * not have to twiddle with printcap etc.
+ */
+ tty->termios->c_iflag = 0;
+ tty->termios->c_oflag = 0;
+
+ /* Insert into hash */
+ hashbin_insert(ircomm_tty, (QUEUE *) self, line, NULL);
+ }
+ self->open_count++;
+
+ tty->driver_data = self;
+ self->tty = tty;
+
+ DEBUG(1, __FUNCTION__"(), %s%d, count = %d\n", tty->driver.name,
+ self->line, self->open_count);
+
+ /* Not really used by us, but lets do it anyway */
+ self->tty->low_latency = (self->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+ /*
+ * If the port is the middle of closing, bail out now
+ */
+ if (tty_hung_up_p(filp) ||
+ (self->flags & ASYNC_CLOSING)) {
+ if (self->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&self->close_wait);
+ /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
+#ifdef SERIAL_DO_RESTART
+ return ((self->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+#else
+ return -EAGAIN;
+#endif
+ }
+
+ /* Check if this is a "normal" ircomm device, or an irlpt device */
+ if (line < 0x10) {
+ self->service_type = IRCOMM_3_WIRE | IRCOMM_9_WIRE;
+ DEBUG(2, __FUNCTION__ "(), IrCOMM device\n");
+ } else {
+ DEBUG(2, __FUNCTION__ "(), IrLPT device\n");
+ self->service_type = IRCOMM_3_WIRE_RAW;
+ }
+
+ ret = ircomm_tty_startup(self);
+ if (ret)
+ return ret;
+
+ ret = ircomm_tty_block_til_ready(self, filp);
+ if (ret) {
+ /* MOD_DEC_USE_COUNT; "info->tty" will cause this? */
+ DEBUG(0, __FUNCTION__
+ "(), returning after block_til_ready with %d\n",
+ ret);
+
+ return ret;
+ }
+#if 0
+ self->session = current->session;
+#endif
+ self->pgrp = current->pgrp;
+
+ return 0;
+}
+
+/*
+ * Function ircomm_tty_close (tty, filp)
+ *
+ * This routine is called when a particular tty device is closed.
+ *
+ */
+static void ircomm_tty_close(struct tty_struct *tty, struct file *filp)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+ unsigned long flags;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ if (!tty)
+ return;
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ save_flags(flags);
+ cli();
+
+ if (tty_hung_up_p(filp)) {
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+
+ DEBUG(2, __FUNCTION__ "(), returning 1\n");
+ return;
+ }
+
+ if (--self->open_count < 0) {
+ ERROR(__FUNCTION__
+ "(), bad serial port count for ttys%d: %d\n",
+ self->line, self->open_count);
+ self->open_count = 0;
+ }
+ if (self->open_count) {
+ MOD_DEC_USE_COUNT;
+ restore_flags(flags);
+
+ DEBUG(2, __FUNCTION__ "(), open count > 0\n");
+ return;
+ }
+ self->flags |= ASYNC_CLOSING;
+
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (self->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, self->closing_wait);
+
+ self->flags &= ~ASYNC_INITIALIZED;
+
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+
+ tty->closing = 0;
+ self->tty = 0;
+
+ if (self->blocked_open) {
+ if (self->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(self->close_delay);
+ }
+ wake_up_interruptible(&self->open_wait);
+ }
+
+ self->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+ ASYNC_CLOSING);
+ wake_up_interruptible(&self->close_wait);
+
+ MOD_DEC_USE_COUNT;
+
+ del_timer(&self->watchdog_timer);
+
+ /* Free parameter buffer */
+ if (self->ctrl_skb) {
+ dev_kfree_skb(self->ctrl_skb);
+ self->ctrl_skb = NULL;
+ }
+
+ /* Free transmit buffer */
+ if (self->tx_skb) {
+ dev_kfree_skb(self->tx_skb);
+ self->tx_skb = NULL;
+ }
+
+ restore_flags(flags);
+
+ ircomm_tty_detach_cable(self);
+ ircomm_close(self->ircomm);
+ self->ircomm = NULL;
+
+#if IRCOMM_TTY_CLOSE_WILL_DEALLOC
+ self->magic = 0;
+
+ hashbin_remove(ircomm_tty, self->line, NULL);
+
+ kfree(self);
+#endif
+}
+
+/*
+ * Function ircomm_tty_flush_buffer (tty)
+ *
+ *
+ *
+ */
+static void ircomm_tty_flush_buffer(struct tty_struct *tty)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ /*
+ * Let do_softint() do this to avoid race condition with
+ * do_softint() ;-)
+ */
+ queue_task(&self->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+/*
+ * Function ircomm_tty_do_softint (private_)
+ *
+ * We use this routine to give the write wakeup to the user at at a
+ * safe time (as fast as possible after write have completed). This
+ * can be compared to the Tx interrupt.
+ */
+static void ircomm_tty_do_softint(void *private_)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) private_;
+ struct tty_struct *tty;
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ if (!self || self->magic != IRCOMM_TTY_MAGIC)
+ return;
+
+ tty = self->tty;
+ if (!tty)
+ return;
+
+ if (tty->hw_stopped)
+ return;
+
+ /* Unlink transmit buffer */
+ save_flags(flags);
+ cli();
+
+ skb = self->tx_skb;
+ self->tx_skb = NULL;
+
+ restore_flags(flags);
+
+ /* Flush transmit buffer if any */
+ if (skb)
+ ircomm_tty_do_event(self, IRCOMM_TTY_DATA_REQUEST, skb, NULL);
+
+ /* Check if user (still) wants to be waken up */
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ {
+ (tty->ldisc.write_wakeup)(tty);
+ }
+ wake_up_interruptible(&tty->write_wait);
+}
+
+/*
+ * Function ircomm_tty_write (tty, from_user, buf, count)
+ *
+ * This routine is called by the kernel to write a series of characters
+ * to the tty device. The characters may come from user space or kernel
+ * space. This routine will return the number of characters actually
+ * accepted for writing. This routine is mandatory.
+ */
+static int ircomm_tty_write(struct tty_struct *tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+ unsigned long flags;
+ struct sk_buff *skb;
+ int tailroom = 0;
+ int len = 0;
+ int size;
+
+ DEBUG(3, __FUNCTION__ "(), count=%d, hw_stopped=%d\n", count,
+ tty->hw_stopped);
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ save_flags(flags);
+ cli();
+
+ /* Fetch current transmit buffer */
+ skb = self->tx_skb;
+
+ /*
+ * Send out all the data we get, possibly as multiple fragmented
+ * frames, but this will only happen if the data is larger than the
+ * max data size. The normal case however is just the opposite, and
+ * this function may be called multiple times, and will then actually
+ * defragment the data and send it out as one packet as soon as
+ * possible, but at a safer point in time
+ */
+ while (count) {
+ size = count;
+
+ /* Adjust data size to the max data size */
+ if (size > self->max_data_size)
+ size = self->max_data_size;
+
+ /*
+ * Do we already have a buffer ready for transmit, or do
+ * we need to allocate a new frame
+ */
+ if (skb) {
+ /*
+ * Any room for more data at the end of the current
+ * transmit buffer? Cannot use skb_tailroom, since
+ * dev_alloc_skb gives us a larger skb than we
+ * requested
+ */
+ if ((tailroom = (self->max_data_size-skb->len)) > 0) {
+ /* Adjust data to tailroom */
+ if (size > tailroom)
+ size = tailroom;
+ } else {
+ /*
+ * Current transmit frame is full, so break
+ * out, so we can send it as soon as possible
+ */
+ break;
+ }
+ } else {
+ /* Prepare a full sized frame */
+ skb = dev_alloc_skb(self->max_data_size+
+ self->max_header_size);
+ if (!skb) {
+ restore_flags(flags);
+ return -ENOBUFS;
+ }
+ skb_reserve(skb, self->max_header_size);
+ self->tx_skb = skb;
+ }
+
+ /* Copy data */
+ if (from_user)
+ copy_from_user(skb_put(skb,size), buf+len, size);
+ else
+ memcpy(skb_put(skb,size), buf+len, size);
+
+ count -= size;
+ len += size;
+ }
+
+ restore_flags(flags);
+
+ /*
+ * Schedule a new thread which will transmit the frame as soon
+ * as possible, but at a safe point in time. We do this so the
+ * "user" can give us data multiple times, as PPP does (because of
+ * its 256 byte tx buffer). We will then defragment and send out
+ * all this data as one single packet.
+ */
+ queue_task(&self->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+
+ return len;
+}
+
+/*
+ * Function ircomm_tty_write_room (tty)
+ *
+ * This routine returns the numbers of characters the tty driver will
+ * accept for queuing to be written. This number is subject to change as
+ * output buffers get emptied, or if the output flow control is acted.
+ */
+static int ircomm_tty_write_room(struct tty_struct *tty)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+ unsigned long flags;
+ int ret;
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ /* Check if we are allowed to transmit any data */
+ if (tty->hw_stopped)
+ ret = 0;
+ else {
+ save_flags(flags);
+ cli();
+ if (self->tx_skb)
+ ret = self->max_data_size - self->tx_skb->len;
+ else
+ ret = self->max_data_size;
+ restore_flags(flags);
+ }
+ DEBUG(2, __FUNCTION__ "(), ret=%d\n", ret);
+
+ return ret;
+}
+
+/*
+ * Function ircomm_tty_wait_until_sent (tty, timeout)
+ *
+ * This routine waits until the device has written out all of the
+ * characters in its transmitter FIFO.
+ */
+static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+ unsigned long orig_jiffies, poll_time;
+
+ DEBUG(0, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ orig_jiffies = jiffies;
+
+ /* Set poll time to 200 ms */
+ poll_time = MIN(timeout, MSECS_TO_JIFFIES(200));
+
+ while (self->tx_skb && self->tx_skb->len) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(poll_time);
+ if (signal_pending(current))
+ break;
+ if (timeout && time_after(jiffies, orig_jiffies + timeout))
+ break;
+ }
+ current->state = TASK_RUNNING;
+}
+
+/*
+ * Function ircomm_tty_throttle (tty)
+ *
+ * This routine notifies the tty driver that input buffers for the line
+ * discipline are close to full, and it should somehow signal that no
+ * more characters should be sent to the tty.
+ */
+static void ircomm_tty_throttle(struct tty_struct *tty)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ /* Software flow control? */
+ if (I_IXOFF(tty))
+ ircomm_tty_send_xchar(tty, STOP_CHAR(tty));
+
+ /* Hardware flow control? */
+ if (tty->termios->c_cflag & CRTSCTS) {
+ self->session.dte &= ~IRCOMM_RTS;
+ self->session.dte |= IRCOMM_DELTA_RTS;
+
+ ircomm_param_request(self, IRCOMM_DTE, TRUE);
+ }
+
+ ircomm_flow_request(self->ircomm, FLOW_STOP);
+}
+
+/*
+ * Function ircomm_tty_unthrottle (tty)
+ *
+ * This routine notifies the tty drivers that it should signals that
+ * characters can now be sent to the tty without fear of overrunning the
+ * input buffers of the line disciplines.
+ */
+static void ircomm_tty_unthrottle(struct tty_struct *tty)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ /* Using software flow control? */
+ if (I_IXOFF(tty)) {
+ ircomm_tty_send_xchar(tty, START_CHAR(tty));
+ }
+
+ /* Using hardware flow control? */
+ if (tty->termios->c_cflag & CRTSCTS) {
+ self->session.dte |= (IRCOMM_RTS|IRCOMM_DELTA_RTS);
+
+ ircomm_param_request(self, IRCOMM_DTE, TRUE);
+ DEBUG(1, __FUNCTION__"(), FLOW_START\n");
+ }
+ ircomm_flow_request(self->ircomm, FLOW_START);
+}
+
+/*
+ * Function ircomm_tty_chars_in_buffer (tty)
+ *
+ * Indicates if there are any data in the buffer
+ *
+ */
+static int ircomm_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+ unsigned long flags;
+ int len = 0;
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ save_flags(flags);
+ cli();
+
+ if (self->tx_skb)
+ len = self->tx_skb->len;
+
+ restore_flags(flags);
+
+ return len;
+}
+
+/*
+ * Function ircomm_tty_hangup (tty)
+ *
+ * This routine notifies the tty driver that it should hangup the tty
+ * device.
+ *
+ */
+static void ircomm_tty_hangup(struct tty_struct *tty)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+ DEBUG(2, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ if (!tty)
+ return;
+
+ /* ircomm_tty_flush_buffer(tty); */
+ ircomm_tty_detach_cable(self);
+ ircomm_close(self->ircomm);
+
+ self->ircomm = NULL;
+
+ self->flags &= ~ASYNC_INITIALIZED;
+
+ self->open_count = 0;
+ self->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+ self->tty = 0;
+
+ wake_up_interruptible(&self->open_wait);
+}
+
+/*
+ * Function ircomm_tty_send_xchar (tty, ch)
+ *
+ * This routine is used to send a high-priority XON/XOFF character to
+ * the device.
+ */
+static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+ DEBUG(0, __FUNCTION__"(), not impl\n");
+}
+
+/*
+ * Function ircomm_tty_start (tty)
+ *
+ * This routine notifies the tty driver that it resume sending
+ * characters to the tty device.
+ */
+void ircomm_tty_start(struct tty_struct *tty)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+ ircomm_flow_request(self->ircomm, FLOW_START);
+}
+
+/*
+ * Function ircomm_tty_stop (tty)
+ *
+ * This routine notifies the tty driver that it should stop outputting
+ * characters to the tty device.
+ */
+void ircomm_tty_stop(struct tty_struct *tty)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ ircomm_flow_request(self->ircomm, FLOW_STOP);
+}
+
+/*
+ * Function ircomm_check_modem_status (self)
+ *
+ * Check for any changes in the DCE's line settings. This function should
+ * be called whenever the dce parameter settings changes, to update the
+ * flow control settings and other things
+ */
+void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
+{
+ struct tty_struct *tty;
+ int status;
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ tty = self->tty;
+
+ status = self->session.dce;
+
+ if (status & IRCOMM_DCE_DELTA_ANY) {
+ /*wake_up_interruptible(&self->delta_msr_wait);*/
+ }
+ if ((self->flags & ASYNC_CHECK_CD) && (status & IRCOMM_DELTA_CD)) {
+ DEBUG(2, __FUNCTION__ "(), ttys%d CD now %s...\n", self->line,
+ (status & IRCOMM_CD) ? "on" : "off");
+
+ if (status & IRCOMM_CD) {
+ wake_up_interruptible(&self->open_wait);
+ } else if (!((self->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (self->flags & ASYNC_CALLOUT_NOHUP)))
+ {
+ DEBUG(2, __FUNCTION__ "(), Doing serial hangup..\n");
+
+ if (tty)
+ tty_hangup(tty);
+ }
+ }
+ if (self->flags & ASYNC_CTS_FLOW) {
+ if (tty->hw_stopped) {
+ if (status & IRCOMM_CTS) {
+ DEBUG(2, __FUNCTION__ "(), CTS tx start...\n");
+
+ tty->hw_stopped = 0;
+
+ queue_task(&self->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ return;
+ }
+ } else {
+ if (!(status & IRCOMM_CTS)) {
+ DEBUG(2, __FUNCTION__ "(), CTS tx stop...\n");
+
+ tty->hw_stopped = 1;
+ }
+ }
+ }
+}
+
+/*
+ * Function ircomm_tty_data_indication (instance, sap, skb)
+ *
+ * Handle incomming data, and deliver it to the line discipline
+ *
+ */
+static int ircomm_tty_data_indication(void *instance, void *sap,
+ struct sk_buff *skb)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+
+ DEBUG(2, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+ ASSERT(skb != NULL, return -1;);
+
+ if (!self->tty)
+ return 0;
+
+ /*
+ * Just give it over to the line discipline. There is no need to
+ * involve the flip buffers, since we are not running in an interrupt
+ * handler
+ */
+ self->tty->ldisc.receive_buf(self->tty, skb->data, NULL, skb->len);
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+/*
+ * Function ircomm_tty_control_indication (instance, sap, skb)
+ *
+ * Parse all incomming parameters (easy!)
+ *
+ */
+static int ircomm_tty_control_indication(void *instance, void *sap,
+ struct sk_buff *skb)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+ int clen;
+
+ DEBUG(4, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+ ASSERT(skb != NULL, return -1;);
+
+ clen = skb->data[0];
+
+ irda_param_extract_all(self, skb->data+1, MIN(skb->len-1, clen),
+ &ircomm_param_info);
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+/*
+ * Function ircomm_tty_flow_indication (instance, sap, cmd)
+ *
+ * This function is called by IrTTP when it wants us to slow down the
+ * transmission of data. We just mark the hardware as stopped, and wait
+ * for IrTTP to notify us that things are OK again.
+ */
+static void ircomm_tty_flow_indication(void *instance, void *sap,
+ LOCAL_FLOW cmd)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
+ struct tty_struct *tty;
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ tty = self->tty;
+
+ switch (cmd) {
+ case FLOW_START:
+ DEBUG(2, __FUNCTION__ "(), hw start!\n");
+ tty->hw_stopped = 0;
+
+ /* ircomm_tty_do_softint will take care of the rest */
+ queue_task(&self->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ break;
+ default:
+ /* If we get here, something is very wrong, better stop */
+ case FLOW_STOP:
+ DEBUG(2, __FUNCTION__ "(), hw stopped!\n");
+ tty->hw_stopped = 1;
+ break;
+ }
+}
+
+static int ircomm_tty_line_info(struct ircomm_tty_cb *self, char *buf)
+{
+ int ret=0;
+
+ ret += sprintf(buf+ret, "State: %s\n", ircomm_tty_state[self->state]);
+
+ ret += sprintf(buf+ret, "Service type: ");
+ if (self->service_type & IRCOMM_9_WIRE)
+ ret += sprintf(buf+ret, "9_WIRE");
+ else if (self->service_type & IRCOMM_3_WIRE)
+ ret += sprintf(buf+ret, "3_WIRE");
+ else if (self->service_type & IRCOMM_3_WIRE_RAW)
+ ret += sprintf(buf+ret, "3_WIRE_RAW");
+ else
+ ret += sprintf(buf+ret, "No common service type!\n");
+ ret += sprintf(buf+ret, "\n");
+
+ ret += sprintf(buf+ret, "Port name: %s\n", self->session.port_name);
+
+ ret += sprintf(buf+ret, "DTE status: ");
+ if (self->session.dte & IRCOMM_RTS)
+ ret += sprintf(buf+ret, "RTS|");
+ if (self->session.dte & IRCOMM_DTR)
+ ret += sprintf(buf+ret, "DTR|");
+ if (self->session.dte)
+ ret--; /* remove the last | */
+ ret += sprintf(buf+ret, "\n");
+
+ ret += sprintf(buf+ret, "DCE status: ");
+ if (self->session.dce & IRCOMM_CTS)
+ ret += sprintf(buf+ret, "CTS|");
+ if (self->session.dce & IRCOMM_DSR)
+ ret += sprintf(buf+ret, "DSR|");
+ if (self->session.dce & IRCOMM_CD)
+ ret += sprintf(buf+ret, "CD|");
+ if (self->session.dce & IRCOMM_RI)
+ ret += sprintf(buf+ret, "RI|");
+ if (self->session.dce)
+ ret--; /* remove the last | */
+ ret += sprintf(buf+ret, "\n");
+
+ ret += sprintf(buf+ret, "Configuration: ");
+ if (!self->session.null_modem)
+ ret += sprintf(buf+ret, "DTE <-> DCE\n");
+ else
+ ret += sprintf(buf+ret,
+ "DTE <-> DTE (null modem emulation)\n");
+
+ ret += sprintf(buf+ret, "Data rate: %d\n", self->session.data_rate);
+
+ ret += sprintf(buf+ret, "Flow control: ");
+ if (self->session.flow_control & IRCOMM_XON_XOFF_IN)
+ ret += sprintf(buf+ret, "XON_XOFF_IN|");
+ if (self->session.flow_control & IRCOMM_XON_XOFF_OUT)
+ ret += sprintf(buf+ret, "XON_XOFF_OUT|");
+ if (self->session.flow_control & IRCOMM_RTS_CTS_IN)
+ ret += sprintf(buf+ret, "RTS_CTS_IN|");
+ if (self->session.flow_control & IRCOMM_RTS_CTS_OUT)
+ ret += sprintf(buf+ret, "RTS_CTS_OUT|");
+ if (self->session.flow_control & IRCOMM_DSR_DTR_IN)
+ ret += sprintf(buf+ret, "DSR_DTR_IN|");
+ if (self->session.flow_control & IRCOMM_DSR_DTR_OUT)
+ ret += sprintf(buf+ret, "DSR_DTR_OUT|");
+ if (self->session.flow_control & IRCOMM_ENQ_ACK_IN)
+ ret += sprintf(buf+ret, "ENQ_ACK_IN|");
+ if (self->session.flow_control & IRCOMM_ENQ_ACK_OUT)
+ ret += sprintf(buf+ret, "ENQ_ACK_OUT|");
+ if (self->session.flow_control)
+ ret--; /* remove the last | */
+ ret += sprintf(buf+ret, "\n");
+
+ ret += sprintf(buf+ret, "Flags: ");
+ if (self->flags & ASYNC_CTS_FLOW)
+ ret += sprintf(buf+ret, "ASYNC_CTS_FLOW|");
+ if (self->flags & ASYNC_CHECK_CD)
+ ret += sprintf(buf+ret, "ASYNC_CHECK_CD|");
+ if (self->flags & ASYNC_INITIALIZED)
+ ret += sprintf(buf+ret, "ASYNC_INITIALIZED|");
+ if (self->flags & ASYNC_LOW_LATENCY)
+ ret += sprintf(buf+ret, "ASYNC_LOW_LATENCY|");
+ if (self->flags & ASYNC_CLOSING)
+ ret += sprintf(buf+ret, "ASYNC_CLOSING|");
+ if (self->flags & ASYNC_NORMAL_ACTIVE)
+ ret += sprintf(buf+ret, "ASYNC_NORMAL_ACTIVE|");
+ if (self->flags & ASYNC_CALLOUT_ACTIVE)
+ ret += sprintf(buf+ret, "ASYNC_CALLOUT_ACTIVE|");
+ if (self->flags)
+ ret--; /* remove the last | */
+ ret += sprintf(buf+ret, "\n");
+
+ ret += sprintf(buf+ret, "Open count: %d\n", self->open_count);
+ if (self->tty)
+ ret += sprintf(buf+ret, "Hardware: %s\n",
+ self->tty->hw_stopped ? "Stopped" : "Running");
+
+ ret += sprintf(buf+ret, "\n");
+ return ret;
+}
+
+
+/*
+ * Function ircomm_tty_read_proc (buf, start, offset, len, eof, unused)
+ *
+ *
+ *
+ */
+static int ircomm_tty_read_proc(char *buf, char **start, off_t offset, int len,
+ int *eof, void *unused)
+{
+ struct ircomm_tty_cb *self;
+ int count = 0, l;
+ off_t begin = 0;
+
+ self = (struct ircomm_tty_cb *) hashbin_get_first(ircomm_tty);
+ while ((self != NULL) && (count < 4000)) {
+ if (self->magic != IRCOMM_TTY_MAGIC)
+ return 0;
+
+ l = ircomm_tty_line_info(self, buf + count);
+ count += l;
+ if (count+begin > offset+len)
+ goto done;
+ if (count+begin < offset) {
+ begin += count;
+ count = 0;
+ }
+
+ self = (struct ircomm_tty_cb *) hashbin_get_next(ircomm_tty);
+ }
+ *eof = 1;
+done:
+ if (offset >= count+begin)
+ return 0;
+ *start = buf + (begin-offset);
+ return ((len < begin+count-offset) ? len : begin+count-offset);
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ return ircomm_tty_init();
+}
+
+void cleanup_module(void)
+{
+ ircomm_tty_cleanup();
+}
+
+#endif /* MODULE */
+
+
+
+
diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c
new file mode 100644
index 000000000..f2466d68c
--- /dev/null
+++ b/net/irda/ircomm/ircomm_tty_attach.c
@@ -0,0 +1,857 @@
+/*********************************************************************
+ *
+ * Filename: ircomm_tty_attach.c
+ * Version:
+ * Description:
+ * Status: Experimental.
+ * Author: Dag Brattli <dagb@cs.uit.no>
+ * Created at: Sat Jun 5 17:42:00 1999
+ * Modified at: Wed Sep 8 11:54:27 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ ********************************************************************/
+
+#include <linux/sched.h>
+#include <linux/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/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(discovery_t *discovery);
+static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
+ struct ias_value *value, void *priv);
+void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self, int timeout);
+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);
+
+char *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 *** ",
+};
+
+char *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 ****",
+};
+
+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)
+{
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ /* Check if somebody has already connected to us */
+ if (ircomm_is_connected(self->ircomm)) {
+ DEBUG(0, __FUNCTION__ "(), already connected!\n");
+ return 0;
+ }
+
+ /* Make sure nobody tries to write before the link is up */
+ self->tty->hw_stopped = 1;
+
+ ircomm_tty_ias_register(self);
+
+ /* Check if somebody has already connected to us */
+ if (ircomm_is_connected(self->ircomm)) {
+ DEBUG(0, __FUNCTION__ "(), already connected!\n");
+ return 0;
+ }
+
+ 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)
+{
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ /* Remove IrCOMM hint bits */
+ irlmp_unregister_client(self->ckey);
+ irlmp_unregister_service(self->skey);
+
+ /* 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->session, 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;
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ if (self->service_type & IRCOMM_3_WIRE_RAW) {
+ hints = irlmp_service_to_hint(S_PRINTER);
+ hints |= irlmp_service_to_hint(S_COMM);
+
+ /* 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);
+ irias_insert_object(self->obj);
+ } else {
+ hints = irlmp_service_to_hint(S_COMM);
+ hints |= irlmp_service_to_hint(S_TELEPHONY);
+
+ /* 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);
+
+ /* 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);
+ irias_insert_object(self->obj);
+ }
+ self->skey = irlmp_register_service(hints);
+ self->ckey = irlmp_register_client(
+ hints, ircomm_tty_discovery_indication, NULL);
+}
+
+/*
+ * Function ircomm_send_initial_parameters (self)
+ *
+ * Send initial parameters to the remote IrCOMM device. These parameters
+ * must be sent before any data.
+ */
+static int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self)
+{
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ if (self->service_type & IRCOMM_3_WIRE_RAW)
+ return 0;
+
+ /* Set default values */
+ self->session.data_rate = 9600;
+ self->session.data_format = IRCOMM_WSIZE_8; /* 8N1 */
+ self->session.flow_control = 0; /* None */
+
+ /* Do not set delta values for the initial parameters */
+ self->session.dte = (IRCOMM_DTR| IRCOMM_RTS);
+
+ 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->session.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(discovery_t *discovery)
+{
+ struct ircomm_tty_cb *self;
+ struct ircomm_tty_info info;
+
+ DEBUG(2, __FUNCTION__"()\n");
+
+ info.daddr = discovery->daddr;
+ info.saddr = discovery->saddr;
+
+ self = (struct ircomm_tty_cb *) hashbin_get_first(ircomm_tty);
+ while (self != NULL) {
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION,
+ NULL, &info);
+
+ self = (struct ircomm_tty_cb *) hashbin_get_next(ircomm_tty);
+ }
+}
+
+/*
+ * 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;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ if (!self->tty)
+ return;
+
+ 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;
+
+ DEBUG(2, __FUNCTION__"()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ /* Check if request succeeded */
+ if (result != IAS_SUCCESS) {
+ DEBUG(4, __FUNCTION__ "(), got NULL value!\n");
+ return;
+ }
+
+ switch (value->type) {
+ case IAS_OCT_SEQ:
+ DEBUG(2, __FUNCTION__"(), got octet sequence\n");
+
+ 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 */
+ DEBUG(2, __FUNCTION__"(), got lsapsel = %d\n",
+ value->t.integer);
+
+ if (value->t.integer == -1) {
+ DEBUG(0, __FUNCTION__"(), invalid value!\n");
+ } else
+ self->dlsap_sel = value->t.integer;
+
+ ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL);
+ break;
+ case IAS_MISSING:
+ DEBUG(0, __FUNCTION__"(), got IAS_MISSING\n");
+ break;
+ default:
+ DEBUG(0, __FUNCTION__"(), got unknown type!\n");
+ break;
+ }
+}
+
+/*
+ * 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;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ self->max_data_size = max_data_size;
+ self->max_header_size = max_header_size;
+
+ ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL);
+}
+
+/*
+ * 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;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ self->max_data_size = max_data_size;
+ self->max_header_size = max_header_size;
+
+ clen = skb->data[0];
+ if (clen)
+ irda_param_extract_all(self, skb->data+1, MIN(skb->len, clen),
+ &ircomm_param_info);
+
+ ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL);
+}
+
+/*
+ * Function ircomm_tty_link_established (self)
+ *
+ * Called when the IrCOMM link is established
+ *
+ */
+void ircomm_tty_link_established(struct ircomm_tty_cb *self)
+{
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ 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
+ * the client will have to wait for the CD to be set.
+ */
+ if (!(self->flags & ASYNC_CTS_FLOW)) {
+ DEBUG(2, __FUNCTION__ "(), starting hardware!\n");
+ if (!self->tty)
+ return;
+ self->tty->hw_stopped = 0;
+ }
+ /* Wake up processes blocked on open */
+ wake_up_interruptible(&self->open_wait);
+
+ /*
+ * Wake up processes blocked on write, or waiting for a write
+ * wakeup notification
+ */
+ queue_task(&self->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+/*
+ * Function irlan_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
+ */
+void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self, int timeout)
+{
+ 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.
+ *
+ */
+void ircomm_tty_watchdog_timer_expired(void *data)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data;
+
+ DEBUG(4, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
+
+ ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL);
+}
+
+/*
+ * 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;
+
+ DEBUG(2, __FUNCTION__": state=%s, event=%s\n",
+ 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;
+
+ iriap_getvaluebyclass_request("IrDA:IrCOMM", "Parameters",
+ self->saddr, self->daddr,
+ ircomm_tty_getvalue_confirm,
+ self);
+
+ 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);
+
+ /* Init connection */
+ ircomm_tty_send_initial_parameters(self);
+ ircomm_tty_link_established(self);
+ 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:
+ DEBUG(2, __FUNCTION__"(), unknown event: %s\n",
+ ircomm_tty_event[event]);
+ return -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;
+
+ DEBUG(2, __FUNCTION__": state=%s, event=%s\n",
+ 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->service_type == IRCOMM_3_WIRE_RAW) {
+ iriap_getvaluebyclass_request("IrLPT",
+ "IrDA:IrLMP:LsapSel",
+ self->saddr, self->daddr,
+ ircomm_tty_getvalue_confirm,
+ self);
+ ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
+ } else {
+ iriap_getvaluebyclass_request("IrDA:IrCOMM",
+ "Parameters",
+ self->saddr, self->daddr,
+ ircomm_tty_getvalue_confirm,
+ self);
+ 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);
+
+ /* Accept connection */
+ ircomm_connect_response(self->ircomm, NULL);
+ ircomm_tty_next_state(self, IRCOMM_TTY_READY);
+
+ /* Init connection */
+ ircomm_tty_send_initial_parameters(self);
+ ircomm_tty_link_established(self);
+ break;
+ case IRCOMM_TTY_WD_TIMER_EXPIRED:
+ /* Try to discover any remote devices */
+ ircomm_tty_start_watchdog_timer(self, 3*HZ);
+ irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
+ break;
+ case IRCOMM_TTY_DETACH_CABLE:
+ ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
+ break;
+ default:
+ DEBUG(2, __FUNCTION__"(), unknown event: %s\n",
+ ircomm_tty_event[event]);
+ return -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;
+
+ DEBUG(2, __FUNCTION__": state=%s, event=%s\n",
+ ircomm_tty_state[self->state], ircomm_tty_event[event]);
+
+ switch (event) {
+ case IRCOMM_TTY_GOT_PARAMETERS:
+ iriap_getvaluebyclass_request("IrDA:IrCOMM",
+ "IrDA:TinyTP:LsapSel",
+ self->saddr, self->daddr,
+ ircomm_tty_getvalue_confirm,
+ self);
+
+ 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);
+
+ /* Accept connection */
+ ircomm_connect_response(self->ircomm, NULL);
+ ircomm_tty_next_state(self, IRCOMM_TTY_READY);
+
+ /* Init connection */
+ ircomm_tty_send_initial_parameters(self);
+ ircomm_tty_link_established(self);
+ break;
+ case IRCOMM_TTY_DETACH_CABLE:
+ ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
+ break;
+ default:
+ DEBUG(2, __FUNCTION__"(), unknown event: %s\n",
+ ircomm_tty_event[event]);
+ return -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;
+
+ DEBUG(2, __FUNCTION__": state=%s, event=%s\n",
+ 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);
+
+ /* Accept connection */
+ ircomm_connect_response(self->ircomm, NULL);
+ ircomm_tty_next_state(self, IRCOMM_TTY_READY);
+
+ /* Init connection */
+ ircomm_tty_send_initial_parameters(self);
+ ircomm_tty_link_established(self);
+ break;
+ case IRCOMM_TTY_DETACH_CABLE:
+ ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
+ break;
+ default:
+ DEBUG(2, __FUNCTION__"(), unknown event: %s\n",
+ ircomm_tty_event[event]);
+ return -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;
+
+ DEBUG(2, __FUNCTION__": state=%s, event=%s\n",
+ ircomm_tty_state[self->state], ircomm_tty_event[event]);
+
+ switch (event) {
+ case IRCOMM_TTY_CONNECT_CONFIRM:
+ del_timer(&self->watchdog_timer);
+ ircomm_tty_next_state(self, IRCOMM_TTY_READY);
+
+ /*
+ * 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);
+ 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);
+
+ /* Init connection */
+ ircomm_tty_send_initial_parameters(self);
+ ircomm_tty_link_established(self);
+ 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:
+ DEBUG(2, __FUNCTION__"(), unknown event: %s\n",
+ ircomm_tty_event[event]);
+ return -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_next_state(self, IRCOMM_TTY_SEARCH);
+ ircomm_tty_start_watchdog_timer(self, 3*HZ);
+
+ /* Drop carrier */
+ self->session.dce = IRCOMM_DELTA_CD;
+ ircomm_tty_check_modem_status(self);
+ break;
+ default:
+ DEBUG(2, __FUNCTION__"(), unknown event: %s\n",
+ ircomm_tty_event[event]);
+ return -EINVAL;
+ }
+ return ret;
+}
+
+/*
+ * 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)
+{
+ DEBUG(2, __FUNCTION__": state=%s, event=%s\n",
+ 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
+ *
+ */
+void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state)
+{
+ self->state = state;
+
+ DEBUG(2, __FUNCTION__": next state=%s, service type=%d\n",
+ ircomm_tty_state[self->state], self->service_type);
+}
+
diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c
new file mode 100644
index 000000000..fdbe2b1f2
--- /dev/null
+++ b/net/irda/ircomm/ircomm_tty_ioctl.c
@@ -0,0 +1,456 @@
+/*********************************************************************
+ *
+ * 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: Tue Aug 31 10:29:36 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/init.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+
+#include <asm/segment.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
+ */
+void ircomm_tty_change_speed(struct ircomm_tty_cb *self)
+{
+ unsigned cflag, cval;
+ int baud;
+
+ 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->session.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->session.flow_control |= IRCOMM_RTS_CTS_IN;
+ } else
+ self->flags &= ~ASYNC_CTS_FLOW;
+
+ 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->session.data_format = cval;
+
+ ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
+ ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
+}
+
+/*
+ * Function ircomm_tty_set_termios (tty, old_termios)
+ *
+ * This routine allows the tty driver to be notified when device's
+ * termios settings have changed. Note that a well-designed tty driver
+ * should be prepared to accept the case where old == NULL, and try to
+ * do something rational.
+ */
+void ircomm_tty_set_termios(struct tty_struct *tty,
+ struct termios *old_termios)
+{
+ struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
+ unsigned int cflag = tty->termios->c_cflag;
+ unsigned long flags;
+
+ 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->session.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->session.dte |= IRCOMM_DTR;
+ if (!(tty->termios->c_cflag & CRTSCTS) ||
+ !test_bit(TTY_THROTTLED, &tty->flags)) {
+ self->session.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_get_modem_info (self, value)
+ *
+ *
+ *
+ */
+static int ircomm_tty_get_modem_info(struct ircomm_tty_cb *self,
+ unsigned int *value)
+{
+ unsigned int result;
+
+ DEBUG(1, __FUNCTION__ "()\n");
+
+ result = ((self->session.dte & IRCOMM_RTS) ? TIOCM_RTS : 0)
+ | ((self->session.dte & IRCOMM_DTR) ? TIOCM_DTR : 0)
+ | ((self->session.dce & IRCOMM_CD) ? TIOCM_CAR : 0)
+ | ((self->session.dce & IRCOMM_RI) ? TIOCM_RNG : 0)
+ | ((self->session.dce & IRCOMM_DSR) ? TIOCM_DSR : 0)
+ | ((self->session.dce & IRCOMM_CTS) ? TIOCM_CTS : 0);
+
+ return put_user(result, value);
+}
+
+/*
+ * Function set_modem_info (driver, cmd, value)
+ *
+ *
+ *
+ */
+static int ircomm_tty_set_modem_info(struct ircomm_tty_cb *self,
+ unsigned int cmd, unsigned int *value)
+{
+ unsigned int arg;
+ __u8 old_rts, old_dtr;
+ int error;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
+
+ error = get_user(arg, value);
+ if (error)
+ return error;
+
+ old_rts = self->session.dte & IRCOMM_RTS;
+ old_dtr = self->session.dte & IRCOMM_DTR;
+
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS)
+ self->session.dte |= IRCOMM_RTS;
+ if (arg & TIOCM_DTR)
+ self->session.dte |= IRCOMM_DTR;
+ break;
+
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS)
+ self->session.dte &= ~IRCOMM_RTS;
+ if (arg & TIOCM_DTR)
+ self->session.dte &= ~IRCOMM_DTR;
+ break;
+
+ case TIOCMSET:
+ self->session.dte =
+ ((self->session.dte & ~(IRCOMM_RTS | IRCOMM_DTR))
+ | ((arg & TIOCM_RTS) ? IRCOMM_RTS : 0)
+ | ((arg & TIOCM_DTR) ? IRCOMM_DTR : 0));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if ((self->session.dte & IRCOMM_RTS) != old_rts)
+ self->session.dte |= IRCOMM_DELTA_RTS;
+
+ if ((self->session.dte & IRCOMM_DTR) != old_dtr)
+ self->session.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 *retinfo)
+{
+ struct serial_struct info;
+
+ if (!retinfo)
+ return -EFAULT;
+
+ DEBUG(1, __FUNCTION__ "()\n");
+
+ memset(&info, 0, sizeof(info));
+ info.line = self->line;
+ info.flags = self->flags;
+ info.baud_base = self->session.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 *tty,
+ struct serial_struct *new_info)
+{
+ struct serial_struct new_serial;
+ struct ircomm_tty_cb old_driver;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+#if 0
+
+ if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+ return -EFAULT;
+
+ old_driver = *driver;
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ if ((new_serial.baud_base != driver->comm->data_rate) ||
+ (new_serial.close_delay != driver->close_delay) ||
+ ((new_serial.flags & ~ASYNC_USR_MASK) !=
+ (driver->flags & ~ASYNC_USR_MASK)))
+ return -EPERM;
+ driver->flags = ((driver->flags & ~ASYNC_USR_MASK) |
+ (new_serial.flags & ASYNC_USR_MASK));
+ driver->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->session.data_rate != new_serial.baud_base) {
+ self->session.data_rate.data_rate = new_serial.baud_base;
+ if (driver->comm->state == IRCOMM_CONN)
+ ircomm_control_request(driver->comm, DATA_RATE);
+ }
+
+ driver->close_delay = new_serial.close_delay * HZ/100;
+ driver->closing_wait = new_serial.closing_wait * HZ/100;
+ driver->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_driver.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;
+
+ 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 TIOCMGET:
+ ret = ircomm_tty_get_modem_info(self, (unsigned int *) arg);
+ break;
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ ret = ircomm_tty_set_modem_info(self, cmd, (unsigned int *) arg);
+ break;
+ case TIOCGSERIAL:
+ ret = ircomm_tty_get_serial_info(self, (struct serial_struct *) arg);
+ break;
+ case TIOCSSERIAL:
+ ret = ircomm_tty_set_serial_info(self, (struct serial_struct *) arg);
+ break;
+ case TIOCMIWAIT:
+ DEBUG(0, "(), TIOCMIWAIT, not impl!\n");
+ break;
+
+ case TIOCGICOUNT:
+ DEBUG(0, __FUNCTION__ "(), TIOCGICOUNT not impl!\n");
+#if 0
+ save_flags(flags); cli();
+ cnow = driver->icount;
+ restore_flags(flags);
+ p_cuser = (struct serial_icounter_struct *) arg;
+ error = put_user(cnow.cts, &p_cuser->cts);
+ if (error) return error;
+ error = put_user(cnow.dsr, &p_cuser->dsr);
+ if (error) return error;
+ error = put_user(cnow.rng, &p_cuser->rng);
+ if (error) return error;
+ error = put_user(cnow.dcd, &p_cuser->dcd);
+ if (error) return error;
+ error = put_user(cnow.rx, &p_cuser->rx);
+ if (error) return error;
+ error = put_user(cnow.tx, &p_cuser->tx);
+ if (error) return error;
+ error = put_user(cnow.frame, &p_cuser->frame);
+ if (error) return error;
+ error = put_user(cnow.overrun, &p_cuser->overrun);
+ if (error) return error;
+ error = put_user(cnow.parity, &p_cuser->parity);
+ if (error) return error;
+ error = put_user(cnow.brk, &p_cuser->brk);
+ if (error) return error;
+ error = put_user(cnow.buf_overrun, &p_cuser->buf_overrun);
+ if (error) return error;
+#endif
+ return 0;
+ default:
+ ret = -ENOIOCTLCMD; /* ioctls which we must ignore */
+ }
+ return ret;
+}
+
+
+
diff --git a/net/irda/ircomm/irvtd_driver.c b/net/irda/ircomm/irvtd_driver.c
deleted file mode 100644
index 7b1ddf3cb..000000000
--- a/net/irda/ircomm/irvtd_driver.c
+++ /dev/null
@@ -1,2071 +0,0 @@
-/*********************************************************************
- *
- * Filename: irvtd_driver.c
- * Version:
- * Description: Virtual tty driver (the "port emulation entity" of IrCOMM)
- * Status: Experimental.
- * Author: Takahide Higuchi <thiguchi@pluto.dti.ne.jp>
- * Source: serial.c by Linus Torvalds
- * isdn_tty.c by Fritz Elfert
- *
- * Copyright (c) 1998-1999, Takahide Higuchi, <thiguchi@pluto.dti.ne.jp>,
- * 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, Takahide Higuchi, provide no warranty for any of this software.
- * This material is provided "AS-IS" and at no charge.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/init.h>
-
-#include <linux/fs.h>
-#include <linux/sched.h>
-#include <linux/termios.h>
-#include <linux/tty.h>
-#include <asm/segment.h>
-#include <asm/uaccess.h>
-
-#include <net/irda/irda.h>
-#include <net/irda/irttp.h>
-#include <net/irda/irias_object.h>
-
-#include <net/irda/irvtd.h>
-
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
-#define DO_RESTART
-#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
-
-struct tty_driver irvtd_drv;
-struct tty_struct *irvtd_table[COMM_MAX_TTY];
-struct termios *irvtd_termios[COMM_MAX_TTY];
-struct termios *irvtd_termios_locked[COMM_MAX_TTY];
-static int irvtd_refcount;
-struct irvtd_cb **irvtd = NULL;
-
-static char *revision_date = "Wed May 26 00:49:11 1999";
-
-
-/*
- * prototypes
- */
-
-int irvtd_open(struct tty_struct *tty, struct file *filp);
-void irvtd_close(struct tty_struct * tty, struct file * filp);
-int irvtd_write(struct tty_struct * tty, int from_user,
- const unsigned char *buf, int count);
-void irvtd_put_char(struct tty_struct *tty, unsigned char ch);
-int irvtd_write_room(struct tty_struct *tty);
-int irvtd_chars_in_buffer(struct tty_struct *tty);
-int irvtd_ioctl(struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg);
-void irvtd_set_termios(struct tty_struct *tty, struct termios * old);
-void irvtd_throttle(struct tty_struct *tty);
-void irvtd_unthrottle(struct tty_struct *tty);
-void irvtd_stop(struct tty_struct *tty);
-void irvtd_start(struct tty_struct *tty);
-void irvtd_hangup(struct tty_struct *tty);
-void irvtd_flush_buffer(struct tty_struct *tty);
-void irvtd_flush_chars(struct tty_struct *tty);
-
-static void change_speed(struct irvtd_cb *driver);
-static void irvtd_write_to_tty( struct irvtd_cb *);
-static void irvtd_send_data_request( struct irvtd_cb *);
-static void irvtd_break(struct tty_struct *tty, int break_state);
-static void irvtd_send_xchar(struct tty_struct *tty, char ch);
-static void irvtd_wait_until_sent(struct tty_struct *tty, int timeout);
-
-static void irvtd_start_tx_timer( struct irvtd_cb *driver, int timeout);
-static void irvtd_tx_timer_expired(unsigned long data);
-static void irvtd_start_rx_timer( struct irvtd_cb *driver, int timeout);
-static void irvtd_rx_timer_expired(unsigned long data);
-
-static int line_info(char *buf, struct irvtd_cb *driver);
-static int irvtd_read_proc(char *buf, char **start, off_t offset, int len,
- int *eof, void *unused);
-
-
-/*
- **********************************************************************
- *
- * ircomm_receive_data() and friends
- *
- * like interrupt handler in the serial.c,we receive data when
- * ircomm_data_indication comes
- *
- **********************************************************************
- */
-
-
-
-/*
- * irvtd_write_to_tty
- * send incoming/queued data to tty
- */
-
-static void irvtd_write_to_tty( struct irvtd_cb *driver)
-{
- int status, c, flag;
- struct sk_buff *skb;
- struct tty_struct *tty = driver->tty;
-
- if(driver->rx_disable)
- return;
-
- skb = skb_dequeue(&driver->rxbuff);
- if(skb == NULL)
- return; /* there's nothing */
-
- /*
- * we should parse controlchannel field here.
- * (see process_data() in ircomm.c)
- */
- ircomm_parse_tuples(driver->comm, skb, CONTROL_CHANNEL);
-
-#ifdef IRVTD_DEBUG_RX
- printk("received data:");
- {
- int i;
- for ( i=0;i<skb->len;i++)
- printk("%02x ", skb->data[i]);
- printk("\n");
- }
-#endif
-
- status = driver->comm->peer_line_status & driver->read_status_mask;
-
- /*
- * if there are too many errors which make a character ignored,
- * drop characters
- */
-
- if(status & driver->ignore_status_mask){
- DEBUG(0,__FUNCTION__":some error:ignore characters.\n");
- dev_kfree_skb(skb);
- return;
- }
-
- c = MIN(skb->len, (TTY_FLIPBUF_SIZE - tty->flip.count));
- DEBUG(4, __FUNCTION__"skb_len=%d, tty->flip.count=%d \n"
- ,(int)skb->len, tty->flip.count);
-
- if (driver->comm->peer_break_signal ) {
- driver->comm->peer_break_signal = 0;
- DEBUG(0,"handling break....\n");
-
- flag = TTY_BREAK;
- if (driver->flags & ASYNC_SAK)
- /*
- * do_SAK() seems to be an implementation of the
- * idea called "Secure Attention Key",
- * which seems to be discribed in "Orange book".
- * (which is published by U.S.military!!?? ,
- * see source of do_SAK())
- *
- * but what kind of security do we need
- * when we use infrared communication??? :p)
- */
- do_SAK(tty);
- }else if (status & LSR_PE)
- flag = TTY_PARITY;
- else if (status & LSR_FE)
- flag = TTY_FRAME;
- else if (status & LSR_OE)
- flag = TTY_OVERRUN;
- else
- flag = TTY_NORMAL;
-
- if(c){
- DEBUG(4,"writing %d chars to tty\n",c);
- driver->icount.rx += c;
- memset(tty->flip.flag_buf_ptr, flag, c);
- memcpy(tty->flip.char_buf_ptr, skb->data, c);
- tty->flip.flag_buf_ptr += c;
- tty->flip.char_buf_ptr += c;
- tty->flip.count += c;
- skb_pull(skb,c);
- }
-
- if(skb->len == 0)
- dev_kfree_skb(skb);
- else
- {
- /* queue rest of data again */
- DEBUG(4,__FUNCTION__":retrying frame!\n");
-
- /* build a dummy control channel */
- skb_push(skb,1);
- *skb->data = 0; /* clen is 0 */
- skb_queue_head( &driver->rxbuff, skb );
- }
-
- if(c)
- /* let the process read its buffer! */
- tty_flip_buffer_push(tty);
-
- if(skb_queue_len(&driver->rxbuff)< IRVTD_RX_QUEUE_LOW &&
- driver->ttp_stoprx){
- DEBUG(1, __FUNCTION__"():FLOW_START\n");
- /*
- * next 2 lines must follow this order since irttp_flow_request()
- * will run its rx queue
- */
- driver->ttp_stoprx = 0;
- irttp_flow_request(driver->comm->tsap, FLOW_START);
- }
-
- if(skb_queue_empty(&driver->rxbuff) && driver->disconnect_pend){
- /* disconnect */
- driver->disconnect_pend = 0;
- driver->rx_disable = 1;
- tty_hangup(driver->tty);
- }
-}
-
-static int irvtd_receive_data(void *instance, void *sap, struct sk_buff *skb)
-{
- struct irvtd_cb *driver = (struct irvtd_cb *)instance;
-
- ASSERT(driver != NULL, return -1;);
- ASSERT(driver->magic == IRVTD_MAGIC, return -1;);
- DEBUG(4, __FUNCTION__"(): queue frame\n");
-
- /* queue incoming data and make bottom half handler ready */
-
- skb_queue_tail( &driver->rxbuff, skb );
-
- if(skb_queue_len(&driver->rxbuff) > IRVTD_RX_QUEUE_HIGH){
- DEBUG(1, __FUNCTION__"():FLOW_STOP\n");
- irttp_flow_request(driver->comm->tsap, FLOW_STOP);
- driver->ttp_stoprx = 1;
- }
- irvtd_write_to_tty(driver);
-
- if(!skb_queue_empty(&driver->rxbuff))
- irvtd_start_rx_timer(driver,0);
- return 0;
-}
-
-/*
- ***********************************************************************
- *
- * irvtd_send_data() and friends
- *
- * like interrupt handler in the serial.c,we send data when
- * a timer is expired
- *
- ***********************************************************************
- */
-
-
-static void irvtd_start_tx_timer( struct irvtd_cb *driver, int timeout)
-{
- ASSERT( driver != NULL, return;);
- ASSERT( driver->magic == IRVTD_MAGIC, return;);
-
- del_timer( &driver->tx_timer);
-
- driver->tx_timer.data = (unsigned long) driver;
- driver->tx_timer.function = &irvtd_tx_timer_expired;
- driver->tx_timer.expires = jiffies + timeout;
-
- add_timer( &driver->tx_timer);
-}
-
-static void irvtd_start_rx_timer( struct irvtd_cb *driver, int timeout)
-{
- ASSERT( driver != NULL, return;);
- ASSERT( driver->magic == IRVTD_MAGIC, return;);
-
- del_timer( &driver->rx_timer);
-
- driver->rx_timer.data = (unsigned long) driver;
- driver->rx_timer.function = &irvtd_rx_timer_expired;
- driver->rx_timer.expires = jiffies + timeout;
-
- add_timer( &driver->rx_timer);
-}
-
-
-static void irvtd_tx_timer_expired(unsigned long data)
-{
- struct irvtd_cb *driver = (struct irvtd_cb *)data;
-
- ASSERT(driver != NULL,return;);
- ASSERT(driver->magic == IRVTD_MAGIC,return;);
- DEBUG(4, __FUNCTION__"()\n");
-
- irvtd_send_data_request(driver);
-}
-
-static void irvtd_rx_timer_expired(unsigned long data)
-{
- struct irvtd_cb *driver = (struct irvtd_cb *)data;
-
- ASSERT(driver != NULL,return;);
- ASSERT(driver->magic == IRVTD_MAGIC,return;);
- DEBUG(4, __FUNCTION__"()\n");
-
- while(TTY_FLIPBUF_SIZE - driver->tty->flip.count
- && !skb_queue_empty(&driver->rxbuff))
- irvtd_write_to_tty(driver);
-
- DEBUG(1, __FUNCTION__"(): room in flip_buffer = %d\n",
- TTY_FLIPBUF_SIZE - driver->tty->flip.count);
-
- if(!skb_queue_empty(&driver->rxbuff))
- /* handle it later */
- irvtd_start_rx_timer(driver, 1);
-}
-
-
-static void irvtd_send_data_request(struct irvtd_cb *driver)
-{
- int err;
- struct sk_buff *skb = driver->txbuff;
-
- ASSERT(skb != NULL,return;);
- DEBUG(4, __FUNCTION__"()\n");
-
- if(driver->tty->hw_stopped || driver->tx_disable)
- return;
- if(!skb->len)
- return; /* no data to send */
-
-#ifdef IRVTD_DEBUG_TX
- DEBUG(4, "flush_txbuff:count(%d)\n",(int)skb->len);
- {
- int i;
- for ( i=0;i<skb->len;i++)
- printk("%02x", skb->data[i]);
- printk("\n");
- }
-#endif
-
- DEBUG(1, __FUNCTION__"():len = %d, room = %d\n",(int)skb->len,
- skb_tailroom(skb));
- driver->icount.tx += skb->len;
- err = ircomm_data_request(driver->comm, driver->txbuff);
- if (err){
- ASSERT(err == 0,;);
- DEBUG(1,"%d chars are lost\n",(int)skb->len);
- skb_trim(skb, 0);
- }
-
- /* allocate a new frame */
- skb = driver->txbuff
- = dev_alloc_skb(driver->tx_max_sdu_size + driver->max_header_size);
- if (skb == NULL){
- printk(__FUNCTION__"():alloc_skb failed!\n");
- } else {
- skb_reserve(skb, driver->max_header_size);
- }
-
- wake_up_interruptible(&driver->tty->write_wait);
-}
-
-
-/*
- ***********************************************************************
- *
- * indication/confirmation handlers:
- *
- * these routines are handlers for IrCOMM protocol stack
- *
- ***********************************************************************
- */
-
-/*
- * Function irvtd_connect_confirm (instance, sap, qos, max_sdu_size, skb)
- *
- * ircomm_connect_request which we have send, has succeeded!
- *
- */
-void irvtd_connect_confirm(void *instance, void *sap, struct qos_info *qos,
- __u32 max_sdu_size, __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct irvtd_cb *driver = (struct irvtd_cb *)instance;
- ASSERT(driver != NULL, return;);
- ASSERT(driver->magic == IRVTD_MAGIC, return;);
-
-
- driver->tx_max_sdu_size = max_sdu_size;
- driver->max_header_size = max_header_size;
- /*
- * set default value
- */
-
- driver->msr |= (MSR_DCD|MSR_RI|MSR_DSR|MSR_CTS);
-
- /*
- * sending initial control parameters here
- */
- if (driver->comm->servicetype == THREE_WIRE_RAW)
- return; /* do nothing */
-
- driver->comm->dte |= (MCR_DTR | MCR_RTS | DELTA_DTR | DELTA_RTS);
-
- ircomm_control_request(driver->comm, SERVICETYPE);
- ircomm_control_request(driver->comm, DATA_RATE);
- ircomm_control_request(driver->comm, DATA_FORMAT);
- ircomm_control_request(driver->comm, FLOW_CONTROL);
- ircomm_control_request(driver->comm, XON_XOFF_CHAR);
- /* ircomm_control_request(driver->comm, ENQ_ACK_CHAR); */
-
- switch (driver->comm->servicetype) {
- case CENTRONICS:
- break;
-
- case NINE_WIRE:
- ircomm_control_request(driver->comm, DTELINE_STATE);
- break;
- default:
- }
-
- driver->tx_disable = 0;
- wake_up_interruptible(&driver->open_wait);
-}
-
-/*
- * Function irvtd_connect_indication (instance, sap, qos, max_sdu_size, skb)
- *
- * we are discovered and being requested to connect by remote device !
- *
- */
-void irvtd_connect_indication(void *instance, void *sap, struct qos_info *qos,
- __u32 max_sdu_size, __u8 max_header_size,
- struct sk_buff *skb)
-{
- struct irvtd_cb *driver = (struct irvtd_cb *)instance;
- struct ircomm_cb *comm = (struct ircomm_cb *)sap;
-
- ASSERT(driver != NULL, return;);
- ASSERT(driver->magic == IRVTD_MAGIC, return;);
- ASSERT(comm != NULL, return;);
- ASSERT(comm->magic == IRCOMM_MAGIC, return;);
-
- driver->tx_max_sdu_size = max_sdu_size;
- driver->max_header_size = max_header_size;
- DEBUG(4, __FUNCTION__ "():sending connect_response...\n");
-
- ircomm_connect_response(comm, NULL, SAR_DISABLE );
-
- driver->tx_disable = 0;
-
- /*
- * send initial control parameters
- */
- if (driver->comm->servicetype == THREE_WIRE_RAW)
- return; /* do nothing */
-
- driver->comm->dte |= (MCR_DTR | MCR_RTS | DELTA_DTR | DELTA_RTS);
-
- switch(driver->comm->servicetype){
- case NINE_WIRE:
- ircomm_control_request(driver->comm, DTELINE_STATE);
- break;
- default:
- DEBUG(0, __FUNCTION__ "(), not implemented!\n");
- }
-
-
- driver->msr |= (MSR_DCD|MSR_RI|MSR_DSR|MSR_CTS);
- wake_up_interruptible(&driver->open_wait);
-}
-
-/*
- * Function irvtd_disconnect_indication (instance, sap, reason, skb)
- *
- * This is a handler for ircomm_disconnect_indication. since this
- * function is called in the context of interrupt handler,
- * interruptible_sleep_on() MUST not be used.
- */
-void irvtd_disconnect_indication(void *instance, void *sap , LM_REASON reason,
- struct sk_buff *skb)
-{
- struct irvtd_cb *driver = (struct irvtd_cb *)instance;
-
- ASSERT(driver != NULL, return;);
- ASSERT(driver->tty != NULL, return;);
- ASSERT(driver->magic == IRVTD_MAGIC, return;);
-
- DEBUG(4,"irvtd_disconnect_indication:\n");
-
- driver->tx_disable = 1;
- if(skb_queue_empty(&driver->rxbuff)){
- /* disconnect */
- driver->rx_disable = 1;
- tty_hangup(driver->tty);
- } else {
- driver->disconnect_pend = 1;
- }
-}
-
-/*
- * Function irvtd_control_indication (instance, sap, cmd)
- *
- *
- *
- */
-void irvtd_control_indication(void *instance, void *sap, IRCOMM_CMD cmd)
-{
- struct irvtd_cb *driver = (struct irvtd_cb *)instance;
-
- ASSERT(driver != NULL, return;);
- ASSERT(driver->magic == IRVTD_MAGIC, return;);
-
- DEBUG(4,__FUNCTION__"()\n");
-
- if(cmd == TX_READY){
- driver->ttp_stoptx = 0;
- driver->tty->hw_stopped = driver->cts_stoptx;
-
- if(driver->cts_stoptx)
- return;
-
- /* push tx queue so that client can send at least 1 octet */
- irvtd_send_data_request(driver);
- /*
- * driver->tty->write_wait will keep asleep if
- * our txbuff is full.
- * now that we can send packets to IrTTP layer,
- * we kick it here.
- */
- if ((driver->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- driver->tty->ldisc.write_wakeup)
- (driver->tty->ldisc.write_wakeup)(driver->tty);
- return;
- }
-
- if(cmd == TX_BUSY){
- driver->ttp_stoptx = driver->tty->hw_stopped = 1;
- del_timer( &driver->tx_timer);
- return;
- }
-
-
- ASSERT(cmd == CONTROL_CHANNEL,return;);
-
- switch(driver->comm->pi){
-
- case DCELINE_STATE:
- driver->msr = driver->comm->peer_dce;
-
- if(driver->msr & (DELTA_CTS|DELTA_DSR|DELTA_RI|DELTA_DCD)){
- if(driver->msr & DELTA_CTS)
- driver->icount.cts++;
- if(driver->msr & DELTA_DSR)
- driver->icount.dsr++;
- if(driver->msr & DELTA_RI)
- driver->icount.rng++;
- if(driver->msr & DELTA_DCD)
- driver->icount.dcd++;
- wake_up_interruptible(&driver->delta_msr_wait);
- }
-
- if ((driver->flags & ASYNC_CHECK_CD) && (driver->msr & DELTA_DCD)) {
-
- DEBUG(0,"CD now %s...\n",
- (driver->msr & MSR_DCD) ? "on" : "off");
-
- if (driver->msr & MSR_DCD)
- {
- /* DCD raised! */
- wake_up_interruptible(&driver->open_wait);
- }
- else
- {
- /* DCD falled */
- DEBUG(0,__FUNCTION__"():hangup..\n");
- tty_hangup(driver->tty);
- }
-
- }
-
- if (driver->comm->flow_ctrl & USE_CTS) {
- if (driver->tty->hw_stopped) {
- if (driver->msr & MSR_CTS) {
- DEBUG(0,"CTS tx start...\n");
-
- driver->cts_stoptx = 0;
- driver->tty->hw_stopped = driver->ttp_stoptx;
- /*
- * replacement of
- * rs_sched_event(info, RS_EVENT_WRITE_WAKEUP)
- * in serial.c
- */
-
- if ((driver->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- driver->tty->ldisc.write_wakeup)
- (driver->tty->ldisc.write_wakeup)(driver->tty);
-
- wake_up_interruptible(&driver->tty->write_wait);
- return;
- }
- } else {
- if (!(driver->msr & MSR_CTS)) {
- DEBUG(0,"CTS tx stop...");
-
- driver->cts_stoptx = 1;
- driver->tty->hw_stopped = 1;
- }
- }
- }
- break;
-
- case FLOW_CONTROL:
- case DATA_RATE:
- case XON_XOFF_CHAR:
- case DTELINE_STATE:
- case ENQ_ACK_CHAR: /* got this from win95 */
- /* (maybe) nothing to do */
- break;
- default:
- DEBUG(0,__FUNCTION__"():PI = 0x%02x is not implemented\n",
- (int)driver->comm->pi);
- }
-}
-
-/*
- ***********************************************************************
- *
- * driver kernel interfaces
- * these functions work as an interface between the kernel and this driver
- *
- ***********************************************************************
- */
-
-
-
-/*
- * ----------------------------------------------------------------------
- * irvtd_open() and friends
- *
- * ----------------------------------------------------------------------
- */
-
-
-static int irvtd_block_til_ready(struct tty_struct *tty, struct file * filp,
- struct irvtd_cb *driver)
-{
-
- DECLARE_WAITQUEUE(wait,current);
- int retval = 0;
- int do_clocal = 0;
-
- /*
- * If non-blocking mode is set, or the port is not enabled,
- * then make the check up front and then exit.
- */
-
- if ((filp->f_flags & O_NONBLOCK) ||
- (tty->flags & (1 << TTY_IO_ERROR))) {
-
- return 0;
- }
-
- if (tty->termios->c_cflag & CLOCAL)
- do_clocal = 1;
-
-
- /*
- * We wait until ircomm_connect_request() succeed or
- * ircomm_connect_indication comes
- */
-
-
- add_wait_queue(&driver->open_wait, &wait);
-
- DEBUG(0,__FUNCTION__"():before block( line = %d, count = %d )\n",
- driver->line, driver->count);
-
- driver->blocked_open++;
-
- /* wait for a connection established */
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
-
- if (tty_hung_up_p(filp) ||
- !(driver->flags & ASYNC_INITIALIZED)) {
-#ifdef DO_RESTART
- if (driver->flags & ASYNC_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTSYS;
-#else
- retval = -EAGAIN;
-#endif
- break;
- }
-
- if (!(driver->flags & ASYNC_CLOSING) &&
- (driver->comm->state == COMM_CONN) &&
- ( do_clocal || (driver->msr & MSR_DCD)))
- break;
-
- if(signal_pending(current)){
- retval = -ERESTARTSYS;
- break;
- }
-
-
- DEBUG(4,__FUNCTION__"():blocking( %s%d, count = %d )\n",
- tty->driver.name, driver->line, driver->count);
-
- schedule();
- }
-
- current->state = TASK_RUNNING;
- remove_wait_queue(&driver->open_wait, &wait);
-
- driver->blocked_open--;
-
- DEBUG(1, __FUNCTION__"():after blocking\n");
-
- if (retval)
- return retval;
- return 0;
-}
-
-static void change_speed(struct irvtd_cb *driver)
-{
- unsigned cflag,cval;
-
- if (!driver->tty || !driver->tty->termios || !driver->comm)
- return;
- cflag = driver->tty->termios->c_cflag;
-
- /*
- * byte size and parity
- */
- switch (cflag & CSIZE)
- {
- case CS5: cval = IRCOMM_WLEN5; break;
- case CS6: cval = IRCOMM_WLEN6; break;
- case CS7: cval = IRCOMM_WLEN7; break;
- case CS8: cval = IRCOMM_WLEN8; break;
- default: cval = IRCOMM_WLEN5; break; /* too keep GCC shut... */
- }
- if (cflag & CSTOPB) { /* use 2 stop bit mode */
- cval |= IRCOMM_STOP2;
- }
- if (cflag & PARENB)
- cval |= IRCOMM_PARENB; /* enable parity check */
- if (!(cflag & PARODD))
- cval |= IRCOMM_PAREVEN; /* even parity */
-
- /* CTS flow control flag and modem status interrupts */
-
- if (cflag & CRTSCTS)
- driver->comm->flow_ctrl |= USE_CTS;
- else
- driver->comm->flow_ctrl |= ~USE_CTS;
-
- if (cflag & CLOCAL)
- driver->flags &= ~ASYNC_CHECK_CD;
- else
- driver->flags |= ASYNC_CHECK_CD;
-
- /*
- * Set up parity check flag
- */
-
- driver->read_status_mask = LSR_OE;
- if (I_INPCK(driver->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(driver->tty)) {
- driver->ignore_status_mask |= LSR_BI;
- /*
- * If we're ignore parity and break indicators, ignore
- * overruns too. (For real raw support).
- */
- if (I_IGNPAR(driver->tty))
- driver->ignore_status_mask |= LSR_OE;
- }
-
- driver->comm->data_format = cval;
- ircomm_control_request(driver->comm, DATA_FORMAT);
- ircomm_control_request(driver->comm, FLOW_CONTROL);
-}
-
-
-
-
-static int irvtd_startup(struct irvtd_cb *driver)
-{
- struct ias_object* obj;
- struct notify_t irvtd_notify;
-
- /* FIXME: it should not be hard coded */
- __u8 oct_seq[6] = { 0,1,6,1,1,1 };
-
- DEBUG(4,__FUNCTION__"()\n" );
- if(driver->flags & ASYNC_INITIALIZED)
- return 0;
-
- /*
- * initialize our tx/rx buffer
- */
-
- skb_queue_head_init(&driver->rxbuff);
- driver->txbuff = dev_alloc_skb(COMM_DEFAULT_SDU_SIZE + COMM_MAX_HEADER_SIZE);
- if (!driver->txbuff){
- DEBUG(0,__FUNCTION__"(), alloc_skb failed!\n");
- return -ENOMEM;
- }
- skb_reserve(driver->txbuff, COMM_MAX_HEADER_SIZE);
-
- irda_notify_init(&irvtd_notify);
- irvtd_notify.data_indication = irvtd_receive_data;
- irvtd_notify.connect_confirm = irvtd_connect_confirm;
- irvtd_notify.connect_indication = irvtd_connect_indication;
- irvtd_notify.disconnect_indication = irvtd_disconnect_indication;
- irvtd_notify.flow_indication = irvtd_control_indication;
- strncpy( irvtd_notify.name, "ircomm_tty", NOTIFY_MAX_NAME);
- irvtd_notify.instance = driver;
-
- driver->comm = ircomm_open_instance(irvtd_notify);
- if (!driver->comm)
- return -ENODEV;
-
-
- /*
- * Register with LM-IAS as a server
- */
-
- obj = irias_new_object( "IrDA:IrCOMM", IAS_IRCOMM_ID);
- irias_add_integer_attrib( obj, "IrDA:TinyTP:LsapSel",
- driver->comm->tsap->stsap_sel );
-
- irias_add_octseq_attrib( obj, "Parameters", &oct_seq[0], 6);
- irias_insert_object( obj);
-
- driver->flags |= ASYNC_INITIALIZED;
-
- if (driver->tty)
- clear_bit(TTY_IO_ERROR, &driver->tty->flags);
-
- change_speed(driver);
-
- /*
- * discover a peer device
- */
- if(driver->tty->termios->c_cflag & CRTSCTS)
- ircomm_connect_request(driver->comm, NINE_WIRE);
- else
- ircomm_connect_request(driver->comm, THREE_WIRE);
-
- /* irvtd_start_timer( driver); */
-
- driver->rx_disable = 0;
- driver->tx_disable = 1;
- driver->disconnect_pend = 0;
- return 0;
-}
-
-
-int irvtd_open(struct tty_struct * tty, struct file * filp)
-{
- struct irvtd_cb *driver;
- int retval;
- int line;
-
- DEBUG(4, __FUNCTION__"():\n");
- MOD_INC_USE_COUNT;
-
- line = MINOR(tty->device) - tty->driver.minor_start;
- if ((line <0) || (line >= COMM_MAX_TTY)){
- MOD_DEC_USE_COUNT;
- return -ENODEV;
- }
-
- driver = irvtd[line];
- ASSERT(driver != NULL, MOD_DEC_USE_COUNT;return -ENOMEM;);
- ASSERT(driver->magic == IRVTD_MAGIC, MOD_DEC_USE_COUNT;return -EINVAL;);
-
- driver->count++;
-
- DEBUG(0, __FUNCTION__"():%s%d count %d\n", tty->driver.name, line,
- driver->count);
-
- tty->driver_data = driver;
- driver->tty = tty;
-
- driver->tty->low_latency = (driver->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-
- /*
- * If the device is in the middle of being closed, then block
- * (sleep) until it's done, then exit.
- */
-
- if (tty_hung_up_p(filp) ||
- (driver->flags & ASYNC_CLOSING)) {
- if (driver->flags & ASYNC_CLOSING)
- interruptible_sleep_on(&driver->close_wait);
-#ifdef DO_RESTART
- if (driver->flags & ASYNC_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
-#else
- return -EAGAIN;
-#endif
- }
-
- /*
- * start up discovering process and ircomm_layer
- */
-
- retval = irvtd_startup(driver);
- if (retval){
- DEBUG(0, __FUNCTION__"():irvtd_startup returns %d\n",retval);
- return retval;
- }
-
- retval = irvtd_block_til_ready(tty, filp, driver);
- if (retval){
- DEBUG(0,__FUNCTION__
- "():returning after block_til_ready (errno = %d)\n", retval);
- return retval;
- }
-
- driver->session = current->session;
- driver->pgrp = current->pgrp;
- return 0;
-}
-
-
-
-
-
-/*
- * ----------------------------------------------------------------------
- * irvtd_close() and friends
- *
- * most of this function is stolen from serial.c
- * ----------------------------------------------------------------------
- */
-
-/*
- * Function irvtd_wait_until_sent (tty, timeout)
- *
- * wait until Tx queue of IrTTP is empty
- *
- */
-static void irvtd_wait_until_sent(struct tty_struct *tty, int timeout)
-{
- struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
- unsigned long orig_jiffies;
-
- ASSERT(driver != NULL, return;);
- ASSERT(driver->magic == IRVTD_MAGIC, return;);
- ASSERT(driver->comm != NULL, return;);
-
- DEBUG(1, __FUNCTION__"():\n");
- if(!tty->closing)
- return; /* nothing to do */
-
- /*
- * at disconnection, we should wait until Tx queue of IrTTP is
- * flushed
- */
-
- ircomm_disconnect_request(driver->comm, NULL, P_NORMAL);
- orig_jiffies = jiffies;
-
- while (driver->comm->tsap->disconnect_pend) {
- DEBUG(1, __FUNCTION__"():wait..\n");
- current->state = TASK_INTERRUPTIBLE;
- current->counter = 0; /* make us low-priority */
- schedule_timeout(HZ); /* 1sec */
- if (signal_pending(current))
- break;
- if (timeout && time_after(jiffies, orig_jiffies + timeout))
- break;
- }
- current->state = TASK_RUNNING;
-}
-
-
-static void irvtd_shutdown(struct irvtd_cb * driver)
-{
- unsigned long flags;
-
- if (!(driver->flags & ASYNC_INITIALIZED))
- return;
-
- DEBUG(1,__FUNCTION__"()\n");
-
- /*
- * This comment is written in serial.c:
- *
- * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
- * here so the queue might never be waken up
- */
- wake_up_interruptible(&driver->delta_msr_wait);
-
- /* clear DTR and RTS */
- if (!driver->tty || (driver->tty->termios->c_cflag & HUPCL))
- driver->mcr &= ~(MCR_DTR|MCR_RTS);
-
- driver->comm->dte = driver->mcr;
- ircomm_control_request(driver->comm, DTELINE_STATE );
-
-
-
- save_flags(flags); cli(); /* Disable interrupts */
-
- if (driver->tty)
- set_bit(TTY_IO_ERROR, &driver->tty->flags);
-
- del_timer( &driver->tx_timer);
- del_timer( &driver->rx_timer);
-
- irias_delete_object("IrDA:IrCOMM");
-
- /*
- * Free the transmit buffer here
- */
-
- while(skb_queue_len(&driver->rxbuff)){
- struct sk_buff *skb;
- skb = skb_dequeue( &driver->rxbuff);
- dev_kfree_skb(skb);
- }
- if(driver->txbuff){
- dev_kfree_skb(driver->txbuff);
- driver->txbuff = NULL;
- }
- ircomm_close_instance(driver->comm);
- driver->comm = NULL;
- driver->flags &= ~ASYNC_INITIALIZED;
- restore_flags(flags);
-}
-
-void irvtd_close(struct tty_struct * tty, struct file * filp)
-{
- struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
- int line;
- unsigned long flags;
-
- DEBUG(1, __FUNCTION__"():refcount= %d\n",irvtd_refcount);
-
- ASSERT(driver != NULL, return;);
- ASSERT(driver->magic == IRVTD_MAGIC, return;);
-
- save_flags(flags);cli();
-
-
- if(tty_hung_up_p(filp)){
- /*
- * upper tty layer caught a HUP signal and called irvtd_hangup()
- * before. so we do nothing here.
- */
- DEBUG(1, __FUNCTION__"():tty_hung_up_p.\n");
- MOD_DEC_USE_COUNT;
- restore_flags(flags);
- return;
- }
-
-
- line = MINOR(tty->device) - tty->driver.minor_start;
- DEBUG(0, __FUNCTION__"():%s%d count %d\n", tty->driver.name, line,
- driver->count);
-
- if ((tty->count == 1) && (driver->count != 1)) {
- /*
- * Uh, oh. tty->count is 1, which means that the tty
- * structure will be freed. Driver->count should always
- * be one in these conditions. If it's greater than
- * one, we've got real problems, since it means the
- * ircomm service layer won't be shutdown.
- */
- printk(KERN_ERR"irvtd_close: bad serial port count;"
- "tty->count is 1, but driver->count is %d\n", driver->count);
- driver->count = 1;
- }
- if (--driver->count < 0) {
- printk(KERN_ERR"irvtd_close: bad count for line%d: %d\n",
- line, driver->count);
- driver->count = 0;
- }
-
- if (driver->count) { /* do nothing */
- DEBUG(0, __FUNCTION__"():driver->count is not 0\n");
- MOD_DEC_USE_COUNT;
- restore_flags(flags);
- return;
- }
-
- driver->flags |= ASYNC_CLOSING;
-
- /*
- * Now we wait for the transmit buffer to clear; and we notify
- * the line discipline to only process XON/XOFF characters.
- */
- tty->closing = 1;
- if (driver->closing_wait != ASYNC_CLOSING_WAIT_NONE){
- DEBUG(4, __FUNCTION__"():calling tty_wait_until_sent()\n");
- tty_wait_until_sent(tty, driver->closing_wait);
- }
- /*
- * we can send disconnect_request with P_HIGH since
- * tty_wait_until_sent() and irvtd_wait_until_sent() should
- * have disconnected the link
- */
- ircomm_disconnect_request(driver->comm, NULL, P_HIGH);
-
- /*
- * Now we stop accepting input.
- */
-
- driver->rx_disable = TRUE;
- /* drop ldisc's buffer */
- if (tty->ldisc.flush_buffer)
- tty->ldisc.flush_buffer(tty);
-
- if (tty->driver.flush_buffer)
- tty->driver.flush_buffer(driver->tty);
-
- tty->closing = 0;
- driver->tty = NULL;
-
- if (driver->blocked_open)
- {
- if (driver->close_delay) {
- /* kill time */
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(driver->close_delay);
- }
- wake_up_interruptible(&driver->open_wait);
- }
- irvtd_shutdown(driver);
- driver->flags &= ~ASYNC_CLOSING;
- wake_up_interruptible(&driver->close_wait);
-
- MOD_DEC_USE_COUNT;
- restore_flags(flags);
- DEBUG(4, __FUNCTION__"():done\n");
-}
-
-/*
- * ----------------------------------------------------------------------
- * irvtd_write() and friends
- * This routine will be called when something data are passed from
- * kernel or user.
- * ----------------------------------------------------------------------
- */
-
-int irvtd_write(struct tty_struct * tty, int from_user,
- const unsigned char *buf, int count)
-{
- struct irvtd_cb *driver;
- int c = 0;
- int wrote = 0;
- unsigned long flags;
- struct sk_buff *skb;
- __u8 *frame;
-
- ASSERT(tty != NULL, return -EFAULT;);
- driver = (struct irvtd_cb *)tty->driver_data;
- ASSERT(driver != NULL, return -EFAULT;);
- ASSERT(driver->magic == IRVTD_MAGIC, return -EFAULT;);
-
- DEBUG(4, __FUNCTION__"()\n");
-
- save_flags(flags);
- while(count > 0){
- cli();
- skb = driver->txbuff;
- ASSERT(skb != NULL, break;);
- c = MIN(count, (skb_tailroom(skb)));
- if (c <= 0)
- {
- if(!driver->ttp_stoptx)
- {
- irvtd_send_data_request(driver);
- continue;
- }
- else
- break;
- }
-
- /* write to the frame */
-
- frame = skb_put(skb,c);
- if(from_user){
- copy_from_user(frame,buf,c);
- } else
- memcpy(frame, buf, c);
-
- restore_flags(flags);
- wrote += c;
- count -= c;
- buf += c;
- }
- restore_flags(flags);
- irvtd_send_data_request(driver);
- return (wrote);
-}
-
-void irvtd_flush_chars(struct tty_struct *tty)
-{
- struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
- ASSERT( driver != NULL, return;);
- ASSERT( driver->magic == IRVTD_MAGIC, return;);
-
- DEBUG(4, __FUNCTION__"()\n");
- irvtd_send_data_request(driver);
-}
-
-
-/*
- * Function irvtd_put_char (tty, ch)
- *
- * This routine is called by the kernel to pass a single character.
- * If we exausted our buffer,we can ignore the character!
- *
- */
-void irvtd_put_char(struct tty_struct *tty, unsigned char ch)
-{
- __u8 *frame ;
- struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
- struct sk_buff *skb;
- unsigned long flags;
-
- ASSERT(driver != NULL, return;);
- DEBUG(4, __FUNCTION__"()\n");
-
-
- again:
- save_flags(flags);cli();
- skb = driver->txbuff;
- ASSERT(skb != NULL,return;);
- if(!skb_tailroom(skb))
- {
- restore_flags(flags);
- irvtd_send_data_request(driver);
- goto again;
- }
- ASSERT(skb_tailroom(skb) > 0, return;);
- DEBUG(4, "irvtd_put_char(0x%02x) skb_len(%d) room(%d):\n",
- (int)ch ,(int)skb->len,
- skb_tailroom(skb));
-
- /* append a character */
- frame = skb_put(skb,1);
- frame[0] = ch;
-
- restore_flags(flags);
- irvtd_start_tx_timer(driver,20);
- return;
-}
-
-/*
- * Function irvtd_write_room (tty)
- *
- * This routine returns the room that our buffer has now.
- *
- */
-int irvtd_write_room(struct tty_struct *tty){
-
- int ret;
- struct sk_buff *skb = ((struct irvtd_cb *) tty->driver_data)->txbuff;
-
- ASSERT(skb !=NULL, return 0;);
-
- ret = skb_tailroom(skb);
-
- DEBUG(4, __FUNCTION__"(): room is %d bytes\n",ret);
-
- return(ret);
-}
-
-/*
- * Function irvtd_chars_in_buffer (tty)
- *
- * This function returns how many characters which have not been sent yet
- * are still in buffer.
- *
- */
-int irvtd_chars_in_buffer(struct tty_struct *tty){
-
- struct sk_buff *skb;
- unsigned long flags;
-
- DEBUG(4, __FUNCTION__"()\n");
-
- save_flags(flags);cli();
- skb = ((struct irvtd_cb *) tty->driver_data)->txbuff;
- if(skb == NULL) goto err;
-
- restore_flags(flags);
- return (skb->len );
-err:
- ASSERT(skb != NULL, ;);
- restore_flags(flags);
- return 0; /* why not -EFAULT or such? see driver/char/serial.c */
-}
-
-/*
- * Function irvtd_break (tty, break_state)
- *
- * Routine which turns the break handling on or off
- *
- */
-static void irvtd_break(struct tty_struct *tty, int break_state){
-
- struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
- unsigned long flags;
-
- DEBUG(0, __FUNCTION__"()\n");
- ASSERT(tty->driver_data != NULL, return;);
- ASSERT(driver->magic == IRVTD_MAGIC, return;);
-
- save_flags(flags);cli();
- if (break_state == -1)
- {
- driver->comm->break_signal = 0x01;
- ircomm_control_request(driver->comm, BREAK_SIGNAL);
-
- }
- else
- {
- driver->comm->break_signal = 0x00;
- ircomm_control_request(driver->comm, BREAK_SIGNAL);
-
- }
-
- restore_flags(flags);
-}
-
-/*
- * ----------------------------------------------------------------------
- * irvtd_ioctl() and friends
- * This routine allows us to implement device-specific ioctl's.
- * If passed ioctl number (i.e.cmd) is unknown one, we should return
- * ENOIOCTLCMD.
- *
- * TODO: we can't use setserial on IrCOMM because some ioctls are not implemented.
- * we should add some ioctls and make some tool which is resemble to setserial.
- * ----------------------------------------------------------------------
- */
-
-static int get_modem_info(struct irvtd_cb * driver, unsigned int *value)
-{
- unsigned int result;
- result = ((driver->mcr & MCR_RTS) ? TIOCM_RTS : 0)
- | ((driver->mcr & MCR_DTR) ? TIOCM_DTR : 0)
- | ((driver->msr & DELTA_DCD) ? TIOCM_CAR : 0)
- | ((driver->msr & DELTA_RI) ? TIOCM_RNG : 0)
- | ((driver->msr & DELTA_DSR) ? TIOCM_DSR : 0)
- | ((driver->msr & DELTA_CTS) ? TIOCM_CTS : 0);
- return put_user(result,value);
-}
-
-static int set_modem_info(struct irvtd_cb * driver, unsigned int cmd,
- unsigned int *value)
-{
- int error;
- unsigned int arg;
-
- error = get_user(arg, value);
- if(error)
- return error;
-
- switch (cmd) {
- case TIOCMBIS:
- if (arg & TIOCM_RTS)
- driver->mcr |= MCR_RTS;
- if (arg & TIOCM_DTR)
- driver->mcr |= MCR_DTR;
- break;
-
- case TIOCMBIC:
- if (arg & TIOCM_RTS)
- driver->mcr &= ~MCR_RTS;
- if (arg & TIOCM_DTR)
- driver->mcr &= ~MCR_DTR;
- break;
-
- case TIOCMSET:
- driver->mcr = ((driver->mcr & ~(MCR_RTS | MCR_DTR))
- | ((arg & TIOCM_RTS) ? MCR_RTS : 0)
- | ((arg & TIOCM_DTR) ? MCR_DTR : 0));
- break;
-
- default:
- return -EINVAL;
- }
-
- driver->comm->dte = driver->mcr;
- ircomm_control_request(driver->comm, DTELINE_STATE );
-
- return 0;
-}
-
-static int get_serial_info(struct irvtd_cb * driver,
- struct serial_struct * retinfo)
-{
- struct serial_struct tmp;
-
- if (!retinfo)
- return -EFAULT;
- memset(&tmp, 0, sizeof(tmp));
- tmp.line = driver->line;
- tmp.flags = driver->flags;
- tmp.baud_base = driver->comm->data_rate;
- tmp.close_delay = driver->close_delay;
- tmp.closing_wait = driver->closing_wait;
-
- /* for compatibility */
-
- tmp.type = PORT_16550A;
- tmp.port = 0;
- tmp.irq = 0;
- tmp.xmit_fifo_size = 0;
- tmp.hub6 = 0;
- tmp.custom_divisor = driver->custom_divisor;
-
- if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
- return -EFAULT;
- return 0;
-}
-
-static int set_serial_info(struct irvtd_cb * driver,
- struct serial_struct * new_info)
-{
- struct serial_struct new_serial;
- struct irvtd_cb old_driver;
-
- if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
- return -EFAULT;
-
- old_driver = *driver;
-
- if (!capable(CAP_SYS_ADMIN)) {
- if ((new_serial.baud_base != driver->comm->data_rate) ||
- (new_serial.close_delay != driver->close_delay) ||
- ((new_serial.flags & ~ASYNC_USR_MASK) !=
- (driver->flags & ~ASYNC_USR_MASK)))
- return -EPERM;
- driver->flags = ((driver->flags & ~ASYNC_USR_MASK) |
- (new_serial.flags & ASYNC_USR_MASK));
- driver->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(driver->comm->data_rate != new_serial.baud_base){
- driver->comm->data_rate = new_serial.baud_base;
- if(driver->comm->state == COMM_CONN)
- ircomm_control_request(driver->comm,DATA_RATE);
- }
- driver->close_delay = new_serial.close_delay * HZ/100;
- driver->closing_wait = new_serial.closing_wait * HZ/100;
- driver->custom_divisor = new_serial.custom_divisor;
-
- driver->flags = ((driver->flags & ~ASYNC_FLAGS) |
- (new_serial.flags & ASYNC_FLAGS));
- driver->tty->low_latency = (driver->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-
- check_and_exit:
- if (driver->flags & ASYNC_INITIALIZED) {
- if (((old_driver.flags & ASYNC_SPD_MASK) !=
- (driver->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;
- change_speed(driver);
- }
- }
- return 0;
-}
-
-
-
-
-int irvtd_ioctl(struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg)
-{
- int error;
- unsigned long flags;
- struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
-
- struct serial_icounter_struct cnow,cprev;
- struct serial_icounter_struct *p_cuser; /* user space */
-
-
- DEBUG(4,"irvtd_ioctl:requested ioctl(0x%08x)\n",cmd);
-
-#ifdef IRVTD_DEBUG_IOCTL
- {
- /* kill time so that debug messages will come slowly */
- save_flags(flags);cli();
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + HZ/4; /*0.25sec*/
- schedule();
- restore_flags(flags);
- }
-#endif
-
-
-
- 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 TIOCMGET:
- return get_modem_info(driver, (unsigned int *) arg);
-
- case TIOCMBIS:
- case TIOCMBIC:
- case TIOCMSET:
- return set_modem_info(driver, cmd, (unsigned int *) arg);
- case TIOCGSERIAL:
- return get_serial_info(driver, (struct serial_struct *) arg);
- case TIOCSSERIAL:
- return set_serial_info(driver, (struct serial_struct *) arg);
-
-
- case TIOCMIWAIT:
- save_flags(flags); cli();
- /* note the counters on entry */
- cprev = driver->icount;
- restore_flags(flags);
- while (1) {
- interruptible_sleep_on(&driver->delta_msr_wait);
- /* see if a signal did it */
- if (signal_pending(current))
- return -ERESTARTSYS;
- save_flags(flags); cli();
- cnow = driver->icount; /* atomic copy */
- restore_flags(flags);
- if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
- cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
- return -EIO; /* no change => error */
- if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
- ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
- ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
- ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
- return 0;
- }
- cprev = cnow;
- }
- /* NOTREACHED */
-
- case TIOCGICOUNT:
- save_flags(flags); cli();
- cnow = driver->icount;
- restore_flags(flags);
- p_cuser = (struct serial_icounter_struct *) arg;
- error = put_user(cnow.cts, &p_cuser->cts);
- if (error) return error;
- error = put_user(cnow.dsr, &p_cuser->dsr);
- if (error) return error;
- error = put_user(cnow.rng, &p_cuser->rng);
- if (error) return error;
- error = put_user(cnow.dcd, &p_cuser->dcd);
- if (error) return error;
- error = put_user(cnow.rx, &p_cuser->rx);
- if (error) return error;
- error = put_user(cnow.tx, &p_cuser->tx);
- if (error) return error;
- error = put_user(cnow.frame, &p_cuser->frame);
- if (error) return error;
- error = put_user(cnow.overrun, &p_cuser->overrun);
- if (error) return error;
- error = put_user(cnow.parity, &p_cuser->parity);
- if (error) return error;
- error = put_user(cnow.brk, &p_cuser->brk);
- if (error) return error;
- error = put_user(cnow.buf_overrun, &p_cuser->buf_overrun);
- if (error) return error;
- return 0;
-
-
- /* ioctls which are imcompatible with serial.c */
-
- case TIOCSERGSTRUCT:
- DEBUG(0,__FUNCTION__"():sorry, TIOCSERGSTRUCT is not supported\n");
- return -ENOIOCTLCMD;
- case TIOCSERGETLSR:
- DEBUG(0,__FUNCTION__"():sorry, TIOCSERGETLSR is not supported\n");
- return -ENOIOCTLCMD;
- case TIOCSERCONFIG:
- DEBUG(0,__FUNCTION__"():sorry, TIOCSERCONFIG is not supported\n");
- return -ENOIOCTLCMD;
-
-
- default:
- return -ENOIOCTLCMD; /* ioctls which we must ignore */
- }
- return 0;
-}
-
-
-
-/*
- * ----------------------------------------------------------------------
- * irvtd_set_termios()
- * This is called when termios is changed.
- * If things that changed is significant for us,(i.e. changing baud rate etc.)
- * send something to peer device.
- * ----------------------------------------------------------------------
- */
-
-void irvtd_set_termios(struct tty_struct *tty, struct termios * old_termios){
-
- struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
-
- ASSERT(driver != NULL,return;);
- ASSERT(driver->magic == IRVTD_MAGIC ,return;);
-
- DEBUG(0, "irvtd_set_termios:\n");
- return;
-
- if((tty->termios->c_cflag == old_termios->c_cflag) &&
- (RELEVANT_IFLAG(tty->termios->c_iflag)
- == RELEVANT_IFLAG(old_termios->c_iflag)))
- return;
-
- change_speed(driver);
-
- if((old_termios->c_cflag & CRTSCTS) &&
- !(tty->termios->c_cflag & CRTSCTS)) {
- tty->hw_stopped = driver->ttp_stoptx;
- /* irvtd_start(tty); */ /* we don't need this */
- }
-}
-
-/*
- * ----------------------------------------------------------------------
- * irvtd_throttle,irvtd_unthrottle
- * These routines will be called when we have to pause sending up data to tty.
- * We use RTS virtual signal when servicetype is NINE_WIRE
- * ----------------------------------------------------------------------
- */
-
-static void irvtd_send_xchar(struct tty_struct *tty, char ch){
-
- DEBUG(1, __FUNCTION__"():\n");
- irvtd_put_char(tty, ch);
-}
-
-void irvtd_throttle(struct tty_struct *tty){
- struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
-
- DEBUG(1, "irvtd_throttle:\n");
-
- if (I_IXOFF(tty))
- irvtd_put_char(tty, STOP_CHAR(tty));
-
- driver->mcr &= ~MCR_RTS;
- driver->mcr |= DELTA_RTS;
- driver->comm->dte = driver->mcr;
- ircomm_control_request(driver->comm, DTELINE_STATE );
-
- DEBUG(1, __FUNCTION__"():FLOW_STOP\n");
- irttp_flow_request(driver->comm->tsap, FLOW_STOP);
-}
-
-void irvtd_unthrottle(struct tty_struct *tty){
- struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
- DEBUG(1, "irvtd_unthrottle:\n");
-
- if (I_IXOFF(tty))
- irvtd_put_char(tty, START_CHAR(tty));
-
- driver->mcr |= (MCR_RTS|DELTA_RTS);
- driver->comm->dte = driver->mcr;
- ircomm_control_request(driver->comm, DTELINE_STATE );
-
- DEBUG(1, __FUNCTION__"():FLOW_START\n");
- irttp_flow_request(driver->comm->tsap, FLOW_START);
-}
-
-
-/*
- * ------------------------------------------------------------
- * irvtd_stop() and irvtd_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable an interrupt which means "transmitter-is-ready"
- * in serial.c, but I think these routine are not necessary for us.
- * ------------------------------------------------------------
- */
-
-#if 0
-irvtd_stop(struct tty_struct *tty){
- DEBUG(0, "irvtd_stop()\n");
-
- struct irvtd_cb *info = (struct irvtd_cb *)tty->driver_data;
- DEBUG(0, "irvtd_start():not implemented!\n");
-}
-irvtd_start(struct tty_struct *tty){
-
- struct irvtd_cb *info = (struct irvtd_cb *)tty->driver_data;
- DEBUG(0, "irvtd_start():not_implemented!\n");
-}
-#endif
-
-/*
- * ------------------------------------------------------------
- * irvtd_hangup()
- * This routine notifies that tty layer have got HUP signal
- * ------------------------------------------------------------
- */
-
-void irvtd_hangup(struct tty_struct *tty){
-
- struct irvtd_cb *info = (struct irvtd_cb *)tty->driver_data;
- DEBUG(0, __FUNCTION__"()\n");
-
- irvtd_flush_buffer(tty);
- irvtd_shutdown(info);
- info->count = 0;
- info->tty = NULL;
- wake_up_interruptible(&info->open_wait);
-}
-
-void irvtd_flush_buffer(struct tty_struct *tty){
-
- struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data;
- struct sk_buff *skb;
-
- skb = driver->txbuff;
- ASSERT(skb != NULL, return;);
-
- if(skb->len){
- DEBUG(0, __FUNCTION__"():%d chars in txbuff are lost..\n",(int)skb->len);
- skb_trim(skb,0);
- }
-
- /* write_wait is a wait queue of tty_wait_until_sent().
- * see tty_io.c of kernel
- */
- wake_up_interruptible(&tty->write_wait);
-
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
-}
-
-
-
-/*
- * Function ircomm_register_device(void), init_module() and friends
- *
- * we register "port emulation entity"(see IrCOMM specification) here
- * as a tty device.
- */
-
-int irvtd_register_ttydriver(void){
-
- DEBUG( 4, "-->irvtd_register_ttydriver\n");
-
- /* setup virtual serial port device */
-
- /* Initialize the tty_driver structure ,which is defined in
- tty_driver.h */
-
- memset(&irvtd_drv, 0, sizeof(struct tty_driver));
- irvtd_drv.magic = IRVTD_MAGIC;
- irvtd_drv.driver_name = "IrCOMM_tty";
- irvtd_drv.name = "irnine";
- irvtd_drv.major = IRCOMM_MAJOR;
- irvtd_drv.minor_start = IRVTD_MINOR;
- irvtd_drv.num = COMM_MAX_TTY;
- irvtd_drv.type = TTY_DRIVER_TYPE_SERIAL; /* see tty_driver.h */
-
-
- /*
- * see drivers/char/tty_io.c and termios(3)
- */
-
- irvtd_drv.init_termios = tty_std_termios;
- irvtd_drv.init_termios.c_cflag =
- B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- irvtd_drv.flags = TTY_DRIVER_REAL_RAW; /* see tty_driver.h */
- irvtd_drv.refcount = &irvtd_refcount;
-
- /* pointer to the tty data structures */
-
- irvtd_drv.table = irvtd_table;
- irvtd_drv.termios = irvtd_termios;
- irvtd_drv.termios_locked = irvtd_termios_locked;
-
- /*
- * Interface table from the kernel(tty driver) to the ircomm
- * layer
- */
-
- irvtd_drv.open = irvtd_open;
- irvtd_drv.close = irvtd_close;
- irvtd_drv.write = irvtd_write;
- irvtd_drv.put_char = irvtd_put_char;
- irvtd_drv.flush_chars = irvtd_flush_chars;
- irvtd_drv.write_room = irvtd_write_room;
- irvtd_drv.chars_in_buffer = irvtd_chars_in_buffer;
- irvtd_drv.flush_buffer = irvtd_flush_buffer;
- irvtd_drv.ioctl = irvtd_ioctl;
- irvtd_drv.throttle = irvtd_throttle;
- irvtd_drv.unthrottle = irvtd_unthrottle;
- irvtd_drv.set_termios = irvtd_set_termios;
- irvtd_drv.stop = NULL; /* irvtd_stop; */
- irvtd_drv.start = NULL; /* irvtd_start; */
- irvtd_drv.hangup = irvtd_hangup;
-
- irvtd_drv.send_xchar = irvtd_send_xchar;
- irvtd_drv.break_ctl = irvtd_break;
- irvtd_drv.read_proc = irvtd_read_proc;
- irvtd_drv.wait_until_sent = irvtd_wait_until_sent;
-
-
-
- if (tty_register_driver(&irvtd_drv)){
- DEBUG(0,"IrCOMM:Couldn't register tty driver\n");
- return(1);
- }
-
- DEBUG( 4, "irvtd_register_ttydriver: done.\n");
- return(0);
-}
-
-
-/*
- * Function irvtd_unregister_device(void)
- * it will be called when you rmmod
- */
-
-void irvtd_unregister_ttydriver(void){
-
- int err;
- DEBUG( 4, "--> irvtd_unregister_device\n");
-
- /* unregister tty device */
-
- err = tty_unregister_driver(&irvtd_drv);
- if (err)
- printk("IrCOMM: failed to unregister vtd driver(%d)\n",err);
- DEBUG( 4, "irvtd_unregister_device -->\n");
- return;
-}
-
-/*
- **********************************************************************
- * proc stuff
- *
- **********************************************************************
- */
-
-static int line_info(char *buf, struct irvtd_cb *driver)
-{
- int ret=0;
-
- ASSERT(driver != NULL,goto exit;);
- ASSERT(driver->magic == IRVTD_MAGIC,goto exit;);
-
- ret += sprintf(buf, "tx: %d rx: %d"
- ,driver->icount.tx, driver->icount.rx);
-
- if (driver->icount.frame)
- ret += sprintf(buf+ret, " fe:%d", driver->icount.frame);
- if (driver->icount.parity)
- ret += sprintf(buf+ret, " pe:%d", driver->icount.parity);
- if (driver->icount.brk)
- ret += sprintf(buf+ret, " brk:%d", driver->icount.brk);
- if (driver->icount.overrun)
- ret += sprintf(buf+ret, " oe:%d", driver->icount.overrun);
-
- if (driver->mcr & MCR_RTS)
- ret += sprintf(buf+ret, "|RTS");
- if (driver->msr & MSR_CTS)
- ret += sprintf(buf+ret, "|CTS");
- if (driver->mcr & MCR_DTR)
- ret += sprintf(buf+ret, "|DTR");
- if (driver->msr & MSR_DSR)
- ret += sprintf(buf+ret, "|DSR");
- if (driver->msr & MSR_DCD)
- ret += sprintf(buf+ret, "|CD");
- if (driver->msr & MSR_RI)
- ret += sprintf(buf+ret, "|RI");
-
- ret += sprintf(buf+ret, "\n");
- ret += sprintf(buf+ret, "rx queue:%d",
- skb_queue_len( &driver->rxbuff));
- ret += sprintf(buf+ret, "ttp_stoprx:%s",
- driver->ttp_stoprx?"TRUE":"FALSE");
-
- exit:
- ret += sprintf(buf+ret, "\n");
- return ret;
-}
-
-
-
-static int irvtd_read_proc(char *buf, char **start, off_t offset, int len,
- int *eof, void *unused)
-{
- int i, count = 0, l;
- off_t begin = 0;
-
- count += sprintf(buf, "driver revision:%s\n", revision_date);
- for (i = 0; i < COMM_MAX_TTY && count < 4000; i++) {
- l = line_info(buf + count, irvtd[i]);
- count += l;
- if (count+begin > offset+len)
- goto done;
- if (count+begin < offset) {
- begin += count;
- count = 0;
- }
- }
- *eof = 1;
-done:
- if (offset >= count+begin)
- return 0;
- *start = buf + (begin-offset);
- return ((len < begin+count-offset) ? len : begin+count-offset);
-}
-
-
-
-
-/************************************************************
- * init & cleanup this module
- ************************************************************/
-
-__initfunc(int irvtd_init(void))
-{
- int i;
-
- DEBUG( 4, __FUNCTION__"()\n");
- printk( KERN_INFO
- "ircomm_tty: virtual tty driver for IrCOMM ( revision:%s )\n",
- revision_date);
-
-
- /* allocate a master array */
-
- irvtd = (struct irvtd_cb **) kmalloc( sizeof(void *) *
- COMM_MAX_TTY,GFP_KERNEL);
- if ( irvtd == NULL) {
- printk( KERN_WARNING __FUNCTION__"(): kmalloc failed!\n");
- return -ENOMEM;
- }
-
- memset( irvtd, 0, sizeof(void *) * COMM_MAX_TTY);
-
- for (i=0; i < COMM_MAX_TTY; i++){
- irvtd[i] = kmalloc( sizeof(struct irvtd_cb), GFP_KERNEL);
- if(irvtd[i] == NULL){
- printk(KERN_ERR __FUNCTION__"(): kmalloc failed!\n");
- return -ENOMEM;
- }
- memset( irvtd[i], 0, sizeof(struct irvtd_cb));
- irvtd[i]->magic = IRVTD_MAGIC;
- irvtd[i]->line = i;
- irvtd[i]->closing_wait = 10*HZ ;
- irvtd[i]->close_delay = 5*HZ/10 ;
- init_waitqueue_head(&irvtd[i]->open_wait);
- init_waitqueue_head(&irvtd[i]->close_wait);
- init_waitqueue_head(&irvtd[i]->tx_wait);
- init_waitqueue_head(&irvtd[i]->delta_msr_wait);
- }
-
- /*
- * initialize a "port emulation entity"
- */
-
- if(irvtd_register_ttydriver()){
- printk( KERN_WARNING "IrCOMM: Error in ircomm_register_device\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-void irvtd_cleanup(void)
-{
- int i;
- DEBUG( 4, __FUNCTION__"()\n");
-
- /*
- * free some resources
- */
- if (irvtd) {
- for (i=0; i<COMM_MAX_TTY; i++) {
- if (irvtd[i]) {
- if(irvtd[i]->comm)
- ircomm_close_instance(irvtd[i]->comm);
- if(irvtd[i]->txbuff)
- dev_kfree_skb(irvtd[i]->txbuff);
- DEBUG( 4, "freeing structures\n");
- kfree(irvtd[i]);
- irvtd[i] = NULL;
- }
- }
- DEBUG( 4, "freeing master array\n");
- kfree(irvtd);
- irvtd = NULL;
- }
-
- irvtd_unregister_ttydriver();
-
-}
-
-#ifdef MODULE
-
-int init_module(void)
-{
- irvtd_init();
- return 0;
-}
-
-/*
- * Function ircomm_cleanup (void)
- * This is called when you rmmod.
- */
-
-void cleanup_module(void)
-{
- irvtd_cleanup();
-}
-
-#endif /* MODULE */