⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ctctty.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * $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 + -