欢迎来到虫虫下载站 | 资源下载 资源专辑 关于我们
虫虫下载站

n_hdlc.c

linux 内核源代码
C
第 1 页 / 共 2 页
字号:
/* generic HDLC line discipline for Linux * * Written by Paul Fulghum paulkf@microgate.com * for Microgate Corporation * * Microgate and SyncLink are registered trademarks of Microgate Corporation * * Adapted from ppp.c, written by Michael Callahan <callahan@maths.ox.ac.uk>, *	Al Longyear <longyear@netcom.com>, *	Paul Mackerras <Paul.Mackerras@cs.anu.edu.au> * * Original release 01/11/99 * $Id: n_hdlc.c,v 4.8 2003/05/06 21:18:51 paulkf Exp $ * * This code is released under the GNU General Public License (GPL) * * This module implements the tty line discipline N_HDLC for use with * tty device drivers that support bit-synchronous HDLC communications. * * All HDLC data is frame oriented which means: * * 1. tty write calls represent one complete transmit frame of data *    The device driver should accept the complete frame or none of  *    the frame (busy) in the write method. Each write call should have *    a byte count in the range of 2-65535 bytes (2 is min HDLC frame *    with 1 addr byte and 1 ctrl byte). The max byte count of 65535 *    should include any crc bytes required. For example, when using *    CCITT CRC32, 4 crc bytes are required, so the maximum size frame *    the application may transmit is limited to 65531 bytes. For CCITT *    CRC16, the maximum application frame size would be 65533. * * * 2. receive callbacks from the device driver represents *    one received frame. The device driver should bypass *    the tty flip buffer and call the line discipline receive *    callback directly to avoid fragmenting or concatenating *    multiple frames into a single receive callback. * *    The HDLC line discipline queues the receive frames in separate *    buffers so complete receive frames can be returned by the *    tty read calls. * * 3. tty read calls returns an entire frame of data or nothing. *     * 4. all send and receive data is considered raw. No processing *    or translation is performed by the line discipline, regardless *    of the tty flags * * 5. When line discipline is queried for the amount of receive *    data available (FIOC), 0 is returned if no data available, *    otherwise the count of the next available frame is returned. *    (instead of the sum of all received frame counts). * * These conventions allow the standard tty programming interface * to be used for synchronous HDLC applications when used with * this line discipline (or another line discipline that is frame * oriented such as N_PPP). * * The SyncLink driver (synclink.c) implements both asynchronous * (using standard line discipline N_TTY) and synchronous HDLC * (using N_HDLC) communications, with the latter using the above * conventions. * * This implementation is very basic and does not maintain * any statistics. The main point is to enforce the raw data * and frame orientation of HDLC communications. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */#define HDLC_MAGIC 0x239e#define HDLC_VERSION "$Revision: 4.8 $"#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#undef VERSION#define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch))#include <linux/poll.h>#include <linux/in.h>#include <linux/ioctl.h>#include <linux/slab.h>#include <linux/tty.h>#include <linux/errno.h>#include <linux/string.h>	/* used in new tty drivers */#include <linux/signal.h>	/* used in new tty drivers */#include <linux/if.h>#include <linux/bitops.h>#include <asm/system.h>#include <asm/termios.h>#include <asm/uaccess.h>/* * Buffers for individual HDLC frames */#define MAX_HDLC_FRAME_SIZE 65535 #define DEFAULT_RX_BUF_COUNT 10#define MAX_RX_BUF_COUNT 60#define DEFAULT_TX_BUF_COUNT 1struct n_hdlc_buf {	struct n_hdlc_buf *link;	int		  count;	char		  buf[1];};#define	N_HDLC_BUF_SIZE	(sizeof(struct n_hdlc_buf) + maxframe)struct n_hdlc_buf_list {	struct n_hdlc_buf *head;	struct n_hdlc_buf *tail;	int		  count;	spinlock_t	  spinlock;};/** * struct n_hdlc - per device instance data structure * @magic - magic value for structure * @flags - miscellaneous control flags * @tty - ptr to TTY structure * @backup_tty - TTY to use if tty gets closed * @tbusy - reentrancy flag for tx wakeup code * @woke_up - FIXME: describe this field * @tbuf - currently transmitting tx buffer * @tx_buf_list - list of pending transmit frame buffers * @rx_buf_list - list of received frame buffers * @tx_free_buf_list - list unused transmit frame buffers * @rx_free_buf_list - list unused received frame buffers */struct n_hdlc {	int			magic;	__u32			flags;	struct tty_struct	*tty;	struct tty_struct	*backup_tty;	int			tbusy;	int			woke_up;	struct n_hdlc_buf	*tbuf;	struct n_hdlc_buf_list	tx_buf_list;	struct n_hdlc_buf_list	rx_buf_list;	struct n_hdlc_buf_list	tx_free_buf_list;	struct n_hdlc_buf_list	rx_free_buf_list;};/* * HDLC buffer list manipulation functions */static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list);static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,			   struct n_hdlc_buf *buf);static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list);/* Local functions */static struct n_hdlc *n_hdlc_alloc (void);/* debug level can be set by insmod for debugging purposes */#define DEBUG_LEVEL_INFO	1static int debuglevel;/* max frame size for memory allocations */static int maxframe = 4096;/* TTY callbacks */static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,			   __u8 __user *buf, size_t nr);static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,			    const unsigned char *buf, size_t nr);static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,			    unsigned int cmd, unsigned long arg);static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,				    poll_table *wait);static int n_hdlc_tty_open(struct tty_struct *tty);static void n_hdlc_tty_close(struct tty_struct *tty);static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *cp,			       char *fp, int count);static void n_hdlc_tty_wakeup(struct tty_struct *tty);#define bset(p,b)	((p)[(b) >> 5] |= (1 << ((b) & 0x1f)))#define tty2n_hdlc(tty)	((struct n_hdlc *) ((tty)->disc_data))#define n_hdlc2tty(n_hdlc)	((n_hdlc)->tty)static struct tty_ldisc n_hdlc_ldisc = {	.owner		= THIS_MODULE,	.magic		= TTY_LDISC_MAGIC,	.name		= "hdlc",	.open		= n_hdlc_tty_open,	.close		= n_hdlc_tty_close,	.read		= n_hdlc_tty_read,	.write		= n_hdlc_tty_write,	.ioctl		= n_hdlc_tty_ioctl,	.poll		= n_hdlc_tty_poll,	.receive_buf	= n_hdlc_tty_receive,	.write_wakeup	= n_hdlc_tty_wakeup,};/** * n_hdlc_release - release an n_hdlc per device line discipline info structure * @n_hdlc - per device line discipline info structure */static void n_hdlc_release(struct n_hdlc *n_hdlc){	struct tty_struct *tty = n_hdlc2tty (n_hdlc);	struct n_hdlc_buf *buf;		if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_release() called\n",__FILE__,__LINE__);			/* Ensure that the n_hdlcd process is not hanging on select()/poll() */	wake_up_interruptible (&tty->read_wait);	wake_up_interruptible (&tty->write_wait);	if (tty->disc_data == n_hdlc)		tty->disc_data = NULL;	/* Break the tty->n_hdlc link */	/* Release transmit and receive buffers */	for(;;) {		buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list);		if (buf) {			kfree(buf);		} else			break;	}	for(;;) {		buf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list);		if (buf) {			kfree(buf);		} else			break;	}	for(;;) {		buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list);		if (buf) {			kfree(buf);		} else			break;	}	for(;;) {		buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);		if (buf) {			kfree(buf);		} else			break;	}	kfree(n_hdlc->tbuf);	kfree(n_hdlc);	}	/* end of n_hdlc_release() *//** * n_hdlc_tty_close - line discipline close * @tty - pointer to tty info structure * * Called when the line discipline is changed to something * else, the tty is closed, or the tty detects a hangup. */static void n_hdlc_tty_close(struct tty_struct *tty){	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);	if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_tty_close() called\n",__FILE__,__LINE__);			if (n_hdlc != NULL) {		if (n_hdlc->magic != HDLC_MAGIC) {			printk (KERN_WARNING"n_hdlc: trying to close unopened tty!\n");			return;		}#if defined(TTY_NO_WRITE_SPLIT)		clear_bit(TTY_NO_WRITE_SPLIT,&tty->flags);#endif		tty->disc_data = NULL;		if (tty == n_hdlc->backup_tty)			n_hdlc->backup_tty = NULL;		if (tty != n_hdlc->tty)			return;		if (n_hdlc->backup_tty) {			n_hdlc->tty = n_hdlc->backup_tty;		} else {			n_hdlc_release (n_hdlc);		}	}		if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_tty_close() success\n",__FILE__,__LINE__);		}	/* end of n_hdlc_tty_close() *//** * n_hdlc_tty_open - called when line discipline changed to n_hdlc * @tty - pointer to tty info structure * * Returns 0 if success, otherwise error code */static int n_hdlc_tty_open (struct tty_struct *tty){	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);	if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_tty_open() called (device=%s)\n",		__FILE__,__LINE__,		tty->name);			/* There should not be an existing table for this slot. */	if (n_hdlc) {		printk (KERN_ERR"n_hdlc_tty_open:tty already associated!\n" );		return -EEXIST;	}		n_hdlc = n_hdlc_alloc();	if (!n_hdlc) {		printk (KERN_ERR "n_hdlc_alloc failed\n");		return -ENFILE;	}			tty->disc_data = n_hdlc;	n_hdlc->tty    = tty;	tty->receive_room = 65536;	#if defined(TTY_NO_WRITE_SPLIT)	/* change tty_io write() to not split large writes into 8K chunks */	set_bit(TTY_NO_WRITE_SPLIT,&tty->flags);#endif		/* Flush any pending characters in the driver and discipline. */		if (tty->ldisc.flush_buffer)		tty->ldisc.flush_buffer (tty);	if (tty->driver->flush_buffer)		tty->driver->flush_buffer (tty);			if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_tty_open() success\n",__FILE__,__LINE__);			return 0;	}	/* end of n_tty_hdlc_open() *//** * n_hdlc_send_frames - send frames on pending send buffer list * @n_hdlc - pointer to ldisc instance data * @tty - pointer to tty instance data * * Send frames on pending send buffer list until the driver does not accept a * frame (busy) this function is called after adding a frame to the send buffer * list and by the tty wakeup callback. */static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty){	register int actual;	unsigned long flags;	struct n_hdlc_buf *tbuf;	if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_send_frames() called\n",__FILE__,__LINE__); check_again:		 	spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);	if (n_hdlc->tbusy) {		n_hdlc->woke_up = 1; 		spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);		return;	}	n_hdlc->tbusy = 1;	n_hdlc->woke_up = 0;	spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);	/* get current transmit buffer or get new transmit */	/* buffer from list of pending transmit buffers */			tbuf = n_hdlc->tbuf;	if (!tbuf)		tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);			while (tbuf) {		if (debuglevel >= DEBUG_LEVEL_INFO)				printk("%s(%d)sending frame %p, count=%d\n",				__FILE__,__LINE__,tbuf,tbuf->count);					/* Send the next block of data to device */		tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);		actual = tty->driver->write(tty, tbuf->buf, tbuf->count);		/* rollback was possible and has been done */		if (actual == -ERESTARTSYS) {			n_hdlc->tbuf = tbuf;			break;		}		/* if transmit error, throw frame away by */		/* pretending it was accepted by driver */		if (actual < 0)			actual = tbuf->count;				if (actual == tbuf->count) {			if (debuglevel >= DEBUG_LEVEL_INFO)					printk("%s(%d)frame %p completed\n",					__FILE__,__LINE__,tbuf);								/* free current transmit buffer */			n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf);						/* this tx buffer is done */			n_hdlc->tbuf = NULL;						/* wait up sleeping writers */			wake_up_interruptible(&tty->write_wait);				/* get next pending transmit buffer */			tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);		} else {			if (debuglevel >= DEBUG_LEVEL_INFO)					printk("%s(%d)frame %p pending\n",					__FILE__,__LINE__,tbuf);								/* buffer not accepted by driver */			/* set this buffer as pending buffer */			n_hdlc->tbuf = tbuf;			break;		}	}		if (!tbuf)		tty->flags  &= ~(1 << TTY_DO_WRITE_WAKEUP);		/* Clear the re-entry flag */	spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);	n_hdlc->tbusy = 0;	spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); 	        if (n_hdlc->woke_up)	  goto check_again;	if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_send_frames() exit\n",__FILE__,__LINE__);		}	/* end of n_hdlc_send_frames() *//** * n_hdlc_tty_wakeup - Callback for transmit wakeup * @tty	- pointer to associated tty instance data * * Called when low level device driver can accept more send data. */static void n_hdlc_tty_wakeup(struct tty_struct *tty){	struct n_hdlc *n_hdlc = tty2n_hdlc(tty);	if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_tty_wakeup() called\n",__FILE__,__LINE__);			if (!n_hdlc)		return;	if (tty != n_hdlc->tty) {		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);		return;	}	n_hdlc_send_frames (n_hdlc, tty);		}	/* end of n_hdlc_tty_wakeup() *//**

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -