!C99Shell v. 1.0 pre-release build #13!

Software: Apache/2.0.54 (Unix) mod_perl/1.99_09 Perl/v5.8.0 mod_ssl/2.0.54 OpenSSL/0.9.7l DAV/2 FrontPage/5.0.2.2635 PHP/4.4.0 mod_gzip/2.0.26.1a 

uname -a: Linux snow.he.net 4.4.276-v2-mono-1 #1 SMP Wed Jul 21 11:21:17 PDT 2021 i686 

uid=99(nobody) gid=98(nobody) groups=98(nobody) 

Safe-mode: OFF (not secure)

/usr/src/linux-2.4.18-xfs-1.1/drivers/s390/net/   drwxr-xr-x
Free 318.33 GB of 458.09 GB (69.49%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     ctctty.c (35.5 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*
 * $Id: ctctty.c,v 1.8 2001/05/16 16:28:31 felfert Exp $
 *
 * CTC / ESCON network driver, tty interface.
 *
 * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
 * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.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, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#define __NO_VERSION__
#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/serial_reg.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#ifdef CONFIG_DEVFS_FS
#  include <linux/devfs_fs_kernel.h>
#endif
#include "ctctty.h"

#if LINUX_VERSION_CODE < 0x020212
typedef struct wait_queue wait_queue_t;
typedef struct wait_queue *wait_queue_head_t;
#define DECLARE_WAITQUEUE(wait, current) \
    struct wait_queue wait = { current, NULL }
#define init_waitqueue_head(x) *(x)=NULL
#define __set_current_state(state_value) \
    do { current->state = state_value; } while (0)
#ifdef __SMP__
#define set_current_state(state_value) \
    do { __set_current_state(state_value); mb(); } while (0)
#else
#define set_current_state(state_value) __set_current_state(state_value)
#endif
#define init_MUTEX(x) *(x)=MUTEX
#endif

#define CTC_TTY_MAJOR       43
#define CTC_TTY_MAX_DEVICES 64

#define CTC_ASYNC_MAGIC          0x49344C01 /* for paranoia-checking        */
#define CTC_ASYNC_INITIALIZED    0x80000000 /* port was initialized         */
#define CTC_ASYNC_NORMAL_ACTIVE  0x20000000 /* Normal device active         */
#define CTC_ASYNC_CLOSING        0x08000000 /* Serial port is closing       */
#define CTC_ASYNC_CTS_FLOW       0x04000000 /* Do CTS flow control          */
#define CTC_ASYNC_CHECK_CD       0x02000000 /* i.e., CLOCAL                 */
#define CTC_ASYNC_HUP_NOTIFY         0x0001 /* Notify tty on hangups/closes */
#define CTC_ASYNC_NETDEV_OPEN        0x0002 /* Underlying netdev is open    */
#define CTC_ASYNC_TX_LINESTAT        0x0004 /* Must send line status        */
#define CTC_ASYNC_SPLIT_TERMIOS      0x0008 /* Sep. termios for dialin/out  */
#define CTC_TTY_XMIT_SIZE              1024 /* Default bufsize for write    */
#define CTC_SERIAL_XMIT_MAX            4000 /* Maximum bufsize for write    */
#define CTC_SERIAL_TYPE_NORMAL            1

/* Private data (similar to async_struct in <linux/serial.h>) */
typedef struct {
  int            magic;
  int            flags;         /* defined in tty.h               */
  int            mcr;         /* Modem control register         */
  int                   msr;             /* Modem status register          */
  int                   lsr;             /* Line status register           */
  int            line;
  int            count;         /* # of fd on device              */
  int            blocked_open;     /* # of blocked opens             */
  net_device            *netdev;
  struct sk_buff_head   tx_queue;        /* transmit queue                 */
  struct sk_buff_head   rx_queue;        /* receive queue                  */
  struct tty_struct     *tty;            /* Pointer to corresponding tty   */
  struct termios    normal_termios;  /* For saving termios structs     */
  wait_queue_head_t    open_wait;
  wait_queue_head_t    close_wait;
  struct semaphore      write_sem;
  struct tq_struct      tq;
  struct timer_list     stoptimer;
} ctc_tty_info;

/* Description of one CTC-tty */
typedef struct {
  int                refcount;               /* Number of opens        */
  struct tty_driver  ctc_tty_device;           /* tty-device             */
  struct tty_struct  *modem_table[CTC_TTY_MAX_DEVICES];
  struct termios     *modem_termios[CTC_TTY_MAX_DEVICES];
  struct termios     *modem_termios_locked[CTC_TTY_MAX_DEVICES];
  ctc_tty_info       info[CTC_TTY_MAX_DEVICES];       /* Private data           */
} ctc_tty_driver;

static ctc_tty_driver *driver;

/* Leave this unchanged unless you know what you do! */
#define MODEM_PARANOIA_CHECK
#define MODEM_DO_RESTART

#define CTC_TTY_NAME "ctctty"

#ifdef CONFIG_DEVFS_FS
static char *ctc_ttyname = "ctc/" CTC_TTY_NAME "%d";
#else
static char *ctc_ttyname = CTC_TTY_NAME;
#endif

char *ctc_tty_revision = "$Revision: 1.8 $";

static __u32 ctc_tty_magic = CTC_ASYNC_MAGIC;
static int ctc_tty_shuttingdown = 0;

static spinlock_t ctc_tty_lock;

/* ctc_tty_try_read() is called from within ctc_tty_rcv_skb()
 * to stuff incoming data directly into a tty's flip-buffer. If the
 * flip buffer is full, the packet gets queued up.
 *
 * Return:
 *  1 = Success
 *  0 = Failure, data has to be buffered and later processed by
 *      ctc_tty_readmodem().
 */
static int
ctc_tty_try_read(ctc_tty_info * info, struct sk_buff *skb)
{
    int c;
    int len;
    struct tty_struct *tty;

    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;
            }
        }
    }
    return 0;
}

