core: Drop messages sent before binding enables transmission

Some bindings (astlpc) have a handshake that must occur before packets
can be sent across the bus, but after the binding instance has been
constructed. Similarly, some bindings (astlpc) require coordination of
resources between endpoints as part of regular operation, which
motivates the existence of the associated `tx_enabled` state.

If a binding invokes mctp_binding_set_tx_enabled() with a `true`
argument it implies that all required handshaking has been completed
(if it had not, we still would not be able to send packets).

Switch `tx_enabled` from a boolean to a ternary state machine to capture
the `constructed` state of a binding (before required handshaking has
completed). Further, error out if mctp_message_tx_on_bus() is invoked
with a binding in the `constructed` state, as it's possible the
information required to correctly packetise the provided message is
invalid.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Change-Id: If4baf07ab5fe7edd7f522fac29e32fc5c2a6105d
diff --git a/core.c b/core.c
index 7f3d96a..9cc6666 100644
--- a/core.c
+++ b/core.c
@@ -20,13 +20,19 @@
 
 /* Internal data structures */
 
-struct mctp_bus {
-	mctp_eid_t		eid;
-	struct mctp_binding	*binding;
-	bool			tx_enabled;
+enum mctp_bus_state {
+	mctp_bus_state_constructed = 0,
+	mctp_bus_state_tx_enabled,
+	mctp_bus_state_tx_disabled,
+};
 
-	struct mctp_pktbuf	*tx_queue_head;
-	struct mctp_pktbuf	*tx_queue_tail;
+struct mctp_bus {
+	mctp_eid_t eid;
+	struct mctp_binding *binding;
+	enum mctp_bus_state state;
+
+	struct mctp_pktbuf *tx_queue_head;
+	struct mctp_pktbuf *tx_queue_tail;
 
 	/* todo: routing */
 };
@@ -570,7 +576,7 @@
 static int mctp_packet_tx(struct mctp_bus *bus,
 		struct mctp_pktbuf *pkt)
 {
-	if (!bus->tx_enabled)
+	if (bus->state != mctp_bus_state_tx_enabled)
 		return -1;
 
 	return bus->binding->tx(bus->binding, pkt);
@@ -599,9 +605,31 @@
 void mctp_binding_set_tx_enabled(struct mctp_binding *binding, bool enable)
 {
 	struct mctp_bus *bus = binding->bus;
-	bus->tx_enabled = enable;
-	if (enable)
+
+	switch(bus->state) {
+	case mctp_bus_state_constructed:
+		if (!enable)
+			return;
+
+		bus->state = mctp_bus_state_tx_enabled;
+		mctp_prinfo("%s binding started", binding->name);
+		return;
+	case mctp_bus_state_tx_enabled:
+		if (enable)
+			return;
+
+		bus->state = mctp_bus_state_tx_disabled;
+		mctp_prdebug("%s binding Tx disabled", binding->name);
+		return;
+	case mctp_bus_state_tx_disabled:
+		if (!enable)
+			return;
+
+		bus->state = mctp_bus_state_tx_enabled;
+		mctp_prdebug("%s binding Tx enabled", binding->name);
 		mctp_send_tx_queue(bus);
+		return;
+	}
 }
 
 static int mctp_message_tx_on_bus(struct mctp_bus *bus, mctp_eid_t src,
@@ -612,6 +640,9 @@
 	struct mctp_hdr *hdr;
 	int i;
 
+	if (bus->state == mctp_bus_state_constructed)
+		return -ENXIO;
+
 	max_payload_len = bus->binding->pkt_size - sizeof(*hdr);
 
 	mctp_prdebug("%s: Generating packets for transmission of %zu byte message from %hhu to %hhu",