/*
- * $Id: ctctty.c,v 1.17 2004/03/31 17:06:34 ptiedem Exp $
- *
* CTC / ESCON network driver, tty interface.
*
* Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
+#include <linux/tty_flip.h>
#include <linux/serial_reg.h>
#include <linux/interrupt.h>
+#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/devfs_fs_kernel.h>
#include "ctctty.h"
+#include "ctcdbug.h"
#define CTC_TTY_MAJOR 43
#define CTC_TTY_MAX_DEVICES 64
static int
ctc_tty_try_read(ctc_tty_info * info, struct sk_buff *skb)
{
- int c;
int len;
struct tty_struct *tty;
+ DBF_TEXT(trace, 5, __FUNCTION__);
if ((tty = info->tty)) {
if (info->mcr & UART_MCR_RTS) {
- c = TTY_FLIPBUF_SIZE - tty->flip.count;
len = skb->len;
- if (c >= len) {
- memcpy(tty->flip.char_buf_ptr, skb->data, len);
- memset(tty->flip.flag_buf_ptr, 0, len);
- tty->flip.count += len;
- tty->flip.char_buf_ptr += len;
- tty->flip.flag_buf_ptr += len;
- tty_flip_buffer_push(tty);
- kfree_skb(skb);
- return 1;
- }
+ tty_insert_flip_string(tty, skb->data, len);
+ tty_flip_buffer_push(tty);
+ kfree_skb(skb);
+ return 1;
}
}
return 0;
int ret = 1;
struct tty_struct *tty;
+ DBF_TEXT(trace, 5, __FUNCTION__);
if ((tty = info->tty)) {
if (info->mcr & UART_MCR_RTS) {
- int c = TTY_FLIPBUF_SIZE - tty->flip.count;
struct sk_buff *skb;
- if ((c > 0) && (skb = skb_dequeue(&info->rx_queue))) {
+ if ((skb = skb_dequeue(&info->rx_queue))) {
int len = skb->len;
- if (len > c)
- len = c;
- memcpy(tty->flip.char_buf_ptr, skb->data, len);
+ tty_insert_flip_string(tty, skb->data, len);
skb_pull(skb, len);
- memset(tty->flip.flag_buf_ptr, 0, len);
- tty->flip.count += len;
- tty->flip.char_buf_ptr += len;
- tty->flip.flag_buf_ptr += len;
tty_flip_buffer_push(tty);
if (skb->len > 0)
skb_queue_head(&info->rx_queue, skb);
else {
kfree_skb(skb);
- ret = skb_queue_len(&info->rx_queue);
+ ret = !skb_queue_empty(&info->rx_queue);
}
}
}
{
int i;
+ DBF_TEXT(trace, 4, __FUNCTION__);
if ((!driver) || ctc_tty_shuttingdown)
return;
for (i = 0; i < CTC_TTY_MAX_DEVICES; i++)
int i;
ctc_tty_info *info = NULL;
+ DBF_TEXT(trace, 5, __FUNCTION__);
if (!skb)
return;
if ((!skb->dev) || (!driver) || ctc_tty_shuttingdown) {
int wake = 1;
int rc;
+ DBF_TEXT(trace, 4, __FUNCTION__);
if (!info->netdev) {
if (skb)
kfree_skb(skb);
info->flags &= ~CTC_ASYNC_TX_LINESTAT;
if (tty) {
- if (wake && (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
- wake_up_interruptible(&tty->write_wait);
+ tty_wakeup(tty);
}
}
return (skb_queue_empty(&info->tx_queue) ? 0 : 1);
int skb_res;
struct sk_buff *skb;
+ DBF_TEXT(trace, 4, __FUNCTION__);
if (ctc_tty_shuttingdown)
return;
skb_res = info->netdev->hard_header_len + sizeof(info->mcr) +
static void
ctc_tty_transmit_status(ctc_tty_info *info)
{
+ DBF_TEXT(trace, 5, __FUNCTION__);
if (ctc_tty_shuttingdown)
return;
info->flags |= CTC_ASYNC_TX_LINESTAT;
unsigned int quot;
int i;
+ DBF_TEXT(trace, 3, __FUNCTION__);
if (!info->tty || !info->tty->termios)
return;
cflag = info->tty->termios->c_cflag;
static int
ctc_tty_startup(ctc_tty_info * info)
{
+ DBF_TEXT(trace, 3, __FUNCTION__);
if (info->flags & CTC_ASYNC_INITIALIZED)
return 0;
#ifdef CTC_DEBUG_MODEM_OPEN
static void
ctc_tty_shutdown(ctc_tty_info * info)
{
+ DBF_TEXT(trace, 3, __FUNCTION__);
if (!(info->flags & CTC_ASYNC_INITIALIZED))
return;
#ifdef CTC_DEBUG_MODEM_OPEN
* - If dialing, abort dial.
*/
static int
-ctc_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
+ctc_tty_write(struct tty_struct *tty, const u_char * buf, int count)
{
int c;
int total = 0;
ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
+ DBF_TEXT(trace, 5, __FUNCTION__);
if (ctc_tty_shuttingdown)
- return 0;
+ goto ex;
if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_write"))
- return 0;
+ goto ex;
if (!tty)
- return 0;
- if (!info->netdev)
- return -ENODEV;
- if (from_user)
- down(&info->write_sem);
+ goto ex;
+ if (!info->netdev) {
+ total = -ENODEV;
+ goto ex;
+ }
while (1) {
struct sk_buff *skb;
int skb_res;
break;
}
skb_reserve(skb, skb_res);
- if (from_user)
- copy_from_user(skb_put(skb, c), buf, c);
- else
- memcpy(skb_put(skb, c), buf, c);
+ memcpy(skb_put(skb, c), buf, c);
skb_queue_tail(&info->tx_queue, skb);
buf += c;
total += c;
count -= c;
}
- if (skb_queue_len(&info->tx_queue)) {
+ if (!skb_queue_empty(&info->tx_queue)) {
info->lsr &= ~UART_LSR_TEMT;
tasklet_schedule(&info->tasklet);
}
- if (from_user)
- up(&info->write_sem);
+ex:
+ DBF_TEXT(trace, 6, __FUNCTION__);
return total;
}
ctc_tty_info *info;
unsigned long flags;
+ DBF_TEXT(trace, 4, __FUNCTION__);
if (!tty)
- return;
+ goto ex;
spin_lock_irqsave(&ctc_tty_lock, flags);
info = (ctc_tty_info *) tty->driver_data;
if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_flush_buffer")) {
spin_unlock_irqrestore(&ctc_tty_lock, flags);
- return;
+ goto ex;
}
skb_queue_purge(&info->tx_queue);
info->lsr |= UART_LSR_TEMT;
spin_unlock_irqrestore(&ctc_tty_lock, flags);
wake_up_interruptible(&tty->write_wait);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup) (tty);
+ tty_wakeup(tty);
+ex:
+ DBF_TEXT_(trace, 2, "ex: %s ", __FUNCTION__);
+ return;
}
static void
{
ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
+ DBF_TEXT(trace, 4, __FUNCTION__);
if (ctc_tty_shuttingdown)
return;
if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_flush_chars"))
return;
- if (tty->stopped || tty->hw_stopped || (!skb_queue_len(&info->tx_queue)))
+ if (tty->stopped || tty->hw_stopped || skb_queue_empty(&info->tx_queue))
return;
tasklet_schedule(&info->tasklet);
}
{
ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
+ DBF_TEXT(trace, 4, __FUNCTION__);
if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_throttle"))
return;
info->mcr &= ~UART_MCR_RTS;
{
ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
+ DBF_TEXT(trace, 4, __FUNCTION__);
if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_unthrottle"))
return;
info->mcr |= UART_MCR_RTS;
* allows RS485 driver to be written in user space.
*/
static int
-ctc_tty_get_lsr_info(ctc_tty_info * info, uint * value)
+ctc_tty_get_lsr_info(ctc_tty_info * info, uint __user *value)
{
u_char status;
uint result;
ulong flags;
+ DBF_TEXT(trace, 4, __FUNCTION__);
spin_lock_irqsave(&ctc_tty_lock, flags);
status = info->lsr;
spin_unlock_irqrestore(&ctc_tty_lock, flags);
result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
- put_user(result, (uint *) value);
+ put_user(result, value);
return 0;
}
uint result;
ulong flags;
+ DBF_TEXT(trace, 4, __FUNCTION__);
if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_ioctl"))
return -ENODEV;
if (tty->flags & (1 << TTY_IO_ERROR))
{
ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
+ DBF_TEXT(trace, 4, __FUNCTION__);
if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_ioctl"))
return -ENODEV;
if (tty->flags & (1 << TTY_IO_ERROR))
int error;
int retval;
+ DBF_TEXT(trace, 4, __FUNCTION__);
if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_ioctl"))
return -ENODEV;
if (tty->flags & (1 << TTY_IO_ERROR))
printk(KERN_DEBUG "%s%d ioctl TIOCGSOFTCAR\n", CTC_TTY_NAME,
info->line);
#endif
- error = put_user(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg);
+ error = put_user(C_CLOCAL(tty) ? 1 : 0, (ulong __user *) arg);
return error;
case TIOCSSOFTCAR:
#ifdef CTC_DEBUG_MODEM_IOCTL
printk(KERN_DEBUG "%s%d ioctl TIOCSSOFTCAR\n", CTC_TTY_NAME,
info->line);
#endif
- error = get_user(arg, (ulong *) arg);
+ error = get_user(arg, (ulong __user *) arg);
if (error)
return error;
tty->termios->c_cflag =
printk(KERN_DEBUG "%s%d ioctl TIOCSERGETLSR\n", CTC_TTY_NAME,
info->line);
#endif
- error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
- if (error)
- return error;
+ if (access_ok(VERIFY_WRITE, (void __user *) arg, sizeof(uint)))
+ return ctc_tty_get_lsr_info(info, (uint __user *) arg);
else
- return ctc_tty_get_lsr_info(info, (uint *) arg);
+ return -EFAULT;
default:
#ifdef CTC_DEBUG_MODEM_IOCTL
printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on %s%d\n", cmd,
ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
unsigned int cflag = tty->termios->c_cflag;
+ DBF_TEXT(trace, 4, __FUNCTION__);
ctc_tty_change_speed(info);
/* Handle transition to B0 */
unsigned long flags;
int retval;
+ DBF_TEXT(trace, 4, __FUNCTION__);
/*
* If the device is in the middle of being closed, then block
* until it's done, and then try again.
int retval,
line;
+ DBF_TEXT(trace, 3, __FUNCTION__);
line = tty->index;
if (line < 0 || line > CTC_TTY_MAX_DEVICES)
return -ENODEV;
ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
ulong flags;
ulong timeout;
-
+ DBF_TEXT(trace, 3, __FUNCTION__);
if (!info || ctc_tty_paranoia_check(info, tty->name, "ctc_tty_close"))
return;
spin_lock_irqsave(&ctc_tty_lock, flags);
*/
timeout = jiffies + HZ;
while (!(info->lsr & UART_LSR_TEMT)) {
- set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&ctc_tty_lock, flags);
- schedule_timeout(HZ/2);
+ msleep(500);
spin_lock_irqsave(&ctc_tty_lock, flags);
if (time_after(jiffies,timeout))
break;
}
}
ctc_tty_shutdown(info);
- if (tty->driver->flush_buffer)
- tty->driver->flush_buffer(tty);
- if (tty->ldisc.flush_buffer)
- tty->ldisc.flush_buffer(tty);
+ if (tty->driver->flush_buffer) {
+ skb_queue_purge(&info->tx_queue);
+ info->lsr |= UART_LSR_TEMT;
+ }
+ tty_ldisc_flush(tty);
info->tty = 0;
tty->closing = 0;
if (info->blocked_open) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/2);
+ msleep_interruptible(500);
wake_up_interruptible(&info->open_wait);
}
info->flags &= ~(CTC_ASYNC_NORMAL_ACTIVE | CTC_ASYNC_CLOSING);
{
ctc_tty_info *info = (ctc_tty_info *)tty->driver_data;
unsigned long saveflags;
-
+ DBF_TEXT(trace, 3, __FUNCTION__);
if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_hangup"))
return;
ctc_tty_shutdown(info);
unsigned long saveflags;
int again;
+ DBF_TEXT(trace, 3, __FUNCTION__);
spin_lock_irqsave(&ctc_tty_lock, saveflags);
if ((!ctc_tty_shuttingdown) && info) {
again = ctc_tty_tint(info);
ctc_tty_info *info;
struct tty_driver *device;
+ DBF_TEXT(trace, 2, __FUNCTION__);
driver = kmalloc(sizeof(ctc_tty_driver), GFP_KERNEL);
if (driver == NULL) {
printk(KERN_WARNING "Out of memory in ctc_tty_modem_init\n");
char *err;
char *p;
+ DBF_TEXT(trace, 2, __FUNCTION__);
if ((!dev) || (!dev->name)) {
printk(KERN_WARNING
"ctc_tty_register_netdev called "
"with NULL dev or NULL dev-name\n");
return -1;
}
+
+ /*
+ * If the name is a format string the caller wants us to
+ * do a name allocation : format string must end with %d
+ */
+ if (strchr(dev->name, '%'))
+ {
+ int err = dev_alloc_name(dev, dev->name); // dev->name is changed by this
+ if (err < 0) {
+ printk(KERN_DEBUG "dev_alloc returned error %d\n", err);
+ return err;
+ }
+
+ }
+
for (p = dev->name; p && ((*p < '0') || (*p > '9')); p++);
ttynum = simple_strtoul(p, &err, 0);
if ((ttynum < 0) || (ttynum >= CTC_TTY_MAX_DEVICES) ||
unsigned long saveflags;
ctc_tty_info *info = NULL;
+ DBF_TEXT(trace, 2, __FUNCTION__);
spin_lock_irqsave(&ctc_tty_lock, saveflags);
for (i = 0; i < CTC_TTY_MAX_DEVICES; i++)
if (driver->info[i].netdev == dev) {
ctc_tty_cleanup(void) {
unsigned long saveflags;
+ DBF_TEXT(trace, 2, __FUNCTION__);
spin_lock_irqsave(&ctc_tty_lock, saveflags);
ctc_tty_shuttingdown = 1;
spin_unlock_irqrestore(&ctc_tty_lock, saveflags);