diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
commit | db7d4daea91e105e3859cf461d7e53b9b77454b2 (patch) | |
tree | 9bb65b95440af09e8aca63abe56970dd3360cc57 /net/irda/ircomm | |
parent | 9c1c01ead627bdda9211c9abd5b758d6c687d8ac (diff) |
Merge with Linux 2.2.8.
Diffstat (limited to 'net/irda/ircomm')
-rw-r--r-- | net/irda/ircomm/Makefile | 8 | ||||
-rw-r--r-- | net/irda/ircomm/attach.c | 364 | ||||
-rw-r--r-- | net/irda/ircomm/ircomm_common.c | 1489 | ||||
-rw-r--r-- | net/irda/ircomm/irvtd.c | 153 | ||||
-rw-r--r-- | net/irda/ircomm/irvtd_driver.c | 1883 |
5 files changed, 2064 insertions, 1833 deletions
diff --git a/net/irda/ircomm/Makefile b/net/irda/ircomm/Makefile index 1ff2e45be..7df055ea1 100644 --- a/net/irda/ircomm/Makefile +++ b/net/irda/ircomm/Makefile @@ -8,9 +8,9 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := ircomm_n_vtd.o -O_OBJS := ircomm_common.o attach.o irvtd.o irvtd_driver.o +O_OBJS := ircomm_common.o irvtd_driver.o M_OBJS := ircomm.o ircomm_tty.o -MI_OBJS := ircomm_common.o attach.o irvtd.o irvtd_driver.o +MI_OBJS := ircomm_common.o irvtd_driver.o OX_OBJS += @@ -18,10 +18,10 @@ OX_OBJS += ifeq ($(CONFIG_IRCOMM),m) ircomm.o: $(MI_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ ircomm_common.o attach.o + $(LD) $(LD_RFLAG) -r -o $@ ircomm_common.o ircomm_tty.o: $(MI_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ irvtd.o irvtd_driver.o + $(LD) $(LD_RFLAG) -r -o $@ irvtd_driver.o endif include $(TOPDIR)/Rules.make diff --git a/net/irda/ircomm/attach.c b/net/irda/ircomm/attach.c deleted file mode 100644 index 1e5e373ee..000000000 --- a/net/irda/ircomm/attach.c +++ /dev/null @@ -1,364 +0,0 @@ -/********************************************************************* - * - * Filename: attach.c - * Version: - * Description: An implementation of IrCOMM service interface. - * Status: Experimental. - * Author: Takahide Higuchi <thiguchi@pluto.dti.ne.jp> - * - * Copyright (c) 1998, 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. - * - ********************************************************************/ - - -/* - * ---------------------------------------------------------------------- - * IrIAS related things for IrCOMM - * If you are to use ircomm layer, use ircomm_attach_cable to - * setup it and register your program. - * ---------------------------------------------------------------------- - */ - - -#include <linux/sched.h> -#include <linux/tqueue.h> - -#include <net/irda/irlap.h> -#include <net/irda/irttp.h> -#include <net/irda/iriap.h> -#include <net/irda/irias_object.h> - -#include <net/irda/ircomm_common.h> - -extern struct ircomm_cb **ircomm; -struct ircomm_cb *discovering_instance; - -static void got_lsapsel(struct ircomm_cb * info); -static void query_lsapsel(struct ircomm_cb * self); -void ircomm_getvalue_confirm( __u16 obj_id, struct ias_value *value, void *priv ); - -#if 0 -static char *rcsid = "$Id: attach.c,v 1.11 1998/10/22 12:02:20 dagb Exp $"; -#endif - - -/* - * handler for iriap_getvaluebyclass_request() - * - */ - -void ircomm_getvalue_confirm( __u16 obj_id, struct ias_value *value, void *priv ){ - - struct ircomm_cb *self = (struct ircomm_cb *) priv; - - ASSERT( self != NULL, return;); - ASSERT( self->magic == IRCOMM_MAGIC, return;); - - DEBUG(0, __FUNCTION__"type(%d)\n", value->type); - - switch(value->type){ - - case IAS_OCT_SEQ: - /* - * FIXME:we should use data which came here - * it is used for nothing at this time - */ - -#if 1 - DEBUG(0, "octet sequence is:\n"); - { - int i; - for ( i=0;i<value->len;i++) - printk("%02x", - (int)(*value->t.oct_seq + i) ); - printk("\n"); - } -#endif - query_lsapsel(self); - break; - - case IAS_INTEGER: - /* LsapSel seems to be sent to me */ - - if ( value->t.integer == -1){ - DEBUG( 0, "ircomm_getvalue_confirm: invalid value!\n"); - return; - } - if(self->state == COMM_IDLE){ - self->dlsap = value->t.integer; - got_lsapsel(self); - } - break; - - case IAS_STRING: - DEBUG( 0, __FUNCTION__":STRING is not implemented\n"); - DEBUG( 0, __FUNCTION__":received string:%s\n", - value->t.string); - query_lsapsel(self); /* experiment */ - break; - - case IAS_MISSING: - DEBUG( 0, __FUNCTION__":MISSING is not implemented\n"); - break; - - default: - DEBUG( 0, __FUNCTION__":unknown type!\n"); - break; - } -} - - -static void got_lsapsel(struct ircomm_cb * self){ - - struct notify_t notify; - - DEBUG(0, "ircomm:got_lsapsel: got peersap!(%d)\n", self->dlsap ); - - /* remove tsap for server */ - irttp_close_tsap(self->tsap); - - /* create TSAP for initiater ... */ - irda_notify_init(¬ify); - 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; - strncpy( notify.name, "IrCOMM cli", NOTIFY_MAX_NAME); - notify.instance = self; - - self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, - ¬ify ); - ASSERT(self->tsap != NULL, return;); - - - /* - * invoke state machine - * and notify that I'm ready to accept connect_request - */ - - ircomm_next_state(self, COMM_IDLE); - if(self->d_handler) - self->d_handler(self); -} - - - - - -static void query_lsapsel(struct ircomm_cb * self){ - - DEBUG(0, "ircomm:query_lsapsel..\n"); - - /* - * since we've got Parameters field of IAS, we are to get peersap. - */ - - if(!(self->servicetype & THREE_WIRE_RAW)){ - iriap_getvaluebyclass_request - (self->daddr, "IrDA:IrCOMM", "IrDA:TinyTP:LsapSel", - ircomm_getvalue_confirm, self ); - } else { - DEBUG(0,"ircomm:query_lsap:" - "THREE_WIRE_RAW is not implemented!\n"); - } -} - - - -/* - * ircomm_discovery_indication() - * Remote device is discovered, try query the remote IAS to see which - * device it is, and which services it has. - */ - -void ircomm_discovery_indication( DISCOVERY *discovery) -{ - - struct ircomm_cb *self; - - DEBUG( 0, "ircomm_discovery_indication\n"); - - self = discovering_instance; - ASSERT(self != NULL, return;); - ASSERT(self->magic == IRCOMM_MAGIC, return;); - - self->daddr = discovery->daddr; - - DEBUG( 0, "ircomm_discovery_indication:daddr=%08x\n", self->daddr); - - /* query "Parameters" attribute of LM-IAS */ - - DEBUG(0, "ircomm:querying parameters..\n"); -#if 0 - iriap_getvaluebyclass_request(self->daddr, "IrDA:IrCOMM", - - "Parameters", - ircomm_getvalue_confirm, - self); -#else - query_lsapsel(self); -#endif - return; -} - - -struct ircomm_cb * ircomm_attach_cable( __u8 servicetype, - struct notify_t notify, - void *handler ){ - - int i; - struct ircomm_cb *self = NULL; - struct notify_t server_notify; - struct ias_object* obj; - - /* FIXME: it should not be hard coded */ - __u8 oct_seq[6] = { 0,1,4,1,1,1 }; - - ASSERT(ircomm != NULL,return NULL;); - DEBUG(0,"ircomm_attach_cable:\n"); - - - /* find free handle */ - - 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,"ircomm_attach_cable:no free handle!\n"); - return (NULL); - } - - self->in_use = 1; - self->servicetype = servicetype; - - DEBUG(0,"attach_cable:servicetype:%d\n",servicetype); - self->d_handler = handler; - self->notify = notify; - - /* register server.. */ - - /* - * TODO: since server TSAP is currentry hard coded, - * we can use *only one* IrCOMM connection. - * We have to create one more TSAP and register IAS entry dynamically - * each time when we are to allocate new server here. - */ - irda_notify_init(&server_notify); - server_notify.data_indication = ircomm_accept_data_indication; - server_notify.connect_confirm = ircomm_accept_connect_confirm; - server_notify.connect_indication = ircomm_accept_connect_indication; - server_notify.flow_indication = ircomm_accept_flow_indication; - server_notify.disconnect_indication = ircomm_accept_disconnect_indication; - server_notify.instance = self; - strncpy( server_notify.name, "IrCOMM srv", NOTIFY_MAX_NAME); - - self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, - &server_notify); - if(!self->tsap){ - DEBUG(0,"ircomm:Sorry, failed to allocate server_tsap\n"); - return NULL; - } - - /* - * Register with LM-IAS - */ - obj = irias_new_object( "IrDA:IrCOMM", IAS_IRCOMM_ID); - irias_add_integer_attrib( obj, "IrDA:TinyTP:LsapSel", - self->tsap->stsap_sel ); - - /* FIXME: it should not be hard coded */ - - irias_add_octseq_attrib( obj, "Parameters", - &oct_seq[0], 6); - irias_insert_object( obj); - -/* obj = irias_new_object( "IrDA:IrCOMM", IAS_IRCOMM_ID); */ -/* irias_add_octseq_attrib( obj, "Parameters", len, &octseq); */ -/* irias_insert_object( obj); */ - - - - /* and start discovering .. */ - discovering_instance = self; - - switch(servicetype){ - case NINE_WIRE: - DEBUG(0,"ircomm_attach_cable:discovering..\n"); - irlmp_register_layer(S_COMM , CLIENT|SERVER, TRUE, - ircomm_discovery_indication); - break; - -/* case CENTRONICS: */ -/* case THREE_WIRE: */ -/* case THREE_WIRE_RAW: */ - - default: - DEBUG(0,"ircomm_attach_cable:requested servicetype is not " - "implemented!\n"); - return NULL; - } - - ircomm_next_state(self, COMM_IDLE); - return (self); -} - - - - -int ircomm_detach_cable(struct ircomm_cb *self){ - - ASSERT( self != NULL, return -EIO;); - ASSERT( self->magic == IRCOMM_MAGIC, return -EIO;); - - - DEBUG(0,"ircomm_detach_cable:\n"); - - /* shutdown ircomm layer */ - if(self->state != COMM_IDLE ){ - DEBUG(0,"ircomm:detach_cable:not IDLE\n"); - if(self->state != COMM_WAITI) - ircomm_disconnect_request(self, NULL); - } - - - switch(self->servicetype){ -/* case CENTRONICS: */ - case NINE_WIRE: -/* case THREE_WIRE: */ - irlmp_unregister_layer( S_COMM, CLIENT|SERVER ); - break; - -/* case THREE_WIRE_RAW: */ -/* irlmp_unregister( S_COMM ) */ -/* irlmp_unregister( S_PRINTER) */ -/* break; */ - - default: - DEBUG(0,"ircomm_detach_cable:requested servicetype is not " - "implemented!\n"); - return -ENODEV; - } - - /* remove tsaps */ - if(self->tsap) - irttp_close_tsap(self->tsap); - - self->tsap = NULL; - self->in_use = 0; - return 0; -} diff --git a/net/irda/ircomm/ircomm_common.c b/net/irda/ircomm/ircomm_common.c index cfda567ce..bc8758e1e 100644 --- a/net/irda/ircomm/ircomm_common.c +++ b/net/irda/ircomm/ircomm_common.c @@ -33,25 +33,29 @@ #include <linux/proc_fs.h> #include <linux/init.h> -#include <net/irda/irmod.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> -#if 0 -static char *rcsid = "$Id: ircomm_common.c,v 1.13 1998/10/13 12:59:05 takahide Exp $"; -#endif -static char *version = "IrCOMM_common, $Revision: 1.13 $ $Date: 1998/10/13 12:59:05 $ (Takahide Higuchi)"; - +static char *revision_date = "Sun Apr 18 00:40:19 1999"; - -static void ircomm_state_discovery( struct ircomm_cb *self, - IRCOMM_EVENT event, struct sk_buff *skb ); 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, @@ -60,15 +64,43 @@ 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); -void ircomm_next_state( struct ircomm_cb *self, IRCOMM_STATE state); +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"); + -int ircomm_check_handle(int handle); -static void ircomm_parse_control(struct ircomm_cb *self, struct sk_buff *skb, - int type); static char *ircommstate[] = { - "DISCOVERY", "IDLE", + + "DISCOVERY_WAIT", + "QUERYPARAM_WAIT", + "QUERYLSAP_WAIT", + "WAITI", "WAITR", "CONN", @@ -107,38 +139,38 @@ static char *ircommevent[] = { "IRCOMM_DATA_REQUEST", "LMP_DATA_INDICATION", "IRCOMM_CONTROL_REQUEST", -}; -int ircomm_proc_read(char *buf, char **start, off_t offset, - int len, int unused); + "DISCOVERY_INDICATION", + "GOT_PARAMETERS", + "GOT_LSAPSEL", + "QUERYIAS_ERROR", +}; #ifdef CONFIG_PROC_FS -extern struct proc_dir_entry proc_irda; -struct proc_dir_entry proc_ircomm = { - 0, 6, "ircomm", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, NULL, - &ircomm_proc_read, -}; +extern struct proc_dir_entry *proc_irda; #endif static void (*state[])( struct ircomm_cb *self, IRCOMM_EVENT event, struct sk_buff *skb) = { - ircomm_state_discovery, 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( KERN_INFO "%s\n", version); - DEBUG( 4, "ircomm_common:init_module\n"); + printk( "Linux-IrDA: IrCOMM protocol ( revision:%s ) \n", + revision_date); + DEBUG( 4, __FUNCTION__"()\n"); /* allocate master array */ @@ -146,7 +178,7 @@ __initfunc(int ircomm_init(void)) IRCOMM_MAX_CONNECTION, GFP_KERNEL); if ( ircomm == NULL) { - printk( KERN_WARNING "IrCOMM: Can't allocate ircomm array!\n"); + printk( KERN_ERR __FUNCTION__"(): kmalloc failed!\n"); return -ENOMEM; } @@ -158,7 +190,7 @@ __initfunc(int ircomm_init(void)) ircomm[i] = kmalloc( sizeof(struct ircomm_cb), GFP_KERNEL ); if(!ircomm[i]){ - printk(KERN_ERR "ircomm:kmalloc failed!\n"); + printk( KERN_ERR __FUNCTION__"(): kmalloc failed!\n"); return -ENOMEM; } @@ -191,17 +223,20 @@ __initfunc(int ircomm_init(void)) */ #ifdef CONFIG_PROC_FS - proc_register( &proc_irda, &proc_ircomm); + 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_common:cleanup_module\n"); + DEBUG( 4, "ircomm:cleanup_module\n"); /* * free some resources */ @@ -226,9 +261,10 @@ void ircomm_cleanup(void) } #ifdef CONFIG_PROC_FS - proc_unregister( &proc_irda, proc_ircomm.low_ino); -#endif + remove_proc_entry("ircomm", proc_irda); +#endif /* CONFIG_PROC_FS */ } +#endif /* MODULE */ /* * ---------------------------------------------------------------------- @@ -236,21 +272,27 @@ void ircomm_cleanup(void) * ---------------------------------------------------------------------- */ -void ircomm_accept_data_indication(void *instance, void *sap, struct sk_buff *skb){ +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;); - ASSERT( self->magic == IRCOMM_MAGIC, return;); - ASSERT( skb != NULL, return;); + ASSERT( self != NULL, return -1;); + ASSERT( self->magic == IRCOMM_MAGIC, return -1;); + ASSERT( skb != NULL, return -1;); - DEBUG(4,"ircomm_accept_data_indication:\n"); + DEBUG(4,__FUNCTION__"():\n"); ircomm_do_event( self, TTP_DATA_INDICATION, skb); + self->rx_packets++; + + return 0; } -void ircomm_accept_connect_confirm(void *instance, void *sap, +static void ircomm_accept_connect_confirm(void *instance, void *sap, struct qos_info *qos, - int maxsdusize, struct sk_buff *skb){ + __u32 maxsdusize, struct sk_buff *skb) +{ struct ircomm_cb *self = (struct ircomm_cb *)instance; @@ -259,7 +301,7 @@ void ircomm_accept_connect_confirm(void *instance, void *sap, ASSERT( skb != NULL, return;); ASSERT( qos != NULL, return;); - DEBUG(0,"ircomm_accept_connect_confirm:\n"); + DEBUG(0,__FUNCTION__"(): got connected!\n"); if(maxsdusize == SAR_DISABLE) self->max_txbuff_size = qos->data_size.value; @@ -274,11 +316,11 @@ void ircomm_accept_connect_confirm(void *instance, void *sap, ircomm_do_event( self, TTP_CONNECT_CONFIRM, skb); } -void ircomm_accept_connect_indication(void *instance, void *sap, +static void ircomm_accept_connect_indication(void *instance, void *sap, struct qos_info *qos, - int maxsdusize, - struct sk_buff *skb ){ - + __u32 maxsdusize, + struct sk_buff *skb ) +{ struct ircomm_cb *self = (struct ircomm_cb *)instance; ASSERT( self != NULL, return;); @@ -286,7 +328,7 @@ void ircomm_accept_connect_indication(void *instance, void *sap, ASSERT( skb != NULL, return;); ASSERT( qos != NULL, return;); - DEBUG(0,"ircomm_accept_connect_indication:\n"); + DEBUG(0,__FUNCTION__"()\n"); if(maxsdusize == SAR_DISABLE) self->max_txbuff_size = qos->data_size.value; @@ -295,22 +337,31 @@ void ircomm_accept_connect_indication(void *instance, void *sap, self->qos = qos; ircomm_do_event( self, TTP_CONNECT_INDICATION, skb); + + /* stop connecting */ + wake_up_interruptible( &self->discovery_wait); + wake_up_interruptible( &self->ias_wait); + } -void ircomm_accept_disconnect_indication(void *instance, void *sap, LM_REASON reason, - struct sk_buff *skb){ +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;); - ASSERT( skb != NULL, return;); - DEBUG(0,"ircomm_accept_disconnect_indication:\n"); + DEBUG(0,__FUNCTION__"():\n"); ircomm_do_event( self, TTP_DISCONNECT_INDICATION, skb); + } -void ircomm_accept_flow_indication( void *instance, void *sap, LOCAL_FLOW cmd){ - +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;); @@ -318,73 +369,204 @@ void ircomm_accept_flow_indication( void *instance, void *sap, LOCAL_FLOW cmd){ switch(cmd){ case FLOW_START: - DEBUG(0,"ircomm_accept_flow_indication:START\n"); - - self->pi = TX_READY; + 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, cmd); - ircomm_control_request(self); + self, command); break; case FLOW_STOP: - DEBUG(0,"ircomm_accept_flow_indication:STOP\n"); - self->pi = TX_BUSY; + 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, cmd); + self, command); break; default: - DEBUG(0,"ircomm_accept_flow_indication:unknown status!\n"); + 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; + } +} + + + /* * ---------------------------------------------------------------------- - * Implementation of actions,descrived in section 7.4 of the reference. + * Impl. of actions (descrived in section 7.4 of the reference) * ---------------------------------------------------------------------- */ - static void issue_connect_request(struct ircomm_cb *self, - struct sk_buff *userdata ){ - + struct sk_buff *userdata ) +{ /* TODO: we have to send/build userdata field which contains InitialControlParameters */ - /* but userdata field is not implemeted in irttp.c.. */ switch(self->servicetype){ case THREE_WIRE_RAW: - /* not implemented yet! Do nothing */ - DEBUG(0, "ircomm:issue_connect_request:" - "not implemented servicetype!"); + DEBUG(0, __FUNCTION__"():THREE_WIRE_RAW is not implemented\n"); break; case DEFAULT: - irttp_connect_request(self->tsap, self->dlsap, self->daddr, - NULL, self->maxsdusize, NULL); - break; - case THREE_WIRE: case NINE_WIRE: case CENTRONICS: - irttp_connect_request(self->tsap, self->dlsap, self->daddr, - NULL, self->maxsdusize, NULL); + irttp_connect_request(self->tsap, self->dlsap, + self->saddr, self->daddr, + NULL, self->maxsdusize, userdata); break; default: - DEBUG(0,"ircomm:issue_connect_request:Illegal servicetype %d\n" - ,self->servicetype); + printk(KERN_ERR __FUNCTION__"():Illegal servicetype %d\n" + ,self->servicetype); } } - -static void disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb){ +static void disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb) +{ /* * Not implemented parameter"Reason".That is optional. @@ -394,12 +576,13 @@ static void disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb){ if(self->notify.disconnect_indication) self->notify.disconnect_indication( self->notify.instance, self, - self->reason,skb); + self->reason, skb); } static void connect_indication(struct ircomm_cb *self, struct qos_info *qos, - struct sk_buff *skb){ + struct sk_buff *skb) +{ /* If controlparameters don't exist, we use the servicetype"DEFAULT".*/ /* if( !ircomm_parse_controlchannel( self, data)) */ @@ -412,13 +595,16 @@ static void connect_indication(struct ircomm_cb *self, struct qos_info *qos, #if 0 /* it's for THREE_WIRE_RAW.*/ -static void connect_indication_three_wire_raw(void){ +static void connect_indication_three_wire_raw(void) +{ DEBUG(0,"ircomm:connect_indication_threewire():not implemented!"); } #endif -static void connect_confirmation(struct ircomm_cb *self, struct sk_buff *skb){ +static void connect_confirmation(struct ircomm_cb *self, struct sk_buff *skb) +{ + DEBUG(4 ,__FUNCTION__"()\n"); /* give a connect_confirm to the client */ if( self->notify.connect_confirm ) @@ -427,13 +613,13 @@ static void connect_confirmation(struct ircomm_cb *self, struct sk_buff *skb){ } static void issue_connect_response(struct ircomm_cb *self, - struct sk_buff *skb ){ + struct sk_buff *skb) +{ - DEBUG(0,"ircomm:issue_connect_response:\n"); + DEBUG(0,__FUNCTION__"()\n"); if( self->servicetype == THREE_WIRE_RAW){ - DEBUG(0,"ircomm:issue_connect_response():3WIRE-RAW is not " - "implemented yet !\n"); + DEBUG(0,__FUNCTION__"():THREE_WIRE_RAW is not implemented yet\n"); /* irlmp_connect_rsp(); */ } else { irttp_connect_response(self->tsap, self->maxsdusize, skb); @@ -441,64 +627,91 @@ static void issue_connect_response(struct ircomm_cb *self, } static void issue_disconnect_request(struct ircomm_cb *self, - struct sk_buff *userdata ){ + struct sk_buff *userdata) +{ if(self->servicetype == THREE_WIRE_RAW){ - DEBUG(0,"ircomm:issue_disconnect_request():3wireraw is not implemented!"); + DEBUG(0,__FUNCTION__"():3wireraw is not implemented\n"); } else - irttp_disconnect_request(self->tsap, NULL, P_NORMAL); + irttp_disconnect_request(self->tsap, userdata, + self->disconnect_priority); } static void issue_data_request(struct ircomm_cb *self, - struct sk_buff *userdata ){ + struct sk_buff *userdata ) +{ int err; if(self->servicetype == THREE_WIRE_RAW){ /* irlmp_data_request(self->lmhandle,userdata); */ - DEBUG(0,"ircomm:issue_data_request():not implemented!"); + DEBUG(0,__FUNCTION__"():not implemented!"); return; } - DEBUG(4,"ircomm:issue_data_request():sending frame\n"); + DEBUG(4,__FUNCTION__"():sending frame\n"); err = irttp_data_request(self->tsap , userdata ); - if(err) - DEBUG(0,"ircomm:ttp_data_request failed\n"); - if(userdata && err) - dev_kfree_skb( 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 ){ - if(self->servicetype == THREE_WIRE_RAW){ - DEBUG(0,"THREE_WIRE_RAW is not implemented\n"); + 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 { - irttp_data_request(self->tsap,userdata); } -} + 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 ){ +static void process_data(struct ircomm_cb *self, struct sk_buff *skb ) +{ - DEBUG(4,"ircomm:process_data:skb_len is(%d),clen_is(%d)\n", + DEBUG(4,__FUNCTION__":skb->len=%d, ircomm header = 1, clen=%d\n", (int)skb->len ,(int)skb->data[0]); - /* - * we always have to parse control channel - * (see page17 of IrCOMM standard) + /* 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); + /* ircomm_parse_control(self, skb, CONTROL_CHANNEL); */ if(self->notify.data_indication && skb->len) self->notify.data_indication(self->notify.instance, self, skb); } -void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *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; } @@ -510,45 +723,38 @@ void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb){ */ static void ircomm_do_event( struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb) { + struct sk_buff *skb) +{ - DEBUG( 4, "ircomm_do_event: STATE = %s, EVENT = %s\n", + DEBUG( 4, __FUNCTION__": STATE = %s, EVENT = %s\n", ircommstate[self->state], ircommevent[event]); (*state[ self->state ]) ( self, event, skb); } -void ircomm_next_state( struct ircomm_cb *self, IRCOMM_STATE state) { +static void ircomm_next_state( struct ircomm_cb *self, IRCOMM_STATE state) +{ self->state = state; - DEBUG( 0, "ircomm_next_state: NEXT STATE = %d(%s), sv(%d)\n", + DEBUG( 0, __FUNCTION__": NEXT STATE=%d(%s), servicetype=(%d)\n", (int)state, ircommstate[self->state],self->servicetype); } -/* - * we currently need dummy (discovering) state for debugging, - * which state is not defined in the reference. - */ - -static void ircomm_state_discovery( struct ircomm_cb *self, - IRCOMM_EVENT event, struct sk_buff *skb ){ - DEBUG(0,"ircomm_state_discovery: " - "why call me? \n"); - if(skb) - dev_kfree_skb( skb); -} - /* * ircomm_state_idle */ static void ircomm_state_idle( struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb ){ + 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_WAITI); */ + /* issue_connect_request( self, skb ); */ + + ircomm_next_state(self, COMM_DISCOVERY_WAIT); + start_discovering(self); break; case TTP_CONNECT_INDICATION: @@ -559,15 +765,128 @@ static void ircomm_state_idle( struct ircomm_cb *self, IRCOMM_EVENT event, case LMP_CONNECT_INDICATION: - /* I think this is already done in irlpt_event.c */ - - DEBUG(0,"ircomm_state_idle():LMP_CONNECT_IND is notimplemented!"); + DEBUG(0,__FUNCTION__"():LMP_CONNECT_IND is notimplemented!"); /* connect_indication_three_wire_raw(); */ /* ircomm_next_state(self, COMM_WAITR); */ break; default: - DEBUG(0,"ircomm_state_idle():unknown event =%d(%s)\n", + 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]); } } @@ -577,7 +896,8 @@ static void ircomm_state_idle( struct ircomm_cb *self, IRCOMM_EVENT event, */ static void ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb ){ + struct sk_buff *skb ) +{ switch(event){ case TTP_CONNECT_CONFIRM: ircomm_next_state(self, COMM_CONN); @@ -596,7 +916,7 @@ static void ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, /* ircomm_next_state(self, COMM_IDLE); */ /* break; */ default: - DEBUG(0,"ircomm_state_waiti:unknown event =%d(%s)\n", + DEBUG(0,__FUNCTION__"():unknown event =%d(%s)\n", event, ircommevent[event]); } } @@ -607,7 +927,8 @@ static void ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, * ircomm_state_waitr */ static void ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb ) { + struct sk_buff *skb ) +{ switch(event){ case IRCOMM_CONNECT_RESPONSE: @@ -615,8 +936,7 @@ static void ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, /* issue_connect_response */ if(self->servicetype==THREE_WIRE_RAW){ - DEBUG(0,"ircomm:issue_connect_response:" - "THREE_WIRE_RAW is not implemented!\n"); + DEBUG(0,__FUNCTION__"():3WIRE_RAW is not implemented\n"); /* irlmp_connect_response(Vpeersap, * ACCEPT,null); */ @@ -629,6 +949,7 @@ static void ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, case IRCOMM_DISCONNECT_REQUEST: ircomm_next_state(self, COMM_IDLE); issue_disconnect_request(self, skb); + queryias_done(self); break; case TTP_DISCONNECT_INDICATION: @@ -636,6 +957,19 @@ static void ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, 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); */ @@ -651,23 +985,20 @@ static void ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, */ static void ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb ){ + struct sk_buff *skb ) +{ switch(event){ case TTP_DATA_INDICATION: process_data(self, skb); - /* stay CONN state*/ break; case IRCOMM_DATA_REQUEST: issue_data_request(self, skb); - /* stay CONN state*/ break; /* case LMP_DATA_INDICATION: */ /* ircomm_data_indicated(); */ -/* stay CONN state */ /* break; */ case IRCOMM_CONTROL_REQUEST: issue_control_request(self, skb); - /* stay CONN state*/ break; case TTP_DISCONNECT_INDICATION: ircomm_next_state(self, COMM_IDLE); @@ -676,11 +1007,26 @@ static void ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, 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]); @@ -688,16 +1034,116 @@ static void ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, } + /* * ---------------------------------------------------------------------- - * ircomm requests + * IrCOMM service interfaces and supporting functions * * ---------------------------------------------------------------------- */ +/* + * start_discovering() + * + * 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(0,__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) + * + * 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"); -void ircomm_connect_request(struct ircomm_cb *self, int maxsdusize){ + 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"); + } +} +/* + * ircomm_connect_request() + * Impl. of this function is differ from one of the reference. + * This functin 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 @@ -706,14 +1152,19 @@ void ircomm_connect_request(struct ircomm_cb *self, int maxsdusize){ ASSERT( self != NULL, return;); ASSERT( self->magic == IRCOMM_MAGIC, return;); - DEBUG(0,"ircomm_connect_request:\n"); - self->maxsdusize = maxsdusize; + DEBUG(0, __FUNCTION__"():sending connect_request...\n"); + + self->servicetype= servicetype; + /* ircomm_control_request(self, SERVICETYPE); */ /*servictype*/ + + self->maxsdusize = SAR_DISABLE; ircomm_do_event( self, IRCOMM_CONNECT_REQUEST, NULL); } void ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata, - int maxsdusize){ + __u32 maxsdusize) +{ ASSERT( self != NULL, return;); ASSERT( self->magic == IRCOMM_MAGIC, return;); @@ -729,11 +1180,8 @@ void ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata, if(!userdata){ /* FIXME: check for errors and initialize? DB */ userdata = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE); - if(!userdata){ - DEBUG(0, __FUNCTION__"alloc_skb failed\n"); - return; - } - IS_SKB(userdata, return;); + ASSERT(userdata != NULL, return;); + skb_reserve(userdata,COMM_HEADER_SIZE); } @@ -746,31 +1194,73 @@ void ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata, ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata); } -void ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *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; - DEBUG(0,"ircomm_disconnect_request\n"); - ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL); + ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata); } -void ircomm_data_request(struct ircomm_cb *self, struct sk_buff *userdata){ +int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *userdata) +{ + __u8 * frame; - ASSERT( self != NULL, return;); - ASSERT( self->magic == IRCOMM_MAGIC, return;); - ASSERT( userdata != NULL, return;); + 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,"ignore IRCOMM_DATA_REQUEST:not connected\n"); - if(userdata) - dev_kfree_skb(userdata); - return; + DEBUG(4,__FUNCTION__"():not connected, data is ignored\n"); + return -EINVAL; } - DEBUG(4,"ircomm_data_request\n"); + 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; } /* @@ -780,19 +1270,25 @@ void ircomm_data_request(struct ircomm_cb *self, struct sk_buff *userdata){ * ---------------------------------------------------------------------- */ -static void ircomm_tx_ctrlbuffer(struct ircomm_cb *self ){ + +static void ircomm_tx_controlchannel(struct ircomm_cb *self ) +{ __u8 clen; struct sk_buff *skb = self->ctrl_skb; - DEBUG(4,"ircomm_tx_ctrlbuffer:\n"); + 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; - if(clen){ - skb_push(skb,1); - skb->data[0]=clen; + ASSERT(clen != 0,return;); + + skb_push(skb,1); + skb->data[0]=clen; #if 0 printk("tx_ctrl:"); @@ -803,145 +1299,106 @@ static void ircomm_tx_ctrlbuffer(struct ircomm_cb *self ){ printk("\n"); } #endif - - ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb); - - skb = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE); - if (skb==NULL){ - DEBUG(0,"ircomm_tx_ctrlbuffer:alloc_skb failed!\n"); - return; - } - skb_reserve(skb,COMM_HEADER_SIZE); - self->ctrl_skb = skb; - } -} - - -void ircomm_control_request(struct ircomm_cb *self){ - struct sk_buff *skb; + ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb); + self->control_ch_pending = 0; - ASSERT( self != NULL, return;); - ASSERT( self->magic == IRCOMM_MAGIC, return;); + skb = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE); + ASSERT(skb != NULL, return ;); - DEBUG(0, "ircomm_control_request:\n"); - - if(self->ttp_stop || self->state != COMM_CONN){ - DEBUG(0,"ircomm_control_request:can't send it.. ignore it\n"); - return; - } - - skb = self->ctrl_skb; - IS_SKB(skb,return;); - - if(skb->len) - ircomm_tx_ctrlbuffer(self); + skb_reserve(skb,COMM_HEADER_SIZE); + self->ctrl_skb = skb; } -static void append_tuple(struct ircomm_cb *self, - __u8 instruction, __u8 pl , __u8 *value){ - +static void append_tuple(struct ircomm_cb *self, __u8 instruction, __u8 pl , + __u8 *value) +{ __u8 *frame; struct sk_buff *skb; - int i,c; + int i,c = 0; + unsigned long flags; + + save_flags(flags);cli(); skb = self->ctrl_skb; ASSERT(skb != NULL, return;); - IS_SKB(skb,return;); - /*if there is little room in the packet... */ - - if(skb->len > COMM_DEFAULT_DATA_SIZE - COMM_HEADER_SIZE - (pl+2)){ - if(!self->ttp_stop && self->state == COMM_CONN){ - - /* send a packet if we can */ - ircomm_tx_ctrlbuffer(self); - skb = self->ctrl_skb; - } else { - DEBUG(0, "ircomm_append_ctrl:there's no room.. ignore it\n"); - - /* TODO: we have to detect whether we have to resend some - information after ttp_stop is cleared */ - - /* self->resend_ctrl = 1; */ - 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); - c = 0; 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; } /* - * ircomm_append_ctrl(); + * ircomm_control_request(); * this function is exported as a request to send some control-channel tuples * to peer device */ -void ircomm_append_ctrl(struct ircomm_cb *self, __u8 instruction){ +void ircomm_control_request(struct ircomm_cb *self, __u8 instruction) +{ - __u8 pv[70]; + __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->state != COMM_CONN) - return; - if(self->servicetype == THREE_WIRE_RAW){ - DEBUG(0,"THREE_WIRE_RAW shuold not use me!\n"); + DEBUG(0,__FUNCTION__"():THREE_WIRE_RAW shuold not use me!\n"); return; } - DEBUG(4,"ircomm_append_ctrl:\n"); + DEBUG(4,__FUNCTION__"()\n"); /* find parameter and its length */ - switch(instruction){ + if(self->servicetype == THREE_WIRE) goto threewire; + if(self->servicetype == NINE_WIRE) goto ninewire; - case POLL_FOR_LINE_SETTINGS: - case STATUS_QUERY: + + /* 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 SERVICETYPE: - value[0] = self->servicetype; - append_tuple(self,instruction,1,value); + case STATUS_QUERY: + append_tuple(self,instruction,0,NULL); break; - case DATA_FORMAT: - value[0] = self->data_format; + case SET_BUSY_TIMEOUT: + value[0] = self->busy_timeout; append_tuple(self,instruction,1,value); break; - case FLOW_CONTROL: - if(self->null_modem_mode){ + 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; + } - /* 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); + ninewire: + switch(instruction){ + case POLL_FOR_LINE_SETTINGS: + append_tuple(self,instruction,0,NULL); break; case DTELINE_STATE: if(self->null_modem_mode){ @@ -972,8 +1429,42 @@ void ircomm_append_ctrl(struct ircomm_cb *self, __u8 instruction){ value[0] = self->dce; append_tuple(self,instruction,1,value); break; - case SET_BUSY_TIMEOUT: - value[0] = self->busy_timeout; + + 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; @@ -989,12 +1480,6 @@ void ircomm_append_ctrl(struct ircomm_cb *self, __u8 instruction){ append_tuple(self,instruction,2,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; - case DATA_RATE: temp = self->data_rate; value[3] = (__u8)((temp >> 24) & 0x000000ff); @@ -1003,202 +1488,300 @@ void ircomm_append_ctrl(struct ircomm_cb *self, __u8 instruction){ 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 < 70){ + if(temp < 32){ value = (__u8) (self->port_name); append_tuple(self,instruction,temp,value); - } - break; + }else + DEBUG(0,__FUNCTION__"() PORT_NAME:too long\n"); #endif - -/* TODO: control tuples for centronics emulation is not implemented */ -/* case IEEE1284_MODE: */ + break; default: - DEBUG(0,"ircomm_append_ctrl:instruction(0x%02x)is not" - "implemented\n",instruction); + if(notsupp) + DEBUG(0,__FUNCTION__"():instruction(0x%02x)is not" + "implemented\n",instruction); } } -static void ircomm_parse_control(struct ircomm_cb *self, - struct sk_buff *skb, - int type){ +void ircomm_parse_tuples(struct ircomm_cb *self, struct sk_buff *skb, int type) +{ __u8 *data; - __u8 pi,pl,pv[64]; + __u8 pi,plen; int clen = 0; - int i,indicate,count = 0; + int indicate=0; - - data = skb->data; - if(type == IAS_PARAM) - clen = ((data[count++] << 8) & data[count++]); /* MSB first */ - else /* CONTROL_CHANNEL */ - clen = data[count++]; + ASSERT(skb != NULL, return;); + ASSERT(self != NULL, return ;); + ASSERT(self->magic == IRCOMM_MAGIC, return ;); - if(clen == 0){ - skb_pull( skb, 1); /* remove clen field */ - 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; - - while( count < clen ){ /* * parse controlparameters and set value into structure */ - pi = data[count++]; - pl = data[count++]; - - DEBUG(0, "parse_control:instruction(0x%02x)\n",pi) ; + pi = data[0]; + plen = data[1]; + ASSERT( clen >= 2+plen, goto corrupted; ); + DEBUG(4, __FUNCTION__"():instruction=0x%02x,len=%d\n", + pi, plen) ; - /* copy a tuple into pv[] */ -#ifdef IRCOMM_DEBUG_TUPLE - printk("data:"); - for(i=0; i < pl; i++){ - pv[i] = data[count++]; - printk("%02x",pv[i]); - } - printk("\n"); -#else - for(i=0; i < pl; i++) - pv[i] = data[count++]; -#endif + switch(pi) + { + case POLL_FOR_LINE_SETTINGS: + ircomm_control_request(self, DTELINE_STATE); + break; - - /* parse pv */ - indicate = 0; - - switch(pi){ - - /* - * for 3-wire/9-wire/centronics - */ - case SERVICETYPE: - self->peer_servicetype = pv[0]; + self->peer_servicetype = data[2]; break; + case PORT_TYPE: - self->peer_port_type = pv[0]; - break; -#if 0 - case PORT_NAME: - self->peer_port_name = *pv; - break; - case FIXED_PORT_NAME: - self->peer_port_name = *pv; - /* - * We should not connect if user of IrCOMM can't - * recognize the port name - */ - self->port_name_critical = TRUE; - break; -#endif - case DATA_RATE: - self->peer_data_rate = (pv[3]<<24) & (pv[2]<<16) - & (pv[1]<<8) & pv[0]; - indicate = 1; + self->peer_port_type = data[2]; break; + case DATA_FORMAT: - self->peer_data_format = pv[0]; + self->peer_data_format = data[2]; break; + case FLOW_CONTROL: - self->peer_flow_ctrl = pv[0]; - indicate = 1; - break; - case XON_XOFF_CHAR: - self->peer_xon_char = pv[0]; - self->peer_xoff_char = pv[1]; - indicate = 1; - break; - case ENQ_ACK_CHAR: - self->peer_enq_char = pv[0]; - self->peer_ack_char = pv[1]; + self->peer_flow_ctrl = data[2]; indicate = 1; break; + case LINESTATUS: - self->peer_line_status = pv[0]; + self->peer_line_status = data[2]; indicate = 1; break; + case BREAK_SIGNAL: - self->peer_break_signal = pv[0]; + self->peer_break_signal = data[2]; /* indicate = 1; */ break; - - /* - * for 9-wire - */ + + 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(pv[0] & DELTA_DTR) - self->peer_dce |= DELTA_DSR|DELTA_RI|DELTA_DCD; - if(pv[0] & MCR_DTR) - self->peer_dce |= MSR_DSR|MSR_RI|MSR_DCD; - + 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(pv[0] & DELTA_RTS) + if(data[2] & DELTA_RTS) self->peer_dce |= DELTA_CTS; - if(pv[0] & MCR_RTS) + if(data[2] & MCR_RTS) self->peer_dce |= MSR_CTS; }else{ - self->peer_dte = pv[0]; + 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 DCELINE_STATE: - self->peer_dce = pv[0]; + case ENQ_ACK_CHAR: + self->peer_enq_char = data[2]; + self->peer_ack_char = data[3]; indicate = 1; break; - case POLL_FOR_LINE_SETTINGS: - ircomm_append_ctrl(self, DTELINE_STATE); - ircomm_control_request(self); + case DATA_RATE: + self->peer_data_rate = ( data[5]<<24 + & data[4]<<16 + & data[3]<<8 + & data[2]); + indicate = 1; break; - /* - * for centronics .... not implemented yet + 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 */ -/* case STATUS_QUERY: */ -/* case SET_BUSY_TIMEOUT: */ -/* case IEEE1284_MODE_SUPPORT: */ -/* case IEEE1284_DEVICEID: */ -/* case IEEE1284_MODE: */ -/* case IEEE1284_ECP_EPP_DATA_TRANSFER: */ - - default: - DEBUG(0, "ircomm_parse_control:not implemented " - "instruction(%d)\n", pi); + 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(0,"ircomm:parse_control:indicating..:\n"); + + 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, 0); - indicate = 0; + self->notify.flow_indication(self->notify.instance, + self, + CONTROL_CHANNEL); } + skb_pull(skb, 2+plen); + clen -= (2+plen); } - skb_pull( skb, 1+clen); + 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(¬ify); + 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, + ¬ify); + 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() @@ -1209,20 +1792,21 @@ static void ircomm_parse_control(struct ircomm_cb *self, */ #ifdef MODULE +int init_module(void) +{ + int err; -int init_module(void) { - ircomm_init(); + err = ircomm_init(); - DEBUG( 4, "ircomm:init_module:done\n"); - return 0; + DEBUG( 4, __FUNCTION__"():done.\n"); + return err; } void cleanup_module(void) { ircomm_cleanup(); - DEBUG( 0, "ircomm_common:cleanup_module:done.\n"); + DEBUG( 4, __FUNCTION__"():done.\n"); } - #endif /* MODULE */ /************************************************************ @@ -1238,53 +1822,92 @@ void cleanup_module(void) * */ int ircomm_proc_read(char *buf, char **start, off_t offset, - int len, int unused){ + 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, "???\t"); - }else { - 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\t", - ircommservicetype[index]); - if(index){ - 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 ", - ircomm[i]->notify.name); - len += sprintf(buf+len, "nullmodem emulation: %s", - (ircomm[i]->null_modem_mode ? "yes":"no")); - } + 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, "\n"); + 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/irvtd.c b/net/irda/ircomm/irvtd.c deleted file mode 100644 index e01781556..000000000 --- a/net/irda/ircomm/irvtd.c +++ /dev/null @@ -1,153 +0,0 @@ -/********************************************************************* - * - * Filename: irvtd.c - * Version: - * Description: A virtual tty driver implementaion, - * which also may be called as "Port Emulation Entity" - * in IrCOMM specification. - * Status: Experimental. - * Author: Takahide Higuchi <thiguchi@pluto.dti.ne.jp> - * Source: irlpt.c - * - * Copyright (c) 1998, 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 <net/irda/irda.h> -#include <net/irda/irlmp.h> - -#include <net/irda/irvtd.h> -#include <net/irda/irvtd_driver.h> - -struct irvtd_cb **irvtd = NULL; -extern struct ircomm_cb **ircomm; - -#if 0 -static char *rcsid = "$Id: irvtd.c,v 1.2 1998/09/27 08:37:04 takahide Exp $"; -#endif -static char *version = "IrVTD, $Revision: 1.2 $ $Date: 1998/09/27 08:37:04 $ (Takahide Higuchi)"; - - -/************************************************************ - * init & cleanup this module - ************************************************************/ - -/* - * Function init_module(void) - * - * Initializes the ircomm control structure - * This Function is called when you do insmod. - */ - -__initfunc(int irvtd_init(void)) -{ - int i; - - DEBUG( 4, "irvtd:init_module:\n"); - printk( KERN_INFO "%s\n", version); - - /* we allocate master array */ - - irvtd = (struct irvtd_cb **) kmalloc( sizeof(void *) * - COMM_MAX_TTY,GFP_KERNEL); - if ( irvtd == NULL) { - printk( KERN_WARNING "irvtd: Can't allocate array!\n"); - return -ENOMEM; - } - - memset( irvtd, 0, sizeof(void *) * COMM_MAX_TTY); - - - /* we initialize structure */ - - for (i=0; i < COMM_MAX_TTY; i++){ - irvtd[i] = kmalloc( sizeof(struct irvtd_cb), GFP_KERNEL); - if(irvtd[i] == NULL){ - printk(KERN_ERR "ircomm_open(): kmalloc failed!\n"); - return -ENOMEM; - } - - memset( irvtd[i], 0, sizeof(struct irvtd_cb)); - irvtd[i]->magic = IRVTD_MAGIC; - } - - /* - * initialize a "port emulation entity" - */ - - if(irvtd_register_ttydriver()){ - printk( KERN_WARNING "IrCOMM: Error in ircomm_register_device\n"); - return -ENODEV; - } - - - DEBUG( 4, "irvtd:init_module:done\n"); - return 0; -} - -void irvtd_cleanup(void) -{ - int i; - DEBUG( 4, "--> ircomm:cleanup_module\n"); - - /* - * free some resources - */ - if (irvtd) { - for (i=0; i<COMM_MAX_TTY; i++) { - if (irvtd[i]) { - DEBUG( 4, "freeing structures\n"); - /* irvtd_close(); :{| */ - kfree(irvtd[i]); - irvtd[i] = NULL; - } - } - DEBUG( 4, "freeing master array\n"); - kfree(irvtd); - irvtd = NULL; - } - - - - DEBUG( 0, "unregister_ttydriver..\n"); - irvtd_unregister_ttydriver(); - - DEBUG( 4, "ircomm:cleanup_module -->\n"); -} - -#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 */ - - - diff --git a/net/irda/ircomm/irvtd_driver.c b/net/irda/ircomm/irvtd_driver.c index 7738e3e78..2df2fdd60 100644 --- a/net/irda/ircomm/irvtd_driver.c +++ b/net/irda/ircomm/irvtd_driver.c @@ -2,7 +2,7 @@ * * Filename: irvtd_driver.c * Version: - * Description: An implementation of "port emulation entity" of IrCOMM + * 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 @@ -22,17 +22,20 @@ ********************************************************************/ #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> -#include <net/irda/irvtd_driver.h> #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -41,14 +44,15 @@ #define DO_RESTART #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) -static char *irvtd_ttyname = "irnine"; -struct tty_driver irvtd_drv, irvtd_callout_driver; +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 ircomm_vsd_refcount; -extern struct ircomm_cb **ircomm; -extern struct irvtd_cb **irvtd; +static int irvtd_refcount; +struct irvtd_cb **irvtd = NULL; + +static char *revision_date = "Sun Apr 18 17:31:53 1999"; + /* * prototypes @@ -70,158 +74,32 @@ 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 flush_txbuff(struct irvtd_cb *info); static void change_speed(struct irvtd_cb *driver); -static void irvtd_write_to_tty( void *instance ); - +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); -#if 0 -static char *rcsid = "$Id: irvtd_driver.c,v 1.13 1998/12/06 10:09:07 takahide Exp $"; -#endif - - - - -/* - * Function ircomm_register_device(void) - * we register "port emulation entity"(see IrCOMM specification) here - * as a tty device. - * it will be called when you insmod. - * ( This function derives from linux/drivers/char/serial.c ) - */ - -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.name = irvtd_ttyname; - 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 */ - irvtd_drv.subtype = IRVTD_TYPE_NORMAL; /* private type */ - - /* - * 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 = &ircomm_vsd_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 = NULL; - irvtd_drv.wait_until_sent = NULL; - - /* - * The callout device is just like normal device except for - * minor number and the subtype. - */ - - /* What is difference between callout device and normal device? */ - /* My system dosen't have /dev/cua??, so we don't need it? :{| */ - irvtd_callout_driver = irvtd_drv; - irvtd_callout_driver.name = "irninecua"; - irvtd_callout_driver.minor_start = IRVTD_CALLOUT_MINOR; - irvtd_callout_driver.subtype = IRVTD_TYPE_CALLOUT; - - - if (tty_register_driver(&irvtd_drv)){ - DEBUG(0,"IrCOMM:Couldn't register tty driver\n"); - return(1); - } - if (tty_register_driver(&irvtd_callout_driver)) - DEBUG(0,"IrCOMM:Couldn't register callout tty driver\n"); - - 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); - err = tty_unregister_driver(&irvtd_callout_driver); - if (err) - printk("IrCOMM: failed to unregister vtd_callout driver(%d)\n", err); - - DEBUG( 4, "irvtd_unregister_device -->\n"); - return; -} +static void irvtd_start_timer( struct irvtd_cb *driver); +static void irvtd_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); /* - * ---------------------------------------------------------------------- - * Routines for Virtual tty driver + ********************************************************************** * - * most of infomation is descrived in linux/tty_driver.h, but - * a function ircomm_receive() derives from receive_chars() which is - * in 2.0.30 kernel (driver/char/serial.c). - * if you want to understand them, please see related kernel source - * (and my comments :). - * ---------------------------------------------------------------------- - */ - -/* - * ---------------------------------------------------------------------- - * ircomm_receive_data() + * ircomm_receive_data() and friends * * like interrupt handler in the serial.c,we receive data when * ircomm_data_indication comes - * ---------------------------------------------------------------------- + * + ********************************************************************** */ @@ -231,25 +109,24 @@ void irvtd_unregister_ttydriver(void){ * send incoming/queued data to tty */ -static void irvtd_write_to_tty( void *instance ){ - +static void irvtd_write_to_tty( struct irvtd_cb *driver) +{ int status, c, flag; - struct sk_buff *skb; - struct irvtd_cb *driver = (struct irvtd_cb *)instance; struct tty_struct *tty = driver->tty; - /* does instance still exist ? should be checked */ - ASSERT(driver->magic == IRVTD_MAGIC, return;); - - if(driver->rx_disable ){ - DEBUG(0,__FUNCTION__"rx_disable is true:do_nothing..\n"); + if(driver->rx_disable) return; - } - + skb = skb_dequeue(&driver->rxbuff); - ASSERT(skb != NULL, return;); /* there's nothing */ - IS_SKB(skb, return;); + 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:"); @@ -264,12 +141,6 @@ static void irvtd_write_to_tty( void *instance ){ status = driver->comm->peer_line_status & driver->read_status_mask; /* - * FIXME: we must do ircomm_parse_ctrl() here, instead of - * ircomm_common.c!! - */ - - - /* * if there are too many errors which make a character ignored, * drop characters */ @@ -289,7 +160,7 @@ static void irvtd_write_to_tty( void *instance ){ DEBUG(0,"handling break....\n"); flag = TTY_BREAK; - if (driver->flags & IRVTD_ASYNC_SAK) + if (driver->flags & ASYNC_SAK) /* * do_SAK() seems to be an implementation of the * idea called "Secure Attention Key", @@ -311,7 +182,8 @@ static void irvtd_write_to_tty( void *instance ){ flag = TTY_NORMAL; if(c){ - DEBUG(0,"writing %d chars to tty\n",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; @@ -325,205 +197,207 @@ static void irvtd_write_to_tty( void *instance ){ else { /* queue rest of data again */ - DEBUG(0,__FUNCTION__":retrying frame!\n"); + 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 ); } - /* - * in order to optimize this routine, these two tasks should be - * queued in following order - * ( see run_task_queue() and queue_task() in tqueue.h - */ - if(skb_queue_len(&driver->rxbuff)) - /* let me try again! */ - queue_task(&driver->rx_tqueue, &tq_timer); if(c) - /* read your buffer! */ - queue_task(&tty->flip.tqueue, &tq_timer); + /* let the process read its buffer! */ + tty_flip_buffer_push(tty); - - if(skb_queue_len(&driver->rxbuff)< IRVTD_RX_QUEUE_LOW - && driver->ttp_stoprx){ + if(skb_queue_len(&driver->rxbuff)< IRVTD_RX_QUEUE_LOW && + driver->ttp_stoprx){ irttp_flow_request(driver->comm->tsap, FLOW_START); driver->ttp_stoprx = 0; } + + if(skb_queue_empty(&driver->rxbuff) && driver->disconnect_pend){ + /* disconnect */ + driver->disconnect_pend = 0; + driver->rx_disable = 1; + tty_hangup(driver->tty); + } } -void irvtd_receive_data(void *instance, void *sap, struct sk_buff *skb){ - +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;); - ASSERT(driver->magic == IRVTD_MAGIC, return;); + 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) == 1) - irvtd_write_to_tty(driver); + if(skb_queue_len(&driver->rxbuff) > IRVTD_RX_QUEUE_HIGH){ irttp_flow_request(driver->comm->tsap, FLOW_STOP); driver->ttp_stoprx = 1; } - return; + irvtd_write_to_tty(driver); + return 0; } -#if 0 -void irvtd_receive_data(void *instance, void *sap, struct sk_buff *skb){ +/* + *********************************************************************** + * + * irvtd_send_data() and friends + * + * like interrupt handler in the serial.c,we send data when + * a timer is expired + * + *********************************************************************** + */ + + +static void irvtd_start_timer( struct irvtd_cb *driver) +{ + ASSERT( driver != NULL, return;); + ASSERT( driver->magic == IRVTD_MAGIC, return;); + + del_timer( &driver->timer); - int flag,status; - __u8 c; - struct tty_struct *tty; - struct irvtd_cb *driver = (struct irvtd_cb *)instance; + driver->timer.data = (unsigned long) driver; + driver->timer.function = &irvtd_timer_expired; + driver->timer.expires = jiffies + (HZ / 5); /* 200msec */ + + add_timer( &driver->timer); +} - ASSERT(driver != NULL, return;); - ASSERT(driver->magic == IRVTD_MAGIC, return;); - if(driver->rx_disable ){ - DEBUG(0,__FUNCTION__"rx_disable is true:do nothing\n"); - return; - } +static void irvtd_timer_expired(unsigned long data) +{ + struct irvtd_cb *driver = (struct irvtd_cb *)data; - tty = driver->tty; - status = driver->comm->peer_line_status & driver->read_status_mask; + ASSERT(driver != NULL,return;); + ASSERT(driver->magic == IRVTD_MAGIC,return;); + DEBUG(4, __FUNCTION__"()\n"); - c = MIN(skb->len, (TTY_FLIPBUF_SIZE - tty->flip.count)); - DEBUG(0, __FUNCTION__"skb_len=%d, tty->flip.count=%d \n" - ,(int)skb->len, tty->flip.count); + irvtd_send_data_request(driver); -#ifdef IRVTD_DEBUG_RX - printk("received data:"); + irvtd_write_to_tty(driver); + + /* start our timer again and again */ + irvtd_start_timer(driver); +} + + +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("%02x", skb->data[i]); printk("\n"); } #endif - /* - * if there are too many errors which make a character ignored, - * drop characters - */ - - if(status & driver->ignore_status_mask){ - DEBUG(0,__FUNCTION__"I/O error:ignore characters.\n"); - dev_kfree_skb(skb, FREE_READ); - return; - } - - if (driver->comm->peer_break_signal ) { - driver->comm->peer_break_signal = 0; - DEBUG(0,"handling break....\n"); - - flag = TTY_BREAK; - if (driver->flags & IRVTD_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 is "Orange book"!? - */ - 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; + DEBUG(1, __FUNCTION__"():sending %d octets\n",(int)skb->len ); + driver->icount.tx += skb->len; + err = ircomm_data_request(driver->comm, driver->txbuff); + if (err){ + ASSERT(err == 0,;); + DEBUG(0,"%d chars are lost\n",(int)skb->len); + skb_trim(skb, 0); + } - if(c){ - DEBUG(0,"writing %d chars to tty\n",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); - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); + /* allocate a new frame */ + skb = driver->txbuff = dev_alloc_skb(driver->comm->max_txbuff_size); + if (skb == NULL){ + printk(__FUNCTION__"():alloc_skb failed!\n"); + } else { + skb_reserve(skb, COMM_HEADER_SIZE); } - if(skb->len >0) - DEBUG(0,__FUNCTION__":dropping frame!\n"); - dev_kfree_skb(skb, FREE_READ); - DEBUG(4,__FUNCTION__":done\n"); + + wake_up_interruptible(&driver->tty->write_wait); } -#endif + /* - * ---------------------------------------------------------------------- + *********************************************************************** + * * indication/confirmation handlers: - * they will be registerd in irvtd_startup() to know that we - * discovered (or we are discovered by) remote device. - * ---------------------------------------------------------------------- + * + * these routines are handlers for IrCOMM protocol stack + * + *********************************************************************** */ -/* this function is called whed ircomm_attach_cable succeed */ - -void irvtd_attached(struct ircomm_cb *comm){ - - ASSERT(comm != NULL, return;); - ASSERT(comm->magic == IRCOMM_MAGIC, return;); - - DEBUG(0,"irvtd_attached:sending connect_request" - " for servicetype(%d)..\n",comm->servicetype); - ircomm_connect_request(comm, SAR_DISABLE ); -} - /* - * irvtd_connect_confirm() - * ircomm_connect_request which we have send have succeed! + * Function irvtd_connect_confirm (instance, sap, qos, max_sdu_size, skb) + * + * ircomm_connect_request which we have send have succeed! + * */ - void irvtd_connect_confirm(void *instance, void *sap, struct qos_info *qos, - int max_sdu_size, struct sk_buff *skb){ - + __u32 max_sdu_size, struct sk_buff *skb) +{ struct irvtd_cb *driver = (struct irvtd_cb *)instance; ASSERT(driver != NULL, return;); ASSERT(driver->magic == IRVTD_MAGIC, return;); /* + * set default value + */ + + driver->msr |= (MSR_DCD|MSR_RI|MSR_DSR|MSR_CTS); + + /* * sending initial control parameters here - * - * TODO: it must be done in ircomm_connect_request() */ -#if 1 if(driver->comm->servicetype == THREE_WIRE_RAW) return; /* do nothing */ - ircomm_append_ctrl(driver->comm, SERVICETYPE); - /* ircomm_append_ctrl(self, DATA_RATE); */ - ircomm_append_ctrl(driver->comm, DATA_FORMAT); - ircomm_append_ctrl(driver->comm, FLOW_CONTROL); - ircomm_append_ctrl(driver->comm, XON_XOFF_CHAR); - /* ircomm_append_ctrl(driver->comm, ENQ_ACK_CHAR); */ + 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_append_ctrl(driver->comm, DTELINE_STATE); + ircomm_control_request(driver->comm, DTELINE_STATE); break; default: } - ircomm_control_request(driver->comm); -#endif - + driver->tx_disable = 0; wake_up_interruptible(&driver->open_wait); } /* - * irvtd_connect_indication() - * we are discovered and being requested to connect by remote device ! + * 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, - int max_sdu_size, struct sk_buff *skb) + __u32 max_sdu_size, struct sk_buff *skb) { struct irvtd_cb *driver = (struct irvtd_cb *)instance; @@ -535,46 +409,103 @@ void irvtd_connect_indication(void *instance, void *sap, struct qos_info *qos, DEBUG(4,"irvtd_connect_indication:sending connect_response...\n"); - /*TODO: connect_response should send initialcontrolparameters! TH*/ - ircomm_connect_response(comm, NULL, SAR_DISABLE ); - wake_up_interruptible(&driver->open_wait); -} + 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: + } -void irvtd_disconnect_indication(void *instance, void *sap , LM_REASON reason, - struct sk_buff *skb){ + 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"); - tty_hangup(driver->tty); + + driver->tx_disable = 1; + if(skb_queue_empty(&driver->rxbuff)){ + /* disconnect */ + driver->rx_disable = 1; + tty_hangup(driver->tty); + } else { + driver->disconnect_pend = 1; + } } /* - * irvtd_control_indication + * Function irvtd_control_indication (instance, sap, cmd) + * + * * */ - - -void irvtd_control_indication(void *instance, void *sap, LOCAL_FLOW flow){ - +void irvtd_control_indication(void *instance, void *sap, IRCOMM_CMD cmd) +{ struct irvtd_cb *driver = (struct irvtd_cb *)instance; - __u8 pi; /* instruction of control channel */ ASSERT(driver != NULL, return;); ASSERT(driver->magic == IRVTD_MAGIC, return;); - DEBUG(0,"irvtd_control_indication:\n"); + DEBUG(4,__FUNCTION__"()\n"); + + if(cmd == TX_READY){ + driver->ttp_stoptx = 0; + driver->tty->hw_stopped = driver->cts_stoptx; + irvtd_start_timer( driver); + + if(driver->cts_stoptx) + return; + + /* + * 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->timer); + return; + } + - pi = driver->comm->pi; + ASSERT(cmd == CONTROL_CHANNEL,return;); - switch(pi){ + switch(driver->comm->pi){ case DCELINE_STATE: driver->msr = driver->comm->peer_dce; @@ -591,19 +522,23 @@ void irvtd_control_indication(void *instance, void *sap, LOCAL_FLOW flow){ wake_up_interruptible(&driver->delta_msr_wait); } - if ((driver->flags & IRVTD_ASYNC_CHECK_CD) && (driver->msr & DELTA_DCD)) { + 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 & DELTA_DCD) + if (driver->msr & MSR_DCD) + { + /* DCD raised! */ wake_up_interruptible(&driver->open_wait); - else if (!((driver->flags & IRVTD_ASYNC_CALLOUT_ACTIVE) && - (driver->flags & IRVTD_ASYNC_CALLOUT_NOHUP))) { - - DEBUG(0,"irvtd_control_indication:hangup..\n"); + } + else + { + /* DCD falled */ + DEBUG(0,__FUNCTION__"():hangup..\n"); tty_hangup(driver->tty); } + } if (driver->comm->flow_ctrl & USE_CTS) { @@ -632,50 +567,38 @@ void irvtd_control_indication(void *instance, void *sap, LOCAL_FLOW flow){ driver->cts_stoptx = 1; driver->tty->hw_stopped = 1; -/* driver->IER &= ~UART_IER_THRI; */ -/* serial_out(info, UART_IER, info->IER); */ } } } - - break; - case TX_READY: - driver->ttp_stoptx = 0; - driver->tty->hw_stopped = driver->cts_stoptx; - - /* - * driver->tty->write_wait will keep asleep if - * our txbuff is not empty. - * so if we can really send a packet now, - * send it and then wake it up. - */ - - if(driver->cts_stoptx) - break; - - flush_txbuff(driver); - if ((driver->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - driver->tty->ldisc.write_wakeup) - (driver->tty->ldisc.write_wakeup)(driver->tty); - break; - - case TX_BUSY: - driver->ttp_stoptx = driver->tty->hw_stopped = 1; + case FLOW_CONTROL: + case DATA_RATE: + case XON_XOFF_CHAR: + case DTELINE_STATE: + /* (maybe) nothing to do */ break; default: - DEBUG(0,"irvtd:unknown control..\n"); - + 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 * - * * ---------------------------------------------------------------------- */ @@ -685,114 +608,45 @@ static int irvtd_block_til_ready(struct tty_struct *tty, struct file * filp, { struct wait_queue wait = { current, NULL }; - int retval; + int retval = 0; int do_clocal = 0; /* - * If the device is in the middle of being closed, then block - * (sleep) until it's done, and (when being woke up)then try again. - */ - - if (tty_hung_up_p(filp) || - (driver->flags & IRVTD_ASYNC_CLOSING)) { - if (driver->flags & IRVTD_ASYNC_CLOSING) - interruptible_sleep_on(&driver->close_wait); -#ifdef DO_RESTART - if (driver->flags & IRVTD_ASYNC_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; -#else - return -EAGAIN; -#endif - } - - /* - * If this is a callout device, then just make sure the normal - * device isn't being used. - */ - - if (tty->driver.subtype == IRVTD_TYPE_CALLOUT) { - if (driver->flags & IRVTD_ASYNC_NORMAL_ACTIVE) - return -EBUSY; - if ((driver->flags & IRVTD_ASYNC_CALLOUT_ACTIVE) && - (driver->flags & IRVTD_ASYNC_SESSION_LOCKOUT) && - (driver->session != current->session)) - return -EBUSY; - if ((driver->flags & IRVTD_ASYNC_CALLOUT_ACTIVE) && - (driver->flags & IRVTD_ASYNC_PGRP_LOCKOUT) && - (driver->pgrp != current->pgrp)) - return -EBUSY; - - driver->flags |= IRVTD_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))) { - if (driver->flags & IRVTD_ASYNC_CALLOUT_ACTIVE) - return -EBUSY; - driver->flags |= IRVTD_ASYNC_NORMAL_ACTIVE; return 0; } - if (driver->flags & IRVTD_ASYNC_CALLOUT_ACTIVE) { - if (driver->normal_termios.c_cflag & CLOCAL) - do_clocal = 1; - } else { - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - } - + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + + /* * We wait until ircomm_connect_request() succeed or * ircomm_connect_indication comes - * - * This is what is written in serial.c: - * "Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, driver->count is dropped by one, so that - * rs_close() knows when to free things. We restore it upon - * exit, either normal or abnormal." */ - retval = 0; + add_wait_queue(&driver->open_wait, &wait); - DEBUG(0,"block_til_ready before block: line%d, count = %d\n", - driver->line, driver->count); + DEBUG(0,__FUNCTION__"():before block( line = %d, count = %d )\n", + driver->line, driver->count); - cli(); - if (!tty_hung_up_p(filp)) - driver->count--; - sti(); driver->blocked_open++; - + /* wait for a connection established */ while (1) { current->state = TASK_INTERRUPTIBLE; - - if (!(driver->flags & IRVTD_ASYNC_CALLOUT_ACTIVE) && - (driver->comm->state == COMM_CONN)){ - /* - * signal DTR and RTS - */ - driver->comm->dte = driver->mcr |= (MCR_DTR | MCR_RTS |DELTA_DTR|DELTA_RTS); - - ircomm_append_ctrl(driver->comm, DTELINE_STATE); - ircomm_control_request(driver->comm); - } - + if (tty_hung_up_p(filp) || - !(driver->flags & IRVTD_ASYNC_INITIALIZED)) { + !(driver->flags & ASYNC_INITIALIZED)) { #ifdef DO_RESTART - if (driver->flags & IRVTD_ASYNC_HUP_NOTIFY) + if (driver->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; @@ -802,15 +656,9 @@ static int irvtd_block_til_ready(struct tty_struct *tty, struct file * filp, break; } - /* - * if clocal == 0 or received DCD or state become CONN,then break - */ - - if (!(driver->flags & IRVTD_ASYNC_CALLOUT_ACTIVE) && - !(driver->flags & IRVTD_ASYNC_CLOSING) && + if (!(driver->flags & ASYNC_CLOSING) && (driver->comm->state == COMM_CONN) && - ( do_clocal || (driver->msr & MSR_DCD) ) - ) + ( do_clocal || (driver->msr & MSR_DCD))) break; if(signal_pending(current)){ @@ -818,63 +666,51 @@ static int irvtd_block_til_ready(struct tty_struct *tty, struct file * filp, break; } -#ifdef IRVTD_DEBUG_OPEN - printk(KERN_INFO"block_til_ready blocking:" - " ttys%d, count = %d\n", driver->line, driver->count); -#endif + + 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); - if (!tty_hung_up_p(filp)) - driver->count++; driver->blocked_open--; -#ifdef IRVTD_DEBUG_OPEN - printk("block_til_ready after blocking: ttys%d, count = %d\n", - driver->line, driver->count); -#endif + + DEBUG(0, __FUNCTION__"():after blocking\n"); + if (retval) return retval; - driver->flags |= IRVTD_ASYNC_NORMAL_ACTIVE; return 0; } -static void change_speed(struct irvtd_cb *driver){ - +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; - - - /* - * change baud rate here. but not implemented now - */ - - - - /* * byte size and parity */ - switch (cflag & CSIZE) { - case CS5: cval = 0x00; break; - case CS6: cval = 0x01; break; - case CS7: cval = 0x02; break; - case CS8: cval = 0x03; break; - default: cval = 0x00; break; /* too keep GCC shut... */ + 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 |= 0x04; + cval |= IRCOMM_STOP2; } if (cflag & PARENB) - cval |= 0x08; + cval |= IRCOMM_PARENB; /* enable parity check */ if (!(cflag & PARODD)) - cval |= 0x10; + cval |= IRCOMM_PAREVEN; /* even parity */ /* CTS flow control flag and modem status interrupts */ @@ -884,74 +720,68 @@ static void change_speed(struct irvtd_cb *driver){ driver->comm->flow_ctrl |= ~USE_CTS; if (cflag & CLOCAL) - driver->flags &= ~IRVTD_ASYNC_CHECK_CD; + driver->flags &= ~ASYNC_CHECK_CD; else - driver->flags |= IRVTD_ASYNC_CHECK_CD; + driver->flags |= ASYNC_CHECK_CD; /* * Set up parity check flag */ - driver->read_status_mask = LSR_OE ; + 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; - driver->read_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 | \ - LSR_PE | LSR_FE; - driver->read_status_mask |= LSR_OE | \ - LSR_PE | LSR_FE; - } + if (I_IGNPAR(driver->tty)) + driver->ignore_status_mask |= LSR_OE; } - driver->comm->data_format = cval; - ircomm_append_ctrl(driver->comm, DATA_FORMAT); - ircomm_append_ctrl(driver->comm, FLOW_CONTROL); - ircomm_control_request(driver->comm); - /* output to IrCOMM here*/ + 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){ - +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,4,1,1,1 }; - DEBUG(4,"irvtd_startup:\n" ); + DEBUG(4,__FUNCTION__"()\n" ); + if(driver->flags & ASYNC_INITIALIZED) + return 0; /* * initialize our tx/rx buffer */ - if(driver->flags & IRVTD_ASYNC_INITIALIZED) - return(0); - skb_queue_head_init(&driver->rxbuff); - driver->rx_tqueue.data = driver; - driver->rx_tqueue.routine = irvtd_write_to_tty; - - if(!driver->txbuff){ - driver->txbuff = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE); - if (!driver->txbuff){ - DEBUG(0,"irvtd_open():alloc_skb failed!\n"); - return -ENOMEM; - } - - skb_reserve(driver->txbuff, COMM_HEADER_SIZE); + driver->txbuff = dev_alloc_skb(COMM_DEFAULT_DATA_SIZE); + if (!driver->txbuff){ + DEBUG(0,__FUNCTION__"():alloc_skb failed!\n"); + return -ENOMEM; } + skb_reserve(driver->txbuff, COMM_HEADER_SIZE); irda_notify_init(&irvtd_notify); irvtd_notify.data_indication = irvtd_receive_data; @@ -959,18 +789,33 @@ static int irvtd_startup(struct irvtd_cb *driver){ 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; - strncpy( irvtd_notify.name, "irvtd", NOTIFY_MAX_NAME); + + 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; /* - * register ourself as a service user of IrCOMM - * TODO: other servicetype(i.e. 3wire,3wireraw) + * discover a peer device + * TODO: other servicetype(i.e. 3wire,3wireraw) support */ - - driver->comm = ircomm_attach_cable(NINE_WIRE, irvtd_notify, - irvtd_attached); - if(driver->comm == NULL) - return -ENODEV; + ircomm_connect_request(driver->comm, NINE_WIRE); /* * TODO:we have to initialize control-channel here! @@ -981,63 +826,83 @@ static int irvtd_startup(struct irvtd_cb *driver){ clear_bit(TTY_IO_ERROR, &driver->tty->flags); change_speed(driver); + irvtd_start_timer( driver); - driver->flags |= IRVTD_ASYNC_INITIALIZED; + driver->rx_disable = 0; + driver->tx_disable = 1; + driver->disconnect_pend = 0; return 0; } -int irvtd_open(struct tty_struct * tty, struct file * filp){ - +int irvtd_open(struct tty_struct * tty, struct file * filp) +{ struct irvtd_cb *driver; int retval; int line; - DEBUG(4, "irvtd_open():\n"); + DEBUG(4, __FUNCTION__"():\n"); + MOD_INC_USE_COUNT; line = MINOR(tty->device) - tty->driver.minor_start; - if ((line <0) || (line >= COMM_MAX_TTY)) + if ((line <0) || (line >= COMM_MAX_TTY)){ + MOD_DEC_USE_COUNT; return -ENODEV; + } + driver = irvtd[line]; - driver->line = 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, "irvtd_open : %s%d count %d\n", tty->driver.name, line, + 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) + if (retval){ + DEBUG(0, __FUNCTION__"():irvtd_startup returns %d\n",retval); return retval; - MOD_INC_USE_COUNT; + } retval = irvtd_block_til_ready(tty, filp, driver); if (retval){ - DEBUG(0,"irvtd_open returning after block_til_ready with %d\n", - retval); + DEBUG(0,__FUNCTION__ + "():returning after block_til_ready (errno = %d)\n", retval); return retval; } - if ((driver->count == 1) && driver->flags & IRVTD_ASYNC_SPLIT_TERMIOS){ - if(tty->driver.subtype == IRVTD_TYPE_NORMAL) - *tty->termios = driver->normal_termios; - else - *tty->termios = driver->callout_termios; - - change_speed(driver); - } - driver->session = current->session; driver->pgrp = current->pgrp; - driver->rx_disable = 0; - return (0); + return 0; } @@ -1052,15 +917,55 @@ int irvtd_open(struct tty_struct * tty, struct file * filp){ * ---------------------------------------------------------------------- */ +/* + * 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 & IRVTD_ASYNC_INITIALIZED)) + if (!(driver->flags & ASYNC_INITIALIZED)) return; - DEBUG(4,"irvtd_shutdown:\n"); + DEBUG(1,__FUNCTION__"()\n"); /* * This comment is written in serial.c: @@ -1075,8 +980,8 @@ static void irvtd_shutdown(struct irvtd_cb * driver) driver->mcr &= ~(MCR_DTR|MCR_RTS); driver->comm->dte = driver->mcr; - ircomm_append_ctrl(driver->comm, DTELINE_STATE ); - ircomm_control_request(driver->comm); + ircomm_control_request(driver->comm, DTELINE_STATE ); + save_flags(flags); cli(); /* Disable interrupts */ @@ -1084,42 +989,49 @@ static void irvtd_shutdown(struct irvtd_cb * driver) if (driver->tty) set_bit(TTY_IO_ERROR, &driver->tty->flags); - ircomm_detach_cable(driver->comm); + del_timer( &driver->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); /* is it OK?*/ + dev_kfree_skb(driver->txbuff); driver->txbuff = NULL; } - - driver->flags &= ~IRVTD_ASYNC_INITIALIZED; + 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){ - +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(0, "irvtd_close:refc(%d)\n",ircomm_vsd_refcount); + DEBUG(1, __FUNCTION__"():refcount= %d\n",irvtd_refcount); ASSERT(driver != NULL, return;); ASSERT(driver->magic == IRVTD_MAGIC, return;); save_flags(flags);cli(); - /* - * tty_hung_up_p() is defined as - * " return(filp->f_op == &hung_up_tty_fops); " - * see driver/char/tty_io.c - */ 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; @@ -1127,7 +1039,7 @@ void irvtd_close(struct tty_struct * tty, struct file * filp){ line = MINOR(tty->device) - tty->driver.minor_start; - DEBUG(0, "irvtd_close : %s%d count %d\n", tty->driver.name, line, + DEBUG(0, __FUNCTION__"():%s%d count %d\n", tty->driver.name, line, driver->count); if ((tty->count == 1) && (driver->count != 1)) { @@ -1143,313 +1055,218 @@ void irvtd_close(struct tty_struct * tty, struct file * filp){ driver->count = 1; } if (--driver->count < 0) { - printk("irvtd_close: bad count for line%d: %d\n", + 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 |= IRVTD_ASYNC_CLOSING; - - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - - if (driver->flags & IRVTD_ASYNC_NORMAL_ACTIVE) - driver->normal_termios = *tty->termios; - if (driver->flags & IRVTD_ASYNC_CALLOUT_ACTIVE) - driver->callout_termios = *tty->termios; + 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 != IRVTD_ASYNC_CLOSING_WAIT_NONE) + 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; - - /* - * Now we flush our buffer.., and shutdown ircomm service layer - */ - - /* drop our tx/rx buffer */ - if (tty->driver.flush_buffer) - tty->driver.flush_buffer(tty); - - while(skb_queue_len(&driver->rxbuff)){ - struct sk_buff *skb; - skb = skb_dequeue( &driver->rxbuff); - dev_kfree_skb(skb); - } - - /* drop users buffer? */ + /* 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; - /* - * ad-hoc coding: - * we wait 2 sec before ircomm_detach_cable so that - * irttp will send all contents of its queue - */ - -#if 0 - if (driver->blocked_open) { + if (driver->blocked_open) + { if (driver->close_delay) { -#endif - /* kill time */ current->state = TASK_INTERRUPTIBLE; - schedule_timeout(driver->close_delay + 2*HZ); -#if 0 + schedule_timeout(driver->close_delay); } wake_up_interruptible(&driver->open_wait); } -#endif - - driver->flags &= ~(IRVTD_ASYNC_NORMAL_ACTIVE| - IRVTD_ASYNC_CALLOUT_ACTIVE| - IRVTD_ASYNC_CLOSING); + irvtd_shutdown(driver); + driver->flags &= ~ASYNC_CLOSING; wake_up_interruptible(&driver->close_wait); - irvtd_shutdown(driver); MOD_DEC_USE_COUNT; restore_flags(flags); - DEBUG(4,"irvtd_close:done:refc(%d)\n",ircomm_vsd_refcount); + DEBUG(4, __FUNCTION__"():done\n"); } - - /* * ---------------------------------------------------------------------- * irvtd_write() and friends * This routine will be called when something data are passed from * kernel or user. - * - * NOTE:I have stolen copy_from_user() from 2.0.30 kernel(linux/isdnif.h) - * to access user space of memory carefully. Thanks a lot!:) * ---------------------------------------------------------------------- */ int irvtd_write(struct tty_struct * tty, int from_user, - const unsigned char *buf, int count){ - - struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data; + const unsigned char *buf, int count) +{ + struct irvtd_cb *driver; int c = 0; int wrote = 0; - struct sk_buff *skb = NULL; + unsigned long flags; + struct sk_buff *skb; __u8 *frame; - DEBUG(4, "irvtd_write():\n"); + ASSERT(tty != NULL, return -EFAULT;); + driver = (struct irvtd_cb *)tty->driver_data; + ASSERT(driver != NULL, return -EFAULT;); + ASSERT(driver->magic == IRVTD_MAGIC, return -EFAULT;); - if (!tty || !driver->txbuff) - return 0; + DEBUG(4, __FUNCTION__"()\n"); - - + save_flags(flags); while(1){ + cli(); skb = driver->txbuff; - - c = MIN(count, (skb_tailroom(skb) - COMM_HEADER_SIZE)); - if (c <= 0) + ASSERT(skb != NULL, break;); + c = MIN(count, (skb_tailroom(skb))); + if (c <= 0) break; /* write to the frame */ - frame = skb_put(skb,c); if(from_user){ copy_from_user(frame,buf,c); } else memcpy(frame, buf, c); - /* flush the frame */ - irvtd_flush_chars(tty); + restore_flags(flags); wrote += c; count -= c; + buf += c; + irvtd_send_data_request(driver); } + restore_flags(flags); 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); +} + + /* - * ---------------------------------------------------------------------- - * irvtd_put_char() - * This routine is called by the kernel to pass a single character. - * If we exausted our buffer,we can ignore the character! - * ---------------------------------------------------------------------- + * 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){ - +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 = driver->txbuff; + struct sk_buff *skb; + unsigned long flags; - ASSERT(tty->driver_data != NULL, return;); + ASSERT(driver != NULL, return;); + DEBUG(4, __FUNCTION__"()\n"); - DEBUG(4, "irvtd_put_char:\n"); - if(!driver->txbuff) - return; + save_flags(flags);cli(); + skb = driver->txbuff; + ASSERT(skb != NULL,return;); + ASSERT(skb_tailroom(skb) > 0, return;); DEBUG(4, "irvtd_put_char(0x%02x) skb_len(%d) MAX(%d):\n", (int)ch ,(int)skb->len, - driver->comm->maxsdusize - COMM_HEADER_SIZE); + driver->comm->max_txbuff_size - COMM_HEADER_SIZE); /* append a character */ - frame = skb_put(skb,1); frame[0] = ch; - return; -} - -/* - * ---------------------------------------------------------------------- - * irvtd_flush_chars() and friend - * This routine will be called after a series of characters was written using - * irvtd_put_char().We have to send them down to IrCOMM. - * ---------------------------------------------------------------------- - */ - -static void flush_txbuff(struct irvtd_cb *driver){ - - struct sk_buff *skb = driver->txbuff; - struct tty_struct *tty = driver->tty; - ASSERT(tty != NULL, return;); - -#ifdef IRVTD_DEBUG_TX - printk("flush_txbuff:"); - { - int i; - for ( i=0;i<skb->len;i++) - printk("%02x", skb->data[i]); - printk("\n"); - } -#else - DEBUG(4, "flush_txbuff:count(%d)\n",(int)skb->len); -#endif - - /* add "clen" field */ - skb_push(skb,1); - skb->data[0]=0; /* without control channel */ - - ircomm_data_request(driver->comm, driver->txbuff); - - /* allocate new frame */ - skb = driver->txbuff = dev_alloc_skb(driver->comm->max_txbuff_size); - if (skb == NULL){ - printk(KERN_ERR"flush_txbuff():alloc_skb failed!\n"); - } else { - skb_reserve(skb, COMM_HEADER_SIZE); - } - wake_up_interruptible(&driver->tty->write_wait); -} - -void irvtd_flush_chars(struct tty_struct *tty){ - - struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data; - if(!driver || driver->magic != IRVTD_MAGIC || !driver->txbuff){ - DEBUG(0,"irvtd_flush_chars:null structure:ignore\n"); - return; - } - DEBUG(4, "irvtd_flush_chars():\n"); - - while(tty->hw_stopped){ - DEBUG(4,"irvtd_flush_chars:hw_stopped:sleep..\n"); - tty_wait_until_sent(tty,0); - DEBUG(4,"irvtd_flush_chars:waken up!\n"); - if(!driver->txbuff->len) - return; - } - flush_txbuff(driver); + restore_flags(flags); + return; } - - - /* - * ---------------------------------------------------------------------- - * irvtd_write_room() - * This routine returns the room that our buffer has now. + * Function irvtd_write_room (tty) + * + * This routine returns the room that our buffer has now. * - * NOTE: - * driver/char/n_tty.c drops a character(s) when this routine returns 0, - * and then linux will be frozen after a few minutes :( why? bug? - * ( I found this on linux-2.0.33 ) - * So this routine flushes a buffer if there is few room, TH - * ---------------------------------------------------------------------- */ - int irvtd_write_room(struct tty_struct *tty){ int ret; - struct sk_buff *skb = (struct sk_buff *)((struct irvtd_cb *) tty->driver_data)->txbuff; - - if(!skb){ - DEBUG(0,"irvtd_write_room:NULL skb\n"); - return(0); - } + struct sk_buff *skb = ((struct irvtd_cb *) tty->driver_data)->txbuff; - ret = skb_tailroom(skb) - COMM_HEADER_SIZE; - - if(ret < 0){ - DEBUG(0,"irvtd_write_room:error:room is %d!",ret); - ret = 0; - } - DEBUG(4, "irvtd_write_room:\n"); - DEBUG(4, "retval(%d)\n",ret); + ASSERT(skb !=NULL, return 0;); + ret = skb_tailroom(skb); - /* flush buffer automatically to avoid kernel freeze :< */ - if(ret < 8) /* why 8? there's no reason :) */ - irvtd_flush_chars(tty); + DEBUG(4, __FUNCTION__"(): room is %d bytes\n",ret); return(ret); } /* - * ---------------------------------------------------------------------- - * irvtd_chars_in_buffer() - * This function returns how many characters which have not been sent yet - * are still in buffer. - * ---------------------------------------------------------------------- + * 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 = - (struct sk_buff *) ((struct irvtd_cb *)tty->driver_data) ->txbuff; - DEBUG(4, "irvtd_chars_in_buffer()\n"); + struct sk_buff *skb; + unsigned long flags; - if(!skb){ - printk(KERN_ERR"irvtd_chars_in_buffer:NULL skb\n"); - return(0); - } + 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 */ } /* - * ---------------------------------------------------------------------- - * irvtd_break() - * routine which turns the break handling on or off - * ---------------------------------------------------------------------- + * 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; @@ -1463,14 +1280,14 @@ static void irvtd_break(struct tty_struct *tty, int break_state){ if (break_state == -1) { driver->comm->break_signal = 0x01; - ircomm_append_ctrl(driver->comm, BREAK_SIGNAL); - ircomm_control_request(driver->comm); + ircomm_control_request(driver->comm, BREAK_SIGNAL); + } else { driver->comm->break_signal = 0x00; - ircomm_append_ctrl(driver->comm, BREAK_SIGNAL); - ircomm_control_request(driver->comm); + ircomm_control_request(driver->comm, BREAK_SIGNAL); + } restore_flags(flags); @@ -1497,8 +1314,7 @@ static int get_modem_info(struct irvtd_cb * driver, unsigned int *value) | ((driver->msr & DELTA_RI) ? TIOCM_RNG : 0) | ((driver->msr & DELTA_DSR) ? TIOCM_DSR : 0) | ((driver->msr & DELTA_CTS) ? TIOCM_CTS : 0); - put_user(result,value); - return 0; + return put_user(result,value); } static int set_modem_info(struct irvtd_cb * driver, unsigned int cmd, @@ -1537,19 +1353,111 @@ static int set_modem_info(struct irvtd_cb * driver, unsigned int cmd, } driver->comm->dte = driver->mcr; - ircomm_append_ctrl(driver->comm, DTELINE_STATE ); - ircomm_control_request(driver->comm); + ircomm_control_request(driver->comm, DTELINE_STATE ); + return 0; } -int irvtd_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg){ +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 icounter_struct cnow; - struct icounter_struct *p_cuser; /* user space */ + struct serial_icounter_struct cnow,cprev; + struct serial_icounter_struct *p_cuser; /* user space */ DEBUG(4,"irvtd_ioctl:requested ioctl(0x%08x)\n",cmd); @@ -1557,7 +1465,6 @@ int irvtd_ioctl(struct tty_struct *tty, struct file * file, #ifdef IRVTD_DEBUG_IOCTL { /* kill time so that debug messages will come slowly */ - unsigned long flags; save_flags(flags);cli(); current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + HZ/4; /*0.25sec*/ @@ -1572,7 +1479,6 @@ int irvtd_ioctl(struct tty_struct *tty, struct file * file, (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { if (tty->flags & (1 << TTY_IO_ERROR)){ - DEBUG(0,"irvtd_ioctl:I/O error...\n"); return -EIO; } } @@ -1580,143 +1486,89 @@ int irvtd_ioctl(struct tty_struct *tty, struct file * file, switch (cmd) { case TIOCMGET: - error = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned int)); - if (error) - return error; return get_modem_info(driver, (unsigned int *) arg); case TIOCMBIS: case TIOCMBIC: case TIOCMSET: return set_modem_info(driver, cmd, (unsigned int *) arg); -#if 0 - /* - * we wouldn't implement them since we don't use serial_struct - */ case TIOCGSERIAL: - error = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct serial_struct)); - if (error) - return error; - return irvtd_get_serial_info(driver, - (struct serial_struct *) arg); + return get_serial_info(driver, (struct serial_struct *) arg); case TIOCSSERIAL: - error = verify_area(VERIFY_READ, (void *) arg, - sizeof(struct serial_struct)); - if (error) - return error; - return irvtd_set_serial_info(driver, - (struct serial_struct *) arg); - - - case TIOCSERGETLSR: /* Get line status register */ - error = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(unsigned int)); - if (error) - return error; - else - return get_lsr_info(driver, (unsigned int *) arg); -#endif + return set_serial_info(driver, (struct serial_struct *) arg); -/* - * I think we don't need them - */ -/* case TIOCSERCONFIG: */ - - -/* - * They cannot be implemented because we don't use async_struct - * which is defined in serial.h - */ - -/* case TIOCSERGSTRUCT: */ -/* error = verify_area(VERIFY_WRITE, (void *) arg, */ -/* sizeof(struct async_struct)); */ -/* if (error) */ -/* return error; */ -/* memcpy_tofs((struct async_struct *) arg, */ -/* driver, sizeof(struct async_struct)); */ -/* return 0; */ - -/* case TIOCSERGETMULTI: */ -/* error = verify_area(VERIFY_WRITE, (void *) arg, */ -/* sizeof(struct serial_multiport_struct)); */ -/* if (error) */ -/* return error; */ -/* return get_multiport_struct(driver, */ -/* (struct serial_multiport_struct *) arg); */ -/* case TIOCSERSETMULTI: */ -/* error = verify_area(VERIFY_READ, (void *) arg, */ -/* sizeof(struct serial_multiport_struct)); */ -/* if (error) */ -/* return error; */ -/* return set_multiport_struct(driver, */ -/* (struct serial_multiport_struct *) arg); */ - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) - * to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ 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 (current->signal & ~current->blocked) */ -/* return -ERESTARTSYS; */ - - if ( ((arg & TIOCM_RNG) && (driver->msr & DELTA_RI)) || - ((arg & TIOCM_DSR) && (driver->msr & DELTA_DSR)) || - ((arg & TIOCM_CD) && (driver->msr & DELTA_DCD)) || - ((arg & TIOCM_CTS) && (driver->msr & DELTA_CTS))) { + /* 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: - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ - error = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct icounter_struct)); - if (error) - return error; - cli(); + save_flags(flags); cli(); cnow = driver->icount; - sti(); - p_cuser = (struct icounter_struct *) arg; - put_user(cnow.cts, &p_cuser->cts); - put_user(cnow.dsr, &p_cuser->dsr); - put_user(cnow.rng, &p_cuser->rng); - put_user(cnow.dcd, &p_cuser->dcd); + 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 TIOCGSERIAL: - case TIOCSSERIAL: + 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: - case TIOCSERGWILD: - case TIOCSERSWILD: - case TIOCSERGSTRUCT: - case TIOCSERGETMULTI: - case TIOCSERSETMULTI: - DEBUG(0,"irvtd_ioctl:sorry, ioctl(0x%08x)is not implemented\n",cmd); - return -ENOIOCTLCMD; /* ioctls which are imcompatible with serial.c */ - - case TCSETS: - case TCGETS: - case TCFLSH: + DEBUG(0,__FUNCTION__"():sorry, TIOCSERCONFIG is not supported\n"); + return -ENOIOCTLCMD; + + default: - return -ENOIOCTLCMD; /* ioctls which we must not touch */ + return -ENOIOCTLCMD; /* ioctls which we must ignore */ } return 0; } @@ -1766,14 +1618,14 @@ void irvtd_set_termios(struct tty_struct *tty, struct termios * old_termios){ static void irvtd_send_xchar(struct tty_struct *tty, char ch){ - DEBUG(0, __FUNCTION__"():\n"); + 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(0, "irvtd_throttle:\n"); + DEBUG(1, "irvtd_throttle:\n"); if (I_IXOFF(tty)) irvtd_put_char(tty, STOP_CHAR(tty)); @@ -1781,22 +1633,22 @@ void irvtd_throttle(struct tty_struct *tty){ driver->mcr &= ~MCR_RTS; driver->mcr |= DELTA_RTS; driver->comm->dte = driver->mcr; - ircomm_append_ctrl(driver->comm, DTELINE_STATE ); - ircomm_control_request(driver->comm); + ircomm_control_request(driver->comm, DTELINE_STATE ); + 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(0, "irvtd_unthrottle:\n"); + 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_append_ctrl(driver->comm, DTELINE_STATE ); - ircomm_control_request(driver->comm); + ircomm_control_request(driver->comm, DTELINE_STATE ); + irttp_flow_request(driver->comm->tsap, FLOW_START); } @@ -1829,19 +1681,17 @@ irvtd_start(struct tty_struct *tty){ * ------------------------------------------------------------ * irvtd_hangup() * This routine notifies that tty layer have got HUP signal - * Is this routine right ? :{| * ------------------------------------------------------------ */ void irvtd_hangup(struct tty_struct *tty){ struct irvtd_cb *info = (struct irvtd_cb *)tty->driver_data; - DEBUG(0, "irvtd_hangup()\n"); + DEBUG(0, __FUNCTION__"()\n"); irvtd_flush_buffer(tty); irvtd_shutdown(info); info->count = 0; - info->flags &= ~(IRVTD_ASYNC_NORMAL_ACTIVE|IRVTD_ASYNC_CALLOUT_ACTIVE); info->tty = NULL; wake_up_interruptible(&info->open_wait); } @@ -1851,15 +1701,18 @@ void irvtd_flush_buffer(struct tty_struct *tty){ struct irvtd_cb *driver = (struct irvtd_cb *)tty->driver_data; struct sk_buff *skb; - skb = (struct sk_buff *)driver->txbuff; + skb = driver->txbuff; + ASSERT(skb != NULL, return;); - DEBUG(4, "irvtd_flush_buffer:%d chars are gone..\n",(int)skb->len); - skb_trim(skb,0); + 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); + wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -1867,3 +1720,275 @@ void irvtd_flush_buffer(struct tty_struct *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"); + + 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 ; + } + + /* + * 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 */ |