fsm.c 3.61 KB
Newer Older
1 2 3
/*
 * Alessandro Rubini for CERN, 2011 -- public domain
 */
Alessandro Rubini's avatar
Alessandro Rubini committed
4
#include <ppsi/ppsi.h>
5

6 7
unsigned long pp_global_flags; /* This is the only "global" file in ppsi */

8 9 10 11 12 13
/*
 * This is somehow a duplicate of __pp_diag, but I still want
 * explicit timing in the fsm enter/stay/leave messages,
 * while there's no need to add times to all diagnostic messages
 */
static void pp_fsm_printf(struct pp_instance *ppi, char *fmt, ...)
14 15 16 17 18
{
	va_list args;
	TimeInternal t;
	unsigned long oflags = pp_global_flags;

19
	if (!pp_diag_allow(ppi, fsm, 1))
20 21
		return;

22 23
	/* temporarily set NOTIMELOG, as we'll print the time ourselves */
	pp_global_flags |= PP_FLAG_NOTIMELOG;
24
	ppi->t_ops->get(ppi, &t);
25 26
	pp_global_flags = oflags;

27
	pp_printf("diag-fsm-1-%s: %09d.%03d: ", ppi->iface_name,
28
		  (int)t.seconds, (int)t.nanoseconds / 1000000);
29 30 31 32 33
	va_start(args, fmt);
	pp_vprintf(fmt, args);
	va_end(args);
}

34 35 36 37 38 39 40 41 42 43 44 45 46 47
/*
 * Diagnostics about state machine, enter, leave, remain
 */
enum {
	STATE_ENTER,
	STATE_LOOP,
	STATE_LEAVE
};

static void pp_diag_fsm(struct pp_instance *ppi, char *name, int sequence,
			int plen)
{
	if (sequence == STATE_ENTER) {
		/* enter with or without a packet len */
48
		pp_fsm_printf(ppi, "ENTER %s, packet len %i\n",
49 50 51 52
			  name, plen);
		return;
	}
	if (sequence == STATE_LOOP) {
53
		pp_fsm_printf(ppi, "%s: reenter in %i ms\n", name,
54 55 56 57
				ppi->next_delay);
		return;
	}
	/* leave has one \n more, so different states are separate */
58
	pp_fsm_printf(ppi, "LEAVE %s (next: %3i in %i ms)\n\n",
59 60 61
		name, ppi->next_state, ppi->next_delay);
}

62 63 64 65 66 67 68 69 70 71 72 73
/*
 * This is the state machine code. i.e. the extension-independent
 * function that runs the machine. Errors are managed and reported
 * here (based on the diag module). The returned value is the time
 * in milliseconds to wait before reentering the state machine.
 * the normal protocol. If an extended protocol is used, the table used
 * is that of the extension, otherwise the one in state-table-default.c
 */

int pp_state_machine(struct pp_instance *ppi, uint8_t *packet, int plen)
{
	struct pp_state_table_item *ip;
74
	int state, err = 0;
75

76 77
	if (plen)
		pp_diag(ppi, frames, 1,
78
			"RECV %02d bytes at %d.%09d (type %x, %s)\n", plen,
79 80
			   (int)ppi->last_rcv_time.seconds,
			   (int)ppi->last_rcv_time.nanoseconds,
81
			   packet[0] & 0xf, pp_msg_names[packet[0] & 0xf]);
82 83 84 85 86 87 88 89

	/*
	 * Since all ptp frames have the same header, parse it now.
	 * In case of error continue without a frame, so the current
	 * ptp state can update ppi->next_delay and return a proper value
	 */
	if (plen) {
		if (plen >= PP_HEADER_LENGTH)
90
			err = msg_unpack_header(ppi, packet, plen);
91 92 93 94 95 96
		else
			err = 1;
		if (err) {
			plen = 0;
			packet = NULL;
		}
97
	}
98

99 100 101 102 103 104 105 106 107
	state = ppi->state;

	/* a linear search is affordable up to a few dozen items */
	for (ip = pp_state_table; ip->state != PPS_END_OF_TABLE; ip++) {
		if (ip->state != state)
			continue;
		/* found: handle this state */
		ppi->next_state = state;
		ppi->next_delay = 0;
108
		if (pp_diag_verbosity && ppi->is_new_state)
109
			pp_diag_fsm(ppi, ip->name, STATE_ENTER, plen);
110 111
		err = ip->f1(ppi, packet, plen);
		if (err)
112
			pp_printf("fsm for %s: Error %i in %s\n",
113
				  ppi->iface_name, err, ip->name);
114

115
		/* done: if new state mark it, and enter it now (0 ms) */
116 117 118
		if (ppi->state != ppi->next_state) {
			ppi->state = ppi->next_state;
			ppi->is_new_state = 1;
119 120
			if (pp_diag_verbosity)
				pp_diag_fsm(ppi, ip->name, STATE_LEAVE, 0);
121
			return 0;
122
		}
123 124 125
		ppi->is_new_state = 0;
		if (pp_diag_verbosity)
			pp_diag_fsm(ppi, ip->name, STATE_LOOP, 0);
126 127 128
		return ppi->next_delay;
	}
	/* Unknwon state, can't happen */
129
	pp_printf("fsm: Unknown state for iface %s\n", ppi->iface_name);
130 131
	return 10000; /* No way out. Repeat message every 10s */
}