📄 ctctty.c
字号:
/* * $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 < 0x020212typedef 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_FSstatic char *ctc_ttyname = "ctc/" CTC_TTY_NAME "%d";#elsestatic char *ctc_ttyname = CTC_TTY_NAME;#endifchar *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 intctc_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 intctc_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;}voidctc_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); }}voidctc_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 intctc_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 intctc_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 voidctc_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 voidctc_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 voidctc_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; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -