[PATCH] isdn4linux: Siemens Gigaset drivers - tty interface
And: Tilman Schmidt <tilman@imap.cc> This patch adds the tty interface to the gigaset module. The tty interface provides direct access to the AT command set of the Gigaset devices. Signed-off-by: Hansjoerg Lipp <hjlipp@web.de> Signed-off-by: Tilman Schmidt <tilman@imap.cc> Cc: Karsten Keil <kkeil@suse.de> Cc: Greg KH <greg@kroah.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
3c66a22545
commit
ee8a4b7f85
|
@ -0,0 +1,718 @@
|
|||
/*
|
||||
* interface to user space for the gigaset driver
|
||||
*
|
||||
* Copyright (c) 2004 by Hansjoerg Lipp <hjlipp@web.de>
|
||||
*
|
||||
* =====================================================================
|
||||
* 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.
|
||||
* =====================================================================
|
||||
* Version: $Id: interface.c,v 1.14.4.15 2006/02/04 18:28:16 hjlipp Exp $
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
#include <linux/gigaset_dev.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
|
||||
/*** our ioctls ***/
|
||||
|
||||
static int if_lock(struct cardstate *cs, int *arg)
|
||||
{
|
||||
int cmd = *arg;
|
||||
|
||||
dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd);
|
||||
|
||||
if (cmd > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (cmd < 0) {
|
||||
*arg = atomic_read(&cs->mstate) == MS_LOCKED; //FIXME remove?
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!cmd && atomic_read(&cs->mstate) == MS_LOCKED
|
||||
&& atomic_read(&cs->connected)) {
|
||||
cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS);
|
||||
cs->ops->baud_rate(cs, B115200);
|
||||
cs->ops->set_line_ctrl(cs, CS8);
|
||||
cs->control_state = TIOCM_DTR|TIOCM_RTS;
|
||||
}
|
||||
|
||||
cs->waiting = 1;
|
||||
if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK,
|
||||
NULL, cmd, NULL)) {
|
||||
cs->waiting = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dbg(DEBUG_CMD, "scheduling IF_LOCK");
|
||||
gigaset_schedule_event(cs);
|
||||
|
||||
wait_event(cs->waitqueue, !cs->waiting);
|
||||
|
||||
if (cs->cmd_result >= 0) {
|
||||
*arg = cs->cmd_result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cs->cmd_result;
|
||||
}
|
||||
|
||||
static int if_version(struct cardstate *cs, unsigned arg[4])
|
||||
{
|
||||
static const unsigned version[4] = GIG_VERSION;
|
||||
static const unsigned compat[4] = GIG_COMPAT;
|
||||
unsigned cmd = arg[0];
|
||||
|
||||
dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case GIGVER_DRIVER:
|
||||
memcpy(arg, version, sizeof version);
|
||||
return 0;
|
||||
case GIGVER_COMPAT:
|
||||
memcpy(arg, compat, sizeof compat);
|
||||
return 0;
|
||||
case GIGVER_FWBASE:
|
||||
cs->waiting = 1;
|
||||
if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER,
|
||||
NULL, 0, arg)) {
|
||||
cs->waiting = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dbg(DEBUG_CMD, "scheduling IF_VER");
|
||||
gigaset_schedule_event(cs);
|
||||
|
||||
wait_event(cs->waitqueue, !cs->waiting);
|
||||
|
||||
if (cs->cmd_result >= 0)
|
||||
return 0;
|
||||
|
||||
return cs->cmd_result;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int if_config(struct cardstate *cs, int *arg)
|
||||
{
|
||||
dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg);
|
||||
|
||||
if (*arg != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (atomic_read(&cs->mstate) != MS_LOCKED)
|
||||
return -EBUSY;
|
||||
|
||||
*arg = 0;
|
||||
return gigaset_enterconfigmode(cs);
|
||||
}
|
||||
|
||||
/*** the terminal driver ***/
|
||||
/* stolen from usbserial and some other tty drivers */
|
||||
|
||||
static int if_open(struct tty_struct *tty, struct file *filp);
|
||||
static void if_close(struct tty_struct *tty, struct file *filp);
|
||||
static int if_ioctl(struct tty_struct *tty, struct file *file,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
static int if_write_room(struct tty_struct *tty);
|
||||
static int if_chars_in_buffer(struct tty_struct *tty);
|
||||
static void if_throttle(struct tty_struct *tty);
|
||||
static void if_unthrottle(struct tty_struct *tty);
|
||||
static void if_set_termios(struct tty_struct *tty, struct termios *old);
|
||||
static int if_tiocmget(struct tty_struct *tty, struct file *file);
|
||||
static int if_tiocmset(struct tty_struct *tty, struct file *file,
|
||||
unsigned int set, unsigned int clear);
|
||||
static int if_write(struct tty_struct *tty,
|
||||
const unsigned char *buf, int count);
|
||||
|
||||
static struct tty_operations if_ops = {
|
||||
.open = if_open,
|
||||
.close = if_close,
|
||||
.ioctl = if_ioctl,
|
||||
.write = if_write,
|
||||
.write_room = if_write_room,
|
||||
.chars_in_buffer = if_chars_in_buffer,
|
||||
.set_termios = if_set_termios,
|
||||
.throttle = if_throttle,
|
||||
.unthrottle = if_unthrottle,
|
||||
#if 0
|
||||
.break_ctl = serial_break,
|
||||
#endif
|
||||
.tiocmget = if_tiocmget,
|
||||
.tiocmset = if_tiocmset,
|
||||
};
|
||||
|
||||
static int if_open(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
unsigned long flags;
|
||||
|
||||
dbg(DEBUG_IF, "%d+%d: %s()", tty->driver->minor_start, tty->index,
|
||||
__FUNCTION__);
|
||||
|
||||
tty->driver_data = NULL;
|
||||
|
||||
cs = gigaset_get_cs_by_tty(tty);
|
||||
if (!cs)
|
||||
return -ENODEV;
|
||||
|
||||
if (down_interruptible(&cs->sem))
|
||||
return -ERESTARTSYS; // FIXME -EINTR?
|
||||
tty->driver_data = cs;
|
||||
|
||||
++cs->open_count;
|
||||
|
||||
if (cs->open_count == 1) {
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
cs->tty = tty;
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
tty->low_latency = 1; //FIXME test
|
||||
//FIXME
|
||||
}
|
||||
|
||||
up(&cs->sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void if_close(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
unsigned long flags;
|
||||
|
||||
cs = (struct cardstate *) tty->driver_data;
|
||||
if (!cs) {
|
||||
err("cs==NULL in %s", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__);
|
||||
|
||||
down(&cs->sem);
|
||||
|
||||
if (!cs->open_count)
|
||||
warn("%s: device not opened", __FUNCTION__);
|
||||
else {
|
||||
if (!--cs->open_count) {
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
cs->tty = NULL;
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
//FIXME
|
||||
}
|
||||
}
|
||||
|
||||
up(&cs->sem);
|
||||
}
|
||||
|
||||
static int if_ioctl(struct tty_struct *tty, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
int retval = -ENODEV;
|
||||
int int_arg;
|
||||
unsigned char buf[6];
|
||||
unsigned version[4];
|
||||
|
||||
cs = (struct cardstate *) tty->driver_data;
|
||||
if (!cs) {
|
||||
err("cs==NULL in %s", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __FUNCTION__, cmd);
|
||||
|
||||
if (down_interruptible(&cs->sem))
|
||||
return -ERESTARTSYS; // FIXME -EINTR?
|
||||
|
||||
if (!cs->open_count)
|
||||
warn("%s: device not opened", __FUNCTION__);
|
||||
else {
|
||||
retval = 0;
|
||||
switch (cmd) {
|
||||
case GIGASET_REDIR:
|
||||
retval = get_user(int_arg, (int __user *) arg);
|
||||
if (retval >= 0)
|
||||
retval = if_lock(cs, &int_arg);
|
||||
if (retval >= 0)
|
||||
retval = put_user(int_arg, (int __user *) arg);
|
||||
break;
|
||||
case GIGASET_CONFIG:
|
||||
retval = get_user(int_arg, (int __user *) arg);
|
||||
if (retval >= 0)
|
||||
retval = if_config(cs, &int_arg);
|
||||
if (retval >= 0)
|
||||
retval = put_user(int_arg, (int __user *) arg);
|
||||
break;
|
||||
case GIGASET_BRKCHARS:
|
||||
//FIXME test if MS_LOCKED
|
||||
gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS",
|
||||
6, (const unsigned char *) arg, 1);
|
||||
if (!atomic_read(&cs->connected)) {
|
||||
dbg(DEBUG_ANY, "can't communicate with unplugged device");
|
||||
retval = -ENODEV;
|
||||
break;
|
||||
}
|
||||
retval = copy_from_user(&buf,
|
||||
(const unsigned char __user *) arg, 6)
|
||||
? -EFAULT : 0;
|
||||
if (retval >= 0)
|
||||
retval = cs->ops->brkchars(cs, buf);
|
||||
break;
|
||||
case GIGASET_VERSION:
|
||||
retval = copy_from_user(version, (unsigned __user *) arg,
|
||||
sizeof version) ? -EFAULT : 0;
|
||||
if (retval >= 0)
|
||||
retval = if_version(cs, version);
|
||||
if (retval >= 0)
|
||||
retval = copy_to_user((unsigned __user *) arg, version,
|
||||
sizeof version)
|
||||
? -EFAULT : 0;
|
||||
break;
|
||||
default:
|
||||
dbg(DEBUG_ANY, "%s: arg not supported - 0x%04x",
|
||||
__FUNCTION__, cmd);
|
||||
retval = -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
up(&cs->sem);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_tiocmget(struct tty_struct *tty, struct file *file)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
int retval;
|
||||
|
||||
cs = (struct cardstate *) tty->driver_data;
|
||||
if (!cs) {
|
||||
err("cs==NULL in %s", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__);
|
||||
|
||||
if (down_interruptible(&cs->sem))
|
||||
return -ERESTARTSYS; // FIXME -EINTR?
|
||||
|
||||
// FIXME read from device?
|
||||
retval = cs->control_state & (TIOCM_RTS|TIOCM_DTR);
|
||||
|
||||
up(&cs->sem);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_tiocmset(struct tty_struct *tty, struct file *file,
|
||||
unsigned int set, unsigned int clear)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
int retval;
|
||||
unsigned mc;
|
||||
|
||||
cs = (struct cardstate *) tty->driver_data;
|
||||
if (!cs) {
|
||||
err("cs==NULL in %s", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dbg(DEBUG_IF,
|
||||
"%u: %s(0x%x, 0x%x)", cs->minor_index, __FUNCTION__, set, clear);
|
||||
|
||||
if (down_interruptible(&cs->sem))
|
||||
return -ERESTARTSYS; // FIXME -EINTR?
|
||||
|
||||
if (!atomic_read(&cs->connected)) {
|
||||
dbg(DEBUG_ANY, "can't communicate with unplugged device");
|
||||
retval = -ENODEV;
|
||||
} else {
|
||||
mc = (cs->control_state | set) & ~clear & (TIOCM_RTS|TIOCM_DTR);
|
||||
retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc);
|
||||
cs->control_state = mc;
|
||||
}
|
||||
|
||||
up(&cs->sem);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
int retval = -ENODEV;
|
||||
|
||||
cs = (struct cardstate *) tty->driver_data;
|
||||
if (!cs) {
|
||||
err("cs==NULL in %s", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__);
|
||||
|
||||
if (down_interruptible(&cs->sem))
|
||||
return -ERESTARTSYS; // FIXME -EINTR?
|
||||
|
||||
if (!cs->open_count)
|
||||
warn("%s: device not opened", __FUNCTION__);
|
||||
else if (atomic_read(&cs->mstate) != MS_LOCKED) {
|
||||
warn("can't write to unlocked device");
|
||||
retval = -EBUSY;
|
||||
} else if (!atomic_read(&cs->connected)) {
|
||||
dbg(DEBUG_ANY, "can't write to unplugged device");
|
||||
retval = -EBUSY; //FIXME
|
||||
} else {
|
||||
retval = cs->ops->write_cmd(cs, buf, count,
|
||||
&cs->if_wake_tasklet);
|
||||
}
|
||||
|
||||
up(&cs->sem);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_write_room(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
int retval = -ENODEV;
|
||||
|
||||
cs = (struct cardstate *) tty->driver_data;
|
||||
if (!cs) {
|
||||
err("cs==NULL in %s", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__);
|
||||
|
||||
if (down_interruptible(&cs->sem))
|
||||
return -ERESTARTSYS; // FIXME -EINTR?
|
||||
|
||||
if (!cs->open_count)
|
||||
warn("%s: device not opened", __FUNCTION__);
|
||||
else if (atomic_read(&cs->mstate) != MS_LOCKED) {
|
||||
warn("can't write to unlocked device");
|
||||
retval = -EBUSY; //FIXME
|
||||
} else if (!atomic_read(&cs->connected)) {
|
||||
dbg(DEBUG_ANY, "can't write to unplugged device");
|
||||
retval = -EBUSY; //FIXME
|
||||
} else
|
||||
retval = cs->ops->write_room(cs);
|
||||
|
||||
up(&cs->sem);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_chars_in_buffer(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
int retval = -ENODEV;
|
||||
|
||||
cs = (struct cardstate *) tty->driver_data;
|
||||
if (!cs) {
|
||||
err("cs==NULL in %s", __FUNCTION__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__);
|
||||
|
||||
if (down_interruptible(&cs->sem))
|
||||
return -ERESTARTSYS; // FIXME -EINTR?
|
||||
|
||||
if (!cs->open_count)
|
||||
warn("%s: device not opened", __FUNCTION__);
|
||||
else if (atomic_read(&cs->mstate) != MS_LOCKED) {
|
||||
warn("can't write to unlocked device");
|
||||
retval = -EBUSY;
|
||||
} else if (!atomic_read(&cs->connected)) {
|
||||
dbg(DEBUG_ANY, "can't write to unplugged device");
|
||||
retval = -EBUSY; //FIXME
|
||||
} else
|
||||
retval = cs->ops->chars_in_buffer(cs);
|
||||
|
||||
up(&cs->sem);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void if_throttle(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
|
||||
cs = (struct cardstate *) tty->driver_data;
|
||||
if (!cs) {
|
||||
err("cs==NULL in %s", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__);
|
||||
|
||||
down(&cs->sem);
|
||||
|
||||
if (!cs->open_count)
|
||||
warn("%s: device not opened", __FUNCTION__);
|
||||
else {
|
||||
//FIXME
|
||||
}
|
||||
|
||||
up(&cs->sem);
|
||||
}
|
||||
|
||||
static void if_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
|
||||
cs = (struct cardstate *) tty->driver_data;
|
||||
if (!cs) {
|
||||
err("cs==NULL in %s", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__);
|
||||
|
||||
down(&cs->sem);
|
||||
|
||||
if (!cs->open_count)
|
||||
warn("%s: device not opened", __FUNCTION__);
|
||||
else {
|
||||
//FIXME
|
||||
}
|
||||
|
||||
up(&cs->sem);
|
||||
}
|
||||
|
||||
static void if_set_termios(struct tty_struct *tty, struct termios *old)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
unsigned int iflag;
|
||||
unsigned int cflag;
|
||||
unsigned int old_cflag;
|
||||
unsigned int control_state, new_state;
|
||||
|
||||
cs = (struct cardstate *) tty->driver_data;
|
||||
if (!cs) {
|
||||
err("cs==NULL in %s", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__);
|
||||
|
||||
down(&cs->sem);
|
||||
|
||||
if (!cs->open_count) {
|
||||
warn("%s: device not opened", __FUNCTION__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!atomic_read(&cs->connected)) {
|
||||
dbg(DEBUG_ANY, "can't communicate with unplugged device");
|
||||
goto out;
|
||||
}
|
||||
|
||||
// stolen from mct_u232.c
|
||||
iflag = tty->termios->c_iflag;
|
||||
cflag = tty->termios->c_cflag;
|
||||
old_cflag = old ? old->c_cflag : cflag; //FIXME?
|
||||
dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x", cs->minor_index,
|
||||
iflag, cflag, old_cflag);
|
||||
|
||||
/* get a local copy of the current port settings */
|
||||
control_state = cs->control_state;
|
||||
|
||||
/*
|
||||
* Update baud rate.
|
||||
* Do not attempt to cache old rates and skip settings,
|
||||
* disconnects screw such tricks up completely.
|
||||
* Premature optimization is the root of all evil.
|
||||
*/
|
||||
|
||||
/* reassert DTR and (maybe) RTS on transition from B0 */
|
||||
if ((old_cflag & CBAUD) == B0) {
|
||||
new_state = control_state | TIOCM_DTR;
|
||||
/* don't set RTS if using hardware flow control */
|
||||
if (!(old_cflag & CRTSCTS))
|
||||
new_state |= TIOCM_RTS;
|
||||
dbg(DEBUG_IF, "%u: from B0 - set DTR%s", cs->minor_index,
|
||||
(new_state & TIOCM_RTS) ? " only" : "/RTS");
|
||||
cs->ops->set_modem_ctrl(cs, control_state, new_state);
|
||||
control_state = new_state;
|
||||
}
|
||||
|
||||
cs->ops->baud_rate(cs, cflag & CBAUD);
|
||||
|
||||
if ((cflag & CBAUD) == B0) {
|
||||
/* Drop RTS and DTR */
|
||||
dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index);
|
||||
new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS);
|
||||
cs->ops->set_modem_ctrl(cs, control_state, new_state);
|
||||
control_state = new_state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update line control register (LCR)
|
||||
*/
|
||||
|
||||
cs->ops->set_line_ctrl(cs, cflag);
|
||||
|
||||
#if 0
|
||||
//FIXME this hangs M101 [ts 2005-03-09]
|
||||
//FIXME do we need this?
|
||||
/*
|
||||
* Set flow control: well, I do not really now how to handle DTR/RTS.
|
||||
* Just do what we have seen with SniffUSB on Win98.
|
||||
*/
|
||||
/* Drop DTR/RTS if no flow control otherwise assert */
|
||||
dbg(DEBUG_IF, "%u: control_state %x", cs->minor_index, control_state);
|
||||
new_state = control_state;
|
||||
if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS))
|
||||
new_state |= TIOCM_DTR | TIOCM_RTS;
|
||||
else
|
||||
new_state &= ~(TIOCM_DTR | TIOCM_RTS);
|
||||
if (new_state != control_state) {
|
||||
dbg(DEBUG_IF, "%u: new_state %x", cs->minor_index, new_state);
|
||||
gigaset_set_modem_ctrl(cs, control_state, new_state); // FIXME: mct_u232.c sets the old state here. is this a bug?
|
||||
control_state = new_state;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* save off the modified port settings */
|
||||
cs->control_state = control_state;
|
||||
|
||||
out:
|
||||
up(&cs->sem);
|
||||
}
|
||||
|
||||
|
||||
/* wakeup tasklet for the write operation */
|
||||
static void if_wake(unsigned long data)
|
||||
{
|
||||
struct cardstate *cs = (struct cardstate *) data;
|
||||
struct tty_struct *tty;
|
||||
|
||||
tty = cs->tty;
|
||||
if (!tty)
|
||||
return;
|
||||
|
||||
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
|
||||
tty->ldisc.write_wakeup) {
|
||||
dbg(DEBUG_IF, "write wakeup call");
|
||||
tty->ldisc.write_wakeup(tty);
|
||||
}
|
||||
|
||||
wake_up_interruptible(&tty->write_wait);
|
||||
}
|
||||
|
||||
/*** interface to common ***/
|
||||
|
||||
void gigaset_if_init(struct cardstate *cs)
|
||||
{
|
||||
struct gigaset_driver *drv;
|
||||
|
||||
drv = cs->driver;
|
||||
if (!drv->have_tty)
|
||||
return;
|
||||
|
||||
tasklet_init(&cs->if_wake_tasklet, &if_wake, (unsigned long) cs);
|
||||
tty_register_device(drv->tty, cs->minor_index, NULL);
|
||||
}
|
||||
|
||||
void gigaset_if_free(struct cardstate *cs)
|
||||
{
|
||||
struct gigaset_driver *drv;
|
||||
|
||||
drv = cs->driver;
|
||||
if (!drv->have_tty)
|
||||
return;
|
||||
|
||||
tasklet_disable(&cs->if_wake_tasklet);
|
||||
tasklet_kill(&cs->if_wake_tasklet);
|
||||
tty_unregister_device(drv->tty, cs->minor_index);
|
||||
}
|
||||
|
||||
void gigaset_if_receive(struct cardstate *cs,
|
||||
unsigned char *buffer, size_t len)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct tty_struct *tty;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if ((tty = cs->tty) == NULL)
|
||||
dbg(DEBUG_ANY, "receive on closed device");
|
||||
else {
|
||||
tty_buffer_request_room(tty, len);
|
||||
tty_insert_flip_string(tty, buffer, len);
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_if_receive);
|
||||
|
||||
/* gigaset_if_initdriver
|
||||
* Initialize tty interface.
|
||||
* parameters:
|
||||
* drv Driver
|
||||
* procname Name of the driver (e.g. for /proc/tty/drivers)
|
||||
* devname Name of the device files (prefix without minor number)
|
||||
* devfsname Devfs name of the device files without %d
|
||||
*/
|
||||
void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
|
||||
const char *devname, const char *devfsname)
|
||||
{
|
||||
unsigned minors = drv->minors;
|
||||
int ret;
|
||||
struct tty_driver *tty;
|
||||
|
||||
drv->have_tty = 0;
|
||||
|
||||
if ((drv->tty = alloc_tty_driver(minors)) == NULL)
|
||||
goto enomem;
|
||||
tty = drv->tty;
|
||||
|
||||
tty->magic = TTY_DRIVER_MAGIC,
|
||||
tty->major = GIG_MAJOR,
|
||||
tty->type = TTY_DRIVER_TYPE_SERIAL,
|
||||
tty->subtype = SERIAL_TYPE_NORMAL,
|
||||
tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
|
||||
|
||||
tty->driver_name = procname;
|
||||
tty->name = devname;
|
||||
tty->minor_start = drv->minor;
|
||||
tty->num = drv->minors;
|
||||
|
||||
tty->owner = THIS_MODULE;
|
||||
tty->devfs_name = devfsname;
|
||||
|
||||
tty->init_termios = tty_std_termios; //FIXME
|
||||
tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //FIXME
|
||||
tty_set_operations(tty, &if_ops);
|
||||
|
||||
ret = tty_register_driver(tty);
|
||||
if (ret < 0) {
|
||||
warn("failed to register tty driver (error %d)", ret);
|
||||
goto error;
|
||||
}
|
||||
dbg(DEBUG_IF, "tty driver initialized");
|
||||
drv->have_tty = 1;
|
||||
return;
|
||||
|
||||
enomem:
|
||||
warn("could not allocate tty structures");
|
||||
error:
|
||||
if (drv->tty)
|
||||
put_tty_driver(drv->tty);
|
||||
}
|
||||
|
||||
void gigaset_if_freedriver(struct gigaset_driver *drv)
|
||||
{
|
||||
if (!drv->have_tty)
|
||||
return;
|
||||
|
||||
drv->have_tty = 0;
|
||||
tty_unregister_driver(drv->tty);
|
||||
put_tty_driver(drv->tty);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* interface to user space for the gigaset driver
|
||||
*
|
||||
* Copyright (c) 2004 by Hansjoerg Lipp <hjlipp@web.de>
|
||||
*
|
||||
* =====================================================================
|
||||
* 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.
|
||||
* =====================================================================
|
||||
* Version: $Id: gigaset_dev.h,v 1.4.4.4 2005/11/21 22:28:09 hjlipp Exp $
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#ifndef GIGASET_INTERFACE_H
|
||||
#define GIGASET_INTERFACE_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#define GIGASET_IOCTL 0x47
|
||||
|
||||
#define GIGVER_DRIVER 0
|
||||
#define GIGVER_COMPAT 1
|
||||
#define GIGVER_FWBASE 2
|
||||
|
||||
#define GIGASET_REDIR _IOWR (GIGASET_IOCTL, 0, int)
|
||||
#define GIGASET_CONFIG _IOWR (GIGASET_IOCTL, 1, int)
|
||||
#define GIGASET_BRKCHARS _IOW (GIGASET_IOCTL, 2, unsigned char[6]) //FIXME [6] okay?
|
||||
#define GIGASET_VERSION _IOWR (GIGASET_IOCTL, 3, unsigned[4])
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue