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(¬ify); |
notify.data_indication = ircomm_lmp_data_indication; |
notify.connect_confirm = ircomm_lmp_connect_confirm; |
notify.connect_indication = ircomm_lmp_connect_indication; |
notify.disconnect_indication = ircomm_lmp_disconnect_indication; |
notify.instance = self; |
strncpy(notify.name, "IrCOMM", NOTIFY_MAX_NAME); |
|
self->lsap = irlmp_open_lsap(LSAP_ANY, ¬ify, 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(¬ify); |
/* These callbacks we must handle ourselves */ |
notify.data_indication = ircomm_tty_data_indication; |
notify.udata_indication = ircomm_tty_control_indication; |
notify.flow_indication = ircomm_tty_flow_indication; |
|
/* Use the ircomm_tty interface for these ones */ |
notify.disconnect_indication = ircomm_tty_disconnect_indication; |
notify.connect_confirm = ircomm_tty_connect_confirm; |
notify.connect_indication = ircomm_tty_connect_indication; |
strncpy(notify.name, "ircomm_tty", NOTIFY_MAX_NAME); |
notify.instance = self; |
|
if (!self->ircomm) { |
self->ircomm = ircomm_open(¬ify, self->service_type, |
self->line); |
} |
if (!self->ircomm) |
return -ENODEV; |
|
self->slsap_sel = self->ircomm->slsap_sel; |
|
/* Connect IrCOMM link with remote device */ |
ret = ircomm_tty_attach_cable(self); |
if (ret < 0) { |
ERROR("%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(¬ify); |
notify.data_indication = ircomm_ttp_data_indication; |
notify.connect_confirm = ircomm_ttp_connect_confirm; |
notify.connect_indication = ircomm_ttp_connect_indication; |
notify.flow_indication = ircomm_ttp_flow_indication; |
notify.disconnect_indication = ircomm_ttp_disconnect_indication; |
notify.instance = self; |
strncpy(notify.name, "IrCOMM", NOTIFY_MAX_NAME); |
|
self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, |
¬ify); |
if (!self->tsap) { |
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); |
} |
|
|