/* ctc_tty_readmodem() is called periodically from within timer-interrupt.
 * It tries getting received data from the receive queue an stuff it into
 * the tty's flip-buffer.
 */
static int
ctc_tty_readmodem(ctc_tty_info *info)
{
    int ret = 1;
    struct tty_struct *tty;

    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))) {
                int len = skb->len;
                if (len > c)
                    len = c;
                memcpy(tty->flip.char_buf_ptr, 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);
                }
            }
        }
    }
    return ret;
}

void
ctc_tty_setcarrier(net_device *netdev, int on)
{
    int i;

    if ((!driver) || ctc_tty_shuttingdown)
        return;
    for (i = 0; i < CTC_TTY_MAX_DEVICES; i++)
        if (driver->info[i].netdev == netdev) {
            ctc_tty_info *info = &driver->info[i];
            if (on)
                info->msr |= UART_MSR_DCD;
            else
                info->msr &= ~UART_MSR_DCD;
            if ((info->flags & CTC_ASYNC_CHECK_CD) && (!on))
                tty_hangup(info->tty);
        }
}

void
ctc_tty_netif_rx(struct sk_buff *skb)
{
    int i;
    ctc_tty_info *info = NULL;

    if (!skb)
        return;
    if ((!skb->dev) || (!driver) || ctc_tty_shuttingdown) {
        dev_kfree_skb(skb);
        return;
    }
    for (i = 0; i < CTC_TTY_MAX_DEVICES; i++)
        if (driver->info[i].netdev == skb->dev) {
            info = &driver->info[i];
            break;
        }
    if (!info) {
        dev_kfree_skb(skb);
        return;
    }
    if (skb->len < 6) {
        dev_kfree_skb(skb);
        return;
    }
    if (memcmp(skb->data, &ctc_tty_magic, sizeof(__u32))) {
        dev_kfree_skb(skb);
        return;
    }
    skb_pull(skb, sizeof(__u32));

    i = *((int *)skb->data);
    skb_pull(skb, sizeof(info->mcr));
    if (i & UART_MCR_RTS) {
        info->msr |= UART_MSR_CTS;
        if (info->flags & CTC_ASYNC_CTS_FLOW)
            info->tty->hw_stopped = 0;
    } else {
        info->msr &= ~UART_MSR_CTS;
        if (info->flags & CTC_ASYNC_CTS_FLOW)
            info->tty->hw_stopped = 1;
    }
    if (i & UART_MCR_DTR)
        info->msr |= UART_MSR_DSR;
    else
        info->msr &= ~UART_MSR_DSR;
    if (skb->len <= 0) {
        kfree_skb(skb);
        return;
    }
    /* Try to deliver directly via tty-flip-buf if queue is empty */
    if (skb_queue_empty(&info->rx_queue))
        if (ctc_tty_try_read(info, skb))
            return;
    /* Direct deliver failed or queue wasn't empty.
     * Queue up for later dequeueing via timer-irq.
     */
    skb_queue_tail(&info->rx_queue, skb);
    /* Schedule dequeuing */
    queue_task(&info->tq, &tq_immediate);
    mark_bh(IMMEDIATE_BH);
}

static int
ctc_tty_tint(ctc_tty_info * info)
{
    struct sk_buff *skb = skb_dequeue(&info->tx_queue);
    int stopped = (info->tty->hw_stopped || info->tty->stopped);
    int wake = 1;
    int rc;

    if (!info->netdev) {
        if (skb)
            kfree(skb);
        return 0;
    }
    if (info->flags & CTC_ASYNC_TX_LINESTAT) {
        int skb_res = info->netdev->hard_header_len +
            sizeof(info->mcr) + sizeof(__u32);
        /* If we must update line status,
         * create an empty dummy skb and insert it.
         */
        if (skb)
            skb_queue_head(&info->tx_queue, skb);

        skb = dev_alloc_skb(skb_res);
        if (!skb) {
            printk(KERN_WARNING
                   "ctc_tty: Out of memory in %s%d tint\n",
                   CTC_TTY_NAME, info->line);
            return 1;
        }
        skb_reserve(skb, skb_res);
        stopped = 0;
        wake = 0;
    }
    if (!skb)
        return 0;
    if (stopped) {
        skb_queue_head(&info->tx_queue, skb);
        return 1;
    }
#if 0
    if (skb->len > 0)
        printk(KERN_DEBUG "tint: %d %02x\n", skb->len, *(skb->data));
    else
        printk(KERN_DEBUG "tint: %d STAT\n", skb->len);
#endif
    memcpy(skb_push(skb, sizeof(info->mcr)), &info->mcr, sizeof(info->mcr));
    memcpy(skb_push(skb, sizeof(__u32)), &ctc_tty_magic, sizeof(__u32));
    rc = info->netdev->hard_start_xmit(skb, info->netdev);
    if (rc) {
        skb_pull(skb, sizeof(info->mcr) + sizeof(__u32));
        if (skb->len > 0)
            skb_queue_head(&info->tx_queue, skb);
        else
            kfree_skb(skb);
    } else {
        struct tty_struct *tty = info->tty;

        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);
        }
    }
    return (skb_queue_empty(&info->tx_queue) ? 0 : 1);
}

/************************************************************
 *
 * Modem-functions
 *
 * mostly "stolen" from original Linux-serial.c and friends.
 *
 ************************************************************/

static inline int
ctc_tty_paranoia_check(ctc_tty_info * info, kdev_t device, const char *routine)
{
#ifdef MODEM_PARANOIA_CHECK
    if (!info) {
        printk(KERN_WARNING "ctc_tty: null info_struct for (%d, %d) in %s\n",
               MAJOR(device), MINOR(device), routine);
        return 1;
    }
    if (info->magic != CTC_ASYNC_MAGIC) {
        printk(KERN_WARNING "ctc_tty: bad magic for info struct (%d, %d) in %s\n",
               MAJOR(device), MINOR(device), routine);
        return 1;
    }
#endif
    return 0;
}

static void
ctc_tty_inject(ctc_tty_info *info, char c)
{
    int skb_res;
    struct sk_buff *skb;
    
    if (ctc_tty_shuttingdown)
        return;
    skb_res = info->netdev->hard_header_len + sizeof(info->mcr) +
        sizeof(__u32) + 1;
    skb = dev_alloc_skb(skb_res);
    if (!skb) {
        printk(KERN_WARNING
               "ctc_tty: Out of memory in %s%d tx_inject\n",
               CTC_TTY_NAME, info->line);
        return;
    }
    skb_reserve(skb, skb_res);
    *(skb_put(skb, 1)) = c;
    skb_queue_head(&info->tx_queue, skb);
    queue_task(&info->tq, &tq_immediate);
    mark_bh(IMMEDIATE_BH);
}

static void
ctc_tty_transmit_status(ctc_tty_info *info)
{
    if (ctc_tty_shuttingdown)
        return;
    info->flags |= CTC_ASYNC_TX_LINESTAT;
    queue_task(&info->tq, &tq_immediate);
    mark_bh(IMMEDIATE_BH);
}

static void
ctc_tty_change_speed(ctc_tty_info * info)
{
    unsigned int cflag;
    unsigned int quot;
    int i;

    if (!info->tty || !info->tty->termios)
        return;
    cflag = info->tty->termios->c_cflag;

    quot = i = cflag & CBAUD;
    if (i & CBAUDEX) {
        i &= ~CBAUDEX;
        if (i < 1 || i > 2)
            info->tty->termios->c_cflag &= ~CBAUDEX;
        else
            i += 15;
    }
    if (quot) {
        info->mcr |= UART_MCR_DTR;
        info->mcr |= UART_MCR_RTS;
        ctc_tty_transmit_status(info);
    } else {
        info->mcr &= ~UART_MCR_DTR;
        info->mcr &= ~UART_MCR_RTS;
        ctc_tty_transmit_status(info);
        return;
    }

    /* CTS flow control flag and modem status interrupts */
    if (cflag & CRTSCTS) {
        info->flags |= CTC_ASYNC_CTS_FLOW;
    } else
        info->flags &= ~CTC_ASYNC_CTS_FLOW;
    if (cflag & CLOCAL)
        info->flags &= ~CTC_ASYNC_CHECK_CD;
    else {
        info->flags |= CTC_ASYNC_CHECK_CD;
    }
}

static int
ctc_tty_startup(ctc_tty_info * info)
{
    if (info->flags & CTC_ASYNC_INITIALIZED)
        return 0;
#ifdef CTC_DEBUG_MODEM_OPEN
    printk(KERN_DEBUG "starting up %s%d ...\n", CTC_TTY_NAME, info->line);
#endif
    /*
     * Now, initialize the UART
     */
    info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
    if (info->tty)
        clear_bit(TTY_IO_ERROR, &info->tty->flags);
    /*
     * and set the speed of the serial port
     */
    ctc_tty_change_speed(info);

    info->flags |= CTC_ASYNC_INITIALIZED;
    if (!(info->flags & CTC_ASYNC_NETDEV_OPEN))
        info->netdev->open(info->netdev);
    info->flags |= CTC_ASYNC_NETDEV_OPEN;
    return 0;
}

static void
ctc_tty_stopdev(unsigned long data)
{
    ctc_tty_info *info = (ctc_tty_info *)data;

    if ((!info) || (!info->netdev) ||
        (info->flags & CTC_ASYNC_INITIALIZED))
        return;
    info->netdev->stop(info->netdev);
    info->flags &= ~CTC_ASYNC_NETDEV_OPEN;
}

/*
 * This routine will shutdown a serial port; interrupts are disabled, and
 * DTR is dropped if the hangup on close termio flag is on.
 */
static void
ctc_tty_shutdown(ctc_tty_info * info)
{
    if (!(info->flags & CTC_ASYNC_INITIALIZED))
        return;
#ifdef CTC_DEBUG_MODEM_OPEN
    printk(KERN_DEBUG "Shutting down %s%d ....\n", CTC_TTY_NAME, info->line);
#endif
    info->msr &= ~UART_MSR_RI;
    if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
        info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
    if (info->tty)
        set_bit(TTY_IO_ERROR, &info->tty->flags);
    mod_timer(&info->stoptimer, jiffies + (10 * HZ));
    skb_queue_purge(&info->tx_queue);
    skb_queue_purge(&info->rx_queue);
    info->flags &= ~CTC_ASYNC_INITIALIZED;
}

/* ctc_tty_write() is the main send-routine. It is called from the upper
 * levels within the kernel to perform sending data. Depending on the
 * online-flag it either directs output to the at-command-interpreter or
 * to the lower level. Additional tasks done here:
 *  - If online, check for escape-sequence (+++)
 *  - If sending audio-data, call ctc_tty_DLEdown() to parse DLE-codes.
 *  - If receiving audio-data, call ctc_tty_end_vrx() to abort if needed.
 *  - If dialing, abort dial.
 */
static int
ctc_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
{
    int c;
    int total = 0;
    ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;

    if (ctc_tty_shuttingdown)
        return 0;
    if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_write"))
        return 0;
    if (!tty)
        return 0;
    if (!info->netdev)
        return -ENODEV;
    if (from_user)
        down(&info->write_sem);
    while (1) {
        struct sk_buff *skb;
        int skb_res;

        c = (count < CTC_TTY_XMIT_SIZE) ? count : CTC_TTY_XMIT_SIZE;
        if (c <= 0)
            break;
        
        skb_res = info->netdev->hard_header_len + sizeof(info->mcr) +
            + sizeof(__u32);
        skb = dev_alloc_skb(skb_res + c);
        if (!skb) {
            printk(KERN_WARNING
                   "ctc_tty: Out of memory in %s%d write\n",
                   CTC_TTY_NAME, info->line);
            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);
        skb_queue_tail(&info->tx_queue, skb);
        buf += c;
        total += c;
        count -= c;
    }
    if (skb_queue_len(&info->tx_queue)) {
        info->lsr &= ~UART_LSR_TEMT;
        queue_task(&info->tq, &tq_immediate);
        mark_bh(IMMEDIATE_BH);
    }
    if (from_user)
        up(&info->write_sem);
    return total;
}

static int
ctc_tty_write_room(struct tty_struct *tty)
{
    ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;

    if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_write_room"))
        return 0;
    return CTC_TTY_XMIT_SIZE;
}

static int
ctc_tty_chars_in_buffer(struct tty_struct *tty)
{
    ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;

    if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_chars_in_buffer"))
        return 0;
    return 0;
}

static void
ctc_tty_flush_buffer(struct tty_struct *tty)
{
    ctc_tty_info *info;
    unsigned long flags;

    save_flags(flags);
    cli();
    if (!tty) {
        restore_flags(flags);
        return;
    }
    info = (ctc_tty_info *) tty->driver_data;
    if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_flush_buffer")) {
        restore_flags(flags);
        return;
    }
    skb_queue_purge(&info->tx_queue);
    info->lsr |= UART_LSR_TEMT;
    restore_flags(flags);
    wake_up_interruptible(&tty->write_wait);
    if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
        tty->ldisc.write_wakeup)
        (tty->ldisc.write_wakeup) (tty);
}

static void
ctc_tty_flush_chars(struct tty_struct *tty)
{
    ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;

    if (ctc_tty_shuttingdown)
        return;
    if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_flush_chars"))
        return;
    if (tty->stopped || tty->hw_stopped || (!skb_queue_len(&info->tx_queue)))
        return;
    queue_task(&info->tq, &tq_immediate);
    mark_bh(IMMEDIATE_BH);
}

/*
 * ------------------------------------------------------------
 * ctc_tty_throttle()
 *
 * This routine is called by the upper-layer tty layer to signal that
 * incoming characters should be throttled.
 * ------------------------------------------------------------
 */
static void
ctc_tty_throttle(struct tty_struct *tty)
{
    ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;

    if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_throttle"))
        return;
    info->mcr &= ~UART_MCR_RTS;
    if (I_IXOFF(tty))
        ctc_tty_inject(info, STOP_CHAR(tty));
    ctc_tty_transmit_status(info);
}

static void
ctc_tty_unthrottle(struct tty_struct *tty)
{
    ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;

    if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_unthrottle"))
        return;
    info->mcr |= UART_MCR_RTS;
    if (I_IXOFF(tty))
        ctc_tty_inject(info, START_CHAR(tty));
    ctc_tty_transmit_status(info);
}

/*
 * ------------------------------------------------------------
 * ctc_tty_ioctl() and friends
 * ------------------------------------------------------------
 */

/*
 * ctc_tty_get_lsr_info - get line status register info
 *
 * Purpose: Let user call ioctl() to get info when the UART physically
 *          is emptied.  On bus types like RS485, the transmitter must
 *          release the bus after transmitting. This must be done when
 *          the transmit shift register is empty, not be done when the
 *          transmit holding register is empty.  This functionality
 *          allows RS485 driver to be written in user space.
 */
static int
ctc_tty_get_lsr_info(ctc_tty_info * info, uint * value)
{
    u_char status;
    uint result;
    ulong flags;

    save_flags(flags);
    cli();
    status = info->lsr;
    restore_flags(flags);
    result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
    put_user(result, (uint *) value);
    return 0;
}


static int
ctc_tty_get_ctc_tty_info(ctc_tty_info * info, uint * value)
{
    u_char control,
     status;
    uint result;
    ulong flags;

    control = info->mcr;
    save_flags(flags);
    cli();
    status = info->msr;
    restore_flags(flags);
    result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
        | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
        | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
        | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
        | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
        | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
    put_user(result, (uint *) value);
    return 0;
}

static int
ctc_tty_set_ctc_tty_info(ctc_tty_info * info, uint cmd, uint * value)
{
    uint arg;
    int old_mcr = info->mcr & (UART_MCR_RTS | UART_MCR_DTR);

    get_user(arg, (uint *) value);
    switch (cmd) {
        case TIOCMBIS:
#ifdef CTC_DEBUG_MODEM_IOCTL
            printk(KERN_DEBUG "%s%d ioctl TIOCMBIS\n", CTC_TTY_NAME,
                   info->line);
#endif
            if (arg & TIOCM_RTS)
                info->mcr |= UART_MCR_RTS;
            if (arg & TIOCM_DTR)
                info->mcr |= UART_MCR_DTR;
            break;
        case TIOCMBIC:
#ifdef CTC_DEBUG_MODEM_IOCTL
            printk(KERN_DEBUG "%s%d ioctl TIOCMBIC\n", CTC_TTY_NAME,
                   info->line);
#endif
            if (arg & TIOCM_RTS)
                info->mcr &= ~UART_MCR_RTS;
            if (arg & TIOCM_DTR)
                info->mcr &= ~UART_MCR_DTR;
            break;
        case TIOCMSET:
#ifdef CTC_DEBUG_MODEM_IOCTL
            printk(KERN_DEBUG "%s%d ioctl TIOCMSET\n", CTC_TTY_NAME,
                   info->line);
#endif
            info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR))
                 | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
                   | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
            break;
        default:
            return -EINVAL;
    }
    if ((info->mcr  & (UART_MCR_RTS | UART_MCR_DTR)) != old_mcr)
        ctc_tty_transmit_status(info);
    return 0;
}

static int
ctc_tty_ioctl(struct tty_struct *tty, struct file *file,
           uint cmd, ulong arg)
{
    ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
    int error;
    int retval;

    if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_ioctl"))
        return -ENODEV;
    if (tty->flags & (1 << TTY_IO_ERROR))
        return -EIO;
    switch (cmd) {
        case TCSBRK:   /* SVID version: non-zero arg --> no break */
#ifdef CTC_DEBUG_MODEM_IOCTL
            printk(KERN_DEBUG "%s%d ioctl TCSBRK\n", CTC_TTY_NAME, info->line);
#endif
            retval = tty_check_change(tty);
            if (retval)
                return retval;
            tty_wait_until_sent(tty, 0);
            return 0;
        case TCSBRKP:  /* support for POSIX tcsendbreak() */
#ifdef CTC_DEBUG_MODEM_IOCTL
            printk(KERN_DEBUG "%s%d ioctl TCSBRKP\n", CTC_TTY_NAME, info->line);
#endif
            retval = tty_check_change(tty);
            if (retval)
                return retval;
            tty_wait_until_sent(tty, 0);
            return 0;
        case TIOCGSOFTCAR:
#ifdef CTC_DEBUG_MODEM_IOCTL
            printk(KERN_DEBUG "%s%d ioctl TIOCGSOFTCAR\n", CTC_TTY_NAME,
                   info->line);
#endif
            error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
            if (error)
                return error;
            put_user(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg);
            return 0;
        case TIOCSSOFTCAR:
#ifdef CTC_DEBUG_MODEM_IOCTL
            printk(KERN_DEBUG "%s%d ioctl TIOCSSOFTCAR\n", CTC_TTY_NAME,
                   info->line);
#endif
            error = verify_area(VERIFY_READ, (void *) arg, sizeof(long));
            if (error)
                return error;
            get_user(arg, (ulong *) arg);
            tty->termios->c_cflag =
                ((tty->termios->c_cflag & ~CLOCAL) |
                 (arg ? CLOCAL : 0));
            return 0;
        case TIOCMGET:
#ifdef CTC_DEBUG_MODEM_IOCTL
            printk(KERN_DEBUG "%s%d ioctl TIOCMGET\n", CTC_TTY_NAME,
                   info->line);
#endif
            error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
            if (error)
                return error;
            return ctc_tty_get_ctc_tty_info(info, (uint *) arg);
        case TIOCMBIS:
        case TIOCMBIC:
        case TIOCMSET:
            error = verify_area(VERIFY_READ, (void *) arg, sizeof(uint));
            if (error)
                return error;
            return ctc_tty_set_ctc_tty_info(info, cmd, (uint *) arg);
        case TIOCSERGETLSR:    /* Get line status register */
#ifdef CTC_DEBUG_MODEM_IOCTL
            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;
            else
                return ctc_tty_get_lsr_info(info, (uint *) arg);
        default:
#ifdef CTC_DEBUG_MODEM_IOCTL
            printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on %s%d\n", cmd,
                   CTC_TTY_NAME, info->line);
#endif
            return -ENOIOCTLCMD;
    }
    return 0;
}

