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

n_hdlc.c

linux 内核源代码
C
第 1 页 / 共 2 页
字号:
 * n_hdlc_tty_receive - Called by tty driver when receive data is available * @tty	- pointer to tty instance data * @data - pointer to received data * @flags - pointer to flags for data * @count - count of received data in bytes * * Called by tty low level driver when receive data is available. Data is * interpreted as one HDLC frame. */static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data,			       char *flags, int count){	register struct n_hdlc *n_hdlc = tty2n_hdlc (tty);	register struct n_hdlc_buf *buf;	if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_tty_receive() called count=%d\n",			__FILE__,__LINE__, count);			/* This can happen if stuff comes in on the backup tty */	if (n_hdlc == 0 || tty != n_hdlc->tty)		return;			/* verify line is using HDLC discipline */	if (n_hdlc->magic != HDLC_MAGIC) {		printk("%s(%d) line not using HDLC discipline\n",			__FILE__,__LINE__);		return;	}		if ( count>maxframe ) {		if (debuglevel >= DEBUG_LEVEL_INFO)				printk("%s(%d) rx count>maxframesize, data discarded\n",			       __FILE__,__LINE__);		return;	}	/* get a free HDLC buffer */		buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list);	if (!buf) {		/* no buffers in free list, attempt to allocate another rx buffer */		/* unless the maximum count has been reached */		if (n_hdlc->rx_buf_list.count < MAX_RX_BUF_COUNT)			buf = kmalloc(N_HDLC_BUF_SIZE, GFP_ATOMIC);	}		if (!buf) {		if (debuglevel >= DEBUG_LEVEL_INFO)				printk("%s(%d) no more rx buffers, data discarded\n",			       __FILE__,__LINE__);		return;	}			/* copy received data to HDLC buffer */	memcpy(buf->buf,data,count);	buf->count=count;	/* add HDLC buffer to list of received frames */	n_hdlc_buf_put(&n_hdlc->rx_buf_list, buf);		/* wake up any blocked reads and perform async signalling */	wake_up_interruptible (&tty->read_wait);	if (n_hdlc->tty->fasync != NULL)		kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN);}	/* end of n_hdlc_tty_receive() *//** * n_hdlc_tty_read - Called to retrieve one frame of data (if available) * @tty - pointer to tty instance data * @file - pointer to open file object * @buf - pointer to returned data buffer * @nr - size of returned data buffer * 	 * Returns the number of bytes returned or error code. */static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,			   __u8 __user *buf, size_t nr){	struct n_hdlc *n_hdlc = tty2n_hdlc(tty);	int ret;	struct n_hdlc_buf *rbuf;	if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_tty_read() called\n",__FILE__,__LINE__);			/* Validate the pointers */	if (!n_hdlc)		return -EIO;	/* verify user access to buffer */	if (!access_ok(VERIFY_WRITE, buf, nr)) {		printk(KERN_WARNING "%s(%d) n_hdlc_tty_read() can't verify user "		"buffer\n", __FILE__, __LINE__);		return -EFAULT;	}	for (;;) {		if (test_bit(TTY_OTHER_CLOSED, &tty->flags))			return -EIO;		n_hdlc = tty2n_hdlc (tty);		if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||			 tty != n_hdlc->tty)			return 0;		rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list);		if (rbuf)			break;					/* no data */		if (file->f_flags & O_NONBLOCK)			return -EAGAIN;					interruptible_sleep_on (&tty->read_wait);		if (signal_pending(current))			return -EINTR;	}			if (rbuf->count > nr)		/* frame too large for caller's buffer (discard frame) */		ret = -EOVERFLOW;	else {		/* Copy the data to the caller's buffer */		if (copy_to_user(buf, rbuf->buf, rbuf->count))			ret = -EFAULT;		else			ret = rbuf->count;	}		/* return HDLC buffer to free list unless the free list */	/* count has exceeded the default value, in which case the */	/* buffer is freed back to the OS to conserve memory */	if (n_hdlc->rx_free_buf_list.count > DEFAULT_RX_BUF_COUNT)		kfree(rbuf);	else			n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf);		return ret;	}	/* end of n_hdlc_tty_read() *//** * n_hdlc_tty_write - write a single frame of data to device * @tty	- pointer to associated tty device instance data * @file - pointer to file object data * @data - pointer to transmit data (one frame) * @count - size of transmit frame in bytes * 		 * Returns the number of bytes written (or error code). */static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,			    const unsigned char *data, size_t count){	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);	int error = 0;	DECLARE_WAITQUEUE(wait, current);	struct n_hdlc_buf *tbuf;	if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_tty_write() called count=%Zd\n",			__FILE__,__LINE__,count);			/* Verify pointers */	if (!n_hdlc)		return -EIO;	if (n_hdlc->magic != HDLC_MAGIC)		return -EIO;	/* verify frame size */	if (count > maxframe ) {		if (debuglevel & DEBUG_LEVEL_INFO)			printk (KERN_WARNING				"n_hdlc_tty_write: truncating user packet "				"from %lu to %d\n", (unsigned long) count,				maxframe );		count = maxframe;	}		add_wait_queue(&tty->write_wait, &wait);	set_current_state(TASK_INTERRUPTIBLE);		/* Allocate transmit buffer */	/* sleep until transmit buffer available */			while (!(tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list))) {		schedule();					n_hdlc = tty2n_hdlc (tty);		if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || 		    tty != n_hdlc->tty) {			printk("n_hdlc_tty_write: %p invalid after wait!\n", n_hdlc);			error = -EIO;			break;		}					if (signal_pending(current)) {			error = -EINTR;			break;		}	}	set_current_state(TASK_RUNNING);	remove_wait_queue(&tty->write_wait, &wait);	if (!error) {				/* Retrieve the user's buffer */		memcpy(tbuf->buf, data, count);		/* Send the data */		tbuf->count = error = count;		n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf);		n_hdlc_send_frames(n_hdlc,tty);	}	return error;	}	/* end of n_hdlc_tty_write() *//** * n_hdlc_tty_ioctl - process IOCTL system call for the tty device. * @tty - pointer to tty instance data * @file - pointer to open file object for device * @cmd - IOCTL command code * @arg - argument for IOCTL call (cmd dependent) * * Returns command dependent result. */static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,			    unsigned int cmd, unsigned long arg){	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);	int error = 0;	int count;	unsigned long flags;		if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_tty_ioctl() called %d\n",			__FILE__,__LINE__,cmd);			/* Verify the status of the device */	if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC)		return -EBADF;	switch (cmd) {	case FIONREAD:		/* report count of read data available */		/* in next available frame (if any) */		spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags);		if (n_hdlc->rx_buf_list.head)			count = n_hdlc->rx_buf_list.head->count;		else			count = 0;		spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags);		error = put_user(count, (int __user *)arg);		break;	case TIOCOUTQ:		/* get the pending tx byte count in the driver */		count = tty->driver->chars_in_buffer ?				tty->driver->chars_in_buffer(tty) : 0;		/* add size of next output frame in queue */		spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags);		if (n_hdlc->tx_buf_list.head)			count += n_hdlc->tx_buf_list.head->count;		spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags);		error = put_user(count, (int __user *)arg);		break;	default:		error = n_tty_ioctl (tty, file, cmd, arg);		break;	}	return error;	}	/* end of n_hdlc_tty_ioctl() *//** * n_hdlc_tty_poll - TTY callback for poll system call * @tty - pointer to tty instance data * @filp - pointer to open file object for device * @poll_table - wait queue for operations *  * Determine which operations (read/write) will not block and return info * to caller. * Returns a bit mask containing info on which ops will not block. */static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,				    poll_table *wait){	struct n_hdlc *n_hdlc = tty2n_hdlc (tty);	unsigned int mask = 0;	if (debuglevel >= DEBUG_LEVEL_INFO)			printk("%s(%d)n_hdlc_tty_poll() called\n",__FILE__,__LINE__);			if (n_hdlc && n_hdlc->magic == HDLC_MAGIC && tty == n_hdlc->tty) {		/* queue current process into any wait queue that */		/* may awaken in the future (read and write) */		poll_wait(filp, &tty->read_wait, wait);		poll_wait(filp, &tty->write_wait, wait);		/* set bits for operations that won't block */		if (n_hdlc->rx_buf_list.head)			mask |= POLLIN | POLLRDNORM;	/* readable */		if (test_bit(TTY_OTHER_CLOSED, &tty->flags))			mask |= POLLHUP;		if (tty_hung_up_p(filp))			mask |= POLLHUP;		if (!tty_is_writelocked(tty) &&				n_hdlc->tx_free_buf_list.head)			mask |= POLLOUT | POLLWRNORM;	/* writable */	}	return mask;}	/* end of n_hdlc_tty_poll() *//** * n_hdlc_alloc - allocate an n_hdlc instance data structure * * Returns a pointer to newly created structure if success, otherwise %NULL */static struct n_hdlc *n_hdlc_alloc(void){	struct n_hdlc_buf *buf;	int i;	struct n_hdlc *n_hdlc = kmalloc(sizeof(*n_hdlc), GFP_KERNEL);	if (!n_hdlc)		return NULL;	memset(n_hdlc, 0, sizeof(*n_hdlc));	n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list);	n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list);	n_hdlc_buf_list_init(&n_hdlc->rx_buf_list);	n_hdlc_buf_list_init(&n_hdlc->tx_buf_list);		/* allocate free rx buffer list */	for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) {		buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL);		if (buf)			n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,buf);		else if (debuglevel >= DEBUG_LEVEL_INFO)				printk("%s(%d)n_hdlc_alloc(), kalloc() failed for rx buffer %d\n",__FILE__,__LINE__, i);	}		/* allocate free tx buffer list */	for(i=0;i<DEFAULT_TX_BUF_COUNT;i++) {		buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL);		if (buf)			n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,buf);		else if (debuglevel >= DEBUG_LEVEL_INFO)				printk("%s(%d)n_hdlc_alloc(), kalloc() failed for tx buffer %d\n",__FILE__,__LINE__, i);	}		/* Initialize the control block */	n_hdlc->magic  = HDLC_MAGIC;	n_hdlc->flags  = 0;		return n_hdlc;	}	/* end of n_hdlc_alloc() *//** * n_hdlc_buf_list_init - initialize specified HDLC buffer list * @list - pointer to buffer list */static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list){	memset(list, 0, sizeof(*list));	spin_lock_init(&list->spinlock);}	/* end of n_hdlc_buf_list_init() *//** * n_hdlc_buf_put - add specified HDLC buffer to tail of specified list * @list - pointer to buffer list * @buf	- pointer to buffer */static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,			   struct n_hdlc_buf *buf){	unsigned long flags;	spin_lock_irqsave(&list->spinlock,flags);		buf->link=NULL;	if (list->tail)		list->tail->link = buf;	else		list->head = buf;	list->tail = buf;	(list->count)++;		spin_unlock_irqrestore(&list->spinlock,flags);	}	/* end of n_hdlc_buf_put() *//** * n_hdlc_buf_get - remove and return an HDLC buffer from list * @list - pointer to HDLC buffer list *  * Remove and return an HDLC buffer from the head of the specified HDLC buffer * list. * Returns a pointer to HDLC buffer if available, otherwise %NULL. */static struct n_hdlc_buf* n_hdlc_buf_get(struct n_hdlc_buf_list *list){	unsigned long flags;	struct n_hdlc_buf *buf;	spin_lock_irqsave(&list->spinlock,flags);		buf = list->head;	if (buf) {		list->head = buf->link;		(list->count)--;	}	if (!list->head)		list->tail = NULL;		spin_unlock_irqrestore(&list->spinlock,flags);	return buf;	}	/* end of n_hdlc_buf_get() */static char hdlc_banner[] __initdata =	KERN_INFO "HDLC line discipline: version " HDLC_VERSION	", maxframe=%u\n";static char hdlc_register_ok[] __initdata =	KERN_INFO "N_HDLC line discipline registered.\n";static char hdlc_register_fail[] __initdata =	KERN_ERR "error registering line discipline: %d\n";static char hdlc_init_fail[] __initdata =	KERN_INFO "N_HDLC: init failure %d\n";static int __init n_hdlc_init(void){	int status;	/* range check maxframe arg */	if (maxframe < 4096)		maxframe = 4096;	else if (maxframe > 65535)		maxframe = 65535;	printk(hdlc_banner, maxframe);	status = tty_register_ldisc(N_HDLC, &n_hdlc_ldisc);	if (!status)		printk(hdlc_register_ok);	else		printk(hdlc_register_fail, status);	if (status)		printk(hdlc_init_fail, status);	return status;	}	/* end of init_module() */static char hdlc_unregister_ok[] __exitdata =	KERN_INFO "N_HDLC: line discipline unregistered\n";static char hdlc_unregister_fail[] __exitdata =	KERN_ERR "N_HDLC: can't unregister line discipline (err = %d)\n";static void __exit n_hdlc_exit(void){	/* Release tty registration of line discipline */	int status = tty_unregister_ldisc(N_HDLC);	if (status)		printk(hdlc_unregister_fail, status);	else		printk(hdlc_unregister_ok);}module_init(n_hdlc_init);module_exit(n_hdlc_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Paul Fulghum paulkf@microgate.com");module_param(debuglevel, int, 0);module_param(maxframe, int, 0);MODULE_ALIAS_LDISC(N_HDLC);

⌨️ 快捷键说明

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