#include "f_module.h" #include "primitives.h" #include "streams.h" #include "kernel.h" #ifndef linux #include #endif #include "x75lib.h" #include "streamlib.h" #include "lap.h" #include "f_malloc.h" #ifndef KERNEL #include "kernel.h" #endif #ifdef linux #ifdef KERNEL #include #else #define jiffies 0 #endif #else #define jiffies 0 #endif /** ** X75 / X25 / Q921 support library. **/ /* * Warning: This code is implemented along the lines of the Q.921 specs and * SDL/GR diagrams. As such, it's a little redundant, but much easier to * verify. GCC 2 does pretty good optimizing work with it. * * NB: The code was lots terser a few hours ago, before I decided to run it * through indent... */ /* * Timeout prototypes */ static void x75_T1 (x75 state); static void x75_T3 (x75 state); static char *x75_sname[]= {"S_free", "S_down", "S_await_up", "S_await_down", "S_up", "S_recover",}; /* * State change. */ static void x75_setstate (x75 state, x75_status status) { if (state->debug & 0x02) printf ("%sx75.%d Setstate %d/%s -> %d/%s\n", KERN_DEBUG,state->debugnr, state->status, x75_sname[state->status], status, x75_sname[status]); if(state->status != S_free) { state->status = status; if(state->status == S_down) state->errors = 0; } } /* * Macros for timeouts */ #ifdef OLD_TIMEOUT #define stop_T(xx,er) do { \ int _ms = splstr(); \ if(state->T##xx) { \ state->T##xx = 0; \ if(state->debug & 0x08) \ printf("%sStop%d T"#xx" %d\n",KERN_DEBUG,state->debugnr,__LINE__); \ untimeout((void *)x75_T##xx,state); \ } splx(_ms); \ (er)=0; \ } while(0) #define start_T(xx,er) do { \ int _ms = splstr(); \ if(! state->T##xx) { \ state->T##xx = 1; \ if(state->debug & 0x08) \ printf("%sStart%d T"#xx" %d %d\n",KERN_DEBUG,state->debugnr,state->RUN_T##xx, __LINE__); \ timeout((void *)x75_T##xx,state,(state->RUN_T##xx * HZ) / 10); \ } splx(_ms); \ (er)=0; \ } while(0) #define restart_T(xx,er) do { \ int _ms = splstr(); \ if(state->T##xx) \ untimeout((void *)x75_T##xx,state); \ state->T##xx = 1; \ if(state->debug & 0x08) \ printf("%sRestart%d T"#xx" %d %d\n",KERN_DEBUG,state->debugnr,state->RUN_T##xx, __LINE__); \ timeout((void *)x75_T##xx,state,(state->RUN_T##xx * HZ) / 10); \ splx(_ms); \ } while(0) #else /* NEW_TIMEOUT */ #define stop_T(xx,er) do { \ int _ms = splstr(); \ if(state->T##xx) { \ state->T##xx = 0; \ if(state->debug & 0x08) \ printf("%sStop%d T"#xx" %d\n",KERN_DEBUG,state->debugnr,__LINE__); \ untimeout(state->timer_T##xx); \ } splx(_ms); \ (er)=0; \ } while(0) #define start_T(xx,er) do { \ int _ms = splstr(); \ if(! state->T##xx) { \ state->T##xx = 1; \ if(state->debug & 0x08) \ printf("%sStart%d T"#xx" %d %d\n",KERN_DEBUG,state->debugnr,state->RUN_T##xx,__LINE__); \ state->timer_T##xx = timeout((void *)x75_T##xx,state,(state->RUN_T##xx * HZ) / 10); \ } splx(_ms); \ (er)=0; \ } while(0) #define restart_T(xx,er) do { \ int _ms = splstr(); \ if(state->T##xx) \ untimeout(state->timer_T##xx); \ state->T##xx = 1; \ if(state->debug & 0x08) \ printf("%sRestart%d T"#xx" %d %d\n",KERN_DEBUG,state->debugnr,state->RUN_T##xx,__LINE__); \ state->timer_T##xx = timeout((void *)x75_T##xx,state,(state->RUN_T##xx * HZ) / 10); \ splx(_ms); \ (er)=0; \ } while(0) #endif /* * Send indication up. */ #define msg_up(state,ind,add) (*state->state)(state->ref,ind,add) /* * Clear state machine -- connection down. */ static int kill_me (x75 state, char ind) /* Abort the connection, reset everything */ { int err2 = 0; int ms = splstr (); x75_status oldstate = state->status; S_flush (&state->I); S_flush (&state->UI); x75_setstate(state, S_down); stop_T (1, err2); stop_T (3, err2); if (ind != 0 && oldstate != S_free && oldstate != S_down) msg_up (state, ind, 0); splx (ms); return 0; } /* * Clear exception conditions. */ static int clr_except (x75 state) { state->RNR = 0; state->sentRR = 1; state->inREJ = 0; state->ack_pend = 0; return 0; } /* * Flush I queue. */ static int flush_I (x75 state) { S_flush (&state->I); state->v_r = state->v_s = state->v_a = 0; if(state->backenable) (*state->backenable) (state->ref); return 0; } /* * Start retransmission. */ static int retransmit (x75 state) { #if 0 if (state->flush != NULL && state->v_s != state->v_a) (*state->flush) (state->ref); #endif state->v_s = state->v_a; return 0; } /* * Send 3-byte header. Actually enqueue only one byte -- the caller is * responsible for attaching the address bytes. However, we preallocate them in * order to go easy on allocb(). */ static int xmit3 (x75 state, char cmd, uchar_t what) { mblk_t *mb; int err; if (state->debug & 0x80) printf ("%sX%d%c%x ", KERN_DEBUG,state->debugnr, cmd ? 'c' : 'r', what); mb = allocb (state->offset + 1, BPRI_HI); if (mb == NULL) { if(state->debug & 0x01) printf("%sNX4 NoMem ",KERN_WARNING); return -ENOENT; } mb->b_rptr += state->offset; mb->b_wptr += state->offset; *mb->b_wptr++ = what; if ((err = (*state->send) (state->ref, cmd, mb)) != 0) freemsg (mb); return err; } /* * Send 4-byte header. Actually enqueue only two bytes -- the caller is * responsible for attaching the address bytes. */ static int xmit4 (x75 state, char cmd, uchar_t what1, uchar_t what2) { mblk_t *mb; int err; if (state->debug & 0x80) printf ("%sX%d%c%x.%x ", KERN_DEBUG,state->debugnr, cmd ? 'c' : 'r', what1, what2); mb = allocb (state->offset + 2, BPRI_HI); if (mb == NULL) { if(state->debug & 0x01) printf("%sNX4 NoMem ",KERN_WARNING); return -ENOENT; } mb->b_rptr += state->offset; mb->b_wptr += state->offset; *mb->b_wptr++ = what1; *mb->b_wptr++ = what2; if ((err = (*state->send) (state->ref, cmd, mb)) != 0) freemsg (mb); return err; } #define establish(s) Xestablish(s,__LINE__) /* * Connection established. */ static int Xestablish (x75 state, int line) { int err, err2; if (state->debug & 0x10) printf ("%sEstablish%d %d\n", KERN_EMERG,state->debugnr, line); if(state->broadcast) { return -ENXIO; } err = clr_except (state); state->RC = 0; x75_setstate(state, S_await_up); if((state->errors += 10) >= 100) { x75_setstate(state, S_down); printf("%sERR_G 1, %d\n",KERN_INFO,state->errors); state->errors = 0; msg_up (state, MDL_ERROR_IND, ERR_G); msg_up (state, DL_RELEASE_IND, 0); x75_setstate(state, S_down); return -ETIMEDOUT; } #if 0 if(!state->wide) { int i; printf("Xestablish %d\n",jiffies); for (i=0;i<64;i++) { printf("%08x ",*(i-1+(unsigned long *)&state)); if(!((i+1) & 0x03)) printf("\n"); } } #endif err2 = xmit3 (state, 1, (state->wide ? L2_SABME : L2_SABM) | L2_PF_U); if (err == 0) err = err2; restart_T (1, err2); if (err == 0) err = err2; stop_T (3, err2); if (err == 0) err = err2; return err; } #define recover_NR(s) Xrecover_NR(s,__LINE__) /* * Reestablish the connection due to lost N_R synchronisation. */ static int Xrecover_NR (x75 state, int line) { int err; if (state->flush != NULL) (*state->flush) (state->ref); printf("%sERR_J 1\n",KERN_INFO); msg_up (state, MDL_ERROR_IND, ERR_J); err = Xestablish (state, line); state->L3_req = 0; return err; } /* * Force sending an enquiry packet (P bit set) */ static int enquiry (x75 state) { int err, err2; if (state->wide) err = xmit4 (state, 1, ((state->sentRR = (state->canrecv == NULL || (*state->canrecv) (state->ref))) ? L2_RR : L2_RNR), (state->v_r << 1) | L2_PF_S); else err = xmit3 (state, 1, ((state->sentRR = (state->canrecv == NULL || (*state->canrecv) (state->ref))) ? L2_RR : L2_RNR) | (state->v_r << 5) | L2_PF); if(err == 0) state->ack_pend = 0; start_T (1, err2); if (err == 0) err = err2; return err; } /* * Respond to an enquiry packet (F bit set) */ static int enq_resp (x75 state) { int err; if (state->wide) err = xmit4 (state, 0, ((state->sentRR = (state->canrecv == NULL || (*state->canrecv) (state->ref))) ? L2_RR : L2_RNR), (state->v_r << 1) | L2_PF_S); else err = xmit3 (state, 0, ((state->sentRR = (state->canrecv == NULL || (*state->canrecv) (state->ref))) ? L2_RR : L2_RNR) | (state->v_r << 5) | L2_PF); if(err == 0) state->ack_pend = 0; return err; } /* * T1 (T201) resends packets because no ack has arrived for them. */ static void x75_T1 (x75 state) { int err2 = 0; state->T1 = 0; if (state->debug & 0x10) printf ("%sT%d.1 %d RC %d\n", KERN_DEBUG,state->debugnr, state->status, state->RC); switch (state->status) { case S_await_up: if (state->RC != 0) { /* temporary kludge */ if (state->RC < state->N1) { state->RC++; if(!state->wide) { #ifdef linux printf("%sXtimeout %ld\n",KERN_DEBUG,jiffies); #endif #if 0 { int i; for (i=0;i<64;i++) { printf("%08x ", *(i-1+(unsigned long *)&state)); if(!((i+1) & 0x03)) printf("\n"); } } #endif } err2 = xmit3 (state, 1, (state->wide ? L2_SABME : L2_SABM) | L2_PF_U); if (err2 == -EAGAIN) state->RC--; start_T (1, err2); } else { flush_I (state); printf("%sERR_G 2, %d\n",KERN_INFO,state->N1); msg_up (state, MDL_ERROR_IND, ERR_G); msg_up (state, DL_RELEASE_IND, 0); x75_setstate(state, S_down); } } else { state->RC = 1; start_T (1, err2); break; } break; case S_up: /* * Implementation decision time. Retransmit the last frame? We choose * not to because we are unable to clear the xmit queue. */ state->RC = 1; enquiry (state); start_T (1, err2); x75_setstate(state, S_recover); break; case S_await_down: if (state->RC < state->N1) { state->RC++; xmit3 (state, 1, L2_DISC | L2_PF_U); start_T (1, err2); } else { printf("%sERR_H 1\n",KERN_INFO); msg_up (state, MDL_ERROR_IND, ERR_H); msg_up (state, DL_RELEASE_CONF, 0); x75_setstate(state, S_down); } break; case S_recover: if (state->RC < state->N1) { enquiry (state); state->RC++; start_T (1, err2); } else { printf("%sERR_I 1 %d\n",KERN_INFO,state->RC); msg_up (state, MDL_ERROR_IND, ERR_I); establish (state); state->L3_req = 0; } break; default:; } x75_check_pending (state, 0); return; } /* * T3/T203 periodically sends an enquiry to make sure that the connection is * still alive. */ static void x75_T3 (x75 state) { state->T3 = 0; if (state->debug & 0x10) printf ("%sT%d.3 %d\n", KERN_DEBUG,state->debugnr, state->status); switch (state->status) { case S_up: x75_setstate(state, S_recover); (void) enquiry (state); /* Errors are handled by retransmission * through T1 */ state->RC = 0; break; default:; } return; } /* * "OOPS" time. The other side sent a bad frame. * * There are some differences between X.25, X.75 and Q.921 in this area, * but given a conforming implementation on the other side this code * should not be executed anyway. (Yeah, right...) */ static int send_FRMR (x75 state, uchar_t pf, uchar_t cntl1, uchar_t cntl2, uchar_t cmd, uchar_t w, uchar_t x, uchar_t y, uchar_t z) { int err = 0; mblk_t *mb = allocb (state->offset + (state->wide ? 6 : 4), BPRI_HI); if (mb == NULL) return -ENOMEM; mb->b_rptr += state->offset; mb->b_wptr += state->offset; *mb->b_wptr++ = L2_FRMR | (pf ? L2_PF : 0); *mb->b_wptr++ = cntl1; if (state->wide) { *mb->b_wptr++ = cntl2; *mb->b_wptr++ = state->v_s << 1; *mb->b_wptr++ = (state->v_r << 1) | (cmd ? 1 : 0); } else { *mb->b_wptr++ = (state->v_r << 5) | (cmd ? 0x10 : 0) | (state->v_s << 1); } *mb->b_wptr++ = (w ? 1 : 0) | (x ? 2 : 0) | (y ? 4 : 0) | (z ? 8 : 0); if ((err = (*state->send) (state->ref, 0, mb)) != 0) freemsg (mb); return err; } /* * Send pending frames. */ #ifdef CONFIG_DEBUG_ISDN int deb_x75_check_pending (const char *deb_file, unsigned int deb_line, x75 state, char fromLow) #else int x75_check_pending (x75 state, char fromLow) #endif { mblk_t *mb, *mb2; char did = 0; int k_now; int err = 0, err2; #if 0 /* def CONFIG_DEBUG_ISDN */ if(state->debug & 1) printf("%sCP%d %s:%d ",KERN_DEBUG,state->debugnr,deb_file,deb_line); #ifdef CONFIG_DEBUG_STREAMS cS_check(deb_file,deb_line,&state->UI,NULL); #endif #else if(state->debug & 1) printf("%sCP%d ",KERN_DEBUG,state->debugnr); #endif if(state->status == S_free) return -ENXIO; while (state->UI.first != NULL && (state->cansend == NULL || (*state->cansend) (state->ref))) { mb2 = S_dequeue (&state->UI); if(mb2 == NULL) break; if( /* XXX */ 0 || DATA_REFS(mb2) > 1 || DATA_START(mb2) > mb2->b_rptr - 1) { mb = allocb (state->offset + 1, BPRI_MED); if (mb == NULL) break; mb->b_rptr += state->offset + 1; mb->b_wptr += state->offset + 1; linkb (mb, mb2); } else mb = mb2; *--mb->b_rptr = L2_UI; if (state->debug & 1) printf ("%sX%dc%x ", KERN_DEBUG,state->debugnr, mb->b_wptr[-1] & 0xFF); if ((err = (*state->send) (state->ref, state->asBroadcast ? 3 : 1, mb)) != 0) { if (err == -EAGAIN) { /* Undo the above */ mb->b_rptr++; mb = pullupm(mb,1); S_requeue (&state->UI, mb); } else freemsg (mb); return 0; } else did ++; } /* * If no connection established, bail out now. If recovering, don't try to * send pending I frames because we're still waiting for an ack. */ if (state->status != S_up) { if((state->I.first != NULL) && state->debug) printf("%sx75.%d: State %d/%s, pending\n",KERN_DEBUG,state->debugnr,state->status,x75_sname[state->status]); if ((state->status == S_await_up) && fromLow) { stop_T(1,err); x75_T1(state); } if (state->status != S_recover) { if(did && state->backenable) (*state->backenable) (state->ref); return -EAGAIN; } } else { did=0; /* * Send frames until queue full or max # of outstanding frames reached. */ k_now = (state->v_s - state->v_a) & (state->wide ? 0x7F : 0x07); /* k_now: Number of sent but unack'd frames. */ while (k_now < state->k && !state->RNR && (state->cansend == NULL || (*state->cansend) (state->ref))) { mb2 = S_nr (&state->I, k_now); if (mb2 == NULL) /* No more work in queue */ break; if( /* XXX */ 0 || DATA_REFS(mb2) > 2 || DATA_START(mb2) > mb2->b_rptr - (state->wide ? 2 : 1)) { int off = state->offset + (state->wide ? 2 : 1); mb = allocb (off, BPRI_HI); if (mb == NULL) break; mb->b_rptr += off; mb->b_wptr += off; linkb(mb,mb2); } else { mb = mb2; } if (state->wide) { *--mb->b_rptr = state->v_r << 1; *--mb->b_rptr = state->v_s << 1; if (state->debug & 1) printf ("%sX%dc%x.%x ", KERN_DEBUG,state->debugnr, mb->b_rptr[0] & 0xFF, mb->b_rptr[1] & 0xFF); } else { *--mb->b_rptr = (state->v_s << 1) | (state->v_r << 5); if (state->debug & 1) printf ("%sX%dc%x ", KERN_DEBUG,state->debugnr, mb->b_rptr[0] & 0xFF); } state->v_s = (state->v_s + 1) & (state->wide ? 0x7F : 0x07); if ((err = (*state->send) (state->ref, 1, mb)) != 0) { freemsg (mb); break; } k_now++; did++; } /* Start T1 if we're now waiting for an ack. */ if (did && !state->T1) { stop_T (3, err); start_T (1, err); } } /* * Send an ack packet if we didn't do it implicitly with a data frame, * above. * * TODO: Delay the ack if we can determine that an immediate ack is * not needed, i.e. if the line delay is lower than (k-1) times the * average(?) frame length. */ if (!state->sentRR) { if (state->canrecv == NULL || (*state->canrecv) (state->ref)) { state->sentRR = 1; did = 0; state->ack_pend = 1; /* Send RR now. This makes sure the if statement, below, fires. */ } } else { if (state->canrecv != NULL && !(*state->canrecv) (state->ref)) state->sentRR = 0; } if (!did && state->ack_pend) { if (state->wide) err2 = xmit4 (state, 0, (state->sentRR ? L2_RR : L2_RNR), state->v_r << 1); else err2 = xmit3 (state, 0, (state->sentRR ? L2_RR : L2_RNR) | (state->v_r << 5)); if(err2 == 0) state->ack_pend = 0; if (err == 0) err = err2; } #if 0 /* def CONFIG_DEBUG_ISDN */ else if(did) printf("%sNX send ",KERN_DEBUG ); else printf("%sNX NoAckPend ",KERN_DEBUG ); #endif /* * Ugly Hack time. Continuously ask the remote side what's going on while * it is on RNR. This is for the benefit of partners who forget to send RR * when they can accept data again. */ if (state->RNR && state->poll && state->trypoll) { if (state->wide) err2 = xmit4 (state, 1, (state->sentRR ? L2_RR : L2_RNR), state->v_r << 1 | L2_PF_S); else err2 = xmit3 (state, 1, (state->sentRR ? L2_RR : L2_RNR) | (state->v_r << 5) | L2_PF); if(err2 == 0) state->ack_pend = 0; if (err == 0) err = err2; } if (state->trypoll) state->trypoll = 0; return err; } /* * Check if the received N_R is reasonable, i.e. between v_a and v_s. */ static int checkV (x75 state, uchar_t n_r) { if ((n_r == state->v_a) && (n_r == state->v_s)) return 1; if (state->debug & 0x08) printf ("%sChk%d %d <= %d <= %d\n",KERN_DEBUG,state->debugnr, state->v_a, n_r, state->v_s); if (state->v_a <= state->v_s) { if (state->v_a <= n_r && n_r <= state->v_s) return 1; } else { if (state->v_a <= n_r || n_r <= state->v_s) return 1; } printf ("\n%s*** X75-%d Sequence error: V_A %d, N_R %d, V_S %d\n",KERN_WARNING, state->debugnr, state->v_a, n_r, state->v_s); return 0; } /* * Deallocate acknowledged frames. */ static int pull_up (x75 state, uchar_t n_r) { int ms; char didsome = (state->v_a != n_r); if (!didsome) return 0; ms = splstr (); while (state->v_a != n_r && state->v_a != state->v_s && state->I.first != NULL) { freemsg (S_dequeue (&state->I)); if(state->errors > 0) --state->errors; state->v_a = (state->v_a + 1) & (state->wide ? 0x7F : 0x07); } if (state->v_a != n_r) { printf ("%sx75.%d consistency problem: v_a %d, n_r %d, v_s %d, nblk %d, firstblk %p\n",KERN_WARNING, state->debugnr, state->v_a, n_r, state->v_s, state->I.nblocks, state->I.first); splx (ms); return -EFAULT; } splx (ms); if (didsome && state->backenable) (*state->backenable) (state->ref); return 0; } /* * Process incoming frames. * * This one's a biggie. Annex B of Q.921 is very helpful if you try to wade * through it all. Turning optimization on (having a compiler with a correct * optimizer may be necessary...) is a good way to make sure that the kernel * likes this code. * * This code went through GNU indent, which unfortunately doubled its * line count... Sometimes, life sucks. ;-) */ int x75_recv (x75 state, char cmd, mblk_t * mb) { uchar_t x1, x2 = 0; char pf = 0; int err = 0, err2; char isbroadcast = (cmd & 2); cmd &= 1; /* * Currently, this code never returns anything other than zero because it * always deallocates the incoming frame, which is because we always mess * around with it. This may or may not be a good idea. I don't like special * code for the first two or three bytes being continuous. Besides, in most * cases the caller deallocates anyway if there is an error. */ if((mb = pullupm (mb, 0)) == NULL) return 0; x1 = *mb->b_rptr++; if (state->debug & 0x80) { if (state->wide) { if ((x1 & L2_m_SU) == L2_is_U) { printf ("%sR%d%c%x ",KERN_DEBUG, state->debugnr, cmd ? 'c' : 'r', x1); } else { if (mb != NULL) printf ("%sR%d%c%x.%x ", KERN_DEBUG,state->debugnr, cmd ? 'c' : 'r', x1, *mb->b_rptr & 0xFF); else printf ("%sR%d.half %x ",KERN_DEBUG, state->debugnr, x1); } } else { printf ("%sR%d%c%x ",KERN_DEBUG, state->debugnr, cmd ? 'c' : 'r', x1); } } mb = pullupm(mb,0); if ((x1 & L2_m_I) == L2_is_I) { /* I frame */ uchar_t n_r, n_s; if (isbroadcast) { /* Can't broadcast I frames! */ if (mb != NULL) freemsg (mb); return /* EINVAL */ 0; } /* Extract N_R, N_S, P/F. */ if (state->wide) { if (mb == NULL) { /* "I frame" without N_R. Not good. */ err2 = send_FRMR (state, pf, x1, x2, cmd, 1, 1, 0, 0); if (err == 0) err = err2; return /* err */ 0; } x2 = *mb->b_rptr++; mb = pullupm (mb, 0); pf = x2 & L2_PF_I; n_s = (x1 >> 1) & 0x7F; n_r = (x2 >> 1) & 0x7F; } else { pf = x1 & L2_PF; x2 = 0; n_s = (x1 >> 1) & 0x07; n_r = (x1 >> 5) & 0x07; } if (!cmd || mb == NULL) { err2 = send_FRMR (state, pf, x1, x2, cmd, 1, 1, 0, 0); if (err == 0) err = err2; if (!cmd) { /* we shall process empty I frames. */ if (mb) freemsg (mb); return /* err */ 0; } } switch (state->status) { case S_up: if ((state->sentRR = (state->canrecv == NULL || (*state->canrecv) (state->ref)))) { /* Room for the packet upstreams? */ if (mb != NULL && n_s == state->v_r) { if ((err2 = (*state->recv) (state->ref, 0, mb)) != 0) { /* Hmmm... Assume I'm not ready after all. */ if (err == 0) err = err2; goto dropit; /* This is ugly, but easiest. */ } else { state->v_r = (state->v_r + 1) & (state->wide ? 0x7f : 0x07); if(state->errors > 0) --state->errors; mb = NULL;/* Accepted, so forget about it here. */ } state->inREJ = 0; if (pf) { /* Want immediate Ack. */ if (state->wide) err2 = xmit4 (state, 0, L2_RR, (state->v_r << 1) | L2_PF_S); else err2 = xmit3 (state, 0, L2_RR | (state->v_r << 5) | L2_PF); if(err2 == 0) state->ack_pend = 0; if (err == 0) err = err2; } else { /* Remember that we have to ack this. */ state->ack_pend = 1; } } else { /* Duplicate or early packet? Tell the other * side to resync. */ if (mb != NULL) { freemsg (mb); mb = NULL; } if (state->inREJ) { /* Don't send more than one REJ; that * would upset the protocol. */ if (pf) { if (state->wide) err2 = xmit4 (state, 0, L2_RR, (state->v_r << 1) | L2_PF_S); else err2 = xmit3 (state, 0, L2_RR | (state->v_r << 5) | L2_PF); if(err2 == 0) state->ack_pend = 0; if (err == 0) err = err2; } } else { /* Send REJ. */ state->inREJ = 1; if (state->wide) err2 = xmit4 (state, 0, L2_REJ, (state->v_r << 1) | (pf ? L2_PF_S : 0)); else err2 = xmit3 (state, 0, L2_REJ | (state->v_r << 5) | (pf ? L2_PF : 0)); if(err2 == 0) state->ack_pend = 0; if (err == 0) err = err2; } } } else { /* Packet not acceptable. Tell them that we * are busy (or something). */ dropit: freemsg (mb); mb = NULL; if (pf) { if (state->wide) err2 = xmit4 (state, 0, L2_RNR, (state->v_r << 1) | L2_PF_S); else err2 = xmit3 (state, 0, L2_RNR | (state->v_r << 5) | L2_PF); if(err2 == 0) state->ack_pend = 0; if (err == 0) err = err2; } } if (checkV (state, n_r)) { /* Packet in range */ if (state->RNR) { /* other side not ready */ err2 = pull_up (state, n_r); if (err == 0) err = err2; } else { if (n_r == state->v_s) { /* Everything ack'd */ err2 = pull_up (state, n_r); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; restart_T (3, err2); if (err == 0) err = err2; } else { if (n_r != state->v_a) { /* Something ack'd */ err2 = pull_up (state, n_r); if (err == 0) err = err2; restart_T (1, err2); if (err == 0) err = err2; } /* Else if nothing ack'd, do nothing. */ } } } else { /* Uh oh. The packet is either totally out of * it, or packets got reordered. Both cases * are seriously bad. */ err2 = recover_NR (state); if (err == 0) err = err2; } break; default:; } /* I frames in other states get dropped */ } else if ((x1 & L2_m_SU) == L2_is_S) { /* S frame */ uchar_t n_r; uchar_t code; if (isbroadcast) { /* No broadcast S frames allowed either. */ if (mb != NULL) freemsg (mb); return /* EINVAL */ 0; } if (state->wide) { /* "I frame" without N_R. Not good. */ if (mb == NULL) { err2 = send_FRMR (state, pf, x1, x2, cmd, 1, 1, 0, 0); if (err == 0) err = err2; return /* err */ 0; } x2 = *mb->b_rptr++; n_r = (x2 >> 1) & 0x7F; code = x1; pf = x2 & L2_PF_S; } else { x2 = 0; n_r = (x1 >> 5) & 0x07; code = x1 & 0x0F; pf = x1 & L2_PF; } mb = pullupm (mb, 0); if (mb != NULL) { /* An S Frame with data field? Huh?? */ err2 = send_FRMR (state, pf, x1, x2, cmd, 1, 1, 0, 0); if (err == 0) err = err2; freemsg (mb); return /* err */ 0; } switch (code) { case L2_RR: state->trypoll = 0; switch (state->status) { case S_up: if (cmd) { if (pf) { err2 = enq_resp (state); if (err == 0) err = err2; } } else { if (pf) { /* This should only happen while in the * S_recover state ... or when doing the * force-poll-while-RNR hack. Yes it _is_ * ugly. I know that. */ if (!(state->RNR && state->poll)) { printf("%sERR_A 1, RNR %d poll %d\n",KERN_INFO,state->RNR,state->poll); err2 = msg_up (state, MDL_ERROR_IND, ERR_A); if (err == 0) err = err2; } } } state->RNR = 0; if (checkV (state, n_r)) { if (n_r == state->v_s) { err2 = pull_up (state, n_r); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; restart_T (3, err2); if (err == 0) err = err2; } else { if (n_r != state->v_a) { err2 = pull_up (state, n_r); if (err == 0) err = err2; restart_T (1, err2); if (err == 0) err = err2; } } } else { err2 = recover_NR (state); if (err == 0) err = err2; } break; case S_recover: state->RNR = 0; if (cmd) { if (pf) { err2 = enq_resp (state); if (err == 0) err = err2; } if (checkV (state, n_r)) { err2 = pull_up (state, n_r); if (err == 0) err = err2; } else { err2 = recover_NR (state); if (err == 0) err = err2; } } else { if (pf) { if (checkV (state, n_r)) { err2 = pull_up (state, n_r); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; start_T (3, err2); if (err == 0) err = err2; err2 = retransmit (state); if (err == 0) err = err2; x75_setstate(state, S_up); } else { err2 = recover_NR (state); if (err == 0) err = err2; } } else { if (checkV (state, n_r)) { err2 = pull_up (state, n_r); if (err == 0) err = err2; } else { err2 = recover_NR (state); if (err == 0) err = err2; } } } break; default:; } break; case L2_RNR: state->trypoll = !pf; switch (state->status) { case S_up: if (cmd) { if (pf) { err2 = enq_resp (state); if (err == 0) err = err2; } } else { if (pf) { if (!(state->poll && state->RNR)) { printf("%sERR_A 2\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_A); if (err == 0) err = err2; } } } state->RNR = 1; if (checkV (state, n_r)) { err2 = pull_up (state, n_r); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; restart_T (3, err2); if (err == 0) err = err2; } else { err2 = recover_NR (state); if (err == 0) err = err2; } break; case S_recover: state->RNR = 1; if (cmd) { if (pf) { err2 = enq_resp (state); if (err == 0) err = err2; } if (checkV (state, n_r)) { err2 = pull_up (state, n_r); if (err == 0) err = err2; } else { err2 = recover_NR (state); if (err == 0) err = err2; } } else { if (pf) { if (checkV (state, n_r)) { err2 = pull_up (state, n_r); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; start_T (3, err2); if (err == 0) err = err2; err2 = retransmit (state); if (err == 0) err = err2; x75_setstate(state, S_up); } else { err2 = recover_NR (state); if (err == 0) err = err2; } } else { if (checkV (state, n_r)) { err2 = pull_up (state, n_r); if (err == 0) err = err2; } else { err2 = recover_NR (state); if (err == 0) err = err2; } } } break; default:; } break; case L2_REJ: state->trypoll = 0; switch (state->status) { case S_up: if (cmd) { if (pf) { err2 = enq_resp (state); if (err == 0) err = err2; } } else { if (pf) { if (!(state->poll && state->RNR)) { printf("%sERR_A 3\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_A); if (err == 0) err = err2; } } } state->RNR = 0; if (checkV (state, n_r)) { err2 = pull_up (state, n_r); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; start_T (3, err2); if (err == 0) err = err2; err2 = retransmit (state); if (err == 0) err = err2; } else { err2 = recover_NR (state); if (err == 0) err = err2; } break; case S_recover: state->RNR = 0; if (cmd) { if (pf) { err2 = enq_resp (state); if (err == 0) err = err2; } if (checkV (state, n_r)) { err2 = pull_up (state, n_r); if (err == 0) err = err2; } else { err2 = recover_NR (state); if (err == 0) err = err2; } } else { if (pf) { if (checkV (state, n_r)) { err2 = pull_up (state, n_r); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; start_T (3, err2); if (err == 0) err = err2; err2 = retransmit (state); if (err == 0) err = err2; x75_setstate(state, S_up); } else { err2 = recover_NR (state); if (err == 0) err = err2; } } else { if (checkV (state, n_r)) { err2 = pull_up (state, n_r); if (err == 0) err = err2; } else { err2 = recover_NR (state); if (err == 0) err = err2; } } } break; default:; } break; default: err2 = send_FRMR (state, pf, x1, x2, cmd, 1, 0, 0, 0); if (err == 0) err = err2; printf("%sERR_L 1\n",KERN_INFO); err2 = msg_up (state, MDL_ERROR_IND, ERR_L); if (err == 0) err = err2; break; } } else { /* U frame */ uchar_t code; if (state->wide) { pf = (x1 & L2_PF_U); code = x1 & ~L2_PF_U; } else { pf = (x1 & L2_PF); code = x1 & ~L2_PF; } if (isbroadcast && (code != L2_UI || !cmd)) { if (mb != NULL) freemsg (mb); return /* EINVAL */ 0; } #define L2__CMD 0x100 /* Out of range -- makes for a simpler case * statement */ switch (code | (cmd ? L2__CMD : 0)) { case L2_SABME | L2__CMD: case L2_SABM | L2__CMD: if (mb != NULL) { err2 = send_FRMR (state, pf, x1, 0, cmd, 1, 1, 0, 0); if (err == 0) err = err2; printf("%sERR_N 1\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_N); if (err == 0) err = err2; break; } if ((code == L2_SABME) != (state->wide != 0)) { /* Configured extended mode, got normal mode, or vice versa */ printf("%sERR_? 1\n", KERN_INFO ); err2 = send_FRMR (state, pf, x1, 0, cmd, 1, 0, 0, 0); if (err == 0) err = err2; break; } switch (state->status) { case S_down: if(state->broadcast) { err2 = xmit3 (state, 0, L2_DM | (pf ? L2_PF : 0)); if (err == 0) err = err2; err2 = clr_except (state); if (err == 0) err = err2; } else { err2 = xmit3 (state, 0, L2_UA | (pf ? L2_PF : 0)); if (err == 0) err = err2; err2 = clr_except (state); if (err == 0) err = err2; err2 = msg_up (state, DL_ESTABLISH_IND, 0); if (err == 0) err = err2; err2 = flush_I (state); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; start_T (3, err2); if (err == 0) err = err2; x75_setstate(state, S_up); if(state->backenable) (*state->backenable) (state->ref); } break; case S_await_up: err2 = xmit3 (state, 0, L2_UA | (pf ? L2_PF : 0)); if (err == 0) err = err2; break; case S_await_down: err2 = xmit3 (state, 0, L2_DM | (pf ? L2_PF : 0)); if (err == 0) err = err2; break; case S_up: case S_recover: err2 = xmit3 (state, 0, L2_UA | (pf ? L2_PF : 0)); if (err == 0) err = err2; err2 = clr_except (state); if (err == 0) err = err2; printf("%sERR_F 1\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_F); if (err == 0) err = err2; if (state->v_s != state->v_a) { err2 = flush_I (state); if (err == 0) err = err2; err2 = msg_up (state, DL_ESTABLISH_IND, 0); if (err == 0) err = err2; } else { err2 = flush_I (state); if (err == 0) err = err2; } stop_T (1, err2); if (err == 0) err = err2; start_T (3, err2); if (err == 0) err = err2; x75_setstate(state, S_up); break; case S_free:; } break; case L2_DISC | L2__CMD: if (mb != NULL) { err2 = send_FRMR (state, pf, x1, x2, cmd, 1, 1, 0, 0); if (err == 0) err = err2; printf("%sERR_N 2\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_N); if (err == 0) err = err2; break; } switch (state->status) { case S_down: case S_await_down: err2 = xmit3 (state, 0, L2_UA | (pf ? L2_PF : 0)); if (err == 0) err = err2; break; case S_await_up: err2 = xmit3 (state, 0, L2_DM | (pf ? L2_PF : 0)); if (err == 0) err = err2; break; case S_up: err2 = flush_I (state); if (err == 0) err = err2; err2 = xmit3 (state, 0, L2_UA | (pf ? L2_PF : 0)); if (err == 0) err = err2; err2 = msg_up (state, DL_RELEASE_IND, 0); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; stop_T (3, err2); if (err == 0) err = err2; x75_setstate(state, S_down); break; case S_recover: err2 = flush_I (state); if (err == 0) err = err2; err2 = xmit3 (state, 0, L2_UA | (pf ? L2_PF : 0)); if (err == 0) err = err2; err2 = msg_up (state, DL_RELEASE_IND, 0); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; x75_setstate(state, S_down); break; case S_free:; } break; case L2_DM: if (mb != NULL) { err2 = send_FRMR (state, pf, x1, x2, cmd, 1, 1, 0, 0); if (err == 0) err = err2; printf("%sERR_N 3\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_N); if (err == 0) err = err2; break; } switch (state->status) { case S_down: if (!pf) { err2 = establish (state); if (err == 0) err = err2; state->L3_req = 0; } break; case S_await_up: if (pf) { err2 = flush_I (state); if (err == 0) err = err2; err2 = msg_up (state, DL_RELEASE_IND, 0); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; x75_setstate(state, S_down); } break; case S_await_down: if (pf) { err2 = flush_I (state); if (err == 0) err = err2; err2 = msg_up (state, DL_RELEASE_CONF, 0); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; x75_setstate(state, S_down); } break; case S_up: case S_recover: if (pf) { printf("%sERR_B 1\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_B); if (err == 0) err = err2; } else { printf("%sERR_E 1\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_E); if (err == 0) err = err2; err2 = establish (state); if (err == 0) err = err2; state->L3_req = 0; } break; case S_free:; } break; case L2_UA: if (mb != NULL) { err2 = send_FRMR (state, pf, x1, x2, cmd, 1, 1, 0, 0); if (err == 0) err = err2; printf("%sERR_N\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_N); if (err == 0) err = err2; break; } switch (state->status) { case S_up: case S_down: case S_recover: printf("%sERR_CD 1\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_C | ERR_D); if (err == 0) err = err2; break; case S_await_up: if (pf) { if (state->L3_req) { err2 = msg_up (state, DL_ESTABLISH_CONF, 0); if (err == 0) err = err2; } else if (state->v_s != state->v_a) { err2 = flush_I (state); if (err == 0) err = err2; err2 = msg_up (state, DL_ESTABLISH_IND, 0); if (err == 0) err = err2; } x75_setstate(state, S_up); stop_T (1, err2); if (err == 0) err = err2; start_T (3, err2); if (err == 0) err = err2; state->v_r = state->v_s = state->v_a = 0; if(state->backenable) (*state->backenable) (state->ref); } else { printf("%sERR_D 1\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_D); } if (err == 0) err = err2; break; case S_await_down: if (pf) { err2 = msg_up (state, DL_RELEASE_CONF, 0); if (err == 0) err = err2; stop_T (1, err2); if (err == 0) err = err2; x75_setstate(state, S_down); } else { printf("%sERR_D 2\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_D); } if (err == 0) err = err2; break; case S_free:; } break; case L2_UI | L2__CMD: if (mb == NULL) { /* Missing data. */ if (!isbroadcast) { err2 = send_FRMR (state, pf, x1, x2, cmd, 1, 1, 0, 0); if (err == 0) err = err2; } break; } if ((err2 = (*state->recv) (state->ref, isbroadcast ? 3 : 1, mb)) != 0) { if (err == 0) err = err2; freemsg (mb); } mb = NULL; break; case L2_XID | L2__CMD: case L2_XID: /* TODO: Do something about XID frames. */ break; case L2_FRMR: case L2_FRMR | L2__CMD: /* technically an invalid frame, but replying with FRMR here is _bad_ */ printf("%sERR_D 3\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_D); if (err == 0) err = err2; if (state->status == S_up || state->status == S_recover) { establish (state); state->L3_req = 0; } break; default: err = -EINVAL; err2 = send_FRMR (state, pf, x1, x2, cmd, 1, 0, 0, 0); if (err == 0) err = err2; printf("%sERR_L 2\n",KERN_INFO ); err2 = msg_up (state, MDL_ERROR_IND, ERR_L); if (err == 0) err = err2; break; } } err2 = x75_check_pending (state, 0); /* if (err == 0) err = err2; */ if (mb != NULL) freemsg (mb); return /* err */ 0; } /* * Enqueue frame to be sent out. * Empty messages are silently discarded. */ int x75_send (x75 state, char isUI, mblk_t * mb) { if (msgdsize(mb) <= 0) freemsg(mb); else if (isUI) S_enqueue (&state->UI, mb); else { if(state->broadcast) return -ENXIO; S_enqueue (&state->I, mb); } state->asBroadcast = (isUI > 1); (void) x75_check_pending (state, 0); /* Send the frame, if possible */ return 0; } /* * Test if we can send. */ int x75_cansend (x75 state, char isUI) { if(state->cansend != NULL) (void)(*state->cansend) (state->ref); /* Trigger bringing L1 up */ if (isUI) return (state->UI.nblocks < 3); /* arbitrary maximum */ else /* This allows us to enqueue one additional * frame, which is a Good Thing. */ return (state->I.nblocks <= state->k); } /* * Test if we can receive. */ int x75_canrecv (x75 state) { /* Just ask the upper layer. */ return (*state->canrecv) (state->ref); } /* * Take the X75 layer up / down. */ #ifdef CONFIG_DEBUG_ISDN int deb_x75_changestate (const char *deb_file,unsigned int deb_line, x75 state, uchar_t ind, char isabort) #else int x75_changestate (x75 state, uchar_t ind, char isabort) #endif { int ms = splstr (); int err = 0, err2 = 0; int nonestablish = 1; #ifdef CONFIG_DEBUG_ISDN if(state->debug & 0x10) printf("%sx75.%d: State %d/%s for ind %d %d from %s:%d\n",KERN_DEBUG,state->debugnr,state->status, x75_sname[state->status],ind,isabort,deb_file,deb_line); #else if(state->debug & 0x10) printf("%sx75.%d: State %d/%s for ind %d %d\n",KERN_DEBUG,state->debugnr,state->status, x75_sname[state->status],ind,isabort); #endif if (isabort) goto doabort; switch (ind) { default: err = -ENOENT; break; case PH_ACTIVATE_IND: case PH_ACTIVATE_CONF: msg_up (state, ind, 0); break; case DL_ESTABLISH_CONF: /* Force establishment, for situations where * we dont need the initial handshake. This * usually happens because an idiot * implementor doesn't want to implement U * frame handling for automode. */ if (state->status == S_up || state->status == S_recover) break; /* Already established. */ if (state->debug & 0x10) printf ("%sX75%d: Forced establish.\n",KERN_DEBUG, state->debugnr); /* flush_I (state); */ state->errors = 0; if (state->status != S_down && state->status != S_free) stop_T (1, err2); if (err == 0) err = err2; start_T (3, err2); if (err == 0) err = err2; x75_setstate(state, S_up); msg_up (state, DL_ESTABLISH_CONF, 0); if(state->backenable) (*state->backenable) (state->ref); break; case DL_ESTABLISH_REQ: case DL_ESTABLISH_IND: /* Take it up. */ switch (state->status) { case S_down: case S_await_down: if(ind == DL_ESTABLISH_IND /* && state->UI.first == NULL && state->UI.first == NULL */ ) { if(0)printf("%sx75.%d: DL_ESTABLISH_IND, down, nothing done\n",KERN_DEBUG,state->debugnr); break; } err = establish (state); nonestablish = 0; state->L3_req = 1; state->errors = 0; break; case S_await_up: if (ind == DL_ESTABLISH_REQ) break; #if 0 /* Q.921 says to do this, but I can't think of a reason to. */ /* This flush also breaks top-down on-demand connection setup, i.e. starting up the lower layer automatically if the upper layer has some data to deliver. */ err = flush_I (state); #endif state->L3_req = 1; break; case S_up: case S_recover: /* L1 reestablishment */ if (ind == DL_ESTABLISH_REQ) break; err = flush_I (state); err2 = establish (state); nonestablish = 0; if (err == 0) err = err2; state->L3_req = 1; break; default:; } break; case DL_RELEASE_REQ: /* Take it down normally. */ state->errors = 0; switch (state->status) { case S_down: err = msg_up (state, DL_RELEASE_CONF, 0); break; case S_up: x75_setstate(state, S_await_down); err = flush_I (state); state->RC = 0; err2 = xmit3 (state, 1, L2_DISC | L2_PF_U); if (err == 0) err = err2; stop_T (3, err2); if (err == 0) err = err2; restart_T (1, err2); if (err == 0) err = err2; break; case S_recover: x75_setstate(state, S_await_down); err = flush_I (state); state->RC = 0; err2 = xmit3 (state, 1, L2_DISC | L2_PF_U); if (err == 0) err = err2; restart_T (1, err2); if (err == 0) err = err2; break; default:; } break; case PH_DISCONNECT_IND: case MDL_REMOVE_REQ: switch (state->status) { case S_await_up: case S_down: case S_up: case S_recover: err = kill_me (state, ind); break; case S_await_down: case S_free:; } case DL_RELEASE_CONF: case PH_DEACTIVATE_IND: case PH_DEACTIVATE_CONF: doabort: /* Just disconnect. The other side will * either also realize that L1 is down, or * time out eventually. */ switch (state->status) { case S_await_up: if (ind == PH_DEACTIVATE_IND) break; case S_down: case S_up: case S_recover: err = kill_me (state, DL_RELEASE_IND); break; case S_await_down: err = kill_me (state, DL_RELEASE_CONF); break; case S_free:; } x75_setstate(state, S_down); } if (err == 0) { err2 = x75_check_pending (state, nonestablish); if (err == 0) err = err2; } splx (ms); return err; } /* * Initialize data. */ int x75_initconn (x75 state) { bzero (&state->I, sizeof (struct _smallq)); bzero (&state->UI, sizeof (struct _smallq)); state->v_a = 0; state->v_s = 0; state->v_r = 0; state->RC = 0; state->status = S_down; state->L3_req = 0; state->RNR = 0; state->sentRR = 1; state->errors = 0; state->ack_pend = 0; state->inREJ = 0; state->T1 = 0; state->T3 = 0; if (state->k == 0 || (state->k > (state->wide ? 127 : 7))) state->k = 1; if (state->N1 == 0) state->N1 = 3; if (state->RUN_T1 == 0) state->RUN_T1 = 10; if (state->RUN_T3 == 0) state->RUN_T3 = 100; if (state->RUN_T3 < state->RUN_T1 * 2) state->RUN_T3 = state->RUN_T1 * 2; if (state->send == NULL || state->recv == NULL || state->state == NULL) return -EFAULT; if(0)printf("%sX75 %d: Init %d %d\n",KERN_DEBUG,state->debugnr,state->RUN_T1,state->RUN_T3); return 0; } #ifdef MODULE static int do_init_module(void) { return 0; } static int do_exit_module(void) { return 0; } #endif