mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
12
net/irda/ircomm/Kconfig
Normal file
12
net/irda/ircomm/Kconfig
Normal file
|
@ -0,0 +1,12 @@
|
|||
config IRCOMM
|
||||
tristate "IrCOMM protocol"
|
||||
depends on IRDA && TTY
|
||||
help
|
||||
Say Y here if you want to build support for the IrCOMM protocol.
|
||||
To compile it as modules, choose M here: the modules will be
|
||||
called ircomm and ircomm_tty.
|
||||
IrCOMM implements serial port emulation, and makes it possible to
|
||||
use all existing applications that understands TTY's with an
|
||||
infrared link. Thus you should be able to use application like PPP,
|
||||
minicom and others.
|
||||
|
8
net/irda/ircomm/Makefile
Normal file
8
net/irda/ircomm/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# Makefile for the Linux IrDA IrCOMM protocol layer.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o
|
||||
|
||||
ircomm-y := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o
|
||||
ircomm-tty-y := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o
|
590
net/irda/ircomm/ircomm_core.c
Normal file
590
net/irda/ircomm/ircomm_core.c
Normal file
|
@ -0,0 +1,590 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* 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.
|
||||
* Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.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
|
||||
extern struct proc_dir_entry *proc_irda;
|
||||
static int ircomm_seq_open(struct inode *, struct file *);
|
||||
|
||||
static const struct file_operations ircomm_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ircomm_seq_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
hashbin_t *ircomm = NULL;
|
||||
|
||||
static int __init ircomm_init(void)
|
||||
{
|
||||
ircomm = hashbin_new(HB_LOCK);
|
||||
if (ircomm == NULL) {
|
||||
IRDA_ERROR("%s(), can't allocate hashbin!\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
{ struct proc_dir_entry *ent;
|
||||
ent = proc_create("ircomm", 0, proc_irda, &ircomm_proc_fops);
|
||||
if (!ent) {
|
||||
printk(KERN_ERR "ircomm_init: can't create /proc entry!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
IRDA_MESSAGE("IrCOMM protocol (Dag Brattli)\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ircomm_cleanup(void)
|
||||
{
|
||||
IRDA_DEBUG(2, "%s()\n", __func__ );
|
||||
|
||||
hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
remove_proc_entry("ircomm", proc_irda);
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
}
|
||||
|
||||
/*
|
||||
* 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", __func__ ,
|
||||
service_type);
|
||||
|
||||
IRDA_ASSERT(ircomm != NULL, return NULL;);
|
||||
|
||||
self = kzalloc(sizeof(struct ircomm_cb), GFP_KERNEL);
|
||||
if (self == NULL)
|
||||
return NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_open);
|
||||
|
||||
/*
|
||||
* Function ircomm_close_instance (self)
|
||||
*
|
||||
* Remove IrCOMM instance
|
||||
*
|
||||
*/
|
||||
static int __ircomm_close(struct ircomm_cb *self)
|
||||
{
|
||||
IRDA_DEBUG(2, "%s()\n", __func__ );
|
||||
|
||||
/* 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;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -EIO;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;);
|
||||
|
||||
IRDA_DEBUG(0, "%s()\n", __func__ );
|
||||
|
||||
entry = hashbin_remove(ircomm, self->line, NULL);
|
||||
|
||||
IRDA_ASSERT(entry == self, return -1;);
|
||||
|
||||
return __ircomm_close(self);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_close);
|
||||
|
||||
/*
|
||||
* 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_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;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_connect_request);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
IRDA_DEBUG(2, "%s()\n", __func__ );
|
||||
|
||||
/*
|
||||
* 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", __func__ );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
|
||||
|
||||
IRDA_DEBUG(4, "%s()\n", __func__ );
|
||||
|
||||
ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_connect_response);
|
||||
|
||||
/*
|
||||
* 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", __func__ );
|
||||
|
||||
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", __func__ );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -EFAULT;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
|
||||
IRDA_ASSERT(skb != NULL, return -EFAULT;);
|
||||
|
||||
ret = ircomm_do_event(self, IRCOMM_DATA_REQUEST, skb, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_data_request);
|
||||
|
||||
/*
|
||||
* 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", __func__ );
|
||||
|
||||
IRDA_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", __func__ );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
IRDA_ASSERT(skb->len > 0, return;);
|
||||
|
||||
clen = skb->data[0];
|
||||
|
||||
/*
|
||||
* Input validation check: a stir4200/mcp2150 combinations sometimes
|
||||
* results in frames with clen > remaining packet size. These are
|
||||
* illegal; if we throw away just this frame then it seems to carry on
|
||||
* fine
|
||||
*/
|
||||
if (unlikely(skb->len < (clen + 1))) {
|
||||
IRDA_DEBUG(2, "%s() throwing away illegal frame\n",
|
||||
__func__ );
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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",
|
||||
__func__ );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -EFAULT;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
|
||||
IRDA_ASSERT(skb != NULL, return -EFAULT;);
|
||||
|
||||
ret = ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_control_request);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
IRDA_DEBUG(2, "%s()\n", __func__ );
|
||||
|
||||
/* Use udata for delivering data on the control channel */
|
||||
if (self->notify.udata_indication) {
|
||||
struct sk_buff *ctrl_skb;
|
||||
|
||||
/* We don't own the skb, so clone it */
|
||||
ctrl_skb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (!ctrl_skb)
|
||||
return;
|
||||
|
||||
/* Remove data channel from control channel */
|
||||
skb_trim(ctrl_skb, clen+1);
|
||||
|
||||
self->notify.udata_indication(self->notify.instance, self,
|
||||
ctrl_skb);
|
||||
|
||||
/* Drop reference count -
|
||||
* see ircomm_tty_control_indication(). */
|
||||
dev_kfree_skb(ctrl_skb);
|
||||
} else {
|
||||
IRDA_DEBUG(0, "%s(), missing handler\n", __func__ );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
|
||||
|
||||
ret = ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata,
|
||||
&info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_disconnect_request);
|
||||
|
||||
/*
|
||||
* 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", __func__ );
|
||||
|
||||
IRDA_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", __func__ );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_flow_request (self, flow)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow)
|
||||
{
|
||||
IRDA_DEBUG(2, "%s()\n", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
|
||||
if (self->service_type == IRCOMM_3_WIRE_RAW)
|
||||
return;
|
||||
|
||||
irttp_flow_request(self->tsap, flow);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(ircomm_flow_request);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void *ircomm_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
struct ircomm_cb *self;
|
||||
loff_t off = 0;
|
||||
|
||||
spin_lock_irq(&ircomm->hb_spinlock);
|
||||
|
||||
for (self = (struct ircomm_cb *) hashbin_get_first(ircomm);
|
||||
self != NULL;
|
||||
self = (struct ircomm_cb *) hashbin_get_next(ircomm)) {
|
||||
if (off++ == *pos)
|
||||
break;
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
static void *ircomm_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
++*pos;
|
||||
|
||||
return (void *) hashbin_get_next(ircomm);
|
||||
}
|
||||
|
||||
static void ircomm_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
spin_unlock_irq(&ircomm->hb_spinlock);
|
||||
}
|
||||
|
||||
static int ircomm_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
const struct ircomm_cb *self = v;
|
||||
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EINVAL; );
|
||||
|
||||
if(self->line < 0x10)
|
||||
seq_printf(seq, "ircomm%d", self->line);
|
||||
else
|
||||
seq_printf(seq, "irlpt%d", self->line - 0x10);
|
||||
|
||||
seq_printf(seq,
|
||||
" state: %s, slsap_sel: %#02x, dlsap_sel: %#02x, mode:",
|
||||
ircomm_state[ self->state],
|
||||
self->slsap_sel, self->dlsap_sel);
|
||||
|
||||
if(self->service_type & IRCOMM_3_WIRE_RAW)
|
||||
seq_printf(seq, " 3-wire-raw");
|
||||
if(self->service_type & IRCOMM_3_WIRE)
|
||||
seq_printf(seq, " 3-wire");
|
||||
if(self->service_type & IRCOMM_9_WIRE)
|
||||
seq_printf(seq, " 9-wire");
|
||||
if(self->service_type & IRCOMM_CENTRONICS)
|
||||
seq_printf(seq, " Centronics");
|
||||
seq_putc(seq, '\n');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations ircomm_seq_ops = {
|
||||
.start = ircomm_seq_start,
|
||||
.next = ircomm_seq_next,
|
||||
.stop = ircomm_seq_stop,
|
||||
.show = ircomm_seq_show,
|
||||
};
|
||||
|
||||
static int ircomm_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &ircomm_seq_ops);
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
MODULE_AUTHOR("Dag Brattli <dag@brattli.net>");
|
||||
MODULE_DESCRIPTION("IrCOMM protocol");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(ircomm_init);
|
||||
module_exit(ircomm_cleanup);
|
248
net/irda/ircomm/ircomm_event.c
Normal file
248
net/irda/ircomm/ircomm_event.c
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#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);
|
||||
|
||||
const char *const ircomm_state[] = {
|
||||
"IRCOMM_IDLE",
|
||||
"IRCOMM_WAITI",
|
||||
"IRCOMM_WAITR",
|
||||
"IRCOMM_CONN",
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IRDA_DEBUG
|
||||
static const char *const 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",
|
||||
};
|
||||
#endif /* CONFIG_IRDA_DEBUG */
|
||||
|
||||
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", __func__ ,
|
||||
ircomm_event[event]);
|
||||
ret = -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", __func__ ,
|
||||
ircomm_event[event]);
|
||||
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", __func__ ,
|
||||
ircomm_event[event]);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_state_conn (self, event, skb)
|
||||
*
|
||||
* IrCOMM is connected to the peer IrCOMM device
|
||||
*
|
||||
*/
|
||||
static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case IRCOMM_DATA_REQUEST:
|
||||
ret = self->issue.data_request(self, skb, 0);
|
||||
break;
|
||||
case IRCOMM_TTP_DATA_INDICATION:
|
||||
ircomm_process_data(self, skb);
|
||||
break;
|
||||
case IRCOMM_LMP_DATA_INDICATION:
|
||||
ircomm_data_indication(self, skb);
|
||||
break;
|
||||
case IRCOMM_CONTROL_REQUEST:
|
||||
/* Just send a separate frame for now */
|
||||
ret = self->issue.data_request(self, skb, skb->len);
|
||||
break;
|
||||
case IRCOMM_TTP_DISCONNECT_INDICATION:
|
||||
case IRCOMM_LMP_DISCONNECT_INDICATION:
|
||||
ircomm_next_state(self, IRCOMM_IDLE);
|
||||
ircomm_disconnect_indication(self, skb, info);
|
||||
break;
|
||||
case IRCOMM_DISCONNECT_REQUEST:
|
||||
ircomm_next_state(self, IRCOMM_IDLE);
|
||||
ret = self->issue.disconnect_request(self, skb, info);
|
||||
break;
|
||||
default:
|
||||
IRDA_DEBUG(0, "%s(), unknown event = %s\n", __func__ ,
|
||||
ircomm_event[event]);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_do_event (self, event, skb)
|
||||
*
|
||||
* Process event
|
||||
*
|
||||
*/
|
||||
int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event,
|
||||
struct sk_buff *skb, struct ircomm_info *info)
|
||||
{
|
||||
IRDA_DEBUG(4, "%s: state=%s, event=%s\n", __func__ ,
|
||||
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", __func__ ,
|
||||
ircomm_state[self->state], self->service_type);
|
||||
}
|
368
net/irda/ircomm/ircomm_lmp.c
Normal file
368
net/irda/ircomm/ircomm_lmp.c
Normal file
|
@ -0,0 +1,368 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* 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.
|
||||
* Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/gfp.h>
|
||||
|
||||
#include <net/irda/irda.h>
|
||||
#include <net/irda/irlmp.h>
|
||||
#include <net/irda/iriap.h>
|
||||
#include <net/irda/irda_device.h> /* struct irda_skb_cb */
|
||||
|
||||
#include <net/irda/ircomm_event.h>
|
||||
#include <net/irda/ircomm_lmp.h>
|
||||
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_connect_request (self, userdata)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static 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", __func__ );
|
||||
|
||||
/* Don't forget to refcount it - should be NULL anyway */
|
||||
if(userdata)
|
||||
skb_get(userdata);
|
||||
|
||||
ret = irlmp_connect_request(self->lsap, info->dlsap_sel,
|
||||
info->saddr, info->daddr, NULL, userdata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_connect_response (self, skb)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int ircomm_lmp_connect_response(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata)
|
||||
{
|
||||
struct sk_buff *tx_skb;
|
||||
|
||||
IRDA_DEBUG(0, "%s()\n", __func__ );
|
||||
|
||||
/* Any userdata supplied? */
|
||||
if (userdata == NULL) {
|
||||
tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
|
||||
if (!tx_skb)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Reserve space for MUX and LAP header */
|
||||
skb_reserve(tx_skb, LMP_MAX_HEADER);
|
||||
} else {
|
||||
/*
|
||||
* Check that the client has reserved enough space for
|
||||
* headers
|
||||
*/
|
||||
IRDA_ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER,
|
||||
return -1;);
|
||||
|
||||
/* Don't forget to refcount it - should be NULL anyway */
|
||||
skb_get(userdata);
|
||||
tx_skb = userdata;
|
||||
}
|
||||
|
||||
return irlmp_connect_response(self->lsap, tx_skb);
|
||||
}
|
||||
|
||||
static int ircomm_lmp_disconnect_request(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata,
|
||||
struct ircomm_info *info)
|
||||
{
|
||||
struct sk_buff *tx_skb;
|
||||
int ret;
|
||||
|
||||
IRDA_DEBUG(0, "%s()\n", __func__ );
|
||||
|
||||
if (!userdata) {
|
||||
tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
|
||||
if (!tx_skb)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Reserve space for MUX and LAP header */
|
||||
skb_reserve(tx_skb, LMP_MAX_HEADER);
|
||||
userdata = tx_skb;
|
||||
} else {
|
||||
/* Don't forget to refcount it - should be NULL anyway */
|
||||
skb_get(userdata);
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
static void ircomm_lmp_flow_control(struct sk_buff *skb)
|
||||
{
|
||||
struct irda_skb_cb *cb;
|
||||
struct ircomm_cb *self;
|
||||
int line;
|
||||
|
||||
IRDA_ASSERT(skb != NULL, return;);
|
||||
|
||||
cb = (struct irda_skb_cb *) skb->cb;
|
||||
|
||||
IRDA_DEBUG(2, "%s()\n", __func__ );
|
||||
|
||||
line = cb->line;
|
||||
|
||||
self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL);
|
||||
if (!self) {
|
||||
IRDA_DEBUG(2, "%s(), didn't find myself\n", __func__ );
|
||||
return;
|
||||
}
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_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", __func__ );
|
||||
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
|
||||
*
|
||||
*/
|
||||
static int ircomm_lmp_data_request(struct ircomm_cb *self,
|
||||
struct sk_buff *skb,
|
||||
int not_used)
|
||||
{
|
||||
struct irda_skb_cb *cb;
|
||||
int ret;
|
||||
|
||||
IRDA_ASSERT(skb != NULL, return -1;);
|
||||
|
||||
cb = (struct irda_skb_cb *) skb->cb;
|
||||
|
||||
cb->line = self->line;
|
||||
|
||||
IRDA_DEBUG(4, "%s(), sending frame\n", __func__ );
|
||||
|
||||
/* Don't forget to refcount it - see ircomm_tty_do_softint() */
|
||||
skb_get(skb);
|
||||
|
||||
skb_orphan(skb);
|
||||
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", __func__ );
|
||||
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) {
|
||||
IRDA_ERROR("%s(), failed\n", __func__);
|
||||
/* irlmp_data_request already free the packet */
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
static 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
|
||||
IRDA_ASSERT(skb != NULL, return -1;);
|
||||
|
||||
ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL);
|
||||
|
||||
/* Drop reference count - see ircomm_tty_data_indication(). */
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size,
|
||||
* max_header_size, skb)
|
||||
*
|
||||
* Connection has been confirmed by peer device
|
||||
*
|
||||
*/
|
||||
static 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
IRDA_ASSERT(skb != NULL, return;);
|
||||
IRDA_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);
|
||||
|
||||
/* Drop reference count - see ircomm_tty_connect_confirm(). */
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size,
|
||||
* max_header_size, skb)
|
||||
*
|
||||
* Peer device wants to make a connection with us
|
||||
*
|
||||
*/
|
||||
static 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
IRDA_ASSERT(skb != NULL, return;);
|
||||
IRDA_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);
|
||||
|
||||
/* Drop reference count - see ircomm_tty_connect_indication(). */
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb)
|
||||
*
|
||||
* Peer device has closed the connection, or the link went down for some
|
||||
* other reason
|
||||
*/
|
||||
static 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
|
||||
info.reason = reason;
|
||||
|
||||
ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info);
|
||||
|
||||
/* Drop reference count - see ircomm_tty_disconnect_indication(). */
|
||||
if(skb)
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
/*
|
||||
* 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", __func__ );
|
||||
|
||||
/* 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;
|
||||
strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
|
||||
|
||||
self->lsap = irlmp_open_lsap(LSAP_ANY, ¬ify, 0);
|
||||
if (!self->lsap) {
|
||||
IRDA_DEBUG(0,"%sfailed to allocate tsap\n", __func__ );
|
||||
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;
|
||||
}
|
504
net/irda/ircomm/ircomm_param.c
Normal file
504
net/irda/ircomm/ircomm_param.c
Normal file
|
@ -0,0 +1,504 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/workqueue.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_request (self, pi, flush)
|
||||
*
|
||||
* Queue a parameter for the control channel
|
||||
*
|
||||
*/
|
||||
int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct sk_buff *skb;
|
||||
int count;
|
||||
|
||||
IRDA_DEBUG(2, "%s()\n", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
/* Make sure we don't send parameters for raw mode */
|
||||
if (self->service_type == IRCOMM_3_WIRE_RAW)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&self->spinlock, flags);
|
||||
|
||||
skb = self->ctrl_skb;
|
||||
if (!skb) {
|
||||
skb = alloc_skb(256, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
spin_unlock_irqrestore(&self->spinlock, 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_pointer(skb),
|
||||
skb_tailroom(skb), &ircomm_param_info);
|
||||
if (count < 0) {
|
||||
IRDA_WARNING("%s(), no room for parameter!\n", __func__);
|
||||
spin_unlock_irqrestore(&self->spinlock, flags);
|
||||
return -1;
|
||||
}
|
||||
skb_put(skb, count);
|
||||
|
||||
spin_unlock_irqrestore(&self->spinlock, flags);
|
||||
|
||||
IRDA_DEBUG(2, "%s(), skb->len=%d\n", __func__ , skb->len);
|
||||
|
||||
if (flush) {
|
||||
/* ircomm_tty_do_softint will take care of the rest */
|
||||
schedule_work(&self->tqueue);
|
||||
}
|
||||
|
||||
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 parameters
|
||||
*
|
||||
*/
|
||||
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;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_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", __func__ );
|
||||
return -1;
|
||||
}
|
||||
IRDA_DEBUG(0, "%s(), services in common=%02x\n", __func__ ,
|
||||
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", __func__ ,
|
||||
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;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_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", __func__ ,
|
||||
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;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (get) {
|
||||
IRDA_DEBUG(0, "%s(), not imp!\n", __func__ );
|
||||
} else {
|
||||
IRDA_DEBUG(0, "%s(), port-name=%s\n", __func__ , 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;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_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", __func__ , 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;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_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;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_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", __func__ , (__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;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_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", __func__ ,
|
||||
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;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_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", __func__ ,
|
||||
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", __func__ );
|
||||
|
||||
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;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (get)
|
||||
param->pv.i = self->settings.dte;
|
||||
else {
|
||||
dte = (__u8) param->pv.i;
|
||||
|
||||
self->settings.dce = 0;
|
||||
|
||||
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", __func__ , (__u8) param->pv.i);
|
||||
|
||||
dce = (__u8) param->pv.i;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_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", __func__ );
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
/* Poll parameters are always of length 0 (just a signal) */
|
||||
if (!get) {
|
||||
/* Respond with DTE line settings */
|
||||
ircomm_param_request(self, IRCOMM_DTE, TRUE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
366
net/irda/ircomm/ircomm_ttp.c
Normal file
366
net/irda/ircomm/ircomm_ttp.c
Normal file
|
@ -0,0 +1,366 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* 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.
|
||||
* Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#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>
|
||||
|
||||
static int ircomm_ttp_data_indication(void *instance, void *sap,
|
||||
struct sk_buff *skb);
|
||||
static 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);
|
||||
static 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);
|
||||
static void ircomm_ttp_flow_indication(void *instance, void *sap,
|
||||
LOCAL_FLOW cmd);
|
||||
static void ircomm_ttp_disconnect_indication(void *instance, void *sap,
|
||||
LM_REASON reason,
|
||||
struct sk_buff *skb);
|
||||
static int ircomm_ttp_data_request(struct ircomm_cb *self,
|
||||
struct sk_buff *skb,
|
||||
int clen);
|
||||
static int ircomm_ttp_connect_request(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata,
|
||||
struct ircomm_info *info);
|
||||
static int ircomm_ttp_connect_response(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata);
|
||||
static int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata,
|
||||
struct ircomm_info *info);
|
||||
|
||||
/*
|
||||
* Function ircomm_open_tsap (self)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
int ircomm_open_tsap(struct ircomm_cb *self)
|
||||
{
|
||||
notify_t notify;
|
||||
|
||||
IRDA_DEBUG(4, "%s()\n", __func__ );
|
||||
|
||||
/* 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;
|
||||
strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
|
||||
|
||||
self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
|
||||
¬ify);
|
||||
if (!self->tsap) {
|
||||
IRDA_DEBUG(0, "%sfailed to allocate tsap\n", __func__ );
|
||||
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)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static 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", __func__ );
|
||||
|
||||
/* Don't forget to refcount it - should be NULL anyway */
|
||||
if(userdata)
|
||||
skb_get(userdata);
|
||||
|
||||
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)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int ircomm_ttp_connect_response(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
IRDA_DEBUG(4, "%s()\n", __func__ );
|
||||
|
||||
/* Don't forget to refcount it - should be NULL anyway */
|
||||
if(userdata)
|
||||
skb_get(userdata);
|
||||
|
||||
ret = irttp_connect_response(self->tsap, TTP_SAR_DISABLE, userdata);
|
||||
|
||||
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.
|
||||
*/
|
||||
static int ircomm_ttp_data_request(struct ircomm_cb *self,
|
||||
struct sk_buff *skb,
|
||||
int clen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
IRDA_ASSERT(skb != NULL, return -1;);
|
||||
|
||||
IRDA_DEBUG(2, "%s(), clen=%d\n", __func__ , clen);
|
||||
|
||||
/*
|
||||
* Insert clen field, currently we either send data only, or control
|
||||
* only frames, to make things easier and avoid queueing
|
||||
*/
|
||||
IRDA_ASSERT(skb_headroom(skb) >= IRCOMM_HEADER_SIZE, return -1;);
|
||||
|
||||
/* Don't forget to refcount it - see ircomm_tty_do_softint() */
|
||||
skb_get(skb);
|
||||
|
||||
skb_push(skb, IRCOMM_HEADER_SIZE);
|
||||
|
||||
skb->data[0] = clen;
|
||||
|
||||
ret = irttp_data_request(self->tsap, skb);
|
||||
if (ret) {
|
||||
IRDA_ERROR("%s(), failed\n", __func__);
|
||||
/* irttp_data_request already free the packet */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_data_indication (instance, sap, skb)
|
||||
*
|
||||
* Incoming data
|
||||
*
|
||||
*/
|
||||
static 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
|
||||
IRDA_ASSERT(skb != NULL, return -1;);
|
||||
|
||||
ircomm_do_event(self, IRCOMM_TTP_DATA_INDICATION, skb, NULL);
|
||||
|
||||
/* Drop reference count - see ircomm_tty_data_indication(). */
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
IRDA_ASSERT(skb != NULL, return;);
|
||||
IRDA_ASSERT(qos != NULL, goto out;);
|
||||
|
||||
if (max_sdu_size != TTP_SAR_DISABLE) {
|
||||
IRDA_ERROR("%s(), SAR not allowed for IrCOMM!\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
out:
|
||||
/* Drop reference count - see ircomm_tty_connect_confirm(). */
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_connect_indication (instance, sap, qos, max_sdu_size,
|
||||
* max_header_size, skb)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
IRDA_ASSERT(skb != NULL, return;);
|
||||
IRDA_ASSERT(qos != NULL, goto out;);
|
||||
|
||||
if (max_sdu_size != TTP_SAR_DISABLE) {
|
||||
IRDA_ERROR("%s(), SAR not allowed for IrCOMM!\n",
|
||||
__func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
out:
|
||||
/* Drop reference count - see ircomm_tty_connect_indication(). */
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_disconnect_request (self, userdata, info)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
|
||||
struct sk_buff *userdata,
|
||||
struct ircomm_info *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Don't forget to refcount it - should be NULL anyway */
|
||||
if(userdata)
|
||||
skb_get(userdata);
|
||||
|
||||
ret = irttp_disconnect_request(self->tsap, userdata, P_NORMAL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_disconnect_indication (instance, sap, reason, skb)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
static 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
|
||||
info.reason = reason;
|
||||
|
||||
ircomm_do_event(self, IRCOMM_TTP_DISCONNECT_INDICATION, skb, &info);
|
||||
|
||||
/* Drop reference count - see ircomm_tty_disconnect_indication(). */
|
||||
if(skb)
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_ttp_flow_indication (instance, sap, cmd)
|
||||
*
|
||||
* Layer below is telling us to start or stop the flow of data
|
||||
*
|
||||
*/
|
||||
static 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", __func__ );
|
||||
|
||||
IRDA_ASSERT(self != NULL, return;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
|
||||
|
||||
if (self->notify.flow_indication)
|
||||
self->notify.flow_indication(self->notify.instance, self, cmd);
|
||||
}
|
||||
|
||||
|
1398
net/irda/ircomm/ircomm_tty.c
Normal file
1398
net/irda/ircomm/ircomm_tty.c
Normal file
File diff suppressed because it is too large
Load diff
1009
net/irda/ircomm/ircomm_tty_attach.c
Normal file
1009
net/irda/ircomm/ircomm_tty_attach.c
Normal file
File diff suppressed because it is too large
Load diff
426
net/irda/ircomm/ircomm_tty_ioctl.c
Normal file
426
net/irda/ircomm/ircomm_tty_ioctl.c
Normal file
|
@ -0,0 +1,426 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/termios.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/serial.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
|
||||
*/
|
||||
static void ircomm_tty_change_speed(struct ircomm_tty_cb *self,
|
||||
struct tty_struct *tty)
|
||||
{
|
||||
unsigned int cflag, cval;
|
||||
int baud;
|
||||
|
||||
IRDA_DEBUG(2, "%s()\n", __func__ );
|
||||
|
||||
if (!self->ircomm)
|
||||
return;
|
||||
|
||||
cflag = 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(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->port.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)
|
||||
IRDA_WARNING("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n", __func__);
|
||||
} else {
|
||||
self->port.flags &= ~ASYNC_CTS_FLOW;
|
||||
self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN;
|
||||
}
|
||||
if (cflag & CLOCAL)
|
||||
self->port.flags &= ~ASYNC_CHECK_CD;
|
||||
else
|
||||
self->port.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 ktermios *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", __func__ );
|
||||
|
||||
if ((cflag == old_termios->c_cflag) &&
|
||||
(RELEVANT_IFLAG(tty->termios.c_iflag) ==
|
||||
RELEVANT_IFLAG(old_termios->c_iflag)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ircomm_tty_change_speed(self, tty);
|
||||
|
||||
/* 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_tiocmget (tty)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
int ircomm_tty_tiocmget(struct tty_struct *tty)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
|
||||
unsigned int result;
|
||||
|
||||
IRDA_DEBUG(2, "%s()\n", __func__ );
|
||||
|
||||
if (tty->flags & (1 << TTY_IO_ERROR))
|
||||
return -EIO;
|
||||
|
||||
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 result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function ircomm_tty_tiocmset (tty, set, clear)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
int ircomm_tty_tiocmset(struct tty_struct *tty,
|
||||
unsigned int set, unsigned int clear)
|
||||
{
|
||||
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
|
||||
|
||||
IRDA_DEBUG(2, "%s()\n", __func__ );
|
||||
|
||||
if (tty->flags & (1 << TTY_IO_ERROR))
|
||||
return -EIO;
|
||||
|
||||
IRDA_ASSERT(self != NULL, return -1;);
|
||||
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
|
||||
|
||||
if (set & TIOCM_RTS)
|
||||
self->settings.dte |= IRCOMM_RTS;
|
||||
if (set & TIOCM_DTR)
|
||||
self->settings.dte |= IRCOMM_DTR;
|
||||
|
||||
if (clear & TIOCM_RTS)
|
||||
self->settings.dte &= ~IRCOMM_RTS;
|
||||
if (clear & TIOCM_DTR)
|
||||
self->settings.dte &= ~IRCOMM_DTR;
|
||||
|
||||
if ((set|clear) & TIOCM_RTS)
|
||||
self->settings.dte |= IRCOMM_DELTA_RTS;
|
||||
if ((set|clear) & TIOCM_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 __user *retinfo)
|
||||
{
|
||||
struct serial_struct info;
|
||||
|
||||
if (!retinfo)
|
||||
return -EFAULT;
|
||||
|
||||
IRDA_DEBUG(2, "%s()\n", __func__ );
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.line = self->line;
|
||||
info.flags = self->port.flags;
|
||||
info.baud_base = self->settings.data_rate;
|
||||
info.close_delay = self->port.close_delay;
|
||||
info.closing_wait = self->port.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 __user *new_info)
|
||||
{
|
||||
#if 0
|
||||
struct serial_struct new_serial;
|
||||
struct ircomm_tty_cb old_state, *state;
|
||||
|
||||
IRDA_DEBUG(0, "%s()\n", __func__ );
|
||||
|
||||
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, cmd, arg)
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
int ircomm_tty_ioctl(struct tty_struct *tty,
|
||||
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", __func__ );
|
||||
|
||||
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 TIOCGSERIAL:
|
||||
ret = ircomm_tty_get_serial_info(self, (struct serial_struct __user *) arg);
|
||||
break;
|
||||
case TIOCSSERIAL:
|
||||
ret = ircomm_tty_set_serial_info(self, (struct serial_struct __user *) arg);
|
||||
break;
|
||||
case TIOCMIWAIT:
|
||||
IRDA_DEBUG(0, "(), TIOCMIWAIT, not impl!\n");
|
||||
break;
|
||||
|
||||
case TIOCGICOUNT:
|
||||
IRDA_DEBUG(0, "%s(), TIOCGICOUNT not impl!\n", __func__ );
|
||||
#if 0
|
||||
save_flags(flags); cli();
|
||||
cnow = driver->icount;
|
||||
restore_flags(flags);
|
||||
p_cuser = (struct serial_icounter_struct __user *) 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;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue