📄 ppp.c
字号:
ppp->tty = ppp->backup_tty; if (ppp_tty_push(ppp)) ppp_output_wakeup(ppp); wake_up_interruptible(&ppp->read_wait); } else { ppp->tty = 0; ppp->sc_xfer = 0; if (ppp->flags & SC_DEBUG) printk(KERN_INFO "ppp: channel %s closing.\n", ppp2dev(ppp)->name); ppp_async_release(ppp); ppp_release(ppp); MOD_DEC_USE_COUNT; }}/* * Read a PPP frame from the rcv_q list, * waiting if necessary */static rw_ret_tppp_tty_read(struct tty_struct *tty, struct file *file, __u8 * buf, rw_count_t nr){ struct ppp *ppp = tty2ppp (tty); struct sk_buff *skb; rw_ret_t len, err; /* * Validate the pointers */ if (!ppp) return -EIO; CHECK_PPP(-ENXIO); /* * Before we attempt to write the frame to the user, ensure that the * user has access to the pages for the total buffer length. */ err = verify_area(VERIFY_WRITE, buf, nr); if (err != 0) return (err); /* * Wait for a frame to arrive if necessary. * We increment the module use count so that the module * can't go away while we're sleeping. */ MOD_INC_USE_COUNT; skb = NULL; for (;;) { ppp = tty2ppp(tty); err = 0; if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse || tty != ppp->tty) break; skb = skb_dequeue(&ppp->rcv_q); if (skb != 0) break; /* * If no frame is available, return -EAGAIN or wait. */ err = -EAGAIN; if (file->f_flags & O_NONBLOCK) break; interruptible_sleep_on(&ppp->read_wait); err = -EINTR; if (signal_pending(current)) break; } MOD_DEC_USE_COUNT; if (skb == 0) return err; /* * Ensure that the frame will fit within the caller's buffer. * If not, just discard the frame. */ len = skb->len; if (len > nr) { if (ppp->flags & SC_DEBUG) printk(KERN_DEBUG "ppp: read of %lu bytes too small for %ld " "frame\n", (unsigned long) nr, (long) len); ppp->stats.ppp_ierrors++; err = -EOVERFLOW; goto out; } /* * Copy the received data from the buffer to the caller's area. */ err = len; if (COPY_TO_USER(buf, skb->data, len)) err = -EFAULT;out: KFREE_SKB(skb); return err;}/* * Writing to a tty in ppp line discipline sends a PPP frame. * Used by pppd to send control packets (LCP, etc.). */static rw_ret_tppp_tty_write(struct tty_struct *tty, struct file *file, const __u8 * data, rw_count_t count){ struct ppp *ppp = tty2ppp (tty); __u8 *new_data; struct sk_buff *skb; /* * Verify the pointers. */ if (!ppp) return -EIO; if (ppp->magic != PPP_MAGIC) return -EIO; CHECK_PPP(-ENXIO); /* * Ensure that the caller does not wish to send too much. */ if (count > PPP_MTU + PPP_HDRLEN) { if (ppp->flags & SC_DEBUG) printk(KERN_WARNING "ppp_tty_write: truncating user packet " "from %lu to mtu %d\n", (unsigned long) count, PPP_MTU + PPP_HDRLEN); count = PPP_MTU + PPP_HDRLEN; } /* * Allocate a buffer for the data and fetch it from the user space. */ skb = alloc_skb(count, GFP_KERNEL); if (skb == NULL) { printk(KERN_ERR "ppp_tty_write: no memory\n"); return 0; } LIBERATE_SKB(skb); new_data = skb_put(skb, count); /* * Retrieve the user's buffer */ if (COPY_FROM_USER(new_data, data, count)) { KFREE_SKB(skb); return -EFAULT; } /* * Send the frame */ ppp_send_ctrl(ppp, skb); return (rw_ret_t) count;}/* * Process the IOCTL call for the tty device. * Only the ioctls that relate to using ppp on async serial lines * are processed here; the rest are handled by ppp_ioctl. */static intppp_tty_ioctl (struct tty_struct *tty, struct file * file, unsigned int param2, unsigned long param3){ struct ppp *ppp = tty2ppp (tty); register int temp_i = 0; int error = -EFAULT; /* * Verify the status of the PPP device. */ if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse) return -ENXIO; /* * The user must have an euid of root to do these requests. */ if (!SUSER()) return -EPERM; switch (param2) { case PPPIOCGASYNCMAP: /* * Retrieve the transmit async map */ if (PUT_USER(ppp->xmit_async_map[0], (int *) param3)) break; error = 0; break; case PPPIOCSASYNCMAP: /* * Set the transmit async map */ if (GET_USER(temp_i, (int *) param3)) break; ppp->xmit_async_map[0] = temp_i; if (ppp->flags & SC_DEBUG) printk(KERN_INFO "ppp_tty_ioctl: set xmit asyncmap %x\n", ppp->xmit_async_map[0]); error = 0; break; case PPPIOCSRASYNCMAP: /* * Set the receive async map */ if (GET_USER(temp_i, (int *) param3)) break; ppp->recv_async_map = temp_i; if (ppp->flags & SC_DEBUG) printk(KERN_INFO "ppp_tty_ioctl: set rcv asyncmap %x\n", ppp->recv_async_map); error = 0; break; case PPPIOCGXASYNCMAP: /* * Get the map of characters to be escaped on transmission. */ if (COPY_TO_USER((void *) param3, ppp->xmit_async_map, sizeof (ppp->xmit_async_map))) break; error = 0; break; case PPPIOCSXASYNCMAP: /* * Set the map of characters to be escaped on transmission. */ { __u32 temp_tbl[8]; if (COPY_FROM_USER(temp_tbl, (void *) param3, sizeof (temp_tbl))) break; temp_tbl[1] = 0x00000000; temp_tbl[2] &= ~0x40000000; temp_tbl[3] |= 0x60000000; memcpy(ppp->xmit_async_map, temp_tbl, sizeof (ppp->xmit_async_map)); if (ppp->flags & SC_DEBUG) printk(KERN_INFO "ppp_tty_ioctl: set xasyncmap\n"); error = 0; } break; case PPPIOCXFERUNIT: /* * Set up this PPP unit to be used next time this * process sets a tty to PPP line discipline. */ ppp->backup_tty = tty; ppp->sc_xfer = current->pid; error = 0; break; case TCGETS: case TCGETA: /* * Allow users to read, but not set, the serial port parameters */ error = n_tty_ioctl (tty, file, param2, param3); break; case TCFLSH: /* * Flush our buffers, then call the generic code to * flush the serial port's buffer. */ if (param3 == TCIFLUSH || param3 == TCIOFLUSH) { struct sk_buff *skb; while ((skb = skb_dequeue(&ppp->rcv_q)) != NULL) KFREE_SKB(skb); } if (param3 == TCIOFLUSH || param3 == TCOFLUSH) ppp_tty_flush_output(ppp); error = n_tty_ioctl (tty, file, param2, param3); break; case FIONREAD: /* * Returns how many bytes are available for a read(). */ { unsigned long flags; struct sk_buff *skb; int count = 0; save_flags(flags); cli(); skb = skb_peek(&ppp->rcv_q); if (skb != 0) count = skb->len; restore_flags(flags); if (PUT_USER(count, (int *) param3)) break; error = 0; } break; default: /* * All other ioctl() events will come here. */ error = ppp_ioctl(ppp, param2, param3); break; } return error;}/* * TTY callback. * * Process the select() or poll() statement for the PPP device. */#if LINUX_VERSION_CODE < VERSION(2,1,23)static intppp_tty_select(struct tty_struct *tty, struct inode *inode, struct file *filp, int sel_type, select_table * wait){ struct ppp *ppp = tty2ppp(tty); int result = 1; /* * Verify the status of the PPP device. */ if (!ppp || tty != ppp->tty) return -EBADF; CHECK_PPP(-EBADF); switch (sel_type) { case SEL_IN: /* The fd is readable if the receive queue isn't empty. */ if (skb_peek(&ppp->rcv_q) != NULL) break; /* fall through */ case SEL_EX: /* Check for exceptions or read errors. */ /* Is this a pty link and the remote disconnected? */ if (tty->flags & (1 << TTY_OTHER_CLOSED)) break; /* Is this a local link and the modem disconnected? */ if (tty_hung_up_p (filp)) break; select_wait(&ppp->read_wait, wait); result = 0; break; case SEL_OUT: /* The fd is always writable. */ break; } return result;}#else /* 2.1.23 or later */static unsigned intppp_tty_poll(struct tty_struct *tty, struct file *filp, poll_table * wait){ struct ppp *ppp = tty2ppp(tty); unsigned int mask = 0; if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) { CHECK_PPP(0); poll_wait(filp, &ppp->read_wait, wait); if (skb_peek(&ppp->rcv_q) != NULL) mask |= POLLIN | POLLRDNORM; if (tty->flags & (1 << TTY_OTHER_CLOSED) || tty_hung_up_p(filp)) mask |= POLLHUP; mask |= POLLOUT | POLLWRNORM; } return mask;}#endif /* >= 2.1.23 *//* * This function is called by the tty driver when the transmit buffer has * additional space. It is used by the ppp code to continue to transmit * the current buffer should the buffer have been partially sent. */static voidppp_tty_wakeup (struct tty_struct *tty){ struct ppp *ppp = tty2ppp (tty); tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); if (!ppp) return; CHECK_PPP_VOID(); if (tty != ppp->tty) return; if (ppp_tty_push(ppp)) ppp_output_wakeup(ppp);}/* * Send a packet to the peer over a synchronous tty line. * All encoding and FCS are handled by hardware. * Addr/Ctrl and Protocol field compression implemented. * Returns -1 iff the packet could not be accepted at present, * 0 if the packet was accepted but we can't accept another yet, or * 1 if we can accept another packet immediately. * If this procedure returns 0, ppp_output_wakeup will be called * exactly once. */static intppp_sync_send(struct ppp *ppp, struct sk_buff *skb){ unsigned char *data; int islcp; CHECK_PPP(0); if (ppp->tpkt != NULL) return -1; ppp->tpkt = skb; data = ppp->tpkt->data; /* * LCP packets with code values between 1 (configure-reqest) * and 7 (code-reject) must be sent as though no options * had been negotiated. */ islcp = PPP_PROTOCOL(data) == PPP_LCP && 1 <= data[PPP_HDRLEN] && data[PPP_HDRLEN] <= 7; /* only reset idle time for data packets */ if (PPP_PROTOCOL(data) < 0x8000) ppp->last_xmit = jiffies; ++ppp->stats.ppp_opackets; ppp->stats.ppp_ooctects += ppp->tpkt->len; if ( !(data[2]) && (ppp->flags & SC_COMP_PROT) ) { /* compress protocol field */ data[2] = data[1]; data[1] = data[0]; skb_pull(ppp->tpkt,1); data = ppp->tpkt->data; } /* * Do address/control compression */ if ((ppp->flags & SC_COMP_AC) && !islcp && PPP_ADDRESS(data) == PPP_ALLSTATIONS && PPP_CONTROL(data) == PPP_UI) { /* strip addr and control field */ skb_pull(ppp->tpkt,2); } return ppp_tty_sync_push(ppp);}/* * Push a synchronous frame out to the tty. * Returns 1 if frame accepted (or discarded), 0 otherwise. */static intppp_tty_sync_push(struct ppp *ppp){ int sent; struct tty_struct *tty = ppp2tty(ppp); unsigned long flags; CHECK_PPP(0); if (ppp->tpkt == NULL) return 0; /* prevent reentrancy with tty_pushing flag */ save_flags(flags); cli(); if (ppp->tty_pushing) { /* record wakeup attempt so we don't lose */ /* a wakeup call while doing push processing */ ppp->woke_up=1; restore_flags(flags); return 0; } ppp->tty_pushing = 1; restore_flags(flags); if (tty == NULL || tty->disc_data != (void *) ppp) goto flush; for(;;){ ppp->woke_up=0; /* Note: Sync driver accepts complete frame or nothing */ tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); sent = tty->driver.write(tty, 0, ppp->tpkt->data, ppp->tpkt->len); if (sent < 0) { /* write error (possible loss of CD) */ /* record error and discard current packet */ ppp->stats.ppp_oerrors++; break; } ppp->stats.ppp_obytes += sent; if (sent < ppp->tpkt->len) { /* driver unable to accept frame just yet */ save_flags(flags); cli(); if (ppp->woke_up) { /* wake up called while processing */ /* try to send the frame again */ restore_flags(flags); continue; } /* wait for wakeup callback to try send again */ ppp->tty_pushing = 0; restore_flags(flags); return 0; } break; }flush: /* done with current packet (sent or discarded) */ KFREE_SKB(ppp->tpkt); ppp->tpkt = 0; ppp->tty_pushing = 0; return 1;}/* * Send a packet to the peer over an async tty line. * Returns -1 iff the packet could not be accepted at present, * 0 if the packet was accepted but we can't accept another yet, or * 1 if we can accept another packet immediately.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -