OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k/trunk/linux/linux-2.4/net/irda/ircomm
    from Rev 1275 to Rev 1765
    Reverse comparison

Rev 1275 → Rev 1765

/ircomm_core.c
0,0 → 1,546
/*********************************************************************
*
* Filename: ircomm_core.c
* Version: 1.0
* Description: IrCOMM service interface
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Jun 6 20:37:34 1999
* Modified at: Tue Dec 21 13:26:41 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
*
* 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);
 
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("%s(), can't allocate hashbin!\n", __FUNCTION__);
return -ENOMEM;
}
#ifdef CONFIG_PROC_FS
create_proc_info_entry("ircomm", 0, proc_irda, ircomm_proc_read);
#endif /* CONFIG_PROC_FS */
MESSAGE("IrCOMM protocol (Dag Brattli)\n");
return 0;
}
 
#ifdef MODULE
void ircomm_cleanup(void)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close);
 
#ifdef CONFIG_PROC_FS
remove_proc_entry("ircomm", proc_irda);
#endif /* CONFIG_PROC_FS */
}
#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;
 
IRDA_DEBUG(2, "%s(), service_type=0x%02x\n", __FUNCTION__,
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) {
kfree(self);
return NULL;
}
 
self->service_type = service_type;
self->line = line;
 
hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL);
 
ircomm_next_state(self, IRCOMM_IDLE);
 
return self;
}
 
/*
* Function ircomm_close_instance (self)
*
* Remove IrCOMM instance
*
*/
static int __ircomm_close(struct ircomm_cb *self)
{
IRDA_DEBUG(2,"%s()\n", __FUNCTION__);
 
/* Disconnect link if any */
ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL);
 
/* Remove TSAP */
if (self->tsap) {
irttp_close_tsap(self->tsap);
self->tsap = NULL;
}
 
/* Remove LSAP */
if (self->lsap) {
irlmp_close_lsap(self->lsap);
self->lsap = NULL;
}
self->magic = 0;
 
kfree(self);
 
return 0;
}
 
/*
* Function ircomm_close (self)
*
* Closes and removes the specified IrCOMM instance
*
*/
int ircomm_close(struct ircomm_cb *self)
{
struct ircomm_cb *entry;
 
ASSERT(self != NULL, return -EIO;);
ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;);
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
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;
 
IRDA_DEBUG(2 ,"%s()\n", __FUNCTION__);
 
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 incoming connection
*
*/
void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
struct ircomm_info *info)
{
int clen = 0;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
/* Check if the packet contains data on the control channel */
if (skb->len > 0)
clen = skb->data[0];
/*
* If there are any data hiding in the control channel, we must
* deliver it first. The side effect is that the control channel
* will be removed from the skb
*/
if (self->notify.connect_indication)
self->notify.connect_indication(self->notify.instance, self,
info->qos, info->max_data_size,
info->max_header_size, skb);
else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__);
dev_kfree_skb(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;);
 
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
 
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)
{
IRDA_DEBUG(4,"%s()\n", __FUNCTION__);
 
if (self->notify.connect_confirm )
self->notify.connect_confirm(self->notify.instance,
self, info->qos,
info->max_data_size,
info->max_header_size, skb);
else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__);
dev_kfree_skb(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;
 
IRDA_DEBUG(4,"%s()\n", __FUNCTION__);
 
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)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
 
ASSERT(skb->len > 0, return;);
 
if (self->notify.data_indication)
self->notify.data_indication(self->notify.instance, self, skb);
else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__);
dev_kfree_skb(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 {
IRDA_DEBUG(4, "%s(), data was control info only!\n", __FUNCTION__);
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;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
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;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
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);
else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__);
dev_kfree_skb(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;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
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)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
ASSERT(info != NULL, return;);
 
if (self->notify.disconnect_indication) {
self->notify.disconnect_indication(self->notify.instance, self,
info->reason, skb);
} else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__);
dev_kfree_skb(skb);
}
}
 
/*
* Function ircomm_flow_request (self, flow)
*
*
*
*/
void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
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)
{
struct ircomm_cb *self;
unsigned long flags;
save_flags(flags);
cli();
 
len = 0;
 
self = (struct ircomm_cb *) hashbin_get_first(ircomm);
while (self != NULL) {
ASSERT(self->magic == IRCOMM_MAGIC, break;);
 
if(self->line < 0x10)
len += sprintf(buf+len, "ircomm%d", self->line);
else
len += sprintf(buf+len, "irlpt%d", self->line - 0x10);
len += sprintf(buf+len, " state: %s, ",
ircomm_state[ self->state]);
len += sprintf(buf+len,
"slsap_sel: %#02x, dlsap_sel: %#02x, mode:",
self->slsap_sel, self->dlsap_sel);
if(self->service_type & IRCOMM_3_WIRE_RAW)
len += sprintf(buf+len, " 3-wire-raw");
if(self->service_type & IRCOMM_3_WIRE)
len += sprintf(buf+len, " 3-wire");
if(self->service_type & IRCOMM_9_WIRE)
len += sprintf(buf+len, " 9-wire");
if(self->service_type & IRCOMM_CENTRONICS)
len += sprintf(buf+len, " Centronics");
len += sprintf(buf+len, "\n");
 
self = (struct ircomm_cb *) hashbin_get_next(ircomm);
}
restore_flags(flags);
 
return len;
}
#endif /* CONFIG_PROC_FS */
 
#ifdef MODULE
MODULE_AUTHOR("Dag Brattli <dag@brattli.net>");
MODULE_DESCRIPTION("IrCOMM protocol");
MODULE_LICENSE("GPL");
 
int init_module(void)
{
return ircomm_init();
}
void cleanup_module(void)
{
ircomm_cleanup();
}
#endif /* MODULE */
 
/ircomm_lmp.c
0,0 → 1,340
/*********************************************************************
*
* Filename: ircomm_lmp.c
* Version: 1.0
* Description: Interface between IrCOMM and IrLMP
* Status: Stable
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Jun 6 20:48:27 1999
* Modified at: Sun Dec 12 13:44:17 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: Previous IrLPT work by Thomas Davis
*
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
*
* 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;
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
/* Register callbacks */
irda_notify_init(&notify);
notify.data_indication = ircomm_lmp_data_indication;
notify.connect_confirm = ircomm_lmp_connect_confirm;
notify.connect_indication = ircomm_lmp_connect_indication;
notify.disconnect_indication = ircomm_lmp_disconnect_indication;
notify.instance = self;
strncpy(notify.name, "IrCOMM", NOTIFY_MAX_NAME);
 
self->lsap = irlmp_open_lsap(LSAP_ANY, &notify, 0);
if (!self->lsap) {
IRDA_DEBUG(0, "%s failed to allocate tsap\n", __FUNCTION__);
return -1;
}
self->slsap_sel = self->lsap->slsap_sel;
 
/*
* Initialize the call-table for issuing commands
*/
self->issue.data_request = ircomm_lmp_data_request;
self->issue.connect_request = ircomm_lmp_connect_request;
self->issue.connect_response = ircomm_lmp_connect_response;
self->issue.disconnect_request = ircomm_lmp_disconnect_request;
 
return 0;
}
 
/*
* 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;
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
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 *userdata)
{
struct sk_buff *skb;
int ret;
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
/* Any userdata supplied? */
if (userdata == NULL) {
skb = dev_alloc_skb(64);
if (!skb)
return -ENOMEM;
 
/* Reserve space for MUX and LAP header */
skb_reserve(skb, LMP_MAX_HEADER);
} else {
skb = userdata;
/*
* Check that the client has reserved enough space for
* headers
*/
ASSERT(skb_headroom(skb) >= LMP_MAX_HEADER, return -1;);
}
 
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;
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
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;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
line = cb->line;
 
self = (struct ircomm_cb *) hashbin_find(ircomm, line, NULL);
if (!self) {
IRDA_DEBUG(2, "%s(), didn't find myself\n", __FUNCTION__);
return;
}
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_MAGIC, return;);
 
self->pkt_count--;
 
if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) {
IRDA_DEBUG(2, "%s(), asking TTY to start again!\n", __FUNCTION__);
self->flow_status = FLOW_START;
if (self->notify.flow_indication)
self->notify.flow_indication(self->notify.instance,
self, FLOW_START);
}
}
/*
* Function ircomm_lmp_data_request (self, userdata)
*
* Send data frame to peer device
*
*/
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;
 
IRDA_DEBUG(4, "%s(), sending frame\n", __FUNCTION__);
 
skb->destructor = ircomm_lmp_flow_control;
if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) {
IRDA_DEBUG(2, "%s(), asking TTY to slow down!\n", __FUNCTION__);
self->flow_status = FLOW_STOP;
if (self->notify.flow_indication)
self->notify.flow_indication(self->notify.instance,
self, FLOW_STOP);
}
ret = irlmp_data_request(self->lsap, skb);
if (ret) {
ERROR("%s(), failed\n", __FUNCTION__);
dev_kfree_skb(skb);
}
 
return ret;
}
 
/*
* Function ircomm_lmp_data_indication (instance, sap, skb)
*
* Incoming data which we must deliver to the state machine, to check
* we are still connected.
*/
int ircomm_lmp_data_indication(void *instance, void *sap,
struct sk_buff *skb)
{
struct ircomm_cb *self = (struct ircomm_cb *) instance;
 
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
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;
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
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;
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
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;
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_MAGIC, return;);
 
info.reason = reason;
 
ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info);
}
/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: Wed Jan 5 14:45:43 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
********************************************************************/
 
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/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;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
if (!self->tty || !self->tty->termios || !self->ircomm)
return;
 
cflag = self->tty->termios->c_cflag;
 
/* byte size and parity */
switch (cflag & CSIZE) {
case CS5: cval = IRCOMM_WSIZE_5; break;
case CS6: cval = IRCOMM_WSIZE_6; break;
case CS7: cval = IRCOMM_WSIZE_7; break;
case CS8: cval = IRCOMM_WSIZE_8; break;
default: cval = IRCOMM_WSIZE_5; break;
}
if (cflag & CSTOPB)
cval |= IRCOMM_2_STOP_BIT;
if (cflag & PARENB)
cval |= IRCOMM_PARITY_ENABLE;
if (!(cflag & PARODD))
cval |= IRCOMM_PARITY_EVEN;
 
/* Determine divisor based on baud rate */
baud = tty_get_baud_rate(self->tty);
if (!baud)
baud = 9600; /* B0 transition handled in rs_set_termios */
 
self->settings.data_rate = baud;
ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
/* CTS flow control flag and modem status interrupts */
if (cflag & CRTSCTS) {
self->flags |= ASYNC_CTS_FLOW;
self->settings.flow_control |= IRCOMM_RTS_CTS_IN;
/* This got me. Bummer. Jean II */
if (self->service_type == IRCOMM_3_WIRE_RAW)
WARNING("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n", __FUNCTION__);
} else {
self->flags &= ~ASYNC_CTS_FLOW;
self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN;
}
if (cflag & CLOCAL)
self->flags &= ~ASYNC_CHECK_CD;
else
self->flags |= ASYNC_CHECK_CD;
#if 0
/*
* Set up parity check flag
*/
 
if (I_INPCK(self->tty))
driver->read_status_mask |= LSR_FE | LSR_PE;
if (I_BRKINT(driver->tty) || I_PARMRK(driver->tty))
driver->read_status_mask |= LSR_BI;
/*
* Characters to ignore
*/
driver->ignore_status_mask = 0;
if (I_IGNPAR(driver->tty))
driver->ignore_status_mask |= LSR_PE | LSR_FE;
 
if (I_IGNBRK(self->tty)) {
self->ignore_status_mask |= LSR_BI;
/*
* If we're ignore parity and break indicators, ignore
* overruns too. (For real raw support).
*/
if (I_IGNPAR(self->tty))
self->ignore_status_mask |= LSR_OE;
}
#endif
self->settings.data_format = cval;
 
ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
}
 
/*
* Function ircomm_tty_set_termios (tty, old_termios)
*
* This routine allows the tty driver to be notified when device's
* termios settings have changed. Note that a well-designed tty driver
* should be prepared to accept the case where old == NULL, and try to
* do something rational.
*/
void ircomm_tty_set_termios(struct tty_struct *tty,
struct termios *old_termios)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
unsigned int cflag = tty->termios->c_cflag;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
if ((cflag == old_termios->c_cflag) &&
(RELEVANT_IFLAG(tty->termios->c_iflag) ==
RELEVANT_IFLAG(old_termios->c_iflag)))
{
return;
}
 
ircomm_tty_change_speed(self);
 
/* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) &&
!(cflag & CBAUD)) {
self->settings.dte &= ~(IRCOMM_DTR|IRCOMM_RTS);
ircomm_param_request(self, IRCOMM_DTE, TRUE);
}
/* Handle transition away from B0 status */
if (!(old_termios->c_cflag & CBAUD) &&
(cflag & CBAUD)) {
self->settings.dte |= IRCOMM_DTR;
if (!(tty->termios->c_cflag & CRTSCTS) ||
!test_bit(TTY_THROTTLED, &tty->flags)) {
self->settings.dte |= IRCOMM_RTS;
}
ircomm_param_request(self, IRCOMM_DTE, TRUE);
}
/* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS))
{
tty->hw_stopped = 0;
ircomm_tty_start(tty);
}
}
 
/*
* Function ircomm_tty_get_modem_info (self, value)
*
*
*
*/
static int ircomm_tty_get_modem_info(struct ircomm_tty_cb *self,
unsigned int *value)
{
unsigned int result;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
result = ((self->settings.dte & IRCOMM_RTS) ? TIOCM_RTS : 0)
| ((self->settings.dte & IRCOMM_DTR) ? TIOCM_DTR : 0)
| ((self->settings.dce & IRCOMM_CD) ? TIOCM_CAR : 0)
| ((self->settings.dce & IRCOMM_RI) ? TIOCM_RNG : 0)
| ((self->settings.dce & IRCOMM_DSR) ? TIOCM_DSR : 0)
| ((self->settings.dce & IRCOMM_CTS) ? TIOCM_CTS : 0);
 
return 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;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
 
if (get_user(arg, value))
return -EFAULT;
 
old_rts = self->settings.dte & IRCOMM_RTS;
old_dtr = self->settings.dte & IRCOMM_DTR;
 
switch (cmd) {
case TIOCMBIS:
if (arg & TIOCM_RTS)
self->settings.dte |= IRCOMM_RTS;
if (arg & TIOCM_DTR)
self->settings.dte |= IRCOMM_DTR;
break;
case TIOCMBIC:
if (arg & TIOCM_RTS)
self->settings.dte &= ~IRCOMM_RTS;
if (arg & TIOCM_DTR)
self->settings.dte &= ~IRCOMM_DTR;
break;
case TIOCMSET:
self->settings.dte =
((self->settings.dte & ~(IRCOMM_RTS | IRCOMM_DTR))
| ((arg & TIOCM_RTS) ? IRCOMM_RTS : 0)
| ((arg & TIOCM_DTR) ? IRCOMM_DTR : 0));
break;
default:
return -EINVAL;
}
if ((self->settings.dte & IRCOMM_RTS) != old_rts)
self->settings.dte |= IRCOMM_DELTA_RTS;
 
if ((self->settings.dte & IRCOMM_DTR) != old_dtr)
self->settings.dte |= IRCOMM_DELTA_DTR;
 
ircomm_param_request(self, IRCOMM_DTE, TRUE);
return 0;
}
 
/*
* Function get_serial_info (driver, retinfo)
*
*
*
*/
static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self,
struct serial_struct *retinfo)
{
struct serial_struct info;
if (!retinfo)
return -EFAULT;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
memset(&info, 0, sizeof(info));
info.line = self->line;
info.flags = self->flags;
info.baud_base = self->settings.data_rate;
info.close_delay = self->close_delay;
info.closing_wait = self->closing_wait;
 
/* For compatibility */
info.type = PORT_16550A;
info.port = 0;
info.irq = 0;
info.xmit_fifo_size = 0;
info.hub6 = 0;
info.custom_divisor = 0;
 
if (copy_to_user(retinfo, &info, sizeof(*retinfo)))
return -EFAULT;
 
return 0;
}
 
