+/*
+ * 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 int
+ppp_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 int
+ppp_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;
+}
+