Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

12
net/irda/ircomm/Kconfig Normal file
View 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
View 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

View 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);

View 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);
}

View 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(&notify);
notify.data_indication = ircomm_lmp_data_indication;
notify.connect_confirm = ircomm_lmp_connect_confirm;
notify.connect_indication = ircomm_lmp_connect_indication;
notify.disconnect_indication = ircomm_lmp_disconnect_indication;
notify.instance = self;
strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
self->lsap = irlmp_open_lsap(LSAP_ANY, &notify, 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;
}

View 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;
}

View 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(&notify);
notify.data_indication = ircomm_ttp_data_indication;
notify.connect_confirm = ircomm_ttp_connect_confirm;
notify.connect_indication = ircomm_ttp_connect_indication;
notify.flow_indication = ircomm_ttp_flow_indication;
notify.disconnect_indication = ircomm_ttp_disconnect_indication;
notify.instance = self;
strlcpy(notify.name, "IrCOMM", sizeof(notify.name));
self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
&notify);
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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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;
}