/*
* Function set_serial_info (driver, new_info)
*
*
*
*/
static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self,
struct serial_struct *new_info)
{
#if 0
struct serial_struct new_serial;
struct ircomm_tty_cb old_state, *state;
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
return -EFAULT;
 
 
state = self
old_state = *self;
if (!capable(CAP_SYS_ADMIN)) {
if ((new_serial.baud_base != state->settings.data_rate) ||
(new_serial.close_delay != state->close_delay) ||
((new_serial.flags & ~ASYNC_USR_MASK) !=
(self->flags & ~ASYNC_USR_MASK)))
return -EPERM;
state->flags = ((state->flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
self->flags = ((self->flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
/* self->custom_divisor = new_serial.custom_divisor; */
goto check_and_exit;
}
 
/*
* OK, past this point, all the error checking has been done.
* At this point, we start making changes.....
*/
 
if (self->settings.data_rate != new_serial.baud_base) {
self->settings.data_rate = new_serial.baud_base;
ircomm_param_request(self, IRCOMM_DATA_RATE, TRUE);
}
 
self->close_delay = new_serial.close_delay * HZ/100;
self->closing_wait = new_serial.closing_wait * HZ/100;
/* self->custom_divisor = new_serial.custom_divisor; */
 
self->flags = ((self->flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS));
self->tty->low_latency = (self->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
check_and_exit:
 
if (self->flags & ASYNC_INITIALIZED) {
if (((old_state.flags & ASYNC_SPD_MASK) !=
(self->flags & ASYNC_SPD_MASK)) ||
(old_driver.custom_divisor != driver->custom_divisor)) {
if ((driver->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
driver->tty->alt_speed = 57600;
if ((driver->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
driver->tty->alt_speed = 115200;
if ((driver->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
driver->tty->alt_speed = 230400;
if ((driver->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
driver->tty->alt_speed = 460800;
ircomm_tty_change_speed(driver);
}
}
#endif
return 0;
}
 
/*
* Function ircomm_tty_ioctl (tty, file, cmd, arg)
*
*
*
*/
int ircomm_tty_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
int ret = 0;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
(cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
}
 
switch (cmd) {
case 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:
IRDA_DEBUG(0, "(), TIOCMIWAIT, not impl!\n");
break;
 
case TIOCGICOUNT:
IRDA_DEBUG(0, "%s(), TIOCGICOUNT not impl!\n", __FUNCTION__);
#if 0
save_flags(flags); cli();
cnow = driver->icount;
restore_flags(flags);
p_cuser = (struct serial_icounter_struct *) arg;
if (put_user(cnow.cts, &p_cuser->cts) ||
put_user(cnow.dsr, &p_cuser->dsr) ||
put_user(cnow.rng, &p_cuser->rng) ||
put_user(cnow.dcd, &p_cuser->dcd) ||
put_user(cnow.rx, &p_cuser->rx) ||
put_user(cnow.tx, &p_cuser->tx) ||
put_user(cnow.frame, &p_cuser->frame) ||
put_user(cnow.overrun, &p_cuser->overrun) ||
put_user(cnow.parity, &p_cuser->parity) ||
put_user(cnow.brk, &p_cuser->brk) ||
put_user(cnow.buf_overrun, &p_cuser->buf_overrun))
return -EFAULT;
#endif
return 0;
default:
ret = -ENOIOCTLCMD; /* ioctls which we must ignore */
}
return ret;
}
 
 
 
/ircomm_param.c
0,0 → 1,524
/*********************************************************************
*
* Filename: ircomm_param.c
* Version: 1.0
* Description: Parameter handling for the IrCOMM protocol
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Mon Jun 7 10:25:11 1999
* Modified at: Sun Jan 30 14:32:03 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
********************************************************************/
 
#include <linux/sched.h>
#include <linux/interrupt.h>
 
#include <net/irda/irda.h>
#include <net/irda/parameters.h>
 
#include <net/irda/ircomm_core.h>
#include <net/irda/ircomm_tty_attach.h>
#include <net/irda/ircomm_tty.h>
 
#include <net/irda/ircomm_param.h>
 
static int ircomm_param_service_type(void *instance, irda_param_t *param,
int get);
static int ircomm_param_port_type(void *instance, irda_param_t *param,
int get);
static int ircomm_param_port_name(void *instance, irda_param_t *param,
int get);
static int ircomm_param_service_type(void *instance, irda_param_t *param,
int get);
static int ircomm_param_data_rate(void *instance, irda_param_t *param,
int get);
static int ircomm_param_data_format(void *instance, irda_param_t *param,
int get);
static int ircomm_param_flow_control(void *instance, irda_param_t *param,
int get);
static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get);
static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get);
static int ircomm_param_line_status(void *instance, irda_param_t *param,
int get);
static int ircomm_param_dte(void *instance, irda_param_t *param, int get);
static int ircomm_param_dce(void *instance, irda_param_t *param, int get);
static int ircomm_param_poll(void *instance, irda_param_t *param, int get);
 
static pi_minor_info_t pi_minor_call_table_common[] = {
{ ircomm_param_service_type, PV_INT_8_BITS },
{ ircomm_param_port_type, PV_INT_8_BITS },
{ ircomm_param_port_name, PV_STRING }
};
static pi_minor_info_t pi_minor_call_table_non_raw[] = {
{ ircomm_param_data_rate, PV_INT_32_BITS | PV_BIG_ENDIAN },
{ ircomm_param_data_format, PV_INT_8_BITS },
{ ircomm_param_flow_control, PV_INT_8_BITS },
{ ircomm_param_xon_xoff, PV_INT_16_BITS },
{ ircomm_param_enq_ack, PV_INT_16_BITS },
{ ircomm_param_line_status, PV_INT_8_BITS }
};
static pi_minor_info_t pi_minor_call_table_9_wire[] = {
{ ircomm_param_dte, PV_INT_8_BITS },
{ ircomm_param_dce, PV_INT_8_BITS },
{ ircomm_param_poll, PV_NO_VALUE },
};
 
static pi_major_info_t pi_major_call_table[] = {
{ pi_minor_call_table_common, 3 },
{ pi_minor_call_table_non_raw, 6 },
{ pi_minor_call_table_9_wire, 3 }
/* { pi_minor_call_table_centronics } */
};
 
pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 };
 
/*
* Function ircomm_param_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)
{
struct tty_struct *tty;
unsigned long flags;
struct sk_buff *skb;
int count;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
 
tty = self->tty;
if (!tty)
return 0;
 
/* Make sure we don't send parameters for raw mode */
if (self->service_type == IRCOMM_3_WIRE_RAW)
return 0;
 
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) {
WARNING("%s(), no room for parameter!\n", __FUNCTION__);
restore_flags(flags);
return -1;
}
skb_put(skb, count);
 
restore_flags(flags);
 
IRDA_DEBUG(2, "%s(), skb->len=%d\n", __FUNCTION__, skb->len);
 
if (flush) {
/* ircomm_tty_do_softint will take care of the rest */
queue_task(&self->tqueue, &tq_immediate);
mark_bh(IMMEDIATE_BH);
}
 
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, irda_param_t *param,
int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
__u8 service_type = (__u8) param->pv.i;
 
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
 
if (get) {
param->pv.i = self->settings.service_type;
return 0;
}
 
/* Find all common service types */
service_type &= self->service_type;
if (!service_type) {
IRDA_DEBUG(2, "%s(), No common service type to use!\n", __FUNCTION__);
return -1;
}
IRDA_DEBUG(0, __FUNCTION__ "%s(), services in common=%02x\n", __FUNCTION__,
service_type);
 
/*
* Now choose a preferred service type of those available
*/
if (service_type & IRCOMM_CENTRONICS)
self->settings.service_type = IRCOMM_CENTRONICS;
else if (service_type & IRCOMM_9_WIRE)
self->settings.service_type = IRCOMM_9_WIRE;
else if (service_type & IRCOMM_3_WIRE)
self->settings.service_type = IRCOMM_3_WIRE;
else if (service_type & IRCOMM_3_WIRE_RAW)
self->settings.service_type = IRCOMM_3_WIRE_RAW;
 
IRDA_DEBUG(0, "%s(), resulting service type=0x%02x\n", __FUNCTION__,
self->settings.service_type);
 
/*
* Now the line is ready for some communication. Check if we are a
* server, and send over some initial parameters.
* Client do it in ircomm_tty_state_setup().
* Note : we may get called from ircomm_tty_getvalue_confirm(),
* therefore before we even have open any socket. And self->client
* is initialised to TRUE only later. So, we check if the link is
* really initialised. - Jean II
*/
if ((self->max_header_size != IRCOMM_TTY_HDR_UNINITIALISED) &&
(!self->client) &&
(self->settings.service_type != IRCOMM_3_WIRE_RAW))
{
/* Init connection */
ircomm_tty_send_initial_parameters(self);
ircomm_tty_link_established(self);
}
 
return 0;
}
 
/*
* Function ircomm_param_port_type (self, param)
*
* The port type parameter tells if the devices are serial or parallel.
* Since we only advertise serial service, this parameter should only
* be equal to IRCOMM_SERIAL.
*/
static int ircomm_param_port_type(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get)
param->pv.i = IRCOMM_SERIAL;
else {
self->settings.port_type = (__u8) param->pv.i;
 
IRDA_DEBUG(0, "%s(), port type=%d\n", __FUNCTION__,
self->settings.port_type);
}
return 0;
}
 
/*
* Function ircomm_param_port_name (self, param)
*
* Exchange port name
*
*/
static int ircomm_param_port_name(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
 
if (get) {
IRDA_DEBUG(0, "%s(), not imp!\n", __FUNCTION__);
} else {
IRDA_DEBUG(0, "%s(), port-name=%s\n", __FUNCTION__, param->pv.c);
strncpy(self->settings.port_name, param->pv.c, 32);
}
 
return 0;
}
 
/*
* Function ircomm_param_data_rate (self, param)
*
* Exchange data rate to be used in this settings
*
*/
static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
 
if (get)
param->pv.i = self->settings.data_rate;
else
self->settings.data_rate = param->pv.i;
IRDA_DEBUG(2, "%s(), data rate = %d\n", __FUNCTION__, param->pv.i);
 
return 0;
}
 
/*
* Function ircomm_param_data_format (self, param)
*
* Exchange data format to be used in this settings
*
*/
static int ircomm_param_data_format(void *instance, irda_param_t *param,
int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
 
if (get)
param->pv.i = self->settings.data_format;
else
self->settings.data_format = (__u8) param->pv.i;
return 0;
}
 
/*
* Function ircomm_param_flow_control (self, param)
*
* Exchange flow control settings to be used in this settings
*
*/
static int ircomm_param_flow_control(void *instance, irda_param_t *param,
int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get)
param->pv.i = self->settings.flow_control;
else
self->settings.flow_control = (__u8) param->pv.i;
 
IRDA_DEBUG(1, "%s(), flow control = 0x%02x\n", __FUNCTION__, (__u8) param->pv.i);
 
return 0;
}
 
/*
* Function ircomm_param_xon_xoff (self, param)
*
* Exchange XON/XOFF characters
*
*/
static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get) {
param->pv.i = self->settings.xonxoff[0];
param->pv.i |= self->settings.xonxoff[1] << 8;
} else {
self->settings.xonxoff[0] = (__u16) param->pv.i & 0xff;
self->settings.xonxoff[1] = (__u16) param->pv.i >> 8;
}
 
IRDA_DEBUG(0, "%s(), XON/XOFF = 0x%02x,0x%02x\n", __FUNCTION__,
param->pv.i & 0xff, param->pv.i >> 8);
 
return 0;
}
 
/*
* Function ircomm_param_enq_ack (self, param)
*
* Exchange ENQ/ACK characters
*
*/
static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get) {
param->pv.i = self->settings.enqack[0];
param->pv.i |= self->settings.enqack[1] << 8;
} else {
self->settings.enqack[0] = (__u16) param->pv.i & 0xff;
self->settings.enqack[1] = (__u16) param->pv.i >> 8;
}
 
IRDA_DEBUG(0, "%s(), ENQ/ACK = 0x%02x,0x%02x\n", __FUNCTION__,
param->pv.i & 0xff, param->pv.i >> 8);
 
return 0;
}
 
