n_hdlc.c
字号:
* 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 + -