static void
ctc_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
    ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
    unsigned int cflag = tty->termios->c_cflag;

    ctc_tty_change_speed(info);

    /* Handle transition to B0 */
    if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) {
        info->mcr &= ~(UART_MCR_DTR|UART_MCR_RTS);
        ctc_tty_transmit_status(info);
    }

    /* Handle transition from B0 to other */
    if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
        info->mcr |= UART_MCR_DTR;
        if (!(tty->termios->c_cflag & CRTSCTS) ||
                    !test_bit(TTY_THROTTLED, &tty->flags)) {
                        info->mcr |= UART_MCR_RTS;
                }
        ctc_tty_transmit_status(info);
    }

    /* Handle turning off CRTSCTS */
    if ((old_termios->c_cflag & CRTSCTS) &&
            !(tty->termios->c_cflag & CRTSCTS))
                tty->hw_stopped = 0;
}

/*
 * ------------------------------------------------------------
 * ctc_tty_open() and friends
 * ------------------------------------------------------------
 */
static int
ctc_tty_block_til_ready(struct tty_struct *tty, struct file *filp, ctc_tty_info *info)
{
    DECLARE_WAITQUEUE(wait, NULL);
    int do_clocal = 0;
    unsigned long flags;
    int retval;

    /*
     * If the device is in the middle of being closed, then block
     * until it's done, and then try again.
     */
    if (tty_hung_up_p(filp) ||
        (info->flags & CTC_ASYNC_CLOSING)) {
        if (info->flags & CTC_ASYNC_CLOSING)
            interruptible_sleep_on(&info->close_wait);
#ifdef MODEM_DO_RESTART
        if (info->flags & CTC_ASYNC_HUP_NOTIFY)
            return -EAGAIN;
        else
            return -ERESTARTSYS;
#else
        return -EAGAIN;
#endif
    }
    /*
     * If non-blocking mode is set, then make the check up front
     * and then exit.
     */
    if ((filp->f_flags & O_NONBLOCK) ||
        (tty->flags & (1 << TTY_IO_ERROR))) {
        info->flags |= CTC_ASYNC_NORMAL_ACTIVE;
        return 0;
    }
    if (tty->termios->c_cflag & CLOCAL)
        do_clocal = 1;
    /*
     * Block waiting for the carrier detect and the line to become
     * free (i.e., not in use by the callout).  While we are in
     * this loop, info->count is dropped by one, so that
     * ctc_tty_close() knows when to free things.  We restore it upon
     * exit, either normal or abnormal.
     */
    retval = 0;
    add_wait_queue(&info->open_wait, &wait);
#ifdef CTC_DEBUG_MODEM_OPEN
    printk(KERN_DEBUG "ctc_tty_block_til_ready before block: %s%d, count = %d\n",
           CTC_TTY_NAME, info->line, info->count);
#endif
    save_flags(flags);
    cli();
    if (!(tty_hung_up_p(filp)))
        info->count--;
    restore_flags(flags);
    info->blocked_open++;
    while (1) {
        set_current_state(TASK_INTERRUPTIBLE);
        if (tty_hung_up_p(filp) ||
            !(info->flags & CTC_ASYNC_INITIALIZED)) {
#ifdef MODEM_DO_RESTART
            if (info->flags & CTC_ASYNC_HUP_NOTIFY)
                retval = -EAGAIN;
            else
                retval = -ERESTARTSYS;
#else
            retval = -EAGAIN;
#endif
            break;
        }
        if (!(info->flags & CTC_ASYNC_CLOSING) &&
            (do_clocal || (info->msr & UART_MSR_DCD))) {
            break;
        }
        if (signal_pending(current)) {
            retval = -ERESTARTSYS;
            break;
        }
#ifdef CTC_DEBUG_MODEM_OPEN
        printk(KERN_DEBUG "ctc_tty_block_til_ready blocking: %s%d, count = %d\n",
               CTC_TTY_NAME, info->line, info->count);
#endif
        schedule();
    }
    current->state = TASK_RUNNING;
    remove_wait_queue(&info->open_wait, &wait);
    if (!tty_hung_up_p(filp))
        info->count++;
    info->blocked_open--;
#ifdef CTC_DEBUG_MODEM_OPEN
    printk(KERN_DEBUG "ctc_tty_block_til_ready after blocking: %s%d, count = %d\n",
           CTC_TTY_NAME, info->line, info->count);
#endif
    if (retval)
        return retval;
    info->flags |= CTC_ASYNC_NORMAL_ACTIVE;
    return 0;
}

/*
 * This routine is called whenever a serial port is opened.  It
 * enables interrupts for a serial port, linking in its async structure into
 * the IRQ chain.   It also performs the serial-specific
 * initialization for the tty structure.
 */
static int
ctc_tty_open(struct tty_struct *tty, struct file *filp)
{
    ctc_tty_info *info;
    unsigned long saveflags;
    int retval,
     line;

    line = MINOR(tty->device) - tty->driver.minor_start;
    if (line < 0 || line > CTC_TTY_MAX_DEVICES)
        return -ENODEV;
    info = &driver->info[line];
    if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_open"))
        return -ENODEV;
    if (!info->netdev)
        return -ENODEV;
#ifdef CTC_DEBUG_MODEM_OPEN
    printk(KERN_DEBUG "ctc_tty_open %s%d, count = %d\n", tty->driver.name,
           info->line, info->count);
#endif
    spin_lock_irqsave(&ctc_tty_lock, saveflags);
    info->count++;
    tty->driver_data = info;
    info->tty = tty;
    spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
    /*
     * Start up serial port
     */
    retval = ctc_tty_startup(info);
    if (retval) {
#ifdef CTC_DEBUG_MODEM_OPEN
        printk(KERN_DEBUG "ctc_tty_open return after startup\n");
#endif
        return retval;
    }
    retval = ctc_tty_block_til_ready(tty, filp, info);
    if (retval) {
#ifdef CTC_DEBUG_MODEM_OPEN
        printk(KERN_DEBUG "ctc_tty_open return after ctc_tty_block_til_ready \n");
#endif
        return retval;
    }
    if ((info->count == 1) && (info->flags & CTC_ASYNC_SPLIT_TERMIOS)) {
        *tty->termios = info->normal_termios;
        ctc_tty_change_speed(info);
    }
#ifdef CTC_DEBUG_MODEM_OPEN
    printk(KERN_DEBUG "ctc_tty_open %s%d successful...\n", CTC_TTY_NAME, info->line);
#endif
    return 0;
}

static void
ctc_tty_close(struct tty_struct *tty, struct file *filp)
{
    ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
    unsigned long saveflags;
    ulong flags;
    ulong timeout;

    if (!info || ctc_tty_paranoia_check(info, tty->device, "ctc_tty_close"))
        return;
    save_flags(flags);
    cli();
    if (tty_hung_up_p(filp)) {
        restore_flags(flags);
#ifdef CTC_DEBUG_MODEM_OPEN
        printk(KERN_DEBUG "ctc_tty_close return after tty_hung_up_p\n");
#endif
        return;
    }
    if ((tty->count == 1) && (info->count != 1)) {
        /*
         * Uh, oh.  tty->count is 1, which means that the tty
         * structure will be freed.  Info->count should always
         * be one in these conditions.  If it's greater than
         * one, we've got real problems, since it means the
         * serial port won't be shutdown.
         */
        printk(KERN_ERR "ctc_tty_close: bad port count; tty->count is 1, "
               "info->count is %d\n", info->count);
        info->count = 1;
    }
    if (--info->count < 0) {
        printk(KERN_ERR "ctc_tty_close: bad port count for %s%d: %d\n",
               CTC_TTY_NAME, info->line, info->count);
        info->count = 0;
    }
    if (info->count) {
        restore_flags(flags);
#ifdef CTC_DEBUG_MODEM_OPEN
        printk(KERN_DEBUG "ctc_tty_close after info->count != 0\n");
#endif
        return;
    }
    info->flags |= CTC_ASYNC_CLOSING;
    /*
     * Save the termios structure, since this port may have
     * separate termios for callout and dialin.
     */
    if (info->flags & CTC_ASYNC_NORMAL_ACTIVE)
        info->normal_termios = *tty->termios;

    tty->closing = 1;
    /*
     * At this point we stop accepting input.  To do this, we
     * disable the receive line status interrupts, and tell the
     * interrupt driver to stop checking the data ready bit in the
     * line status register.
     */
    if (info->flags & CTC_ASYNC_INITIALIZED) {
        tty_wait_until_sent(tty, 3000);    /* 30 seconds timeout */
        /*
         * Before we drop DTR, make sure the UART transmitter
         * has completely drained; this is especially
         * important if there is a transmit FIFO!
         */
        timeout = jiffies + HZ;
        while (!(info->lsr & UART_LSR_TEMT)) {
            set_current_state(TASK_INTERRUPTIBLE);
            schedule_timeout(20);
            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);
    spin_lock_irqsave(&ctc_tty_lock, saveflags);
    info->tty = 0;
    spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
    tty->closing = 0;
    if (info->blocked_open) {
        set_current_state(TASK_INTERRUPTIBLE);
        schedule_timeout(50);
        wake_up_interruptible(&info->open_wait);
    }
    info->flags &= ~(CTC_ASYNC_NORMAL_ACTIVE | CTC_ASYNC_CLOSING);
    wake_up_interruptible(&info->close_wait);
    restore_flags(flags);
#ifdef CTC_DEBUG_MODEM_OPEN
    printk(KERN_DEBUG "ctc_tty_close normal exit\n");
#endif
}

/*
 * ctc_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
 */
static void
ctc_tty_hangup(struct tty_struct *tty)
{
    ctc_tty_info *info = (ctc_tty_info *)tty->driver_data;
    unsigned long saveflags;

    if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_hangup"))
        return;
    ctc_tty_shutdown(info);
    info->count = 0;
    info->flags &= ~CTC_ASYNC_NORMAL_ACTIVE;
    spin_lock_irqsave(&ctc_tty_lock, saveflags);
    info->tty = 0;
    spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
    wake_up_interruptible(&info->open_wait);
}


/*
 * For all online tty's, try sending data to
 * the lower levels.
 */
static void
ctc_tty_task(ctc_tty_info *info)
{
    unsigned long saveflags;
    int again;

    spin_lock_irqsave(&ctc_tty_lock, saveflags);
    if ((!ctc_tty_shuttingdown) && info) {
        again = ctc_tty_tint(info);
        if (!again)
            info->lsr |= UART_LSR_TEMT;
        again |= ctc_tty_readmodem(info);
        if (again) {
            queue_task(&info->tq, &tq_immediate);
            mark_bh(IMMEDIATE_BH);
        }
    }
    spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
}

int
ctc_tty_init(void)
{
    int i;
    ctc_tty_info *info;
    struct tty_driver *device;

    driver = kmalloc(sizeof(ctc_tty_driver), GFP_KERNEL);
    if (driver == NULL) {
        printk(KERN_WARNING "Out of memory in ctc_tty_modem_init\n");
        return -ENOMEM;
    }
    memset(driver, 0, sizeof(ctc_tty_driver));
    device = &driver->ctc_tty_device;

    device->magic = TTY_DRIVER_MAGIC;
    device->name = ctc_ttyname;
    device->major = CTC_TTY_MAJOR;
    device->minor_start = 0;
    device->num = CTC_TTY_MAX_DEVICES;
    device->type = TTY_DRIVER_TYPE_SERIAL;
    device->subtype = CTC_SERIAL_TYPE_NORMAL;
    device->init_termios = tty_std_termios;
    device->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
    device->flags = TTY_DRIVER_REAL_RAW;
    device->refcount = &driver->refcount;
    device->table = driver->modem_table;
    device->termios = driver->modem_termios;
    device->termios_locked = driver->modem_termios_locked;
    device->open = ctc_tty_open;
    device->close = ctc_tty_close;
    device->write = ctc_tty_write;
    device->put_char = NULL;
    device->flush_chars = ctc_tty_flush_chars;
    device->write_room = ctc_tty_write_room;
    device->chars_in_buffer = ctc_tty_chars_in_buffer;
    device->flush_buffer = ctc_tty_flush_buffer;
    device->ioctl = ctc_tty_ioctl;
    device->throttle = ctc_tty_throttle;
    device->unthrottle = ctc_tty_unthrottle;
    device->set_termios = ctc_tty_set_termios;
    device->stop = NULL;
    device->start = NULL;
    device->hangup = ctc_tty_hangup;
    device->driver_name = "ctc_tty";

    if (tty_register_driver(device)) {
        printk(KERN_WARNING "ctc_tty: Couldn't register serial-device\n");
        kfree(driver);
        return -1;
    }
    for (i = 0; i < CTC_TTY_MAX_DEVICES; i++) {
        info = &driver->info[i];
        init_MUTEX(&info->write_sem);
#if LINUX_VERSION_CODE >= 0x020400
        INIT_LIST_HEAD(&info->tq.list);
#else
        info->tq.next    = NULL;
#endif
        info->tq.sync    = 0;
        info->tq.routine = (void *)(void *)ctc_tty_task;
        info->tq.data    = info;
        info->magic = CTC_ASYNC_MAGIC;
        info->line = i;
        info->tty = 0;
        info->count = 0;
        info->blocked_open = 0;
        info->normal_termios = device->init_termios;
        init_waitqueue_head(&info->open_wait);
        init_waitqueue_head(&info->close_wait);
        skb_queue_head_init(&info->tx_queue);
        skb_queue_head_init(&info->rx_queue);
        init_timer(&info->stoptimer);
        info->stoptimer.function = ctc_tty_stopdev;
        info->stoptimer.data = (unsigned long)info;
        info->mcr = UART_MCR_RTS;
    }
    return 0;
}

int
ctc_tty_register_netdev(net_device *dev) {
    int ttynum;
    char *err;
    char *p;

    if ((!dev) || (!dev->name)) {
        printk(KERN_WARNING
               "ctc_tty_register_netdev called "
               "with NULL dev or NULL dev-name\n");
        return -1;
    }
    for (p = dev->name; p && ((*p < '0') || (*p > '9')); p++);
    ttynum = simple_strtoul(p, &err, 0);
    if ((ttynum < 0) || (ttynum >= CTC_TTY_MAX_DEVICES) ||
        (err && *err)) {
        printk(KERN_WARNING
               "ctc_tty_register_netdev called "
               "with number in name '%s'\n", dev->name);
        return -1;
    }
    if (driver->info[ttynum].netdev) {
        printk(KERN_WARNING
               "ctc_tty_register_netdev called "
               "for already registered device '%s'\n",
               dev->name);
        return -1;
    }
    driver->info[ttynum].netdev = dev;
    return 0;
}

void
ctc_tty_unregister_netdev(net_device *dev) {
    int i;
    unsigned long saveflags;
    ctc_tty_info *info = NULL;

    spin_lock_irqsave(&ctc_tty_lock, saveflags);
    for (i = 0; i < CTC_TTY_MAX_DEVICES; i++)
        if (driver->info[i].netdev == dev) {
            info = &driver->info[i];
            break;
        }
    if (info) {
        info->netdev = NULL;
        skb_queue_purge(&info->tx_queue);
        skb_queue_purge(&info->rx_queue);
    }
    spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
}

void
ctc_tty_cleanup(int final) {
    unsigned long saveflags;
    
    spin_lock_irqsave(&ctc_tty_lock, saveflags);
    ctc_tty_shuttingdown = 1;
    if (final) {
        kfree(driver);
        driver = NULL;
    } else {
        int i;

        for (i = 0; i < CTC_TTY_MAX_DEVICES; i++)
            driver->info[i].tq.routine = NULL;
        tty_unregister_driver(&driver->ctc_tty_device);
    }
    spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
}

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.0219 ]--