/*
* Function ircomm_param_line_status (self, param)
*
*
*
*/
static int ircomm_param_line_status(void *instance, irda_param_t *param,
int get)
{
IRDA_DEBUG(2, "%s(), not impl.\n", __FUNCTION__);
 
return 0;
}
 
/*
* Function ircomm_param_dte (instance, param)
*
* If we get here, there must be some sort of null-modem connection, and
* we are probably working in server mode as well.
*/
static int ircomm_param_dte(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
__u8 dte;
 
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
 
if (get)
param->pv.i = self->settings.dte;
else {
dte = (__u8) param->pv.i;
if (dte & IRCOMM_DELTA_DTR)
self->settings.dce |= (IRCOMM_DELTA_DSR|
IRCOMM_DELTA_RI |
IRCOMM_DELTA_CD);
if (dte & IRCOMM_DTR)
self->settings.dce |= (IRCOMM_DSR|
IRCOMM_RI |
IRCOMM_CD);
if (dte & IRCOMM_DELTA_RTS)
self->settings.dce |= IRCOMM_DELTA_CTS;
if (dte & IRCOMM_RTS)
self->settings.dce |= IRCOMM_CTS;
 
/* Take appropriate actions */
ircomm_tty_check_modem_status(self);
 
/* Null modem cable emulator */
self->settings.null_modem = TRUE;
}
 
return 0;
}
 
/*
* Function ircomm_param_dce (instance, param)
*
*
*
*/
static int ircomm_param_dce(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
__u8 dce;
 
IRDA_DEBUG(1, "%s(), dce = 0x%02x\n", __FUNCTION__, (__u8) param->pv.i);
 
dce = (__u8) param->pv.i;
 
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
 
self->settings.dce = dce;
 
/* Check if any of the settings have changed */
if (dce & 0x0f) {
if (dce & IRCOMM_DELTA_CTS) {
IRDA_DEBUG(2, "%s(), CTS \n", __FUNCTION__);
}
}
 
ircomm_tty_check_modem_status(self);
 
return 0;
}
 
/*
* Function ircomm_param_poll (instance, param)
*
* Called when the peer device is polling for the line settings
*
*/
static int ircomm_param_poll(void *instance, irda_param_t *param, int get)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
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;
}
 
 
 
 
 
/ircomm_tty.c
0,0 → 1,1415
/*********************************************************************
*
* 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: Wed Feb 23 00:09:02 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: serial.c and previous IrCOMM work by Takahide Higuchi
*
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
********************************************************************/
 
#include <linux/config.h>
#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 void ircomm_tty_shutdown(struct ircomm_tty_cb *self);
 
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);
#ifdef CONFIG_PROC_FS
static int ircomm_tty_read_proc(char *buf, char **start, off_t offset, int len,
int *eof, void *unused);
#endif /* CONFIG_PROC_FS */
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("%s(), can't allocate hashbin!\n", __FUNCTION__);
return -ENOMEM;
}
 
memset(&driver, 0, sizeof(struct tty_driver));
driver.magic = TTY_DRIVER_MAGIC;
driver.driver_name = "ircomm";
#ifdef CONFIG_DEVFS_FS
driver.name = "ircomm%d";
#else
driver.name = "ircomm";
#endif
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;
#ifdef CONFIG_PROC_FS
driver.read_proc = ircomm_tty_read_proc;
#endif /* CONFIG_PROC_FS */
if (tty_register_driver(&driver)) {
ERROR("%s: Couldn't register serial driver\n", __FUNCTION__);
return -1;
}
return 0;
}
 
#ifdef MODULE
static void __ircomm_tty_cleanup(struct ircomm_tty_cb *self)
{
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
ircomm_tty_shutdown(self);
 
self->magic = 0;
kfree(self);
}
 
/*
* Function ircomm_tty_cleanup ()
*
* Remove IrCOMM TTY layer/driver
*
*/
void ircomm_tty_cleanup(void)
{
int ret;
 
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
 
ret = tty_unregister_driver(&driver);
if (ret) {
ERROR("%s, failed to unregister driver\n", __FUNCTION__);
return;
}
 
hashbin_delete(ircomm_tty, (FREE_FUNC) __ircomm_tty_cleanup);
}
#endif /* MODULE */
 
/*
* Function ircomm_startup (self)
*
*
*
*/
static int ircomm_tty_startup(struct ircomm_tty_cb *self)
{
notify_t notify;
int ret;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
 
/* Already open */
if (self->flags & ASYNC_INITIALIZED) {
IRDA_DEBUG(2, "%s(), already open so break out!\n", __FUNCTION__);
return 0;
}
 
/* Register with IrCOMM */
irda_notify_init(&notify);
/* These callbacks we must handle ourselves */
notify.data_indication = ircomm_tty_data_indication;
notify.udata_indication = ircomm_tty_control_indication;
notify.flow_indication = ircomm_tty_flow_indication;
 
/* Use the ircomm_tty interface for these ones */
notify.disconnect_indication = ircomm_tty_disconnect_indication;
notify.connect_confirm = ircomm_tty_connect_confirm;
notify.connect_indication = ircomm_tty_connect_indication;
strncpy(notify.name, "ircomm_tty", NOTIFY_MAX_NAME);
notify.instance = self;
 
if (!self->ircomm) {
self->ircomm = ircomm_open(&notify, self->service_type,
self->line);
}
if (!self->ircomm)
return -ENODEV;
 
self->slsap_sel = self->ircomm->slsap_sel;
 
/* Connect IrCOMM link with remote device */
ret = ircomm_tty_attach_cable(self);
if (ret < 0) {
ERROR("%s(), error attaching cable!\n", __FUNCTION__);
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;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
tty = self->tty;
 
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 ((self->flags & ASYNC_CALLOUT_ACTIVE) &&
(self->flags & ASYNC_SESSION_LOCKOUT) &&
(self->session != current->session))
return -EBUSY;
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;
 
IRDA_DEBUG(1, "%s(), O_NONBLOCK requested!\n", __FUNCTION__);
return 0;
}
 
if (self->flags & ASYNC_CALLOUT_ACTIVE) {
if (self->normal_termios.c_cflag & CLOCAL) {
IRDA_DEBUG(1, "%s(), doing CLOCAL!\n", __FUNCTION__);
do_clocal = 1;
}
} else {
if (tty->termios->c_cflag & CLOCAL) {
IRDA_DEBUG(1, "%s(), doing CLOCAL!\n", __FUNCTION__);
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);
IRDA_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->settings.dte |= IRCOMM_RTS + IRCOMM_DTR;
ircomm_param_request(self, IRCOMM_DTE, TRUE);
restore_flags(flags);
}
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->settings.dce & IRCOMM_CD)) &&
self->state == IRCOMM_TTY_READY)
{
break;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
IRDA_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--;
IRDA_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;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
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("%s(), kmalloc failed!\n", __FUNCTION__);
MOD_DEC_USE_COUNT;
return -ENOMEM;
}
memset(self, 0, sizeof(struct ircomm_tty_cb));
self->magic = IRCOMM_TTY_MAGIC;
self->flow = FLOW_STOP;
 
self->line = line;
self->tqueue.routine = ircomm_tty_do_softint;
self->tqueue.data = self;
self->max_header_size = IRCOMM_TTY_HDR_UNINITIALISED;
self->max_data_size = IRCOMM_TTY_DATA_UNINITIALISED;
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, (irda_queue_t *) self, line, NULL);
}
self->open_count++;
 
tty->driver_data = self;
self->tty = tty;
 
IRDA_DEBUG(1, "%s(), %s%d, count = %d\n", __FUNCTION__,
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;
self->settings.service_type = IRCOMM_9_WIRE; /* 9 wire as default */
self->settings.dce = IRCOMM_CTS | IRCOMM_CD; /* Default line settings */
IRDA_DEBUG(2, "%s(), IrCOMM device\n", __FUNCTION__);
} else {
IRDA_DEBUG(2, "%s(), IrLPT device\n", __FUNCTION__);
self->service_type = IRCOMM_3_WIRE_RAW;
self->settings.service_type = IRCOMM_3_WIRE_RAW; /* Default */
}
 
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? */
IRDA_DEBUG(2, "%s(), returning after block_til_ready with %d\n",
__FUNCTION__, ret);
 
return ret;
}
 
self->session = current->session;
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;
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
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);
 
IRDA_DEBUG(0, "%s(), returning 1\n", __FUNCTION__);
return;
}
 
if ((tty->count == 1) && (self->open_count != 1)) {
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. state->count should always
* be one in these conditions. If it's greater than
* one, we've got real problems, since it means the
* serial port won't be shutdown.
*/
IRDA_DEBUG(0, "%s(), bad serial port count; "
"tty->count is 1, state->count is %d\n",
__FUNCTION__, self->open_count);
self->open_count = 1;
}
 
if (--self->open_count < 0) {
ERROR("%s(), bad serial port count for ttys%d: %d\n",
__FUNCTION__, self->line, self->open_count);
self->open_count = 0;
}
if (self->open_count) {
MOD_DEC_USE_COUNT;
restore_flags(flags);
 
IRDA_DEBUG(0, "%s(), open count > 0\n", __FUNCTION__);
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);
 
ircomm_tty_shutdown(self);
 
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;
restore_flags(flags);
}
 
/*
* 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, *ctrl_skb;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
if (!self || self->magic != IRCOMM_TTY_MAGIC)
return;
 
tty = self->tty;
if (!tty)
return;
 
/* Unlink control buffer */
save_flags(flags);
cli();
 
ctrl_skb = self->ctrl_skb;
self->ctrl_skb = NULL;
 
restore_flags(flags);
 
/* Flush control buffer if any */
if (ctrl_skb && self->flow == FLOW_START)
ircomm_control_request(self->ircomm, ctrl_skb);
 
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;
 
IRDA_DEBUG(2, "%s(), count=%d, hw_stopped=%d\n",
__FUNCTION__, count, tty->hw_stopped);
 
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
 
/* We may receive packets from the TTY even before we have finished
* our setup. Not cool.
* The problem is that we don't know the final header and data size
* to create the proper skb, so any skb we would create would have
* bogus header and data size, so need care.
* We use a bogus header size to safely detect this condition.
* Another problem is that hw_stopped was set to 0 way before it
* should be, so we would drop this skb. It should now be fixed.
* One option is to not accept data until we are properly setup.
* But, I suspect that when it happens, the ppp line discipline
* just "drops" the data, which might screw up connect scripts.
* The second option is to create a "safe skb", with large header
* and small size (see ircomm_tty_open() for values).
* We just need to make sure that when the real values get filled,
* we don't mess up the original "safe skb" (see tx_data_size).
* Jean II */
if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) {
IRDA_DEBUG(1, "%s() : not initialised\n", __FUNCTION__);
#ifdef IRCOMM_NO_TX_BEFORE_INIT
/* We didn't consume anything, TTY will retry */
return 0;
#endif
}
 
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
* Note : use tx_data_size, because max_data_size
* may have changed and we don't want to overwrite
* the skb. - Jean II
*/
if ((tailroom = (self->tx_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;
/* Remember skb size because max_data_size may
* change later on - Jean II */
self->tx_data_size = self->max_data_size;
}
/* 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;);
 
#ifdef IRCOMM_NO_TX_BEFORE_INIT
/* max_header_size tells us if the channel is initialised or not. */
if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED)
/* Don't bother us yet */
return 0;
#endif
 
/* Check if we are allowed to transmit any data.
* hw_stopped is the regular flow control.
* Jean II */
if (tty->hw_stopped)
ret = 0;
else {
save_flags(flags);
cli();
if (self->tx_skb)
ret = self->tx_data_size - self->tx_skb->len;
else
ret = self->max_data_size;
restore_flags(flags);
}
IRDA_DEBUG(2, "%s(), ret=%d\n", __FUNCTION__, 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;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
orig_jiffies = jiffies;
 
/* Set poll time to 200 ms */
poll_time = IRDA_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;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
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->settings.dte &= ~IRCOMM_RTS;
self->settings.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;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
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->settings.dte |= (IRCOMM_RTS|IRCOMM_DELTA_RTS);
 
ircomm_param_request(self, IRCOMM_DTE, TRUE);
IRDA_DEBUG(1, "%s(), FLOW_START\n", __FUNCTION__);
}
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;
}
 
static void ircomm_tty_shutdown(struct ircomm_tty_cb *self)
{
unsigned long flags;
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
if (!(self->flags & ASYNC_INITIALIZED))
return;
 
save_flags(flags);
cli();
 
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;
}
 
ircomm_tty_detach_cable(self);
 
if (self->ircomm) {
ircomm_close(self->ircomm);
self->ircomm = NULL;
}
self->flags &= ~ASYNC_INITIALIZED;
 
restore_flags(flags);
}
 
/*
* 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;
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
if (!tty)
return;
 
/* ircomm_tty_flush_buffer(tty); */
ircomm_tty_shutdown(self);
 
self->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
self->tty = 0;
self->open_count = 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)
{
IRDA_DEBUG(0, "%s(), not impl\n", __FUNCTION__);
}
 
/*
* 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;
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
tty = self->tty;
 
status = self->settings.dce;
 
if (status & IRCOMM_DCE_DELTA_ANY) {
/*wake_up_interruptible(&self->delta_msr_wait);*/
}
if ((self->flags & ASYNC_CHECK_CD) && (status & IRCOMM_DELTA_CD)) {
IRDA_DEBUG(2, "%s(), ircomm%d CD now %s...\n",
__FUNCTION__, 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)))
{
IRDA_DEBUG(2, "%s(), Doing serial hangup..\n", __FUNCTION__);
if (tty)
tty_hangup(tty);
 
/* Hangup will remote the tty, so better break out */
return;
}
}
if (self->flags & ASYNC_CTS_FLOW) {
if (tty->hw_stopped) {
if (status & IRCOMM_CTS) {
IRDA_DEBUG(2, "%s(), CTS tx start...\n", __FUNCTION__);
tty->hw_stopped = 0;
/* Wake up processes blocked on open */
wake_up_interruptible(&self->open_wait);
 
queue_task(&self->tqueue, &tq_immediate);
mark_bh(IMMEDIATE_BH);
return;
}
} else {
if (!(status & IRCOMM_CTS)) {
IRDA_DEBUG(2, "%s(), CTS tx stop...\n", __FUNCTION__);
tty->hw_stopped = 1;
}
}
}
}
 
/*
* Function ircomm_tty_data_indication (instance, sap, skb)
*
* Handle incoming 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;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
ASSERT(skb != NULL, return -1;);
 
if (!self->tty) {
IRDA_DEBUG(0, "%s(), no tty!\n", __FUNCTION__);
dev_kfree_skb(skb);
return 0;
}
 
/*
* If we receive data when hardware is stopped then something is wrong.
* We try to poll the peers line settings to check if we are up todate.
* Devices like WinCE can do this, and since they don't send any
* params, we can just as well declare the hardware for running.
*/
if (self->tty->hw_stopped && (self->flow == FLOW_START)) {
IRDA_DEBUG(0, "%s(), polling for line settings!\n", __FUNCTION__);
ircomm_param_request(self, IRCOMM_POLL, TRUE);
 
/* We can just as well declare the hardware for running */
ircomm_tty_send_initial_parameters(self);
ircomm_tty_link_established(self);
}
 
