summaryrefslogtreecommitdiff
path: root/xhfc/xhfc_st_state.c
diff options
context:
space:
mode:
authorNoe Rubinstein <nrubinstein@proformatique.com>2010-11-25 12:11:35 +0100
committerNoe Rubinstein <nrubinstein@proformatique.com>2010-11-25 12:11:35 +0100
commit2cff5bae47a83ac4992c13e4dcaf790868983579 (patch)
treec591b8f9e27da6649ff7fd424f17aaac0f3cbf2e /xhfc/xhfc_st_state.c
parentb96bc6ff8387acff617d6efc419472d78dd9b2c1 (diff)
State machine changes
Forgot to commit last friday
Diffstat (limited to 'xhfc/xhfc_st_state.c')
-rw-r--r--xhfc/xhfc_st_state.c172
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