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