📄 ctctty.c
字号:
/* * $Id: ctctty.c,v 1.29 2005/04/05 08:50:44 mschwide 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. * */#include <linux/config.h>#include <linux/module.h>#include <linux/tty.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#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 *//* 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 */ struct 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 */ wait_queue_head_t open_wait; wait_queue_head_t close_wait; struct semaphore write_sem; struct tasklet_struct tasklet; struct timer_list stoptimer;} ctc_tty_info;/* Description of one CTC-tty */typedef struct { struct tty_driver *ctc_tty_device; /* tty-device */ 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"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; 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; } } } 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; 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))) { 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_empty(&info->rx_queue); } } } } return ret;}voidctc_tty_setcarrier(struct net_device *netdev, int on){ int i; DBF_TEXT(trace, 4, __FUNCTION__); 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; DBF_TEXT(trace, 5, __FUNCTION__); 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 */ tasklet_schedule(&info->tasklet);}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; DBF_TEXT(trace, 4, __FUNCTION__); if (!info->netdev) { if (skb) kfree_skb(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) { tty_wakeup(tty); } } 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, char *name, const char *routine){#ifdef MODEM_PARANOIA_CHECK if (!info) { printk(KERN_WARNING "ctc_tty: null info_struct for %s in %s\n", name, routine); return 1; } if (info->magic != CTC_ASYNC_MAGIC) { printk(KERN_WARNING "ctc_tty: bad magic for info struct %s in %s\n", name, routine); return 1; }#endif return 0;}static voidctc_tty_inject(ctc_tty_info *info, char c){ 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) + 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); tasklet_schedule(&info->tasklet);}static voidctc_tty_transmit_status(ctc_tty_info *info){ DBF_TEXT(trace, 5, __FUNCTION__); if (ctc_tty_shuttingdown) return; info->flags |= CTC_ASYNC_TX_LINESTAT; tasklet_schedule(&info->tasklet);}static voidctc_tty_change_speed(ctc_tty_info * info){ unsigned int cflag; unsigned int quot; int i; DBF_TEXT(trace, 3, __FUNCTION__); 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 intctc_tty_startup(ctc_tty_info * info){ DBF_TEXT(trace, 3, __FUNCTION__); 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -