📄 n_hdlc.c
字号:
* * Arguments: tty pointer to tty isntance data * data pointer to received data * flags pointer to flags for data * count count of received data in bytes * * Return Value: None */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 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 = (N_HDLC_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 (&n_hdlc->read_wait); wake_up_interruptible (&n_hdlc->poll_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 retreive one frame of data (if available) * * Arguments: * * tty pointer to tty instance data * file pointer to open file object * buf pointer to returned data buffer * nr size of returned data buffer * * Return Value: * * Number of bytes returned or error code */static rw_ret_t n_hdlc_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf, rw_count_t nr){ struct n_hdlc *n_hdlc = tty2n_hdlc(tty); int error; rw_ret_t ret; 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 */ error = verify_area (VERIFY_WRITE, buf, nr); if (error != 0) { printk(KERN_WARNING"%s(%d) n_hdlc_tty_read() can't verify user " "buffer\n",__FILE__,__LINE__); return (error); } for (;;) { 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 (&n_hdlc->read_wait); if (signal_pending(current)) return -EINTR; } if (rbuf->count > nr) { /* frame too large for caller's buffer (discard frame) */ ret = (rw_ret_t)-EOVERFLOW; } else { /* Copy the data to the caller's buffer */ COPY_TO_USER(error,buf,rbuf->buf,rbuf->count); if (error) ret = (rw_ret_t)error; else ret = (rw_ret_t)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 * * Arguments: 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 * * Return Value: number of bytes written (or error code) */static rw_ret_t n_hdlc_tty_write (struct tty_struct *tty, struct file *file, const __u8 * data, rw_count_t count){ struct n_hdlc *n_hdlc = tty2n_hdlc (tty); int error = 0; DECLARE_WAITQUEUE(wait, current); N_HDLC_BUF *tbuf; if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d)n_hdlc_tty_write() called count=%d\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(&n_hdlc->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(&n_hdlc->write_wait, &wait); if (!error) { /* Retrieve the user's buffer */ COPY_FROM_USER (error, tbuf->buf, data, count); if (error) { /* return tx buffer to free list */ n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,tbuf); } else { /* 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. * * Arguments: * * 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) * * Return Value: Command dependent */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); PUT_USER (error, count, (int *) 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); PUT_USER (error, count, (int*)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. Determine which * operations (read/write) will not block and return * info to caller. * * Arguments: * * tty pointer to tty instance data * filp pointer to open file object for device * poll_table wait queue for operations * * Return Value: * * 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, &n_hdlc->poll_wait, wait); /* set bits for operations that wont block */ if(n_hdlc->rx_buf_list.head) mask |= POLLIN | POLLRDNORM; /* readable */ if(tty->flags & (1 << TTY_OTHER_CLOSED)) mask |= POLLHUP; if(tty_hung_up_p(filp)) mask |= POLLHUP; if(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 * * Arguments: None * Return Value: pointer to structure if success, otherwise 0 */static struct n_hdlc *n_hdlc_alloc (void){ struct n_hdlc *n_hdlc; N_HDLC_BUF *buf; int i; n_hdlc = (struct n_hdlc *)kmalloc(sizeof(struct n_hdlc), GFP_KERNEL); if (!n_hdlc) return 0; 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 = (N_HDLC_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 = (N_HDLC_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; init_waitqueue_head(&n_hdlc->read_wait); init_waitqueue_head(&n_hdlc->poll_wait); init_waitqueue_head(&n_hdlc->write_wait); return n_hdlc; } /* end of n_hdlc_alloc() *//* n_hdlc_buf_list_init() * * initialize specified HDLC buffer list * * Arguments: list pointer to buffer list * Return Value: None */void n_hdlc_buf_list_init(N_HDLC_BUF_LIST *list){ memset(list,0,sizeof(N_HDLC_BUF_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 * * Arguments: * * list pointer to buffer list * buf pointer to buffer * * Return Value: None */void n_hdlc_buf_put(N_HDLC_BUF_LIST *list,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 the * head of the specified HDLC buffer list * * Arguments: * * list pointer to HDLC buffer list * * Return Value: * * pointer to HDLC buffer if available, otherwise NULL */N_HDLC_BUF* n_hdlc_buf_get(N_HDLC_BUF_LIST *list){ unsigned long flags; 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 int __init n_hdlc_init(void){ static struct tty_ldisc n_hdlc_ldisc; int status; /* range check maxframe arg */ if ( maxframe<4096) maxframe=4096; else if ( maxframe>65535) maxframe=65535; printk("HDLC line discipline: version %s, maxframe=%u\n", szVersion, maxframe); /* Register the tty discipline */ memset(&n_hdlc_ldisc, 0, sizeof (n_hdlc_ldisc)); n_hdlc_ldisc.magic = TTY_LDISC_MAGIC; n_hdlc_ldisc.name = "hdlc"; n_hdlc_ldisc.open = n_hdlc_tty_open; n_hdlc_ldisc.close = n_hdlc_tty_close; n_hdlc_ldisc.read = n_hdlc_tty_read; n_hdlc_ldisc.write = n_hdlc_tty_write; n_hdlc_ldisc.ioctl = n_hdlc_tty_ioctl; n_hdlc_ldisc.poll = n_hdlc_tty_poll; n_hdlc_ldisc.receive_room = n_hdlc_tty_room; n_hdlc_ldisc.receive_buf = n_hdlc_tty_receive; n_hdlc_ldisc.write_wakeup = n_hdlc_tty_wakeup; status = tty_register_ldisc(N_HDLC, &n_hdlc_ldisc); if (!status) printk (KERN_INFO"N_HDLC line discipline registered.\n"); else printk (KERN_ERR"error registering line discipline: %d\n",status); if (status) printk(KERN_INFO"N_HDLC: init failure %d\n", status); return (status); } /* end of init_module() */static void __exit n_hdlc_exit(void){ int status; /* Release tty registration of line discipline */ if ((status = tty_register_ldisc(N_HDLC, NULL))) printk("N_HDLC: can't unregister line discipline (err = %d)\n", status); else printk("N_HDLC: line discipline unregistered\n");}module_init(n_hdlc_init);module_exit(n_hdlc_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -