diff options
author | Noe Rubinstein <nrubinstein@proformatique.com> | 2010-11-25 12:11:35 +0100 |
---|---|---|
committer | Noe Rubinstein <nrubinstein@proformatique.com> | 2010-11-25 12:11:35 +0100 |
commit | 2cff5bae47a83ac4992c13e4dcaf790868983579 (patch) | |
tree | c591b8f9e27da6649ff7fd424f17aaac0f3cbf2e /xhfc/xhfc_st_state.c | |
parent | b96bc6ff8387acff617d6efc419472d78dd9b2c1 (diff) |
State machine changes
Forgot to commit last friday
Diffstat (limited to 'xhfc/xhfc_st_state.c')
-rw-r--r-- | xhfc/xhfc_st_state.c | 172 |
1 files changed, 99 insertions, 73 deletions
diff --git a/xhfc/xhfc_st_state.c b/xhfc/xhfc_st_state.c index 538fe30..8c04784 100644 --- a/xhfc/xhfc_st_state.c +++ b/xhfc/xhfc_st_state.c @@ -27,16 +27,28 @@ enum te_state { F0, F1, F2, F3, F4, F5, F6, F7, F8 }; enum nt_state { G0, G1, G2, G3, G4 }; -static void activation_timer_expiry(struct xhfc_span* s) +static void activation_timer_expiry(struct xhfc_span* s); static void signal_f7_transition(struct xhfc_span* s); static void handle_state_change_nt(struct xhfc_span* s); static void handle_state_change_te(struct xhfc_span* s); +static void allow_G2_G3_transition(struct xhfc_span* s); + static void set_st_state(struct xhfc_span* s, int state); static void print_state(struct xhfc_span* s); +void start_state_machine(struct xhfc_span* s) +{ + u8 a_su_wr_sta = 0; + + SET_V_SU_LD_STA(a_su_wr_sta, 0); + + write_xhfc(s->xhfc, R_SU_SEL, s->port); + write_xhfc(s->xhfc, A_SU_WR_STA, 0); +} + void activate_request(struct xhfc_span* s) { u8 a_su_wr_sta = 0; @@ -44,29 +56,37 @@ void activate_request(struct xhfc_span* s) if(DBG_ST && DBG_SPAN(s)) printk(KERN_DEBUG DRIVER_NAME ": activate on port %d\n", - s->port); + s->port + 1); SET_V_SU_ACT(a_su_wr_sta, V_SU_ACT_ACTIVATE); write_xhfc(s->xhfc, R_SU_SEL, s->port); write_xhfc(s->xhfc, A_SU_WR_STA, a_su_wr_sta); - s->activation_timer = NOT_RUNNING; - /* timer activation done by state change handler (xref3) */ + if(!s->nt && GET_V_SU_STA(s->state) == F3 && GET_V_SU_INFO0(s->state)) + s->activation_timer = s->xhfc->ticks + T3_MS; /* xref5 */ + else + s->activation_timer = NOT_RUNNING; + /* timer for NT activated by state change handler (xref3) */ } void deactivate_request(struct xhfc_span* s) /* XXX not called */ { - + u8 a_su_wr_sta = 0; int sta = GET_V_SU_STA(s->state); if(DBG_ST && DBG_SPAN(s)) printk(KERN_DEBUG DRIVER_NAME ": deactivate on port %d\n", - s->port);xhfc_interrupt + s->port + 1); + + SET_V_SU_ACT(a_su_wr_sta, V_SU_ACT_DEACTIVATE); + + write_xhfc(s->xhfc, R_SU_SEL, s->port); + write_xhfc(s->xhfc, A_SU_WR_STA, a_su_wr_sta); if(s->nt && (sta == G2 || sta == G3)) - set_st_state(s, G4); + s->activation_timer = NOT_RUNNING; } /* This function is meant to be ran each time an interface changes state; @@ -81,14 +101,15 @@ void deactivate_request(struct xhfc_span* s) /* XXX not called */ * * - Starting timers: * - T3 is only started after an activation request, therefore we always now - * when T3 has to start + * when T3 has to start (xref5) * - T1 is only started when going to state G2, which can only yield G3 * automatically. This can be disallowed in V_G2_G3_EN so that the timer * can be started before allowing the transition with V_SU_SET_G2_G3. + * (xref6) * - Stopping timers: * - T1 is only stopped on G2->G3. * From G3: - * - G3->G2: if the timer is already running, stop it + * - G3->G2: if the timer is already running, stop it (xref4) * - on deactivate requests stop the timer * - T3 is stopped on going to state F7. I don't think it's possible to * guarantee this; however except F2->F7 which can not happen when T3 is @@ -132,9 +153,15 @@ static void handle_state_change_nt(struct xhfc_span* s) break; case G2: s->span.alarms = DAHDI_ALARM_YELLOW; - if(s->prev_state != G3) - /* xref3 */ + if(s->activation_timer) + s->activation_timer = NOT_RUNNING; /* xref4 */ + else { + /* xref3, this should happen only if the + * previous state is not G3 */ s->activation_timer = s->xhfc->ticks + T1_MS; + allow_G2_G3_transition(s); /* xref6 */ + } + break; case G3: s->span.alarms = DAHDI_ALARM_NONE; @@ -146,37 +173,20 @@ static void handle_state_change_nt(struct xhfc_span* s) static void handle_state_change_te(struct xhfc_span* s) { - switch(GET_V_SU_STA(s->state)) { - case F3: - s->activation_timer = NOT_RUNNING; + int sta = GET_V_SU_STA(s->state); + + if (sta != F7) + s->f6_f7_transition = NOT_RUNNING; - /* Fall through */ + switch(sta) { case F0: case F1: case F2: + case F3: + case F4: case F8: s->span.alarms = DAHDI_ALARM_RED; break; - case F4: - /* XXX If in F3 and receiving INFO0, the state machine - * should change to F4 and start T3. Is the jump to F4 - * done by the XHFC in hard, (in which case the timer - * T3 will be started by the software when detecting a - * change of state towards F4) or should it be driven - * by the soft (in which case upon detection of the - * reception of INFO0 in F3 state, the state would be - * set to F4 and T3 started)? - * - * Andrew did the latter, and I can't seem to get if - * and how mISDN handles this. - * - * Here, the former hypothesis (state change driven by - * hard) is assumed. - */ - - s->activation_timer = s->xhfc->ticks + T3_MS; /* xref3 */ - - /* Fall through */ case F5: case F6: s->span.alarms = DAHDI_ALARM_YELLOW; @@ -220,12 +230,21 @@ static void activation_timer_expiry(struct xhfc_span* s) { int sta = GET_V_SU_STA(s->state); - bool nothing_done = false; + int t1_expiry = s->nt && sta == G2; + int t3_expiry = !s->nt && (sta == F4 || sta == F5 || sta == F8); + + if(DBG_ST && DBG_SPAN(s)) + printk(KERN_INFO DRIVER_NAME + ": %s%s activation timeout" + " in state %c%d on port %d\n", + t1_expiry || t3_expiry ? "" : "no action for ", + s->nt ? "NT" : "TE", + s->nt ? 'G' : 'F', + sta, s->port + 1); s->activation_timer = NOT_RUNNING; - if(s->nt /* T1 expiry */ - && sta == G2) { + if (t1_expiry) { /* When setting the state to G4 by writing * A_SU_WR_STA, is the T2 timer automatically @@ -234,50 +253,29 @@ static void activation_timer_expiry(struct xhfc_span* s) * implemented in hardware. */ - set_st_state(s, G4); + deactivate_request(s); + // set_st_state(s, G4); - } else if(!s->nt /* T2 expiry */ - && (sta == F4 || sta == F5 || sta == F8)) { + } else if(t3_expiry) { - set_st_state(s, F3); + deactivate_request(s); + // set_st_state(s, F3); - } else - nothing_done = true; - - if(DBG_ST && DBG_SPAN(s)) - printk(KERN_INFO DRIVER_NAME - ": %s%s activation timeout" - " in state %c%d on port %d\n", - (nothing_done ? "no action for " : ""), - (s->nt ? "NT" : "TE"), - (s->nt ? 'G' : 'F'), - sta, s->port + 1); + } } -static void set_st_state(struct xhfc_span* s, int state) +static void allow_G2_G3_transition(struct xhfc_span* s) { u8 a_su_wr_sta = 0; - s->prev_state = s->state; - s->state = state; + SET_V_SU_SET_G2_G3(a_su_wr_sta, 1); - if(DBG_ST && DBG_SPAN(s)) { - printk(KERN_DEBUG DRIVER_NAME "port %d: " - "Forcing state change:\n", s->port +1); - print_state(s); - } - - SET_V_SU_SET_STA(a_su_wr_sta, state); - SET_V_SU_LD_STA(a_su_wr_sta, 1); - - write_xhfc(s->xhfc, R_SU_SEL, s->port); - write_xhfc(s->xhfc, A_SU_WR_STA, a_su_wr_sta); + write_xhfc(s->xhfc, R_SU_SEL, s->port); + write_xhfc(s->xhfc, A_SU_WR_STA, a_su_wr_sta); - udelay(6); - - SET_V_SU_LD_STA(a_su_wr_sta, 0); - write_xhfc(s->xhfc, R_SU_SEL, s->port); /* just in case. (XXX?) */ - write_xhfc(s->xhfc, A_SU_WR_STA, a_su_wr_sta); + /* The bit is automatically cleared on G2->G3. G2->G4 is always + * triggered by using set_st_state which clears this bit. Therefore + * this bit is always cleared correctly when exiting G2. */ } static char *state_description[2][16] = { @@ -303,7 +301,7 @@ static char *state_description[2][16] = { static void print_state(struct xhfc_span* s) { int sta = GET_V_SU_STA(s->state); - printk(KERN_DEBUG DRIVER_NAME "port %d: %s state %c%d (%s)\n", + printk(KERN_DEBUG DRIVER_NAME ": port %d: %s state %c%d (%s)\n", s->port + 1, (s->nt ? "NT" : "TE"), (s->nt ? 'G' : 'F'), @@ -311,3 +309,31 @@ static void print_state(struct xhfc_span* s) state_description[s->nt][sta]); } +#if 0 +/* This is not used right now, but it could be in the future */ +static void set_st_state(struct xhfc_span* s, int state) +{ + u8 a_su_wr_sta = 0; + + s->prev_state = s->state; + s->state = state; + + if(DBG_ST && DBG_SPAN(s)) { + printk(KERN_DEBUG DRIVER_NAME "port %d: " + "Forcing state change:\n", s->port +1); + print_state(s); + } + + SET_V_SU_SET_STA(a_su_wr_sta, state); + SET_V_SU_LD_STA(a_su_wr_sta, 1); + + write_xhfc(s->xhfc, R_SU_SEL, s->port); + write_xhfc(s->xhfc, A_SU_WR_STA, a_su_wr_sta); + + udelay(6); + + SET_V_SU_LD_STA(a_su_wr_sta, 0); + write_xhfc(s->xhfc, R_SU_SEL, s->port); /* just in case. (XXX?) */ + write_xhfc(s->xhfc, A_SU_WR_STA, a_su_wr_sta); +} +#endif |