/*
* 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 incoming 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;
 
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
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, IRDA_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:
IRDA_DEBUG(2, "%s(), hw start!\n", __FUNCTION__);
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:
IRDA_DEBUG(2, "%s(), hw stopped!\n", __FUNCTION__);
tty->hw_stopped = 1;
break;
}
self->flow = cmd;
}
 
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->settings.port_name);
 
ret += sprintf(buf+ret, "DTE status: ");
if (self->settings.dte & IRCOMM_RTS)
ret += sprintf(buf+ret, "RTS|");
if (self->settings.dte & IRCOMM_DTR)
ret += sprintf(buf+ret, "DTR|");
if (self->settings.dte)
ret--; /* remove the last | */
ret += sprintf(buf+ret, "\n");
 
ret += sprintf(buf+ret, "DCE status: ");
if (self->settings.dce & IRCOMM_CTS)
ret += sprintf(buf+ret, "CTS|");
if (self->settings.dce & IRCOMM_DSR)
ret += sprintf(buf+ret, "DSR|");
if (self->settings.dce & IRCOMM_CD)
ret += sprintf(buf+ret, "CD|");
if (self->settings.dce & IRCOMM_RI)
ret += sprintf(buf+ret, "RI|");
if (self->settings.dce)
ret--; /* remove the last | */
ret += sprintf(buf+ret, "\n");
 
ret += sprintf(buf+ret, "Configuration: ");
if (!self->settings.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->settings.data_rate);
 
ret += sprintf(buf+ret, "Flow control: ");
if (self->settings.flow_control & IRCOMM_XON_XOFF_IN)
ret += sprintf(buf+ret, "XON_XOFF_IN|");
if (self->settings.flow_control & IRCOMM_XON_XOFF_OUT)
ret += sprintf(buf+ret, "XON_XOFF_OUT|");
if (self->settings.flow_control & IRCOMM_RTS_CTS_IN)
ret += sprintf(buf+ret, "RTS_CTS_IN|");
if (self->settings.flow_control & IRCOMM_RTS_CTS_OUT)
ret += sprintf(buf+ret, "RTS_CTS_OUT|");
if (self->settings.flow_control & IRCOMM_DSR_DTR_IN)
ret += sprintf(buf+ret, "DSR_DTR_IN|");
if (self->settings.flow_control & IRCOMM_DSR_DTR_OUT)
ret += sprintf(buf+ret, "DSR_DTR_OUT|");
if (self->settings.flow_control & IRCOMM_ENQ_ACK_IN)
ret += sprintf(buf+ret, "ENQ_ACK_IN|");
if (self->settings.flow_control & IRCOMM_ENQ_ACK_OUT)
ret += sprintf(buf+ret, "ENQ_ACK_OUT|");
if (self->settings.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, "Role: %s\n", self->client ?
"client" : "server");
ret += sprintf(buf+ret, "Open count: %d\n", self->open_count);
ret += sprintf(buf+ret, "Max data size: %d\n", self->max_data_size);
ret += sprintf(buf+ret, "Max header size: %d\n", self->max_header_size);
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)
*
*
*
*/
#ifdef CONFIG_PROC_FS
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 + (offset-begin);
return ((len < begin+count-offset) ? len : begin+count-offset);
}
#endif /* CONFIG_PROC_FS */
 
#ifdef MODULE
MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
MODULE_DESCRIPTION("IrCOMM serial TTY driver");
MODULE_LICENSE("GPL");
 
int init_module(void)
{
return ircomm_tty_init();
}
 
void cleanup_module(void)
{
ircomm_tty_cleanup();
}
 
#endif /* MODULE */
 
 
 
 
/ircomm_event.c
0,0 → 1,257
/*********************************************************************
*
* Filename: ircomm_event.c
* Version: 1.0
* Description: IrCOMM layer state machine
* Status: Stable
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Jun 6 20:33:11 1999
* Modified at: Sun Dec 12 13:44:32 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
********************************************************************/
 
#include <linux/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:
IRDA_DEBUG(4,"%s(), unknown event: %s\n", __FUNCTION__,
ircomm_event[event]);
if (skb)
dev_kfree_skb(skb);
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:
IRDA_DEBUG(0, "%s(), unknown event: %s\n", __FUNCTION__,
ircomm_event[event]);
if (skb)
dev_kfree_skb(skb);
ret = -EINVAL;
}
return ret;
}
 
/*
* Function ircomm_state_waitr (self, event, skb)
*
* IrCOMM has received an incoming connection request and is awaiting
* response from the user
*/
static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info)
{
int ret = 0;
 
switch (event) {
case IRCOMM_CONNECT_RESPONSE:
ircomm_next_state(self, IRCOMM_CONN);
ret = self->issue.connect_response(self, skb);
break;
case IRCOMM_DISCONNECT_REQUEST:
ircomm_next_state(self, IRCOMM_IDLE);
ret = self->issue.disconnect_request(self, skb, info);
break;
case IRCOMM_TTP_DISCONNECT_INDICATION:
case IRCOMM_LMP_DISCONNECT_INDICATION:
ircomm_next_state(self, IRCOMM_IDLE);
ircomm_disconnect_indication(self, skb, info);
break;
default:
IRDA_DEBUG(0, "%s(), unknown event = %s\n", __FUNCTION__,
ircomm_event[event]);
if (skb)
dev_kfree_skb(skb);
ret = -EINVAL;
}
return ret;
}
 
/*
* Function ircomm_state_conn (self, event, skb)
*
* IrCOMM is connected to the peer IrCOMM device
*
*/
static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info)
{
int ret = 0;
 
switch (event) {
case IRCOMM_DATA_REQUEST:
ret = self->issue.data_request(self, skb, 0);
break;
case IRCOMM_TTP_DATA_INDICATION:
ircomm_process_data(self, skb);
break;
case IRCOMM_LMP_DATA_INDICATION:
ircomm_data_indication(self, skb);
break;
case IRCOMM_CONTROL_REQUEST:
/* Just send a separate frame for now */
ret = self->issue.data_request(self, skb, skb->len);
break;
case IRCOMM_TTP_DISCONNECT_INDICATION:
case IRCOMM_LMP_DISCONNECT_INDICATION:
ircomm_next_state(self, IRCOMM_IDLE);
ircomm_disconnect_indication(self, skb, info);
break;
case IRCOMM_DISCONNECT_REQUEST:
ircomm_next_state(self, IRCOMM_IDLE);
ret = self->issue.disconnect_request(self, skb, info);
break;
default:
IRDA_DEBUG(0, "%s(), unknown event = %s\n", __FUNCTION__,
ircomm_event[event]);
if (skb)
dev_kfree_skb(skb);
ret = -EINVAL;
}
return ret;
}
 
/*
* Function ircomm_do_event (self, event, skb)
*
* Process event
*
*/
int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info)
{
IRDA_DEBUG(4, "%s: state=%s, event=%s\n", __FUNCTION__,
ircomm_state[self->state], ircomm_event[event]);
 
return (*state[self->state])(self, event, skb, info);
}
 
/*
* Function ircomm_next_state (self, state)
*
* Switch state
*
*/
void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state)
{
self->state = state;
IRDA_DEBUG(4, "%s: next state=%s, service type=%d\n", __FUNCTION__,
ircomm_state[self->state], self->service_type);
}
/Config.in
0,0 → 1,3
 
dep_tristate ' IrCOMM protocol' CONFIG_IRCOMM $CONFIG_IRDA
 
/ircomm_tty_attach.c
0,0 → 1,958
/*********************************************************************
*
* Filename: ircomm_tty_attach.c
* Version:
* Description: Code for attaching the serial driver to IrCOMM
* Status: Experimental.
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sat Jun 5 17:42:00 1999
* Modified at: Tue Jan 4 14:20:49 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
*
* 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,
DISCOVERY_MODE mode,
void *priv);
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)
{
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
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)) {
IRDA_DEBUG(0, "%s(), already connected!\n", __FUNCTION__);
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)) {
IRDA_DEBUG(0, "%s(), already connected!\n", __FUNCTION__);
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)
{
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
del_timer(&self->watchdog_timer);
 
/* Remove IrCOMM hint bits */
irlmp_unregister_client(self->ckey);
irlmp_unregister_service(self->skey);
 
if (self->iriap) {
iriap_close(self->iriap);
self->iriap = NULL;
}
 
/* Remove LM-IAS object */
if (self->obj) {
irias_delete_object(self->obj);
self->obj = NULL;
}
 
ircomm_tty_do_event(self, IRCOMM_TTY_DETACH_CABLE, NULL, NULL);
 
/* Reset some values */
self->daddr = self->saddr = 0;
self->dlsap_sel = self->slsap_sel = 0;
 
memset(&self->settings, 0, sizeof(struct ircomm_params));
}
 
/*
* Function ircomm_tty_ias_register (self)
*
* Register with LM-IAS depending on which service type we are
*
*/
static void ircomm_tty_ias_register(struct ircomm_tty_cb *self)
{
__u8 oct_seq[6];
__u16 hints;
 
IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
 
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, IAS_KERNEL_ATTR);
irias_insert_object(self->obj);
} else {
hints = irlmp_service_to_hint(S_COMM);
 
/* Register IrCOMM with LM-IAS */
self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID);
irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel",
self->slsap_sel, IAS_KERNEL_ATTR);
/* Code the parameters into the buffer */
irda_param_pack(oct_seq, "bbbbbb",
IRCOMM_SERVICE_TYPE, 1, self->service_type,
IRCOMM_PORT_TYPE, 1, IRCOMM_SERIAL);
/* Register parameters with LM-IAS */
irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6,
IAS_KERNEL_ATTR);
irias_insert_object(self->obj);
}
self->skey = irlmp_register_service(hints);
self->ckey = irlmp_register_client(
hints, ircomm_tty_discovery_indication, NULL, (void *) self);
}
 
/*
* Function ircomm_send_initial_parameters (self)
*
* Send initial parameters to the remote IrCOMM device. These parameters
* must be sent before any data.
*/
int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self)
{
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, but only if the application for some reason
* haven't set them already
*/
IRDA_DEBUG(2, "%s(), data-rate = %d\n", __FUNCTION__,
self->settings.data_rate);
if (!self->settings.data_rate)
self->settings.data_rate = 9600;
IRDA_DEBUG(2, "%s(), data-format = %d\n", __FUNCTION__,
self->settings.data_format);
if (!self->settings.data_format)
self->settings.data_format = IRCOMM_WSIZE_8; /* 8N1 */
 
IRDA_DEBUG(2, "%s(), flow-control = %d\n", __FUNCTION__,
self->settings.flow_control);
/*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/
 
/* Do not set delta values for the initial parameters */
self->settings.dte = IRCOMM_DTR | IRCOMM_RTS;
 
/* Only send service type parameter when we are the client */
if (self->client)
ircomm_param_request(self, IRCOMM_SERVICE_TYPE, FALSE);
ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE);
ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE);
/* For a 3 wire service, we just flush the last parameter and return */
if (self->settings.service_type == IRCOMM_3_WIRE) {
ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE);
return 0;
}
 
/* Only 9-wire service types continue here */
ircomm_param_request(self, IRCOMM_FLOW_CONTROL, FALSE);
#if 0
ircomm_param_request(self, IRCOMM_XON_XOFF, FALSE);
ircomm_param_request(self, IRCOMM_ENQ_ACK, FALSE);
#endif
/* Notify peer that we are ready to receive data */
ircomm_param_request(self, IRCOMM_DTE, TRUE);
return 0;
}
 
/*
* Function ircomm_tty_discovery_indication (discovery)
*
* Remote device is discovered, try query the remote IAS to see which
* device it is, and which services it has.
*
*/
static void ircomm_tty_discovery_indication(discovery_t *discovery,
DISCOVERY_MODE mode,
void *priv)
{
struct ircomm_tty_cb *self;
struct ircomm_tty_info info;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
/* Important note :
* We need to drop all passive discoveries.
* The LSAP management of IrComm is deficient and doesn't deal
* with the case of two instance connecting to each other
* simultaneously (it will deadlock in LMP).
* The proper fix would be to use the same technique as in IrNET,
* to have one server socket and separate instances for the
* connecting/connected socket.
* The workaround is to drop passive discovery, which drastically
* reduce the probability of this happening.
* Jean II */
if(mode == DISCOVERY_PASSIVE)
return;
 
info.daddr = discovery->daddr;
info.saddr = discovery->saddr;
 
self = (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;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
if (!self->tty)
return;
 
/* This will stop control data transfers */
self->flow = FLOW_STOP;
 
/* Stop data transfers */
self->tty->hw_stopped = 1;
 
ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL,
NULL);
}
 
/*
* Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv)
*
* Got result from the IAS query we make
*
*/
static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
struct ias_value *value,
void *priv)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
/* We probably don't need to make any more queries */
iriap_close(self->iriap);
self->iriap = NULL;
 
/* Check if request succeeded */
if (result != IAS_SUCCESS) {
IRDA_DEBUG(4, "%s(), got NULL value!\n", __FUNCTION__);
return;
}
 
switch (value->type) {
case IAS_OCT_SEQ:
IRDA_DEBUG(2, "%s(), got octet sequence\n", __FUNCTION__);
 
irda_param_extract_all(self, value->t.oct_seq, value->len,
&ircomm_param_info);
 
ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL,
NULL);
break;
case IAS_INTEGER:
/* Got LSAP selector */
IRDA_DEBUG(2, "%s(), got lsapsel = %d\n", __FUNCTION__,
value->t.integer);
 
if (value->t.integer == -1) {
IRDA_DEBUG(0, "%s(), invalid value!\n", __FUNCTION__);
} else
self->dlsap_sel = value->t.integer;
 
ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL);
break;
case IAS_MISSING:
IRDA_DEBUG(0, "%s(), got IAS_MISSING\n", __FUNCTION__);
break;
default:
IRDA_DEBUG(0, "%s(), got unknown type!\n", __FUNCTION__);
break;
}
irias_delete_value(value);
}
 
/*
* Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb)
*
* Connection confirmed
*
*/
void ircomm_tty_connect_confirm(void *instance, void *sap,
struct qos_info *qos,
__u32 max_data_size,
__u8 max_header_size,
struct sk_buff *skb)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
self->client = TRUE;
self->max_data_size = max_data_size;
self->max_header_size = max_header_size;
self->flow = FLOW_START;
 
ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL);
 
dev_kfree_skb(skb);
}
 
/*
* Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size,
* skb)
*
* we are discovered and being requested to connect by remote device !
*
*/
void ircomm_tty_connect_indication(void *instance, void *sap,
struct qos_info *qos,
__u32 max_data_size,
__u8 max_header_size,
struct sk_buff *skb)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
int clen;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
self->client = FALSE;
self->max_data_size = max_data_size;
self->max_header_size = max_header_size;
self->flow = FLOW_START;
 
clen = skb->data[0];
if (clen)
irda_param_extract_all(self, skb->data+1,
IRDA_MIN(skb->len, clen),
&ircomm_param_info);
 
ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL);
 
dev_kfree_skb(skb);
}
 
/*
* Function ircomm_tty_link_established (self)
*
* Called when the IrCOMM link is established
*
*/
void ircomm_tty_link_established(struct ircomm_tty_cb *self)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
if (!self->tty)
return;
del_timer(&self->watchdog_timer);
 
/* Remove LM-IAS object now so it is not reused.
* IrCOMM deals very poorly with multiple incomming connections.
* It should looks a lot more like IrNET, and "dup" a server TSAP
* to the application TSAP (based on various rules).
* This is a cheap workaround allowing multiple clients to
* connect to us. It will not always work.
* Each IrCOMM socket has an IAS entry. Incomming connection will
* pick the first one found. So, when we are fully connected,
* we remove our IAS entries so that the next IAS entry is used.
* We do that for *both* client and server, because a server
* can also create client instances.
* Jean II */
if (self->obj) {
irias_delete_object(self->obj);
self->obj = NULL;
}
 
/*
* IrCOMM link is now up, and if we are not using hardware
* flow-control, then declare the hardware as running. Otherwise we
* will have to wait for the peer device (DCE) to raise the CTS
* line.
*/
if ((self->flags & ASYNC_CTS_FLOW) && ((self->settings.dce & IRCOMM_CTS) == 0)) {
IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __FUNCTION__);
return;
} else {
IRDA_DEBUG(1, "%s(), starting hardware!\n", __FUNCTION__);
 
self->tty->hw_stopped = 0;
/* Wake up processes blocked on open */
wake_up_interruptible(&self->open_wait);
}
 
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)
{
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
irda_start_timer(&self->watchdog_timer, timeout, (void *) self,
ircomm_tty_watchdog_timer_expired);
}
 
/*
* Function ircomm_tty_watchdog_timer_expired (data)
*
* Called when the connect procedure have taken to much time.
*
*/
void ircomm_tty_watchdog_timer_expired(void *data)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
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;
 
IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__,
ircomm_tty_state[self->state], ircomm_tty_event[event]);
switch (event) {
case IRCOMM_TTY_ATTACH_CABLE:
/* Try to discover any remote devices */
ircomm_tty_start_watchdog_timer(self, 3*HZ);
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
break;
case IRCOMM_TTY_DISCOVERY_INDICATION:
self->daddr = info->daddr;
self->saddr = info->saddr;
 
if (self->iriap) {
WARNING("%s(), busy with a previous query\n", __FUNCTION__);
return -EBUSY;
}
 
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
ircomm_tty_getvalue_confirm);
 
iriap_getvaluebyclass_request(self->iriap,
self->saddr, self->daddr,
"IrDA:IrCOMM", "Parameters");
ircomm_tty_start_watchdog_timer(self, 3*HZ);
ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
break;
case IRCOMM_TTY_CONNECT_INDICATION:
del_timer(&self->watchdog_timer);
 
/* Accept connection */
ircomm_connect_response(self->ircomm, NULL);
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
break;
case IRCOMM_TTY_WD_TIMER_EXPIRED:
/* Just stay idle */
break;
case IRCOMM_TTY_DETACH_CABLE:
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__,
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;
 
IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__,
ircomm_tty_state[self->state], ircomm_tty_event[event]);
 
switch (event) {
case IRCOMM_TTY_DISCOVERY_INDICATION:
self->daddr = info->daddr;
self->saddr = info->saddr;
 
if (self->iriap) {
WARNING("%s(), busy with a previous query\n", __FUNCTION__);
return -EBUSY;
}
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
ircomm_tty_getvalue_confirm);
if (self->service_type == IRCOMM_3_WIRE_RAW) {
iriap_getvaluebyclass_request(self->iriap, self->saddr,
self->daddr, "IrLPT",
"IrDA:IrLMP:LsapSel");
ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
} else {
iriap_getvaluebyclass_request(self->iriap, self->saddr,
self->daddr,
"IrDA:IrCOMM",
"Parameters");
 
ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS);
}
ircomm_tty_start_watchdog_timer(self, 3*HZ);
break;
case IRCOMM_TTY_CONNECT_INDICATION:
del_timer(&self->watchdog_timer);
 
/* Accept connection */
ircomm_connect_response(self->ircomm, NULL);
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
break;
case IRCOMM_TTY_WD_TIMER_EXPIRED:
#if 1
/* Give up */
#else
/* Try to discover any remote devices */
ircomm_tty_start_watchdog_timer(self, 3*HZ);
irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS);
#endif
break;
case IRCOMM_TTY_DETACH_CABLE:
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__,
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;
 
IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__,
ircomm_tty_state[self->state], ircomm_tty_event[event]);
 
switch (event) {
case IRCOMM_TTY_GOT_PARAMETERS:
if (self->iriap) {
WARNING("%s(), busy with a previous query\n", __FUNCTION__);
return -EBUSY;
}
self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self,
ircomm_tty_getvalue_confirm);
 
iriap_getvaluebyclass_request(self->iriap, self->saddr,
self->daddr, "IrDA:IrCOMM",
"IrDA:TinyTP:LsapSel");
 
ircomm_tty_start_watchdog_timer(self, 3*HZ);
ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL);
break;
case IRCOMM_TTY_WD_TIMER_EXPIRED:
/* Go back to search mode */
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
ircomm_tty_start_watchdog_timer(self, 3*HZ);
break;
case IRCOMM_TTY_CONNECT_INDICATION:
del_timer(&self->watchdog_timer);
 
/* Accept connection */
ircomm_connect_response(self->ircomm, NULL);
ircomm_tty_next_state(self, IRCOMM_TTY_READY);
break;
case IRCOMM_TTY_DETACH_CABLE:
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__,
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;
 
IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__,
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);
break;
case IRCOMM_TTY_DETACH_CABLE:
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__,
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;
 
IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__,
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);
break;
case IRCOMM_TTY_WD_TIMER_EXPIRED:
/* Go back to search mode */
ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH);
ircomm_tty_start_watchdog_timer(self, 3*HZ);
break;
case IRCOMM_TTY_DETACH_CABLE:
/* ircomm_disconnect_request(self->ircomm, NULL); */
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__,
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);
 
if (self->flags & ASYNC_CHECK_CD) {
/* Drop carrier */
self->settings.dce = IRCOMM_DELTA_CD;
ircomm_tty_check_modem_status(self);
} else {
IRDA_DEBUG(0, "%s(), hanging up!\n", __FUNCTION__);
if (self->tty)
tty_hangup(self->tty);
}
break;
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__,
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)
{
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
 
IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__,
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)
{
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
 
self->state = state;
IRDA_DEBUG(2, "%s: next state=%s, service type=%d\n", __FUNCTION__,
ircomm_tty_state[self->state], self->service_type);
}
 
/Makefile
0,0 → 1,25
#
# Makefile for the Linux IrDA IrCOMM protocol layer.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definition is now in the main makefile...
 
O_TARGET := ircomm_and_tty.o
 
list-multi := ircomm.o ircomm-tty.o
ircomm-objs := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o
ircomm-tty-objs := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o
 
obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o
 
include $(TOPDIR)/Rules.make
 
ircomm.o: $(ircomm-objs)
$(LD) $(LD_RFLAG) -r -o $@ $(ircomm-objs)
 
ircomm-tty.o: $(ircomm-tty-objs)
$(LD) $(LD_RFLAG) -r -o $@ $(ircomm-tty-objs)
 
/ircomm_ttp.c
0,0 → 1,304
/*********************************************************************
*
* Filename: ircomm_ttp.c
* Version: 1.0
* Description: Interface between IrCOMM and IrTTP
* Status: Stable
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Jun 6 20:48:27 1999
* Modified at: Mon Dec 13 11:35:13 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
*
* 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;
 
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
 
/* Register callbacks */
irda_notify_init(&notify);
notify.data_indication = ircomm_ttp_data_indication;
notify.connect_confirm = ircomm_ttp_connect_confirm;
notify.connect_indication = ircomm_ttp_connect_indication;
notify.flow_indication = ircomm_ttp_flow_indication;
notify.disconnect_indication = ircomm_ttp_disconnect_indication;
notify.instance = self;
strncpy(notify.name, "IrCOMM", NOTIFY_MAX_NAME);
 
self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
&notify);
if (!self->tsap) {
IRDA_DEBUG(0, "%s failed to allocate tsap\n", __FUNCTION__);
return -1;
}
self->slsap_sel = self->tsap->stsap_sel;
 
/*
* Initialize the call-table for issuing commands
*/
self->issue.data_request = ircomm_ttp_data_request;
self->issue.connect_request = ircomm_ttp_connect_request;
self->issue.connect_response = ircomm_ttp_connect_response;
self->issue.disconnect_request = ircomm_ttp_disconnect_request;
 
return 0;
}
 
/*
* Function ircomm_ttp_connect_request (self, userdata)
*
*
*
*/
int ircomm_ttp_connect_request(struct ircomm_cb *self,
struct sk_buff *userdata,
struct ircomm_info *info)
{
int ret = 0;
 
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
 
ret = irttp_connect_request(self->tsap, info->dlsap_sel,
info->saddr, info->daddr, NULL,
TTP_SAR_DISABLE, userdata);
return ret;
}
 
/*
* Function ircomm_ttp_connect_response (self, skb)
*
*
*
*/
int ircomm_ttp_connect_response(struct ircomm_cb *self, struct sk_buff *skb)
{
int ret;
 
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
ret = irttp_connect_response(self->tsap, TTP_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;);
 
IRDA_DEBUG(2, "%s(), clen=%d\n", __FUNCTION__, clen);
 
/*
* Insert clen field, currently we either send data only, or control
* only frames, to make things easier and avoid queueing
*/
ASSERT(skb_headroom(skb) >= IRCOMM_HEADER_SIZE, return -1;);
skb_push(skb, IRCOMM_HEADER_SIZE);
 
skb->data[0] = clen;
 
ret = irttp_data_request(self->tsap, skb);
if (ret) {
ERROR("%s(), failed\n", __FUNCTION__);
dev_kfree_skb(skb);
}
 
return ret;
}
 
/*
* Function ircomm_ttp_data_indication (instance, sap, skb)
*
* Incoming data
*
*/
int ircomm_ttp_data_indication(void *instance, void *sap,
struct sk_buff *skb)
{
struct ircomm_cb *self = (struct ircomm_cb *) instance;
 
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
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;
 
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_MAGIC, return;);
ASSERT(skb != NULL, return;);
ASSERT(qos != NULL, return;);
 
if (max_sdu_size != TTP_SAR_DISABLE) {
ERROR("%s(), SAR not allowed for IrCOMM!\n", __FUNCTION__);
dev_kfree_skb(skb);
return;
}
 
info.max_data_size = irttp_get_max_seg_size(self->tsap)
- IRCOMM_HEADER_SIZE;
info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE;
info.qos = qos;
 
ircomm_do_event(self, IRCOMM_TTP_CONNECT_CONFIRM, skb, &info);
}
 
/*
* 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;
 
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_MAGIC, return;);
ASSERT(skb != NULL, return;);
ASSERT(qos != NULL, return;);
 
if (max_sdu_size != TTP_SAR_DISABLE) {
ERROR("%s(), SAR not allowed for IrCOMM!\n", __FUNCTION__);
dev_kfree_skb(skb);
return;
}
 
info.max_data_size = irttp_get_max_seg_size(self->tsap)
- IRCOMM_HEADER_SIZE;
info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE;
info.qos = qos;
 
ircomm_do_event(self, IRCOMM_TTP_CONNECT_INDICATION, skb, &info);
}
 
/*
* 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;
 
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
 
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;
 
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
 
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_MAGIC, return;);
if (self->notify.flow_indication)
self->notify.flow_indication(self->notify.instance, self, cmd);
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.