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

📄 n_hdlc.c

📁 powerpc内核mpc8241linux系统下char驱动程序
💻 C
📖 第 1 页 / 共 3 页
字号:
/* 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 * * 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-4096 bytes (2 is min HDLC frame *    with 1 addr byte and 1 ctrl byte). * * 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 seperate *    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 "1.0"#include <linux/version.h>#include <linux/config.h>#include <linux/module.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))#if LINUX_VERSION_CODE < VERSION(2,1,14)#include <linux/ioport.h>#endif#if LINUX_VERSION_CODE >= VERSION(2,1,23)#include <linux/poll.h>#endif#include <linux/in.h>#include <linux/malloc.h>#include <linux/tty.h>#include <linux/errno.h>#include <linux/sched.h>	/* to get the struct task_struct */#include <linux/string.h>	/* used in new tty drivers */#include <linux/signal.h>	/* used in new tty drivers */#include <asm/system.h>#include <asm/bitops.h>#include <asm/termios.h>#include <linux/if.h>#include <linux/ioctl.h>#ifdef CONFIG_KERNELD#include <linux/kerneld.h>#endif#if LINUX_VERSION_CODE >= VERSION(2,1,4)#include <asm/segment.h>#define GET_USER(error,value,addr) error = get_user(value,addr)#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0#define PUT_USER(error,value,addr) error = put_user(value,addr)#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0#if LINUX_VERSION_CODE >= VERSION(2,1,5)#include <asm/uaccess.h>#endif#else  /* 2.0.x and 2.1.x before 2.1.4 */#define GET_USER(error,value,addr)					  \do {									  \	error = verify_area (VERIFY_READ, (void *) addr, sizeof (value)); \	if (error == 0)							  \		value = get_user(addr);					  \} while (0)#define COPY_FROM_USER(error,dest,src,size)				  \do {									  \	error = verify_area (VERIFY_READ, (void *) src, size);		  \	if (error == 0)							  \		memcpy_fromfs (dest, src, size);			  \} while (0)#define PUT_USER(error,value,addr)					   \do {									   \	error = verify_area (VERIFY_WRITE, (void *) addr, sizeof (value)); \	if (error == 0)							   \		put_user (value, addr);					   \} while (0)#define COPY_TO_USER(error,dest,src,size)				  \do {									  \	error = verify_area (VERIFY_WRITE, (void *) dest, size);		  \	if (error == 0)							  \		memcpy_tofs (dest, src, size);				  \} while (0)#endif#if LINUX_VERSION_CODE < VERSION(2,1,0)#define __inittypedef int spinlock_t;#define spin_lock_irqsave(a,b) {save_flags((b));cli();}#define spin_unlock_irqrestore(a,b) {restore_flags((b));}#define spin_lock(a)#define spin_unlock(a)#define schedule_timeout(a){current->timeout = jiffies + (a); schedule();}#endif#if LINUX_VERSION_CODE < VERSION(2,1,37)#define test_and_set_bit(nr, addr)	set_bit(nr, addr)#endif#if LINUX_VERSION_CODE < VERSION(2,1,57)#define signal_pending(p)	((p)->signal & ~(p)->blocked)#endif#if LINUX_VERSION_CODE < VERSION(2,1,25)#define net_device_stats	enet_statistics#endif#if LINUX_VERSION_CODE < VERSION(2,1,60)typedef int		rw_ret_t;typedef unsigned int	rw_count_t;#elsetypedef ssize_t		rw_ret_t;typedef size_t		rw_count_t;#endif/* * Buffers for individual HDLC frames */#define MAX_HDLC_FRAME_SIZE 4096#define DEFAULT_RX_BUF_COUNT 10#define MAX_RX_BUF_COUNT 30#define DEFAULT_TX_BUF_COUNT 1typedef struct _n_hdlc_buf{	struct _n_hdlc_buf *link;	int count;	char buf[MAX_HDLC_FRAME_SIZE];} N_HDLC_BUF;typedef struct _n_hdlc_buf_list{	N_HDLC_BUF *head;	N_HDLC_BUF *tail;	int count;	spinlock_t spinlock;	} N_HDLC_BUF_LIST;/* * Per device instance data structure */struct n_hdlc {	int		magic;		/* magic value for structure	*/	__u32		flags;		/* miscellaneous control flags	*/		struct tty_struct *tty;		/* ptr to TTY structure	*/	struct tty_struct *backup_tty;	/* TTY to use if tty gets closed */		/* Queues for select() functionality */	struct wait_queue *read_wait;	struct wait_queue *write_wait;	int		tbusy;		/* reentrancy flag for tx wakeup code */	int		woke_up;	N_HDLC_BUF	*tbuf;		/* currently transmitting tx buffer */	N_HDLC_BUF_LIST tx_buf_list;	/* list of pending transmit frame buffers */		N_HDLC_BUF_LIST	rx_buf_list;	/* list of received frame buffers */	N_HDLC_BUF_LIST tx_free_buf_list;	/* list unused transmit frame buffers */		N_HDLC_BUF_LIST	rx_free_buf_list;	/* list unused received frame buffers */};/* * HDLC buffer list manipulation functions */void n_hdlc_buf_list_init(N_HDLC_BUF_LIST *list);void n_hdlc_buf_put(N_HDLC_BUF_LIST *list,N_HDLC_BUF *buf);N_HDLC_BUF* n_hdlc_buf_get(N_HDLC_BUF_LIST *list);/* Local functions */static struct n_hdlc *n_hdlc_alloc (void);#if LINUX_VERSION_CODE >= VERSION(2,1,19) MODULE_PARM(debuglevel, "i");#endif/* debug level can be set by insmod for debugging purposes */#define DEBUG_LEVEL_INFO	1int debuglevel=0;/* TTY callbacks */static rw_ret_t n_hdlc_tty_read(struct tty_struct *,	struct file *, __u8 *, rw_count_t);static rw_ret_t n_hdlc_tty_write(struct tty_struct *,	struct file *, const __u8 *, rw_count_t);static int n_hdlc_tty_ioctl(struct tty_struct *,	struct file *, unsigned int, unsigned long);#if LINUX_VERSION_CODE < VERSION(2,1,23)static int n_hdlc_tty_select (struct tty_struct *tty, struct inode *inode,		      struct file *filp, int sel_type, select_table * wait);#elsestatic unsigned int n_hdlc_tty_poll (struct tty_struct *tty, struct file *filp,				  poll_table * wait);#endifstatic int n_hdlc_tty_open (struct tty_struct *);static void n_hdlc_tty_close (struct tty_struct *);static int n_hdlc_tty_room (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)/* Define this string only once for all macro invocations */static char szVersion[] = HDLC_VERSION;/* n_hdlc_release() * *	release an 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);	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 (&n_hdlc->read_wait);	wake_up_interruptible (&n_hdlc->write_wait);	if (tty != NULL && 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);	}	/* end of n_hdlc_release() *//* n_hdlc_tty_close() * *	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;		}		tty->disc_data = NULL;		if (tty == n_hdlc->backup_tty)			n_hdlc->backup_tty = 0;		if (tty != n_hdlc->tty)			return;		if (n_hdlc->backup_tty) {			n_hdlc->tty = n_hdlc->backup_tty;		} else {			n_hdlc_release (n_hdlc);			MOD_DEC_USE_COUNT;		}	}		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 * 	 * Arguments:	tty	pointer to tty info structure * Return Value:	0 if success, otherwise error code */static int n_hdlc_tty_open (struct tty_struct *tty){	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);

⌨️ 快捷键说明

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