xref: /openbmc/linux/drivers/s390/net/ctcm_fsms.c (revision dd4e356c)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2293d984fSPeter Tiedemann /*
3293d984fSPeter Tiedemann  * Copyright IBM Corp. 2001, 2007
4293d984fSPeter Tiedemann  * Authors:	Fritz Elfert (felfert@millenux.com)
5293d984fSPeter Tiedemann  * 		Peter Tiedemann (ptiedem@de.ibm.com)
6293d984fSPeter Tiedemann  *	MPC additions :
7293d984fSPeter Tiedemann  *		Belinda Thompson (belindat@us.ibm.com)
8293d984fSPeter Tiedemann  *		Andy Richter (richtera@us.ibm.com)
9293d984fSPeter Tiedemann  */
10293d984fSPeter Tiedemann 
11293d984fSPeter Tiedemann #undef DEBUG
12293d984fSPeter Tiedemann #undef DEBUGDATA
13293d984fSPeter Tiedemann #undef DEBUGCCW
14293d984fSPeter Tiedemann 
152a7c6f2cSPeter Tiedemann #define KMSG_COMPONENT "ctcm"
162a7c6f2cSPeter Tiedemann #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
172a7c6f2cSPeter Tiedemann 
18293d984fSPeter Tiedemann #include <linux/module.h>
19293d984fSPeter Tiedemann #include <linux/init.h>
20293d984fSPeter Tiedemann #include <linux/kernel.h>
21293d984fSPeter Tiedemann #include <linux/slab.h>
22293d984fSPeter Tiedemann #include <linux/errno.h>
23293d984fSPeter Tiedemann #include <linux/types.h>
24293d984fSPeter Tiedemann #include <linux/interrupt.h>
25293d984fSPeter Tiedemann #include <linux/timer.h>
26293d984fSPeter Tiedemann #include <linux/bitops.h>
27293d984fSPeter Tiedemann 
28293d984fSPeter Tiedemann #include <linux/signal.h>
29293d984fSPeter Tiedemann #include <linux/string.h>
30293d984fSPeter Tiedemann 
31293d984fSPeter Tiedemann #include <linux/ip.h>
32293d984fSPeter Tiedemann #include <linux/if_arp.h>
33293d984fSPeter Tiedemann #include <linux/tcp.h>
34293d984fSPeter Tiedemann #include <linux/skbuff.h>
35293d984fSPeter Tiedemann #include <linux/ctype.h>
36293d984fSPeter Tiedemann #include <net/dst.h>
37293d984fSPeter Tiedemann 
38293d984fSPeter Tiedemann #include <linux/io.h>
39293d984fSPeter Tiedemann #include <asm/ccwdev.h>
40293d984fSPeter Tiedemann #include <asm/ccwgroup.h>
41293d984fSPeter Tiedemann #include <linux/uaccess.h>
42293d984fSPeter Tiedemann 
43293d984fSPeter Tiedemann #include <asm/idals.h>
44293d984fSPeter Tiedemann 
45293d984fSPeter Tiedemann #include "fsm.h"
46293d984fSPeter Tiedemann 
47293d984fSPeter Tiedemann #include "ctcm_dbug.h"
48293d984fSPeter Tiedemann #include "ctcm_main.h"
49293d984fSPeter Tiedemann #include "ctcm_fsms.h"
50293d984fSPeter Tiedemann 
51293d984fSPeter Tiedemann const char *dev_state_names[] = {
52293d984fSPeter Tiedemann 	[DEV_STATE_STOPPED]		= "Stopped",
53293d984fSPeter Tiedemann 	[DEV_STATE_STARTWAIT_RXTX]	= "StartWait RXTX",
54293d984fSPeter Tiedemann 	[DEV_STATE_STARTWAIT_RX]	= "StartWait RX",
55293d984fSPeter Tiedemann 	[DEV_STATE_STARTWAIT_TX]	= "StartWait TX",
56293d984fSPeter Tiedemann 	[DEV_STATE_STOPWAIT_RXTX]	= "StopWait RXTX",
57293d984fSPeter Tiedemann 	[DEV_STATE_STOPWAIT_RX]		= "StopWait RX",
58293d984fSPeter Tiedemann 	[DEV_STATE_STOPWAIT_TX]		= "StopWait TX",
59293d984fSPeter Tiedemann 	[DEV_STATE_RUNNING]		= "Running",
60293d984fSPeter Tiedemann };
61293d984fSPeter Tiedemann 
62293d984fSPeter Tiedemann const char *dev_event_names[] = {
63293d984fSPeter Tiedemann 	[DEV_EVENT_START]	= "Start",
64293d984fSPeter Tiedemann 	[DEV_EVENT_STOP]	= "Stop",
65293d984fSPeter Tiedemann 	[DEV_EVENT_RXUP]	= "RX up",
66293d984fSPeter Tiedemann 	[DEV_EVENT_TXUP]	= "TX up",
67293d984fSPeter Tiedemann 	[DEV_EVENT_RXDOWN]	= "RX down",
68293d984fSPeter Tiedemann 	[DEV_EVENT_TXDOWN]	= "TX down",
69293d984fSPeter Tiedemann 	[DEV_EVENT_RESTART]	= "Restart",
70293d984fSPeter Tiedemann };
71293d984fSPeter Tiedemann 
72293d984fSPeter Tiedemann const char *ctc_ch_event_names[] = {
73293d984fSPeter Tiedemann 	[CTC_EVENT_IO_SUCCESS]	= "ccw_device success",
74293d984fSPeter Tiedemann 	[CTC_EVENT_IO_EBUSY]	= "ccw_device busy",
75293d984fSPeter Tiedemann 	[CTC_EVENT_IO_ENODEV]	= "ccw_device enodev",
76293d984fSPeter Tiedemann 	[CTC_EVENT_IO_UNKNOWN]	= "ccw_device unknown",
77293d984fSPeter Tiedemann 	[CTC_EVENT_ATTNBUSY]	= "Status ATTN & BUSY",
78293d984fSPeter Tiedemann 	[CTC_EVENT_ATTN]	= "Status ATTN",
79293d984fSPeter Tiedemann 	[CTC_EVENT_BUSY]	= "Status BUSY",
80293d984fSPeter Tiedemann 	[CTC_EVENT_UC_RCRESET]	= "Unit check remote reset",
81293d984fSPeter Tiedemann 	[CTC_EVENT_UC_RSRESET]	= "Unit check remote system reset",
82293d984fSPeter Tiedemann 	[CTC_EVENT_UC_TXTIMEOUT] = "Unit check TX timeout",
83293d984fSPeter Tiedemann 	[CTC_EVENT_UC_TXPARITY]	= "Unit check TX parity",
84293d984fSPeter Tiedemann 	[CTC_EVENT_UC_HWFAIL]	= "Unit check Hardware failure",
85293d984fSPeter Tiedemann 	[CTC_EVENT_UC_RXPARITY]	= "Unit check RX parity",
86293d984fSPeter Tiedemann 	[CTC_EVENT_UC_ZERO]	= "Unit check ZERO",
87293d984fSPeter Tiedemann 	[CTC_EVENT_UC_UNKNOWN]	= "Unit check Unknown",
88293d984fSPeter Tiedemann 	[CTC_EVENT_SC_UNKNOWN]	= "SubChannel check Unknown",
89293d984fSPeter Tiedemann 	[CTC_EVENT_MC_FAIL]	= "Machine check failure",
90293d984fSPeter Tiedemann 	[CTC_EVENT_MC_GOOD]	= "Machine check operational",
91293d984fSPeter Tiedemann 	[CTC_EVENT_IRQ]		= "IRQ normal",
92293d984fSPeter Tiedemann 	[CTC_EVENT_FINSTAT]	= "IRQ final",
93293d984fSPeter Tiedemann 	[CTC_EVENT_TIMER]	= "Timer",
94293d984fSPeter Tiedemann 	[CTC_EVENT_START]	= "Start",
95293d984fSPeter Tiedemann 	[CTC_EVENT_STOP]	= "Stop",
96293d984fSPeter Tiedemann 	/*
97293d984fSPeter Tiedemann 	* additional MPC events
98293d984fSPeter Tiedemann 	*/
99293d984fSPeter Tiedemann 	[CTC_EVENT_SEND_XID]	= "XID Exchange",
100293d984fSPeter Tiedemann 	[CTC_EVENT_RSWEEP_TIMER] = "MPC Group Sweep Timer",
101293d984fSPeter Tiedemann };
102293d984fSPeter Tiedemann 
103293d984fSPeter Tiedemann const char *ctc_ch_state_names[] = {
104293d984fSPeter Tiedemann 	[CTC_STATE_IDLE]	= "Idle",
105293d984fSPeter Tiedemann 	[CTC_STATE_STOPPED]	= "Stopped",
106293d984fSPeter Tiedemann 	[CTC_STATE_STARTWAIT]	= "StartWait",
107293d984fSPeter Tiedemann 	[CTC_STATE_STARTRETRY]	= "StartRetry",
108293d984fSPeter Tiedemann 	[CTC_STATE_SETUPWAIT]	= "SetupWait",
109293d984fSPeter Tiedemann 	[CTC_STATE_RXINIT]	= "RX init",
110293d984fSPeter Tiedemann 	[CTC_STATE_TXINIT]	= "TX init",
111293d984fSPeter Tiedemann 	[CTC_STATE_RX]		= "RX",
112293d984fSPeter Tiedemann 	[CTC_STATE_TX]		= "TX",
113293d984fSPeter Tiedemann 	[CTC_STATE_RXIDLE]	= "RX idle",
114293d984fSPeter Tiedemann 	[CTC_STATE_TXIDLE]	= "TX idle",
115293d984fSPeter Tiedemann 	[CTC_STATE_RXERR]	= "RX error",
116293d984fSPeter Tiedemann 	[CTC_STATE_TXERR]	= "TX error",
117293d984fSPeter Tiedemann 	[CTC_STATE_TERM]	= "Terminating",
118293d984fSPeter Tiedemann 	[CTC_STATE_DTERM]	= "Restarting",
119293d984fSPeter Tiedemann 	[CTC_STATE_NOTOP]	= "Not operational",
120293d984fSPeter Tiedemann 	/*
121293d984fSPeter Tiedemann 	* additional MPC states
122293d984fSPeter Tiedemann 	*/
123293d984fSPeter Tiedemann 	[CH_XID0_PENDING]	= "Pending XID0 Start",
124293d984fSPeter Tiedemann 	[CH_XID0_INPROGRESS]	= "In XID0 Negotiations ",
125293d984fSPeter Tiedemann 	[CH_XID7_PENDING]	= "Pending XID7 P1 Start",
126293d984fSPeter Tiedemann 	[CH_XID7_PENDING1]	= "Active XID7 P1 Exchange ",
127293d984fSPeter Tiedemann 	[CH_XID7_PENDING2]	= "Pending XID7 P2 Start ",
128293d984fSPeter Tiedemann 	[CH_XID7_PENDING3]	= "Active XID7 P2 Exchange ",
129293d984fSPeter Tiedemann 	[CH_XID7_PENDING4]	= "XID7 Complete - Pending READY ",
130293d984fSPeter Tiedemann };
131293d984fSPeter Tiedemann 
132293d984fSPeter Tiedemann static void ctcm_action_nop(fsm_instance *fi, int event, void *arg);
133293d984fSPeter Tiedemann 
134293d984fSPeter Tiedemann /*
135293d984fSPeter Tiedemann  * ----- static ctcm actions for channel statemachine -----
136293d984fSPeter Tiedemann  *
137293d984fSPeter Tiedemann */
138293d984fSPeter Tiedemann static void chx_txdone(fsm_instance *fi, int event, void *arg);
139293d984fSPeter Tiedemann static void chx_rx(fsm_instance *fi, int event, void *arg);
140293d984fSPeter Tiedemann static void chx_rxidle(fsm_instance *fi, int event, void *arg);
141293d984fSPeter Tiedemann static void chx_firstio(fsm_instance *fi, int event, void *arg);
142293d984fSPeter Tiedemann static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg);
143293d984fSPeter Tiedemann static void ctcm_chx_start(fsm_instance *fi, int event, void *arg);
144293d984fSPeter Tiedemann static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg);
145293d984fSPeter Tiedemann static void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg);
146293d984fSPeter Tiedemann static void ctcm_chx_stop(fsm_instance *fi, int event, void *arg);
147293d984fSPeter Tiedemann static void ctcm_chx_fail(fsm_instance *fi, int event, void *arg);
148293d984fSPeter Tiedemann static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg);
149293d984fSPeter Tiedemann static void ctcm_chx_restart(fsm_instance *fi, int event, void *arg);
150293d984fSPeter Tiedemann static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg);
151293d984fSPeter Tiedemann static void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg);
152293d984fSPeter Tiedemann static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg);
153293d984fSPeter Tiedemann static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg);
154293d984fSPeter Tiedemann static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg);
155293d984fSPeter Tiedemann static void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg);
156293d984fSPeter Tiedemann 
157293d984fSPeter Tiedemann /*
158293d984fSPeter Tiedemann  * ----- static ctcmpc actions for ctcmpc channel statemachine -----
159293d984fSPeter Tiedemann  *
160293d984fSPeter Tiedemann */
161293d984fSPeter Tiedemann static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg);
162293d984fSPeter Tiedemann static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg);
163293d984fSPeter Tiedemann static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg);
164293d984fSPeter Tiedemann /* shared :
165293d984fSPeter Tiedemann static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg);
166293d984fSPeter Tiedemann static void ctcm_chx_start(fsm_instance *fi, int event, void *arg);
167293d984fSPeter Tiedemann static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg);
168293d984fSPeter Tiedemann static void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg);
169293d984fSPeter Tiedemann static void ctcm_chx_stop(fsm_instance *fi, int event, void *arg);
170293d984fSPeter Tiedemann static void ctcm_chx_fail(fsm_instance *fi, int event, void *arg);
171293d984fSPeter Tiedemann static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg);
172293d984fSPeter Tiedemann static void ctcm_chx_restart(fsm_instance *fi, int event, void *arg);
173293d984fSPeter Tiedemann static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg);
174293d984fSPeter Tiedemann static void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg);
175293d984fSPeter Tiedemann static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg);
176293d984fSPeter Tiedemann static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg);
177293d984fSPeter Tiedemann static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg);
178293d984fSPeter Tiedemann static void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg);
179293d984fSPeter Tiedemann */
180293d984fSPeter Tiedemann static void ctcmpc_chx_attn(fsm_instance *fsm, int event, void *arg);
181293d984fSPeter Tiedemann static void ctcmpc_chx_attnbusy(fsm_instance *, int, void *);
182293d984fSPeter Tiedemann static void ctcmpc_chx_resend(fsm_instance *, int, void *);
183293d984fSPeter Tiedemann static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg);
184293d984fSPeter Tiedemann 
185a962cc4bSHeiko Carstens /*
18625985edcSLucas De Marchi  * Check return code of a preceding ccw_device call, halt_IO etc...
187293d984fSPeter Tiedemann  *
188293d984fSPeter Tiedemann  * ch	:	The channel, the error belongs to.
189293d984fSPeter Tiedemann  * Returns the error code (!= 0) to inspect.
190293d984fSPeter Tiedemann  */
ctcm_ccw_check_rc(struct channel * ch,int rc,char * msg)191293d984fSPeter Tiedemann void ctcm_ccw_check_rc(struct channel *ch, int rc, char *msg)
192293d984fSPeter Tiedemann {
193293d984fSPeter Tiedemann 	CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
194aa3f2cb6SPeter Tiedemann 		"%s(%s): %s: %04x\n",
195aa3f2cb6SPeter Tiedemann 		CTCM_FUNTAIL, ch->id, msg, rc);
196293d984fSPeter Tiedemann 	switch (rc) {
197293d984fSPeter Tiedemann 	case -EBUSY:
1982a7c6f2cSPeter Tiedemann 		pr_info("%s: The communication peer is busy\n",
1992a7c6f2cSPeter Tiedemann 			ch->id);
200293d984fSPeter Tiedemann 		fsm_event(ch->fsm, CTC_EVENT_IO_EBUSY, ch);
201293d984fSPeter Tiedemann 		break;
202293d984fSPeter Tiedemann 	case -ENODEV:
2032a7c6f2cSPeter Tiedemann 		pr_err("%s: The specified target device is not valid\n",
2042a7c6f2cSPeter Tiedemann 		       ch->id);
205293d984fSPeter Tiedemann 		fsm_event(ch->fsm, CTC_EVENT_IO_ENODEV, ch);
206293d984fSPeter Tiedemann 		break;
207293d984fSPeter Tiedemann 	default:
2082a7c6f2cSPeter Tiedemann 		pr_err("An I/O operation resulted in error %04x\n",
2092a7c6f2cSPeter Tiedemann 		       rc);
210293d984fSPeter Tiedemann 		fsm_event(ch->fsm, CTC_EVENT_IO_UNKNOWN, ch);
211293d984fSPeter Tiedemann 	}
212293d984fSPeter Tiedemann }
213293d984fSPeter Tiedemann 
ctcm_purge_skb_queue(struct sk_buff_head * q)214293d984fSPeter Tiedemann void ctcm_purge_skb_queue(struct sk_buff_head *q)
215293d984fSPeter Tiedemann {
216293d984fSPeter Tiedemann 	struct sk_buff *skb;
217293d984fSPeter Tiedemann 
218aa3f2cb6SPeter Tiedemann 	CTCM_DBF_TEXT(TRACE, CTC_DBF_DEBUG, __func__);
219293d984fSPeter Tiedemann 
220293d984fSPeter Tiedemann 	while ((skb = skb_dequeue(q))) {
221bba5850cSDavid S. Miller 		refcount_dec(&skb->users);
222293d984fSPeter Tiedemann 		dev_kfree_skb_any(skb);
223293d984fSPeter Tiedemann 	}
224293d984fSPeter Tiedemann }
225293d984fSPeter Tiedemann 
226a962cc4bSHeiko Carstens /*
227293d984fSPeter Tiedemann  * NOP action for statemachines
228293d984fSPeter Tiedemann  */
ctcm_action_nop(fsm_instance * fi,int event,void * arg)229293d984fSPeter Tiedemann static void ctcm_action_nop(fsm_instance *fi, int event, void *arg)
230293d984fSPeter Tiedemann {
231293d984fSPeter Tiedemann }
232293d984fSPeter Tiedemann 
233293d984fSPeter Tiedemann /*
234293d984fSPeter Tiedemann  * Actions for channel - statemachines.
235293d984fSPeter Tiedemann  */
236293d984fSPeter Tiedemann 
237a962cc4bSHeiko Carstens /*
238293d984fSPeter Tiedemann  * Normal data has been send. Free the corresponding
239293d984fSPeter Tiedemann  * skb (it's in io_queue), reset dev->tbusy and
240293d984fSPeter Tiedemann  * revert to idle state.
241293d984fSPeter Tiedemann  *
242293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
243293d984fSPeter Tiedemann  * event	The event, just happened.
244293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
245293d984fSPeter Tiedemann  */
chx_txdone(fsm_instance * fi,int event,void * arg)246293d984fSPeter Tiedemann static void chx_txdone(fsm_instance *fi, int event, void *arg)
247293d984fSPeter Tiedemann {
248293d984fSPeter Tiedemann 	struct channel *ch = arg;
249293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
250261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
251293d984fSPeter Tiedemann 	struct sk_buff *skb;
252293d984fSPeter Tiedemann 	int first = 1;
253293d984fSPeter Tiedemann 	int i;
254293d984fSPeter Tiedemann 	unsigned long duration;
255ee6edb97SAya Mahfouz 	unsigned long done_stamp = jiffies;
256293d984fSPeter Tiedemann 
257aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name);
258aa3f2cb6SPeter Tiedemann 
259ee6edb97SAya Mahfouz 	duration = done_stamp - ch->prof.send_stamp;
260293d984fSPeter Tiedemann 	if (duration > ch->prof.tx_time)
261293d984fSPeter Tiedemann 		ch->prof.tx_time = duration;
262293d984fSPeter Tiedemann 
26323d805b6SPeter Oberparleiter 	if (ch->irb->scsw.cmd.count != 0)
264aa3f2cb6SPeter Tiedemann 		CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
265aa3f2cb6SPeter Tiedemann 			"%s(%s): TX not complete, remaining %d bytes",
266aa3f2cb6SPeter Tiedemann 			     CTCM_FUNTAIL, dev->name, ch->irb->scsw.cmd.count);
267293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
268293d984fSPeter Tiedemann 	while ((skb = skb_dequeue(&ch->io_queue))) {
269293d984fSPeter Tiedemann 		priv->stats.tx_packets++;
270293d984fSPeter Tiedemann 		priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
271293d984fSPeter Tiedemann 		if (first) {
272293d984fSPeter Tiedemann 			priv->stats.tx_bytes += 2;
273293d984fSPeter Tiedemann 			first = 0;
274293d984fSPeter Tiedemann 		}
275bba5850cSDavid S. Miller 		refcount_dec(&skb->users);
276293d984fSPeter Tiedemann 		dev_kfree_skb_irq(skb);
277293d984fSPeter Tiedemann 	}
278293d984fSPeter Tiedemann 	spin_lock(&ch->collect_lock);
279293d984fSPeter Tiedemann 	clear_normalized_cda(&ch->ccw[4]);
280293d984fSPeter Tiedemann 	if (ch->collect_len > 0) {
281293d984fSPeter Tiedemann 		int rc;
282293d984fSPeter Tiedemann 
283293d984fSPeter Tiedemann 		if (ctcm_checkalloc_buffer(ch)) {
284293d984fSPeter Tiedemann 			spin_unlock(&ch->collect_lock);
285293d984fSPeter Tiedemann 			return;
286293d984fSPeter Tiedemann 		}
287293d984fSPeter Tiedemann 		ch->trans_skb->data = ch->trans_skb_data;
288293d984fSPeter Tiedemann 		skb_reset_tail_pointer(ch->trans_skb);
289293d984fSPeter Tiedemann 		ch->trans_skb->len = 0;
290293d984fSPeter Tiedemann 		if (ch->prof.maxmulti < (ch->collect_len + 2))
291293d984fSPeter Tiedemann 			ch->prof.maxmulti = ch->collect_len + 2;
292293d984fSPeter Tiedemann 		if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
293293d984fSPeter Tiedemann 			ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
294293d984fSPeter Tiedemann 		*((__u16 *)skb_put(ch->trans_skb, 2)) = ch->collect_len + 2;
295293d984fSPeter Tiedemann 		i = 0;
296293d984fSPeter Tiedemann 		while ((skb = skb_dequeue(&ch->collect_queue))) {
297293d984fSPeter Tiedemann 			skb_copy_from_linear_data(skb,
298293d984fSPeter Tiedemann 				skb_put(ch->trans_skb, skb->len), skb->len);
299293d984fSPeter Tiedemann 			priv->stats.tx_packets++;
300293d984fSPeter Tiedemann 			priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
301bba5850cSDavid S. Miller 			refcount_dec(&skb->users);
302293d984fSPeter Tiedemann 			dev_kfree_skb_irq(skb);
303293d984fSPeter Tiedemann 			i++;
304293d984fSPeter Tiedemann 		}
305293d984fSPeter Tiedemann 		ch->collect_len = 0;
306293d984fSPeter Tiedemann 		spin_unlock(&ch->collect_lock);
307293d984fSPeter Tiedemann 		ch->ccw[1].count = ch->trans_skb->len;
308293d984fSPeter Tiedemann 		fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
309ee6edb97SAya Mahfouz 		ch->prof.send_stamp = jiffies;
3104f6e01f3SJulian Wiedmann 		rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
311293d984fSPeter Tiedemann 		ch->prof.doios_multi++;
312293d984fSPeter Tiedemann 		if (rc != 0) {
313293d984fSPeter Tiedemann 			priv->stats.tx_dropped += i;
314293d984fSPeter Tiedemann 			priv->stats.tx_errors += i;
315293d984fSPeter Tiedemann 			fsm_deltimer(&ch->timer);
316293d984fSPeter Tiedemann 			ctcm_ccw_check_rc(ch, rc, "chained TX");
317293d984fSPeter Tiedemann 		}
318293d984fSPeter Tiedemann 	} else {
319293d984fSPeter Tiedemann 		spin_unlock(&ch->collect_lock);
320293d984fSPeter Tiedemann 		fsm_newstate(fi, CTC_STATE_TXIDLE);
321293d984fSPeter Tiedemann 	}
322293d984fSPeter Tiedemann 	ctcm_clear_busy_do(dev);
323293d984fSPeter Tiedemann }
324293d984fSPeter Tiedemann 
325a962cc4bSHeiko Carstens /*
326293d984fSPeter Tiedemann  * Initial data is sent.
327293d984fSPeter Tiedemann  * Notify device statemachine that we are up and
328293d984fSPeter Tiedemann  * running.
329293d984fSPeter Tiedemann  *
330293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
331293d984fSPeter Tiedemann  * event	The event, just happened.
332293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
333293d984fSPeter Tiedemann  */
ctcm_chx_txidle(fsm_instance * fi,int event,void * arg)334293d984fSPeter Tiedemann void ctcm_chx_txidle(fsm_instance *fi, int event, void *arg)
335293d984fSPeter Tiedemann {
336293d984fSPeter Tiedemann 	struct channel *ch = arg;
337293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
338261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
339293d984fSPeter Tiedemann 
340aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name);
341aa3f2cb6SPeter Tiedemann 
342293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
343293d984fSPeter Tiedemann 	fsm_newstate(fi, CTC_STATE_TXIDLE);
344293d984fSPeter Tiedemann 	fsm_event(priv->fsm, DEV_EVENT_TXUP, ch->netdev);
345293d984fSPeter Tiedemann }
346293d984fSPeter Tiedemann 
347a962cc4bSHeiko Carstens /*
348293d984fSPeter Tiedemann  * Got normal data, check for sanity, queue it up, allocate new buffer
349293d984fSPeter Tiedemann  * trigger bottom half, and initiate next read.
350293d984fSPeter Tiedemann  *
351293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
352293d984fSPeter Tiedemann  * event	The event, just happened.
353293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
354293d984fSPeter Tiedemann  */
chx_rx(fsm_instance * fi,int event,void * arg)355293d984fSPeter Tiedemann static void chx_rx(fsm_instance *fi, int event, void *arg)
356293d984fSPeter Tiedemann {
357293d984fSPeter Tiedemann 	struct channel *ch = arg;
358293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
359261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
36023d805b6SPeter Oberparleiter 	int len = ch->max_bufsize - ch->irb->scsw.cmd.count;
361293d984fSPeter Tiedemann 	struct sk_buff *skb = ch->trans_skb;
362293d984fSPeter Tiedemann 	__u16 block_len = *((__u16 *)skb->data);
363293d984fSPeter Tiedemann 	int check_len;
364293d984fSPeter Tiedemann 	int rc;
365293d984fSPeter Tiedemann 
366293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
367293d984fSPeter Tiedemann 	if (len < 8) {
368aa3f2cb6SPeter Tiedemann 		CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
369aa3f2cb6SPeter Tiedemann 			"%s(%s): got packet with length %d < 8\n",
370aa3f2cb6SPeter Tiedemann 					CTCM_FUNTAIL, dev->name, len);
371293d984fSPeter Tiedemann 		priv->stats.rx_dropped++;
372293d984fSPeter Tiedemann 		priv->stats.rx_length_errors++;
373293d984fSPeter Tiedemann 		goto again;
374293d984fSPeter Tiedemann 	}
375293d984fSPeter Tiedemann 	if (len > ch->max_bufsize) {
376aa3f2cb6SPeter Tiedemann 		CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
377aa3f2cb6SPeter Tiedemann 			"%s(%s): got packet with length %d > %d\n",
378aa3f2cb6SPeter Tiedemann 				CTCM_FUNTAIL, dev->name, len, ch->max_bufsize);
379293d984fSPeter Tiedemann 		priv->stats.rx_dropped++;
380293d984fSPeter Tiedemann 		priv->stats.rx_length_errors++;
381293d984fSPeter Tiedemann 		goto again;
382293d984fSPeter Tiedemann 	}
383293d984fSPeter Tiedemann 
384293d984fSPeter Tiedemann 	/*
385293d984fSPeter Tiedemann 	 * VM TCP seems to have a bug sending 2 trailing bytes of garbage.
386293d984fSPeter Tiedemann 	 */
387293d984fSPeter Tiedemann 	switch (ch->protocol) {
388293d984fSPeter Tiedemann 	case CTCM_PROTO_S390:
389293d984fSPeter Tiedemann 	case CTCM_PROTO_OS390:
390293d984fSPeter Tiedemann 		check_len = block_len + 2;
391293d984fSPeter Tiedemann 		break;
392293d984fSPeter Tiedemann 	default:
393293d984fSPeter Tiedemann 		check_len = block_len;
394293d984fSPeter Tiedemann 		break;
395293d984fSPeter Tiedemann 	}
396293d984fSPeter Tiedemann 	if ((len < block_len) || (len > check_len)) {
397aa3f2cb6SPeter Tiedemann 		CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
398aa3f2cb6SPeter Tiedemann 			"%s(%s): got block length %d != rx length %d\n",
399aa3f2cb6SPeter Tiedemann 				CTCM_FUNTAIL, dev->name, block_len, len);
400293d984fSPeter Tiedemann 		if (do_debug)
401293d984fSPeter Tiedemann 			ctcmpc_dump_skb(skb, 0);
402293d984fSPeter Tiedemann 
403293d984fSPeter Tiedemann 		*((__u16 *)skb->data) = len;
404293d984fSPeter Tiedemann 		priv->stats.rx_dropped++;
405293d984fSPeter Tiedemann 		priv->stats.rx_length_errors++;
406293d984fSPeter Tiedemann 		goto again;
407293d984fSPeter Tiedemann 	}
408fb8585fcSRoel Kluin 	if (block_len > 2) {
409fb8585fcSRoel Kluin 		*((__u16 *)skb->data) = block_len - 2;
410293d984fSPeter Tiedemann 		ctcm_unpack_skb(ch, skb);
411293d984fSPeter Tiedemann 	}
412293d984fSPeter Tiedemann  again:
413293d984fSPeter Tiedemann 	skb->data = ch->trans_skb_data;
414293d984fSPeter Tiedemann 	skb_reset_tail_pointer(skb);
415293d984fSPeter Tiedemann 	skb->len = 0;
416293d984fSPeter Tiedemann 	if (ctcm_checkalloc_buffer(ch))
417293d984fSPeter Tiedemann 		return;
418293d984fSPeter Tiedemann 	ch->ccw[1].count = ch->max_bufsize;
4194f6e01f3SJulian Wiedmann 	rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
420293d984fSPeter Tiedemann 	if (rc != 0)
421293d984fSPeter Tiedemann 		ctcm_ccw_check_rc(ch, rc, "normal RX");
422293d984fSPeter Tiedemann }
423293d984fSPeter Tiedemann 
424a962cc4bSHeiko Carstens /*
425293d984fSPeter Tiedemann  * Initialize connection by sending a __u16 of value 0.
426293d984fSPeter Tiedemann  *
427293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
428293d984fSPeter Tiedemann  * event	The event, just happened.
429293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
430293d984fSPeter Tiedemann  */
chx_firstio(fsm_instance * fi,int event,void * arg)431293d984fSPeter Tiedemann static void chx_firstio(fsm_instance *fi, int event, void *arg)
432293d984fSPeter Tiedemann {
433293d984fSPeter Tiedemann 	int rc;
434aa3f2cb6SPeter Tiedemann 	struct channel *ch = arg;
435aa3f2cb6SPeter Tiedemann 	int fsmstate = fsm_getstate(fi);
436293d984fSPeter Tiedemann 
437aa3f2cb6SPeter Tiedemann 	CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
438aa3f2cb6SPeter Tiedemann 		"%s(%s) : %02x",
439aa3f2cb6SPeter Tiedemann 		CTCM_FUNTAIL, ch->id, fsmstate);
440293d984fSPeter Tiedemann 
441aa3f2cb6SPeter Tiedemann 	ch->sense_rc = 0;	/* reset unit check report control */
442aa3f2cb6SPeter Tiedemann 	if (fsmstate == CTC_STATE_TXIDLE)
443aa3f2cb6SPeter Tiedemann 		CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
444aa3f2cb6SPeter Tiedemann 			"%s(%s): remote side issued READ?, init.\n",
445aa3f2cb6SPeter Tiedemann 				CTCM_FUNTAIL, ch->id);
446293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
447293d984fSPeter Tiedemann 	if (ctcm_checkalloc_buffer(ch))
448293d984fSPeter Tiedemann 		return;
449aa3f2cb6SPeter Tiedemann 	if ((fsmstate == CTC_STATE_SETUPWAIT) &&
450293d984fSPeter Tiedemann 	    (ch->protocol == CTCM_PROTO_OS390)) {
451293d984fSPeter Tiedemann 		/* OS/390 resp. z/OS */
4523c09e264SUrsula Braun 		if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
453293d984fSPeter Tiedemann 			*((__u16 *)ch->trans_skb->data) = CTCM_INITIAL_BLOCKLEN;
454293d984fSPeter Tiedemann 			fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC,
455293d984fSPeter Tiedemann 				     CTC_EVENT_TIMER, ch);
456293d984fSPeter Tiedemann 			chx_rxidle(fi, event, arg);
457293d984fSPeter Tiedemann 		} else {
458293d984fSPeter Tiedemann 			struct net_device *dev = ch->netdev;
459261893d3SPeter Tiedemann 			struct ctcm_priv *priv = dev->ml_priv;
460293d984fSPeter Tiedemann 			fsm_newstate(fi, CTC_STATE_TXIDLE);
461293d984fSPeter Tiedemann 			fsm_event(priv->fsm, DEV_EVENT_TXUP, dev);
462293d984fSPeter Tiedemann 		}
463293d984fSPeter Tiedemann 		return;
464293d984fSPeter Tiedemann 	}
465293d984fSPeter Tiedemann 	/*
466293d984fSPeter Tiedemann 	 * Don't setup a timer for receiving the initial RX frame
467293d984fSPeter Tiedemann 	 * if in compatibility mode, since VM TCP delays the initial
468293d984fSPeter Tiedemann 	 * frame until it has some data to send.
469293d984fSPeter Tiedemann 	 */
4703c09e264SUrsula Braun 	if ((CHANNEL_DIRECTION(ch->flags) == CTCM_WRITE) ||
471293d984fSPeter Tiedemann 	    (ch->protocol != CTCM_PROTO_S390))
472293d984fSPeter Tiedemann 		fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
473293d984fSPeter Tiedemann 
474293d984fSPeter Tiedemann 	*((__u16 *)ch->trans_skb->data) = CTCM_INITIAL_BLOCKLEN;
475293d984fSPeter Tiedemann 	ch->ccw[1].count = 2;	/* Transfer only length */
476293d984fSPeter Tiedemann 
4773c09e264SUrsula Braun 	fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)
478293d984fSPeter Tiedemann 		     ? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
4794f6e01f3SJulian Wiedmann 	rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
480293d984fSPeter Tiedemann 	if (rc != 0) {
481293d984fSPeter Tiedemann 		fsm_deltimer(&ch->timer);
482293d984fSPeter Tiedemann 		fsm_newstate(fi, CTC_STATE_SETUPWAIT);
483293d984fSPeter Tiedemann 		ctcm_ccw_check_rc(ch, rc, "init IO");
484293d984fSPeter Tiedemann 	}
485293d984fSPeter Tiedemann 	/*
486293d984fSPeter Tiedemann 	 * If in compatibility mode since we don't setup a timer, we
487293d984fSPeter Tiedemann 	 * also signal RX channel up immediately. This enables us
488293d984fSPeter Tiedemann 	 * to send packets early which in turn usually triggers some
489293d984fSPeter Tiedemann 	 * reply from VM TCP which brings up the RX channel to it's
490293d984fSPeter Tiedemann 	 * final state.
491293d984fSPeter Tiedemann 	 */
4923c09e264SUrsula Braun 	if ((CHANNEL_DIRECTION(ch->flags) == CTCM_READ) &&
493293d984fSPeter Tiedemann 	    (ch->protocol == CTCM_PROTO_S390)) {
494293d984fSPeter Tiedemann 		struct net_device *dev = ch->netdev;
495261893d3SPeter Tiedemann 		struct ctcm_priv *priv = dev->ml_priv;
496293d984fSPeter Tiedemann 		fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
497293d984fSPeter Tiedemann 	}
498293d984fSPeter Tiedemann }
499293d984fSPeter Tiedemann 
500a962cc4bSHeiko Carstens /*
501293d984fSPeter Tiedemann  * Got initial data, check it. If OK,
502293d984fSPeter Tiedemann  * notify device statemachine that we are up and
503293d984fSPeter Tiedemann  * running.
504293d984fSPeter Tiedemann  *
505293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
506293d984fSPeter Tiedemann  * event	The event, just happened.
507293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
508293d984fSPeter Tiedemann  */
chx_rxidle(fsm_instance * fi,int event,void * arg)509293d984fSPeter Tiedemann static void chx_rxidle(fsm_instance *fi, int event, void *arg)
510293d984fSPeter Tiedemann {
511293d984fSPeter Tiedemann 	struct channel *ch = arg;
512293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
513261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
514293d984fSPeter Tiedemann 	__u16 buflen;
515293d984fSPeter Tiedemann 	int rc;
516293d984fSPeter Tiedemann 
517293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
518293d984fSPeter Tiedemann 	buflen = *((__u16 *)ch->trans_skb->data);
519aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("%s: %s: Initial RX count = %d\n",
520aa3f2cb6SPeter Tiedemann 			__func__, dev->name, buflen);
521293d984fSPeter Tiedemann 
522293d984fSPeter Tiedemann 	if (buflen >= CTCM_INITIAL_BLOCKLEN) {
523293d984fSPeter Tiedemann 		if (ctcm_checkalloc_buffer(ch))
524293d984fSPeter Tiedemann 			return;
525293d984fSPeter Tiedemann 		ch->ccw[1].count = ch->max_bufsize;
526293d984fSPeter Tiedemann 		fsm_newstate(fi, CTC_STATE_RXIDLE);
5274f6e01f3SJulian Wiedmann 		rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
528293d984fSPeter Tiedemann 		if (rc != 0) {
529293d984fSPeter Tiedemann 			fsm_newstate(fi, CTC_STATE_RXINIT);
530293d984fSPeter Tiedemann 			ctcm_ccw_check_rc(ch, rc, "initial RX");
531293d984fSPeter Tiedemann 		} else
532293d984fSPeter Tiedemann 			fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
533293d984fSPeter Tiedemann 	} else {
534aa3f2cb6SPeter Tiedemann 		CTCM_PR_DEBUG("%s: %s: Initial RX count %d not %d\n",
535aa3f2cb6SPeter Tiedemann 				__func__, dev->name,
536aa3f2cb6SPeter Tiedemann 					buflen, CTCM_INITIAL_BLOCKLEN);
537293d984fSPeter Tiedemann 		chx_firstio(fi, event, arg);
538293d984fSPeter Tiedemann 	}
539293d984fSPeter Tiedemann }
540293d984fSPeter Tiedemann 
541a962cc4bSHeiko Carstens /*
542293d984fSPeter Tiedemann  * Set channel into extended mode.
543293d984fSPeter Tiedemann  *
544293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
545293d984fSPeter Tiedemann  * event	The event, just happened.
546293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
547293d984fSPeter Tiedemann  */
ctcm_chx_setmode(fsm_instance * fi,int event,void * arg)548293d984fSPeter Tiedemann static void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg)
549293d984fSPeter Tiedemann {
550293d984fSPeter Tiedemann 	struct channel *ch = arg;
551293d984fSPeter Tiedemann 	int rc;
552293d984fSPeter Tiedemann 	unsigned long saveflags = 0;
553293d984fSPeter Tiedemann 	int timeout = CTCM_TIME_5_SEC;
554293d984fSPeter Tiedemann 
555293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
556293d984fSPeter Tiedemann 	if (IS_MPC(ch)) {
557293d984fSPeter Tiedemann 		timeout = 1500;
558aa3f2cb6SPeter Tiedemann 		CTCM_PR_DEBUG("enter %s: cp=%i ch=0x%p id=%s\n",
559aa3f2cb6SPeter Tiedemann 				__func__, smp_processor_id(), ch, ch->id);
560293d984fSPeter Tiedemann 	}
561293d984fSPeter Tiedemann 	fsm_addtimer(&ch->timer, timeout, CTC_EVENT_TIMER, ch);
562293d984fSPeter Tiedemann 	fsm_newstate(fi, CTC_STATE_SETUPWAIT);
563aa3f2cb6SPeter Tiedemann 	CTCM_CCW_DUMP((char *)&ch->ccw[6], sizeof(struct ccw1) * 2);
564293d984fSPeter Tiedemann 
565293d984fSPeter Tiedemann 	if (event == CTC_EVENT_TIMER)	/* only for timer not yet locked */
566293d984fSPeter Tiedemann 		spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
567293d984fSPeter Tiedemann 			/* Such conditional locking is undeterministic in
568293d984fSPeter Tiedemann 			 * static view. => ignore sparse warnings here. */
569293d984fSPeter Tiedemann 
5704f6e01f3SJulian Wiedmann 	rc = ccw_device_start(ch->cdev, &ch->ccw[6], 0, 0xff, 0);
571293d984fSPeter Tiedemann 	if (event == CTC_EVENT_TIMER)	/* see above comments */
572293d984fSPeter Tiedemann 		spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
573293d984fSPeter Tiedemann 	if (rc != 0) {
574293d984fSPeter Tiedemann 		fsm_deltimer(&ch->timer);
575293d984fSPeter Tiedemann 		fsm_newstate(fi, CTC_STATE_STARTWAIT);
576293d984fSPeter Tiedemann 		ctcm_ccw_check_rc(ch, rc, "set Mode");
577293d984fSPeter Tiedemann 	} else
578293d984fSPeter Tiedemann 		ch->retry = 0;
579293d984fSPeter Tiedemann }
580293d984fSPeter Tiedemann 
581a962cc4bSHeiko Carstens /*
582293d984fSPeter Tiedemann  * Setup channel.
583293d984fSPeter Tiedemann  *
584293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
585293d984fSPeter Tiedemann  * event	The event, just happened.
586293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
587293d984fSPeter Tiedemann  */
ctcm_chx_start(fsm_instance * fi,int event,void * arg)588293d984fSPeter Tiedemann static void ctcm_chx_start(fsm_instance *fi, int event, void *arg)
589293d984fSPeter Tiedemann {
590293d984fSPeter Tiedemann 	struct channel *ch	= arg;
591293d984fSPeter Tiedemann 	unsigned long saveflags;
592aa3f2cb6SPeter Tiedemann 	int rc;
593293d984fSPeter Tiedemann 
594aa3f2cb6SPeter Tiedemann 	CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s): %s",
595aa3f2cb6SPeter Tiedemann 		CTCM_FUNTAIL, ch->id,
5963c09e264SUrsula Braun 		(CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? "RX" : "TX");
597293d984fSPeter Tiedemann 
598293d984fSPeter Tiedemann 	if (ch->trans_skb != NULL) {
599293d984fSPeter Tiedemann 		clear_normalized_cda(&ch->ccw[1]);
600293d984fSPeter Tiedemann 		dev_kfree_skb(ch->trans_skb);
601293d984fSPeter Tiedemann 		ch->trans_skb = NULL;
602293d984fSPeter Tiedemann 	}
6033c09e264SUrsula Braun 	if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
604293d984fSPeter Tiedemann 		ch->ccw[1].cmd_code = CCW_CMD_READ;
605293d984fSPeter Tiedemann 		ch->ccw[1].flags = CCW_FLAG_SLI;
606293d984fSPeter Tiedemann 		ch->ccw[1].count = 0;
607293d984fSPeter Tiedemann 	} else {
608293d984fSPeter Tiedemann 		ch->ccw[1].cmd_code = CCW_CMD_WRITE;
609293d984fSPeter Tiedemann 		ch->ccw[1].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
610293d984fSPeter Tiedemann 		ch->ccw[1].count = 0;
611293d984fSPeter Tiedemann 	}
612293d984fSPeter Tiedemann 	if (ctcm_checkalloc_buffer(ch)) {
613aa3f2cb6SPeter Tiedemann 		CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
614aa3f2cb6SPeter Tiedemann 			"%s(%s): %s trans_skb alloc delayed "
615aa3f2cb6SPeter Tiedemann 			"until first transfer",
616aa3f2cb6SPeter Tiedemann 			CTCM_FUNTAIL, ch->id,
6173c09e264SUrsula Braun 			(CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ?
6183c09e264SUrsula Braun 				"RX" : "TX");
619293d984fSPeter Tiedemann 	}
620293d984fSPeter Tiedemann 	ch->ccw[0].cmd_code = CCW_CMD_PREPARE;
621293d984fSPeter Tiedemann 	ch->ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
622293d984fSPeter Tiedemann 	ch->ccw[0].count = 0;
623293d984fSPeter Tiedemann 	ch->ccw[0].cda = 0;
624293d984fSPeter Tiedemann 	ch->ccw[2].cmd_code = CCW_CMD_NOOP;	/* jointed CE + DE */
625293d984fSPeter Tiedemann 	ch->ccw[2].flags = CCW_FLAG_SLI;
626293d984fSPeter Tiedemann 	ch->ccw[2].count = 0;
627293d984fSPeter Tiedemann 	ch->ccw[2].cda = 0;
628293d984fSPeter Tiedemann 	memcpy(&ch->ccw[3], &ch->ccw[0], sizeof(struct ccw1) * 3);
629293d984fSPeter Tiedemann 	ch->ccw[4].cda = 0;
630293d984fSPeter Tiedemann 	ch->ccw[4].flags &= ~CCW_FLAG_IDA;
631293d984fSPeter Tiedemann 
632293d984fSPeter Tiedemann 	fsm_newstate(fi, CTC_STATE_STARTWAIT);
633293d984fSPeter Tiedemann 	fsm_addtimer(&ch->timer, 1000, CTC_EVENT_TIMER, ch);
634293d984fSPeter Tiedemann 	spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
6354f6e01f3SJulian Wiedmann 	rc = ccw_device_halt(ch->cdev, 0);
636293d984fSPeter Tiedemann 	spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
637293d984fSPeter Tiedemann 	if (rc != 0) {
638293d984fSPeter Tiedemann 		if (rc != -EBUSY)
639293d984fSPeter Tiedemann 			fsm_deltimer(&ch->timer);
640293d984fSPeter Tiedemann 		ctcm_ccw_check_rc(ch, rc, "initial HaltIO");
641293d984fSPeter Tiedemann 	}
642293d984fSPeter Tiedemann }
643293d984fSPeter Tiedemann 
644a962cc4bSHeiko Carstens /*
645293d984fSPeter Tiedemann  * Shutdown a channel.
646293d984fSPeter Tiedemann  *
647293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
648293d984fSPeter Tiedemann  * event	The event, just happened.
649293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
650293d984fSPeter Tiedemann  */
ctcm_chx_haltio(fsm_instance * fi,int event,void * arg)651293d984fSPeter Tiedemann static void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg)
652293d984fSPeter Tiedemann {
653293d984fSPeter Tiedemann 	struct channel *ch = arg;
654293d984fSPeter Tiedemann 	unsigned long saveflags = 0;
655293d984fSPeter Tiedemann 	int rc;
656293d984fSPeter Tiedemann 	int oldstate;
657293d984fSPeter Tiedemann 
658293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
659293d984fSPeter Tiedemann 	if (IS_MPC(ch))
660293d984fSPeter Tiedemann 		fsm_deltimer(&ch->sweep_timer);
661293d984fSPeter Tiedemann 
662293d984fSPeter Tiedemann 	fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
663293d984fSPeter Tiedemann 
664293d984fSPeter Tiedemann 	if (event == CTC_EVENT_STOP)	/* only for STOP not yet locked */
665293d984fSPeter Tiedemann 		spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
666293d984fSPeter Tiedemann 			/* Such conditional locking is undeterministic in
667293d984fSPeter Tiedemann 			 * static view. => ignore sparse warnings here. */
668293d984fSPeter Tiedemann 	oldstate = fsm_getstate(fi);
669293d984fSPeter Tiedemann 	fsm_newstate(fi, CTC_STATE_TERM);
6704f6e01f3SJulian Wiedmann 	rc = ccw_device_halt(ch->cdev, 0);
671293d984fSPeter Tiedemann 
672293d984fSPeter Tiedemann 	if (event == CTC_EVENT_STOP)
673293d984fSPeter Tiedemann 		spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
674293d984fSPeter Tiedemann 			/* see remark above about conditional locking */
675293d984fSPeter Tiedemann 
676293d984fSPeter Tiedemann 	if (rc != 0 && rc != -EBUSY) {
677293d984fSPeter Tiedemann 		fsm_deltimer(&ch->timer);
678293d984fSPeter Tiedemann 		if (event != CTC_EVENT_STOP) {
679293d984fSPeter Tiedemann 			fsm_newstate(fi, oldstate);
680aa3f2cb6SPeter Tiedemann 			ctcm_ccw_check_rc(ch, rc, (char *)__func__);
681293d984fSPeter Tiedemann 		}
682293d984fSPeter Tiedemann 	}
683293d984fSPeter Tiedemann }
684293d984fSPeter Tiedemann 
685a962cc4bSHeiko Carstens /*
686293d984fSPeter Tiedemann  * Cleanup helper for chx_fail and chx_stopped
687293d984fSPeter Tiedemann  * cleanup channels queue and notify interface statemachine.
688293d984fSPeter Tiedemann  *
689293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
690293d984fSPeter Tiedemann  * state	The next state (depending on caller).
691293d984fSPeter Tiedemann  * ch		The channel to operate on.
692293d984fSPeter Tiedemann  */
ctcm_chx_cleanup(fsm_instance * fi,int state,struct channel * ch)693293d984fSPeter Tiedemann static void ctcm_chx_cleanup(fsm_instance *fi, int state,
694293d984fSPeter Tiedemann 		struct channel *ch)
695293d984fSPeter Tiedemann {
696293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
697261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
698293d984fSPeter Tiedemann 
699aa3f2cb6SPeter Tiedemann 	CTCM_DBF_TEXT_(SETUP, CTC_DBF_NOTICE,
700aa3f2cb6SPeter Tiedemann 			"%s(%s): %s[%d]\n",
701aa3f2cb6SPeter Tiedemann 			CTCM_FUNTAIL, dev->name, ch->id, state);
702293d984fSPeter Tiedemann 
703293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
704293d984fSPeter Tiedemann 	if (IS_MPC(ch))
705293d984fSPeter Tiedemann 		fsm_deltimer(&ch->sweep_timer);
706293d984fSPeter Tiedemann 
707293d984fSPeter Tiedemann 	fsm_newstate(fi, state);
708293d984fSPeter Tiedemann 	if (state == CTC_STATE_STOPPED && ch->trans_skb != NULL) {
709293d984fSPeter Tiedemann 		clear_normalized_cda(&ch->ccw[1]);
710293d984fSPeter Tiedemann 		dev_kfree_skb_any(ch->trans_skb);
711293d984fSPeter Tiedemann 		ch->trans_skb = NULL;
712293d984fSPeter Tiedemann 	}
713293d984fSPeter Tiedemann 
714293d984fSPeter Tiedemann 	ch->th_seg = 0x00;
715293d984fSPeter Tiedemann 	ch->th_seq_num = 0x00;
7163c09e264SUrsula Braun 	if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
717293d984fSPeter Tiedemann 		skb_queue_purge(&ch->io_queue);
718293d984fSPeter Tiedemann 		fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
719293d984fSPeter Tiedemann 	} else {
720293d984fSPeter Tiedemann 		ctcm_purge_skb_queue(&ch->io_queue);
721293d984fSPeter Tiedemann 		if (IS_MPC(ch))
722293d984fSPeter Tiedemann 			ctcm_purge_skb_queue(&ch->sweep_queue);
723293d984fSPeter Tiedemann 		spin_lock(&ch->collect_lock);
724293d984fSPeter Tiedemann 		ctcm_purge_skb_queue(&ch->collect_queue);
725293d984fSPeter Tiedemann 		ch->collect_len = 0;
726293d984fSPeter Tiedemann 		spin_unlock(&ch->collect_lock);
727293d984fSPeter Tiedemann 		fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
728293d984fSPeter Tiedemann 	}
729293d984fSPeter Tiedemann }
730293d984fSPeter Tiedemann 
731a962cc4bSHeiko Carstens /*
732293d984fSPeter Tiedemann  * A channel has successfully been halted.
733293d984fSPeter Tiedemann  * Cleanup it's queue and notify interface statemachine.
734293d984fSPeter Tiedemann  *
735293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
736293d984fSPeter Tiedemann  * event	The event, just happened.
737293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
738293d984fSPeter Tiedemann  */
ctcm_chx_stopped(fsm_instance * fi,int event,void * arg)739293d984fSPeter Tiedemann static void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg)
740293d984fSPeter Tiedemann {
741293d984fSPeter Tiedemann 	ctcm_chx_cleanup(fi, CTC_STATE_STOPPED, arg);
742293d984fSPeter Tiedemann }
743293d984fSPeter Tiedemann 
744a962cc4bSHeiko Carstens /*
745293d984fSPeter Tiedemann  * A stop command from device statemachine arrived and we are in
746293d984fSPeter Tiedemann  * not operational mode. Set state to stopped.
747293d984fSPeter Tiedemann  *
748293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
749293d984fSPeter Tiedemann  * event	The event, just happened.
750293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
751293d984fSPeter Tiedemann  */
ctcm_chx_stop(fsm_instance * fi,int event,void * arg)752293d984fSPeter Tiedemann static void ctcm_chx_stop(fsm_instance *fi, int event, void *arg)
753293d984fSPeter Tiedemann {
754293d984fSPeter Tiedemann 	fsm_newstate(fi, CTC_STATE_STOPPED);
755293d984fSPeter Tiedemann }
756293d984fSPeter Tiedemann 
757a962cc4bSHeiko Carstens /*
758293d984fSPeter Tiedemann  * A machine check for no path, not operational status or gone device has
759293d984fSPeter Tiedemann  * happened.
760293d984fSPeter Tiedemann  * Cleanup queue and notify interface statemachine.
761293d984fSPeter Tiedemann  *
762293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
763293d984fSPeter Tiedemann  * event	The event, just happened.
764293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
765293d984fSPeter Tiedemann  */
ctcm_chx_fail(fsm_instance * fi,int event,void * arg)766293d984fSPeter Tiedemann static void ctcm_chx_fail(fsm_instance *fi, int event, void *arg)
767293d984fSPeter Tiedemann {
768293d984fSPeter Tiedemann 	ctcm_chx_cleanup(fi, CTC_STATE_NOTOP, arg);
769293d984fSPeter Tiedemann }
770293d984fSPeter Tiedemann 
771a962cc4bSHeiko Carstens /*
772293d984fSPeter Tiedemann  * Handle error during setup of channel.
773293d984fSPeter Tiedemann  *
774293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
775293d984fSPeter Tiedemann  * event	The event, just happened.
776293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
777293d984fSPeter Tiedemann  */
ctcm_chx_setuperr(fsm_instance * fi,int event,void * arg)778293d984fSPeter Tiedemann static void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg)
779293d984fSPeter Tiedemann {
780293d984fSPeter Tiedemann 	struct channel *ch = arg;
781293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
782261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
783293d984fSPeter Tiedemann 
784293d984fSPeter Tiedemann 	/*
785293d984fSPeter Tiedemann 	 * Special case: Got UC_RCRESET on setmode.
786293d984fSPeter Tiedemann 	 * This means that remote side isn't setup. In this case
787293d984fSPeter Tiedemann 	 * simply retry after some 10 secs...
788293d984fSPeter Tiedemann 	 */
789293d984fSPeter Tiedemann 	if ((fsm_getstate(fi) == CTC_STATE_SETUPWAIT) &&
790293d984fSPeter Tiedemann 	    ((event == CTC_EVENT_UC_RCRESET) ||
791293d984fSPeter Tiedemann 	     (event == CTC_EVENT_UC_RSRESET))) {
792293d984fSPeter Tiedemann 		fsm_newstate(fi, CTC_STATE_STARTRETRY);
793293d984fSPeter Tiedemann 		fsm_deltimer(&ch->timer);
794293d984fSPeter Tiedemann 		fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
7953c09e264SUrsula Braun 		if (!IS_MPC(ch) &&
7963c09e264SUrsula Braun 		    (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)) {
7974f6e01f3SJulian Wiedmann 			int rc = ccw_device_halt(ch->cdev, 0);
798293d984fSPeter Tiedemann 			if (rc != 0)
799293d984fSPeter Tiedemann 				ctcm_ccw_check_rc(ch, rc,
800293d984fSPeter Tiedemann 					"HaltIO in chx_setuperr");
801293d984fSPeter Tiedemann 		}
802293d984fSPeter Tiedemann 		return;
803293d984fSPeter Tiedemann 	}
804293d984fSPeter Tiedemann 
805293d984fSPeter Tiedemann 	CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT,
806aa3f2cb6SPeter Tiedemann 		"%s(%s) : %s error during %s channel setup state=%s\n",
807aa3f2cb6SPeter Tiedemann 		CTCM_FUNTAIL, dev->name, ctc_ch_event_names[event],
8083c09e264SUrsula Braun 		(CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? "RX" : "TX",
809293d984fSPeter Tiedemann 		fsm_getstate_str(fi));
810293d984fSPeter Tiedemann 
8113c09e264SUrsula Braun 	if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
812293d984fSPeter Tiedemann 		fsm_newstate(fi, CTC_STATE_RXERR);
813293d984fSPeter Tiedemann 		fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
814293d984fSPeter Tiedemann 	} else {
815293d984fSPeter Tiedemann 		fsm_newstate(fi, CTC_STATE_TXERR);
816293d984fSPeter Tiedemann 		fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
817293d984fSPeter Tiedemann 	}
818293d984fSPeter Tiedemann }
819293d984fSPeter Tiedemann 
820a962cc4bSHeiko Carstens /*
821293d984fSPeter Tiedemann  * Restart a channel after an error.
822293d984fSPeter Tiedemann  *
823293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
824293d984fSPeter Tiedemann  * event	The event, just happened.
825293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
826293d984fSPeter Tiedemann  */
ctcm_chx_restart(fsm_instance * fi,int event,void * arg)827293d984fSPeter Tiedemann static void ctcm_chx_restart(fsm_instance *fi, int event, void *arg)
828293d984fSPeter Tiedemann {
829293d984fSPeter Tiedemann 	struct channel *ch = arg;
830293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
831293d984fSPeter Tiedemann 	unsigned long saveflags = 0;
832293d984fSPeter Tiedemann 	int oldstate;
833293d984fSPeter Tiedemann 	int rc;
834293d984fSPeter Tiedemann 
835aa3f2cb6SPeter Tiedemann 	CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
836aa3f2cb6SPeter Tiedemann 		"%s: %s[%d] of %s\n",
837aa3f2cb6SPeter Tiedemann 			CTCM_FUNTAIL, ch->id, event, dev->name);
838aa3f2cb6SPeter Tiedemann 
839293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
840aa3f2cb6SPeter Tiedemann 
841293d984fSPeter Tiedemann 	fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
842293d984fSPeter Tiedemann 	oldstate = fsm_getstate(fi);
843293d984fSPeter Tiedemann 	fsm_newstate(fi, CTC_STATE_STARTWAIT);
844293d984fSPeter Tiedemann 	if (event == CTC_EVENT_TIMER)	/* only for timer not yet locked */
845293d984fSPeter Tiedemann 		spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
846293d984fSPeter Tiedemann 			/* Such conditional locking is a known problem for
847293d984fSPeter Tiedemann 			 * sparse because its undeterministic in static view.
848293d984fSPeter Tiedemann 			 * Warnings should be ignored here. */
8494f6e01f3SJulian Wiedmann 	rc = ccw_device_halt(ch->cdev, 0);
850293d984fSPeter Tiedemann 	if (event == CTC_EVENT_TIMER)
851293d984fSPeter Tiedemann 		spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
852293d984fSPeter Tiedemann 	if (rc != 0) {
853293d984fSPeter Tiedemann 		if (rc != -EBUSY) {
854293d984fSPeter Tiedemann 		    fsm_deltimer(&ch->timer);
855293d984fSPeter Tiedemann 		    fsm_newstate(fi, oldstate);
856293d984fSPeter Tiedemann 		}
857293d984fSPeter Tiedemann 		ctcm_ccw_check_rc(ch, rc, "HaltIO in ctcm_chx_restart");
858293d984fSPeter Tiedemann 	}
859293d984fSPeter Tiedemann }
860293d984fSPeter Tiedemann 
861a962cc4bSHeiko Carstens /*
862293d984fSPeter Tiedemann  * Handle error during RX initial handshake (exchange of
863293d984fSPeter Tiedemann  * 0-length block header)
864293d984fSPeter Tiedemann  *
865293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
866293d984fSPeter Tiedemann  * event	The event, just happened.
867293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
868293d984fSPeter Tiedemann  */
ctcm_chx_rxiniterr(fsm_instance * fi,int event,void * arg)869293d984fSPeter Tiedemann static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg)
870293d984fSPeter Tiedemann {
871293d984fSPeter Tiedemann 	struct channel *ch = arg;
872293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
873261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
874293d984fSPeter Tiedemann 
875293d984fSPeter Tiedemann 	if (event == CTC_EVENT_TIMER) {
876293d984fSPeter Tiedemann 		if (!IS_MPCDEV(dev))
877293d984fSPeter Tiedemann 			/* TODO : check if MPC deletes timer somewhere */
878293d984fSPeter Tiedemann 			fsm_deltimer(&ch->timer);
879293d984fSPeter Tiedemann 		if (ch->retry++ < 3)
880293d984fSPeter Tiedemann 			ctcm_chx_restart(fi, event, arg);
881293d984fSPeter Tiedemann 		else {
882293d984fSPeter Tiedemann 			fsm_newstate(fi, CTC_STATE_RXERR);
883293d984fSPeter Tiedemann 			fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
884293d984fSPeter Tiedemann 		}
8852a7c6f2cSPeter Tiedemann 	} else {
8862a7c6f2cSPeter Tiedemann 		CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
8872a7c6f2cSPeter Tiedemann 			"%s(%s): %s in %s", CTCM_FUNTAIL, ch->id,
8882a7c6f2cSPeter Tiedemann 			ctc_ch_event_names[event], fsm_getstate_str(fi));
8892a7c6f2cSPeter Tiedemann 
8902a7c6f2cSPeter Tiedemann 		dev_warn(&dev->dev,
8912a7c6f2cSPeter Tiedemann 			"Initialization failed with RX/TX init handshake "
8922a7c6f2cSPeter Tiedemann 			"error %s\n", ctc_ch_event_names[event]);
8932a7c6f2cSPeter Tiedemann 	}
894293d984fSPeter Tiedemann }
895293d984fSPeter Tiedemann 
896a962cc4bSHeiko Carstens /*
897293d984fSPeter Tiedemann  * Notify device statemachine if we gave up initialization
898293d984fSPeter Tiedemann  * of RX channel.
899293d984fSPeter Tiedemann  *
900293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
901293d984fSPeter Tiedemann  * event	The event, just happened.
902293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
903293d984fSPeter Tiedemann  */
ctcm_chx_rxinitfail(fsm_instance * fi,int event,void * arg)904293d984fSPeter Tiedemann static void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg)
905293d984fSPeter Tiedemann {
906293d984fSPeter Tiedemann 	struct channel *ch = arg;
907293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
908261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
909293d984fSPeter Tiedemann 
910aa3f2cb6SPeter Tiedemann 	CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
911aa3f2cb6SPeter Tiedemann 			"%s(%s): RX %s busy, init. fail",
912aa3f2cb6SPeter Tiedemann 				CTCM_FUNTAIL, dev->name, ch->id);
913293d984fSPeter Tiedemann 	fsm_newstate(fi, CTC_STATE_RXERR);
914293d984fSPeter Tiedemann 	fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
915293d984fSPeter Tiedemann }
916293d984fSPeter Tiedemann 
917a962cc4bSHeiko Carstens /*
918293d984fSPeter Tiedemann  * Handle RX Unit check remote reset (remote disconnected)
919293d984fSPeter Tiedemann  *
920293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
921293d984fSPeter Tiedemann  * event	The event, just happened.
922293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
923293d984fSPeter Tiedemann  */
ctcm_chx_rxdisc(fsm_instance * fi,int event,void * arg)924293d984fSPeter Tiedemann static void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg)
925293d984fSPeter Tiedemann {
926293d984fSPeter Tiedemann 	struct channel *ch = arg;
927293d984fSPeter Tiedemann 	struct channel *ch2;
928293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
929261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
930293d984fSPeter Tiedemann 
931aa3f2cb6SPeter Tiedemann 	CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
932aa3f2cb6SPeter Tiedemann 			"%s: %s: remote disconnect - re-init ...",
933aa3f2cb6SPeter Tiedemann 				CTCM_FUNTAIL, dev->name);
934293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
935293d984fSPeter Tiedemann 	/*
936293d984fSPeter Tiedemann 	 * Notify device statemachine
937293d984fSPeter Tiedemann 	 */
938293d984fSPeter Tiedemann 	fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
939293d984fSPeter Tiedemann 	fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
940293d984fSPeter Tiedemann 
941293d984fSPeter Tiedemann 	fsm_newstate(fi, CTC_STATE_DTERM);
9423c09e264SUrsula Braun 	ch2 = priv->channel[CTCM_WRITE];
943293d984fSPeter Tiedemann 	fsm_newstate(ch2->fsm, CTC_STATE_DTERM);
944293d984fSPeter Tiedemann 
9454f6e01f3SJulian Wiedmann 	ccw_device_halt(ch->cdev, 0);
9464f6e01f3SJulian Wiedmann 	ccw_device_halt(ch2->cdev, 0);
947293d984fSPeter Tiedemann }
948293d984fSPeter Tiedemann 
949a962cc4bSHeiko Carstens /*
950293d984fSPeter Tiedemann  * Handle error during TX channel initialization.
951293d984fSPeter Tiedemann  *
952293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
953293d984fSPeter Tiedemann  * event	The event, just happened.
954293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
955293d984fSPeter Tiedemann  */
ctcm_chx_txiniterr(fsm_instance * fi,int event,void * arg)956293d984fSPeter Tiedemann static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg)
957293d984fSPeter Tiedemann {
958293d984fSPeter Tiedemann 	struct channel *ch = arg;
959293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
960261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
961293d984fSPeter Tiedemann 
962293d984fSPeter Tiedemann 	if (event == CTC_EVENT_TIMER) {
963293d984fSPeter Tiedemann 		fsm_deltimer(&ch->timer);
964293d984fSPeter Tiedemann 		if (ch->retry++ < 3)
965293d984fSPeter Tiedemann 			ctcm_chx_restart(fi, event, arg);
966293d984fSPeter Tiedemann 		else {
967293d984fSPeter Tiedemann 			fsm_newstate(fi, CTC_STATE_TXERR);
968293d984fSPeter Tiedemann 			fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
969293d984fSPeter Tiedemann 		}
970293d984fSPeter Tiedemann 	} else {
971293d984fSPeter Tiedemann 		CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
972aa3f2cb6SPeter Tiedemann 			"%s(%s): %s in %s", CTCM_FUNTAIL, ch->id,
973aa3f2cb6SPeter Tiedemann 			ctc_ch_event_names[event], fsm_getstate_str(fi));
974293d984fSPeter Tiedemann 
9752a7c6f2cSPeter Tiedemann 		dev_warn(&dev->dev,
9762a7c6f2cSPeter Tiedemann 			"Initialization failed with RX/TX init handshake "
9772a7c6f2cSPeter Tiedemann 			"error %s\n", ctc_ch_event_names[event]);
978293d984fSPeter Tiedemann 	}
979293d984fSPeter Tiedemann }
980293d984fSPeter Tiedemann 
981a962cc4bSHeiko Carstens /*
982293d984fSPeter Tiedemann  * Handle TX timeout by retrying operation.
983293d984fSPeter Tiedemann  *
984293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
985293d984fSPeter Tiedemann  * event	The event, just happened.
986293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
987293d984fSPeter Tiedemann  */
ctcm_chx_txretry(fsm_instance * fi,int event,void * arg)988293d984fSPeter Tiedemann static void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg)
989293d984fSPeter Tiedemann {
990293d984fSPeter Tiedemann 	struct channel *ch = arg;
991293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
992261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
993293d984fSPeter Tiedemann 	struct sk_buff *skb;
994293d984fSPeter Tiedemann 
995aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("Enter: %s: cp=%i ch=0x%p id=%s\n",
996aa3f2cb6SPeter Tiedemann 			__func__, smp_processor_id(), ch, ch->id);
997293d984fSPeter Tiedemann 
998293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
999293d984fSPeter Tiedemann 	if (ch->retry++ > 3) {
1000293d984fSPeter Tiedemann 		struct mpc_group *gptr = priv->mpcg;
1001aa3f2cb6SPeter Tiedemann 		CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO,
1002aa3f2cb6SPeter Tiedemann 				"%s: %s: retries exceeded",
1003aa3f2cb6SPeter Tiedemann 					CTCM_FUNTAIL, ch->id);
1004293d984fSPeter Tiedemann 		fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
1005293d984fSPeter Tiedemann 		/* call restart if not MPC or if MPC and mpcg fsm is ready.
1006293d984fSPeter Tiedemann 			use gptr as mpc indicator */
1007293d984fSPeter Tiedemann 		if (!(gptr && (fsm_getstate(gptr->fsm) != MPCG_STATE_READY)))
1008293d984fSPeter Tiedemann 			ctcm_chx_restart(fi, event, arg);
1009293d984fSPeter Tiedemann 		goto done;
1010293d984fSPeter Tiedemann 	}
1011293d984fSPeter Tiedemann 
1012aa3f2cb6SPeter Tiedemann 	CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
1013aa3f2cb6SPeter Tiedemann 			"%s : %s: retry %d",
1014aa3f2cb6SPeter Tiedemann 				CTCM_FUNTAIL, ch->id, ch->retry);
1015293d984fSPeter Tiedemann 	skb = skb_peek(&ch->io_queue);
1016293d984fSPeter Tiedemann 	if (skb) {
1017293d984fSPeter Tiedemann 		int rc = 0;
1018293d984fSPeter Tiedemann 		unsigned long saveflags = 0;
1019293d984fSPeter Tiedemann 		clear_normalized_cda(&ch->ccw[4]);
1020293d984fSPeter Tiedemann 		ch->ccw[4].count = skb->len;
1021293d984fSPeter Tiedemann 		if (set_normalized_cda(&ch->ccw[4], skb->data)) {
1022aa3f2cb6SPeter Tiedemann 			CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO,
1023aa3f2cb6SPeter Tiedemann 				"%s: %s: IDAL alloc failed",
1024aa3f2cb6SPeter Tiedemann 						CTCM_FUNTAIL, ch->id);
1025293d984fSPeter Tiedemann 			fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
1026293d984fSPeter Tiedemann 			ctcm_chx_restart(fi, event, arg);
1027293d984fSPeter Tiedemann 			goto done;
1028293d984fSPeter Tiedemann 		}
1029293d984fSPeter Tiedemann 		fsm_addtimer(&ch->timer, 1000, CTC_EVENT_TIMER, ch);
1030293d984fSPeter Tiedemann 		if (event == CTC_EVENT_TIMER) /* for TIMER not yet locked */
1031293d984fSPeter Tiedemann 			spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
1032293d984fSPeter Tiedemann 			/* Such conditional locking is a known problem for
1033293d984fSPeter Tiedemann 			 * sparse because its undeterministic in static view.
1034293d984fSPeter Tiedemann 			 * Warnings should be ignored here. */
1035293d984fSPeter Tiedemann 		if (do_debug_ccw)
1036293d984fSPeter Tiedemann 			ctcmpc_dumpit((char *)&ch->ccw[3],
1037293d984fSPeter Tiedemann 					sizeof(struct ccw1) * 3);
1038293d984fSPeter Tiedemann 
10394f6e01f3SJulian Wiedmann 		rc = ccw_device_start(ch->cdev, &ch->ccw[3], 0, 0xff, 0);
1040293d984fSPeter Tiedemann 		if (event == CTC_EVENT_TIMER)
1041293d984fSPeter Tiedemann 			spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev),
1042293d984fSPeter Tiedemann 					saveflags);
1043293d984fSPeter Tiedemann 		if (rc != 0) {
1044293d984fSPeter Tiedemann 			fsm_deltimer(&ch->timer);
1045293d984fSPeter Tiedemann 			ctcm_ccw_check_rc(ch, rc, "TX in chx_txretry");
1046293d984fSPeter Tiedemann 			ctcm_purge_skb_queue(&ch->io_queue);
1047293d984fSPeter Tiedemann 		}
1048293d984fSPeter Tiedemann 	}
1049293d984fSPeter Tiedemann done:
1050293d984fSPeter Tiedemann 	return;
1051293d984fSPeter Tiedemann }
1052293d984fSPeter Tiedemann 
1053a962cc4bSHeiko Carstens /*
1054293d984fSPeter Tiedemann  * Handle fatal errors during an I/O command.
1055293d984fSPeter Tiedemann  *
1056293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
1057293d984fSPeter Tiedemann  * event	The event, just happened.
1058293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
1059293d984fSPeter Tiedemann  */
ctcm_chx_iofatal(fsm_instance * fi,int event,void * arg)1060293d984fSPeter Tiedemann static void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg)
1061293d984fSPeter Tiedemann {
1062293d984fSPeter Tiedemann 	struct channel *ch = arg;
1063293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
1064261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
1065aa3f2cb6SPeter Tiedemann 	int rd = CHANNEL_DIRECTION(ch->flags);
1066293d984fSPeter Tiedemann 
1067293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
1068aa3f2cb6SPeter Tiedemann 	CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
1069aa3f2cb6SPeter Tiedemann 		"%s: %s: %s unrecoverable channel error",
10703c09e264SUrsula Braun 			CTCM_FUNTAIL, ch->id, rd == CTCM_READ ? "RX" : "TX");
1071aa3f2cb6SPeter Tiedemann 
1072293d984fSPeter Tiedemann 	if (IS_MPC(ch)) {
1073293d984fSPeter Tiedemann 		priv->stats.tx_dropped++;
1074293d984fSPeter Tiedemann 		priv->stats.tx_errors++;
1075293d984fSPeter Tiedemann 	}
10763c09e264SUrsula Braun 	if (rd == CTCM_READ) {
1077293d984fSPeter Tiedemann 		fsm_newstate(fi, CTC_STATE_RXERR);
1078293d984fSPeter Tiedemann 		fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
1079293d984fSPeter Tiedemann 	} else {
1080293d984fSPeter Tiedemann 		fsm_newstate(fi, CTC_STATE_TXERR);
1081293d984fSPeter Tiedemann 		fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
1082293d984fSPeter Tiedemann 	}
1083293d984fSPeter Tiedemann }
1084293d984fSPeter Tiedemann 
1085293d984fSPeter Tiedemann /*
1086293d984fSPeter Tiedemann  * The ctcm statemachine for a channel.
1087293d984fSPeter Tiedemann  */
1088293d984fSPeter Tiedemann const fsm_node ch_fsm[] = {
1089293d984fSPeter Tiedemann 	{ CTC_STATE_STOPPED,	CTC_EVENT_STOP,		ctcm_action_nop  },
1090293d984fSPeter Tiedemann 	{ CTC_STATE_STOPPED,	CTC_EVENT_START,	ctcm_chx_start  },
1091293d984fSPeter Tiedemann 	{ CTC_STATE_STOPPED,	CTC_EVENT_FINSTAT,	ctcm_action_nop  },
1092293d984fSPeter Tiedemann 	{ CTC_STATE_STOPPED,	CTC_EVENT_MC_FAIL,	ctcm_action_nop  },
1093293d984fSPeter Tiedemann 
1094293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_STOP,		ctcm_chx_stop  },
1095293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_START,	ctcm_action_nop  },
1096293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_FINSTAT,	ctcm_action_nop  },
1097293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_MC_FAIL,	ctcm_action_nop  },
1098293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_MC_GOOD,	ctcm_chx_start  },
1099293d984fSPeter Tiedemann 
1100293d984fSPeter Tiedemann 	{ CTC_STATE_STARTWAIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1101293d984fSPeter Tiedemann 	{ CTC_STATE_STARTWAIT,	CTC_EVENT_START,	ctcm_action_nop  },
1102293d984fSPeter Tiedemann 	{ CTC_STATE_STARTWAIT,	CTC_EVENT_FINSTAT,	ctcm_chx_setmode  },
1103293d984fSPeter Tiedemann 	{ CTC_STATE_STARTWAIT,	CTC_EVENT_TIMER,	ctcm_chx_setuperr  },
1104293d984fSPeter Tiedemann 	{ CTC_STATE_STARTWAIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1105293d984fSPeter Tiedemann 	{ CTC_STATE_STARTWAIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1106293d984fSPeter Tiedemann 
1107293d984fSPeter Tiedemann 	{ CTC_STATE_STARTRETRY,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1108293d984fSPeter Tiedemann 	{ CTC_STATE_STARTRETRY,	CTC_EVENT_TIMER,	ctcm_chx_setmode  },
1109293d984fSPeter Tiedemann 	{ CTC_STATE_STARTRETRY,	CTC_EVENT_FINSTAT,	ctcm_action_nop  },
1110293d984fSPeter Tiedemann 	{ CTC_STATE_STARTRETRY,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1111293d984fSPeter Tiedemann 
1112293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1113293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_START,	ctcm_action_nop  },
1114293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_FINSTAT,	chx_firstio  },
1115293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
1116293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
1117293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_TIMER,	ctcm_chx_setmode  },
1118293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1119293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1120293d984fSPeter Tiedemann 
1121293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1122293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_START,	ctcm_action_nop  },
1123293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_FINSTAT,	chx_rxidle  },
1124293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_UC_RCRESET,	ctcm_chx_rxiniterr  },
1125293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_UC_RSRESET,	ctcm_chx_rxiniterr  },
1126293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_TIMER,	ctcm_chx_rxiniterr  },
1127293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_ATTNBUSY,	ctcm_chx_rxinitfail  },
1128293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1129293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_UC_ZERO,	chx_firstio  },
1130293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1131293d984fSPeter Tiedemann 
1132293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1133293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_START,	ctcm_action_nop  },
1134293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_FINSTAT,	chx_rx  },
1135293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_UC_RCRESET,	ctcm_chx_rxdisc  },
1136293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1137293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1138293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_UC_ZERO,	chx_rx  },
1139293d984fSPeter Tiedemann 
1140293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1141293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_START,	ctcm_action_nop  },
1142293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_FINSTAT,	ctcm_chx_txidle  },
1143293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_UC_RCRESET,	ctcm_chx_txiniterr  },
1144293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_UC_RSRESET,	ctcm_chx_txiniterr  },
1145293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_TIMER,	ctcm_chx_txiniterr  },
1146293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1147293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1148293d984fSPeter Tiedemann 
1149293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1150293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_START,	ctcm_action_nop  },
1151293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_FINSTAT,	chx_firstio  },
1152293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_UC_RCRESET,	ctcm_action_nop  },
1153293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_UC_RSRESET,	ctcm_action_nop  },
1154293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1155293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1156293d984fSPeter Tiedemann 
1157293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_STOP,		ctcm_action_nop  },
1158293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_START,	ctcm_chx_restart  },
1159293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_FINSTAT,	ctcm_chx_stopped  },
1160293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_UC_RCRESET,	ctcm_action_nop  },
1161293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_UC_RSRESET,	ctcm_action_nop  },
1162293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1163293d984fSPeter Tiedemann 
1164293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1165293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_START,	ctcm_chx_restart  },
1166293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_FINSTAT,	ctcm_chx_setmode  },
1167293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_UC_RCRESET,	ctcm_action_nop  },
1168293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_UC_RSRESET,	ctcm_action_nop  },
1169293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1170293d984fSPeter Tiedemann 
1171293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_STOP,		ctcm_chx_haltio  },
1172293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_START,	ctcm_action_nop  },
1173293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_FINSTAT,	chx_txdone  },
1174293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_UC_RCRESET,	ctcm_chx_txretry  },
1175293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_UC_RSRESET,	ctcm_chx_txretry  },
1176293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_TIMER,	ctcm_chx_txretry  },
1177293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1178293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1179293d984fSPeter Tiedemann 
1180293d984fSPeter Tiedemann 	{ CTC_STATE_RXERR,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1181293d984fSPeter Tiedemann 	{ CTC_STATE_TXERR,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1182293d984fSPeter Tiedemann 	{ CTC_STATE_TXERR,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1183293d984fSPeter Tiedemann 	{ CTC_STATE_RXERR,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1184293d984fSPeter Tiedemann };
1185293d984fSPeter Tiedemann 
1186293d984fSPeter Tiedemann int ch_fsm_len = ARRAY_SIZE(ch_fsm);
1187293d984fSPeter Tiedemann 
1188293d984fSPeter Tiedemann /*
1189293d984fSPeter Tiedemann  * MPC actions for mpc channel statemachine
1190293d984fSPeter Tiedemann  * handling of MPC protocol requires extra
1191293d984fSPeter Tiedemann  * statemachine and actions which are prefixed ctcmpc_ .
1192293d984fSPeter Tiedemann  * The ctc_ch_states and ctc_ch_state_names,
1193293d984fSPeter Tiedemann  * ctc_ch_events and ctc_ch_event_names share the ctcm definitions
1194293d984fSPeter Tiedemann  * which are expanded by some elements.
1195293d984fSPeter Tiedemann  */
1196293d984fSPeter Tiedemann 
1197293d984fSPeter Tiedemann /*
1198293d984fSPeter Tiedemann  * Actions for mpc channel statemachine.
1199293d984fSPeter Tiedemann  */
1200293d984fSPeter Tiedemann 
1201a962cc4bSHeiko Carstens /*
1202293d984fSPeter Tiedemann  * Normal data has been send. Free the corresponding
1203293d984fSPeter Tiedemann  * skb (it's in io_queue), reset dev->tbusy and
1204293d984fSPeter Tiedemann  * revert to idle state.
1205293d984fSPeter Tiedemann  *
1206293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
1207293d984fSPeter Tiedemann  * event	The event, just happened.
1208293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
1209293d984fSPeter Tiedemann  */
ctcmpc_chx_txdone(fsm_instance * fi,int event,void * arg)1210293d984fSPeter Tiedemann static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
1211293d984fSPeter Tiedemann {
1212293d984fSPeter Tiedemann 	struct channel		*ch = arg;
1213293d984fSPeter Tiedemann 	struct net_device	*dev = ch->netdev;
1214261893d3SPeter Tiedemann 	struct ctcm_priv	*priv = dev->ml_priv;
1215293d984fSPeter Tiedemann 	struct mpc_group	*grp = priv->mpcg;
1216293d984fSPeter Tiedemann 	struct sk_buff		*skb;
1217293d984fSPeter Tiedemann 	int		first = 1;
1218293d984fSPeter Tiedemann 	int		i;
1219293d984fSPeter Tiedemann 	__u32		data_space;
1220293d984fSPeter Tiedemann 	unsigned long	duration;
1221293d984fSPeter Tiedemann 	struct sk_buff	*peekskb;
1222293d984fSPeter Tiedemann 	int		rc;
1223293d984fSPeter Tiedemann 	struct th_header *header;
1224293d984fSPeter Tiedemann 	struct pdu	*p_header;
1225ee6edb97SAya Mahfouz 	unsigned long done_stamp = jiffies;
1226293d984fSPeter Tiedemann 
1227aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("Enter %s: %s cp:%i\n",
1228aa3f2cb6SPeter Tiedemann 			__func__, dev->name, smp_processor_id());
1229293d984fSPeter Tiedemann 
1230ee6edb97SAya Mahfouz 	duration = done_stamp - ch->prof.send_stamp;
1231293d984fSPeter Tiedemann 	if (duration > ch->prof.tx_time)
1232293d984fSPeter Tiedemann 		ch->prof.tx_time = duration;
1233293d984fSPeter Tiedemann 
123423d805b6SPeter Oberparleiter 	if (ch->irb->scsw.cmd.count != 0)
1235aa3f2cb6SPeter Tiedemann 		CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
1236aa3f2cb6SPeter Tiedemann 			"%s(%s): TX not complete, remaining %d bytes",
1237aa3f2cb6SPeter Tiedemann 			     CTCM_FUNTAIL, dev->name, ch->irb->scsw.cmd.count);
1238293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
1239293d984fSPeter Tiedemann 	while ((skb = skb_dequeue(&ch->io_queue))) {
1240293d984fSPeter Tiedemann 		priv->stats.tx_packets++;
1241293d984fSPeter Tiedemann 		priv->stats.tx_bytes += skb->len - TH_HEADER_LENGTH;
1242293d984fSPeter Tiedemann 		if (first) {
1243293d984fSPeter Tiedemann 			priv->stats.tx_bytes += 2;
1244293d984fSPeter Tiedemann 			first = 0;
1245293d984fSPeter Tiedemann 		}
1246bba5850cSDavid S. Miller 		refcount_dec(&skb->users);
1247293d984fSPeter Tiedemann 		dev_kfree_skb_irq(skb);
1248293d984fSPeter Tiedemann 	}
1249293d984fSPeter Tiedemann 	spin_lock(&ch->collect_lock);
1250293d984fSPeter Tiedemann 	clear_normalized_cda(&ch->ccw[4]);
1251293d984fSPeter Tiedemann 	if ((ch->collect_len <= 0) || (grp->in_sweep != 0)) {
1252293d984fSPeter Tiedemann 		spin_unlock(&ch->collect_lock);
1253293d984fSPeter Tiedemann 		fsm_newstate(fi, CTC_STATE_TXIDLE);
1254293d984fSPeter Tiedemann 		goto done;
1255293d984fSPeter Tiedemann 	}
1256293d984fSPeter Tiedemann 
1257293d984fSPeter Tiedemann 	if (ctcm_checkalloc_buffer(ch)) {
1258293d984fSPeter Tiedemann 		spin_unlock(&ch->collect_lock);
1259293d984fSPeter Tiedemann 		goto done;
1260293d984fSPeter Tiedemann 	}
1261293d984fSPeter Tiedemann 	ch->trans_skb->data = ch->trans_skb_data;
1262293d984fSPeter Tiedemann 	skb_reset_tail_pointer(ch->trans_skb);
1263293d984fSPeter Tiedemann 	ch->trans_skb->len = 0;
1264293d984fSPeter Tiedemann 	if (ch->prof.maxmulti < (ch->collect_len + TH_HEADER_LENGTH))
1265293d984fSPeter Tiedemann 		ch->prof.maxmulti = ch->collect_len + TH_HEADER_LENGTH;
1266293d984fSPeter Tiedemann 	if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
1267293d984fSPeter Tiedemann 		ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
1268293d984fSPeter Tiedemann 	i = 0;
1269aa3f2cb6SPeter Tiedemann 	p_header = NULL;
1270293d984fSPeter Tiedemann 	data_space = grp->group_max_buflen - TH_HEADER_LENGTH;
1271293d984fSPeter Tiedemann 
1272aa3f2cb6SPeter Tiedemann 	CTCM_PR_DBGDATA("%s: building trans_skb from collect_q"
1273aa3f2cb6SPeter Tiedemann 		       " data_space:%04x\n",
1274aa3f2cb6SPeter Tiedemann 		       __func__, data_space);
1275aa3f2cb6SPeter Tiedemann 
1276293d984fSPeter Tiedemann 	while ((skb = skb_dequeue(&ch->collect_queue))) {
127759ae1d12SJohannes Berg 		skb_put_data(ch->trans_skb, skb->data, skb->len);
1278293d984fSPeter Tiedemann 		p_header = (struct pdu *)
1279293d984fSPeter Tiedemann 			(skb_tail_pointer(ch->trans_skb) - skb->len);
1280293d984fSPeter Tiedemann 		p_header->pdu_flag = 0x00;
12815cd77c13SHans Wippel 		if (be16_to_cpu(skb->protocol) == ETH_P_SNAP)
1282293d984fSPeter Tiedemann 			p_header->pdu_flag |= 0x60;
1283293d984fSPeter Tiedemann 		else
1284293d984fSPeter Tiedemann 			p_header->pdu_flag |= 0x20;
1285293d984fSPeter Tiedemann 
1286aa3f2cb6SPeter Tiedemann 		CTCM_PR_DBGDATA("%s: trans_skb len:%04x \n",
1287aa3f2cb6SPeter Tiedemann 				__func__, ch->trans_skb->len);
1288aa3f2cb6SPeter Tiedemann 		CTCM_PR_DBGDATA("%s: pdu header and data for up"
1289aa3f2cb6SPeter Tiedemann 				" to 32 bytes sent to vtam\n", __func__);
1290aa3f2cb6SPeter Tiedemann 		CTCM_D3_DUMP((char *)p_header, min_t(int, skb->len, 32));
1291aa3f2cb6SPeter Tiedemann 
1292293d984fSPeter Tiedemann 		ch->collect_len -= skb->len;
1293293d984fSPeter Tiedemann 		data_space -= skb->len;
1294293d984fSPeter Tiedemann 		priv->stats.tx_packets++;
1295293d984fSPeter Tiedemann 		priv->stats.tx_bytes += skb->len;
1296bba5850cSDavid S. Miller 		refcount_dec(&skb->users);
1297293d984fSPeter Tiedemann 		dev_kfree_skb_any(skb);
1298293d984fSPeter Tiedemann 		peekskb = skb_peek(&ch->collect_queue);
1299293d984fSPeter Tiedemann 		if (peekskb->len > data_space)
1300293d984fSPeter Tiedemann 			break;
1301293d984fSPeter Tiedemann 		i++;
1302293d984fSPeter Tiedemann 	}
1303293d984fSPeter Tiedemann 	/* p_header points to the last one we handled */
1304293d984fSPeter Tiedemann 	if (p_header)
1305293d984fSPeter Tiedemann 		p_header->pdu_flag |= PDU_LAST;	/*Say it's the last one*/
130694e0028aSSebastian Andrzej Siewior 
130794e0028aSSebastian Andrzej Siewior 	header = skb_push(ch->trans_skb, TH_HEADER_LENGTH);
130894e0028aSSebastian Andrzej Siewior 	memset(header, 0, TH_HEADER_LENGTH);
130994e0028aSSebastian Andrzej Siewior 
1310293d984fSPeter Tiedemann 	header->th_ch_flag = TH_HAS_PDU;  /* Normal data */
1311293d984fSPeter Tiedemann 	ch->th_seq_num++;
1312293d984fSPeter Tiedemann 	header->th_seq_num = ch->th_seq_num;
1313293d984fSPeter Tiedemann 
1314aa3f2cb6SPeter Tiedemann 	CTCM_PR_DBGDATA("%s: ToVTAM_th_seq= %08x\n" ,
1315aa3f2cb6SPeter Tiedemann 					__func__, ch->th_seq_num);
1316293d984fSPeter Tiedemann 
1317aa3f2cb6SPeter Tiedemann 	CTCM_PR_DBGDATA("%s: trans_skb len:%04x \n",
1318aa3f2cb6SPeter Tiedemann 		       __func__, ch->trans_skb->len);
1319aa3f2cb6SPeter Tiedemann 	CTCM_PR_DBGDATA("%s: up-to-50 bytes of trans_skb "
1320aa3f2cb6SPeter Tiedemann 			"data to vtam from collect_q\n", __func__);
1321aa3f2cb6SPeter Tiedemann 	CTCM_D3_DUMP((char *)ch->trans_skb->data,
1322293d984fSPeter Tiedemann 				min_t(int, ch->trans_skb->len, 50));
1323293d984fSPeter Tiedemann 
1324293d984fSPeter Tiedemann 	spin_unlock(&ch->collect_lock);
1325293d984fSPeter Tiedemann 	clear_normalized_cda(&ch->ccw[1]);
1326205a8b64SBelinda Thompson 
1327205a8b64SBelinda Thompson 	CTCM_PR_DBGDATA("ccwcda=0x%p data=0x%p\n",
1328205a8b64SBelinda Thompson 			(void *)(unsigned long)ch->ccw[1].cda,
1329205a8b64SBelinda Thompson 			ch->trans_skb->data);
1330205a8b64SBelinda Thompson 	ch->ccw[1].count = ch->max_bufsize;
1331205a8b64SBelinda Thompson 
1332293d984fSPeter Tiedemann 	if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {
1333293d984fSPeter Tiedemann 		dev_kfree_skb_any(ch->trans_skb);
1334293d984fSPeter Tiedemann 		ch->trans_skb = NULL;
1335aa3f2cb6SPeter Tiedemann 		CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR,
1336aa3f2cb6SPeter Tiedemann 			"%s: %s: IDAL alloc failed",
1337aa3f2cb6SPeter Tiedemann 				CTCM_FUNTAIL, ch->id);
1338293d984fSPeter Tiedemann 		fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
1339293d984fSPeter Tiedemann 		return;
1340293d984fSPeter Tiedemann 	}
1341205a8b64SBelinda Thompson 
1342205a8b64SBelinda Thompson 	CTCM_PR_DBGDATA("ccwcda=0x%p data=0x%p\n",
1343205a8b64SBelinda Thompson 			(void *)(unsigned long)ch->ccw[1].cda,
1344205a8b64SBelinda Thompson 			ch->trans_skb->data);
1345205a8b64SBelinda Thompson 
1346293d984fSPeter Tiedemann 	ch->ccw[1].count = ch->trans_skb->len;
1347293d984fSPeter Tiedemann 	fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
1348ee6edb97SAya Mahfouz 	ch->prof.send_stamp = jiffies;
1349293d984fSPeter Tiedemann 	if (do_debug_ccw)
1350293d984fSPeter Tiedemann 		ctcmpc_dumpit((char *)&ch->ccw[0], sizeof(struct ccw1) * 3);
13514f6e01f3SJulian Wiedmann 	rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
1352293d984fSPeter Tiedemann 	ch->prof.doios_multi++;
1353293d984fSPeter Tiedemann 	if (rc != 0) {
1354293d984fSPeter Tiedemann 		priv->stats.tx_dropped += i;
1355293d984fSPeter Tiedemann 		priv->stats.tx_errors += i;
1356293d984fSPeter Tiedemann 		fsm_deltimer(&ch->timer);
1357293d984fSPeter Tiedemann 		ctcm_ccw_check_rc(ch, rc, "chained TX");
1358293d984fSPeter Tiedemann 	}
1359293d984fSPeter Tiedemann done:
1360293d984fSPeter Tiedemann 	ctcm_clear_busy(dev);
1361293d984fSPeter Tiedemann 	return;
1362293d984fSPeter Tiedemann }
1363293d984fSPeter Tiedemann 
1364a962cc4bSHeiko Carstens /*
1365293d984fSPeter Tiedemann  * Got normal data, check for sanity, queue it up, allocate new buffer
1366293d984fSPeter Tiedemann  * trigger bottom half, and initiate next read.
1367293d984fSPeter Tiedemann  *
1368293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
1369293d984fSPeter Tiedemann  * event	The event, just happened.
1370293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
1371293d984fSPeter Tiedemann  */
ctcmpc_chx_rx(fsm_instance * fi,int event,void * arg)1372293d984fSPeter Tiedemann static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg)
1373293d984fSPeter Tiedemann {
1374293d984fSPeter Tiedemann 	struct channel		*ch = arg;
1375293d984fSPeter Tiedemann 	struct net_device	*dev = ch->netdev;
1376261893d3SPeter Tiedemann 	struct ctcm_priv	*priv = dev->ml_priv;
1377293d984fSPeter Tiedemann 	struct mpc_group	*grp = priv->mpcg;
1378293d984fSPeter Tiedemann 	struct sk_buff		*skb = ch->trans_skb;
1379293d984fSPeter Tiedemann 	struct sk_buff		*new_skb;
1380293d984fSPeter Tiedemann 	unsigned long		saveflags = 0;	/* avoids compiler warning */
138123d805b6SPeter Oberparleiter 	int len	= ch->max_bufsize - ch->irb->scsw.cmd.count;
1382293d984fSPeter Tiedemann 
1383aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("%s: %s: cp:%i %s maxbuf : %04x, len: %04x\n",
1384aa3f2cb6SPeter Tiedemann 			CTCM_FUNTAIL, dev->name, smp_processor_id(),
1385aa3f2cb6SPeter Tiedemann 				ch->id, ch->max_bufsize, len);
1386293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
1387293d984fSPeter Tiedemann 
1388293d984fSPeter Tiedemann 	if (skb == NULL) {
1389aa3f2cb6SPeter Tiedemann 		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
1390aa3f2cb6SPeter Tiedemann 			"%s(%s): TRANS_SKB = NULL",
1391aa3f2cb6SPeter Tiedemann 				CTCM_FUNTAIL, dev->name);
1392293d984fSPeter Tiedemann 		goto again;
1393293d984fSPeter Tiedemann 	}
1394293d984fSPeter Tiedemann 
1395293d984fSPeter Tiedemann 	if (len < TH_HEADER_LENGTH) {
1396aa3f2cb6SPeter Tiedemann 		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
1397*4f3dda8bSTong Zhang 				"%s(%s): packet length %d too short",
1398aa3f2cb6SPeter Tiedemann 					CTCM_FUNTAIL, dev->name, len);
1399293d984fSPeter Tiedemann 		priv->stats.rx_dropped++;
1400293d984fSPeter Tiedemann 		priv->stats.rx_length_errors++;
1401293d984fSPeter Tiedemann 	} else {
1402293d984fSPeter Tiedemann 		/* must have valid th header or game over */
1403293d984fSPeter Tiedemann 		__u32	block_len = len;
1404293d984fSPeter Tiedemann 		len = TH_HEADER_LENGTH + XID2_LENGTH + 4;
1405293d984fSPeter Tiedemann 		new_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC);
1406293d984fSPeter Tiedemann 
1407293d984fSPeter Tiedemann 		if (new_skb == NULL) {
1408aa3f2cb6SPeter Tiedemann 			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
14099961d6d5SHeiko Carstens 				"%s(%s): skb allocation failed",
1410aa3f2cb6SPeter Tiedemann 						CTCM_FUNTAIL, dev->name);
1411293d984fSPeter Tiedemann 			fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
1412293d984fSPeter Tiedemann 			goto again;
1413293d984fSPeter Tiedemann 		}
1414293d984fSPeter Tiedemann 		switch (fsm_getstate(grp->fsm)) {
1415293d984fSPeter Tiedemann 		case MPCG_STATE_RESET:
1416293d984fSPeter Tiedemann 		case MPCG_STATE_INOP:
1417293d984fSPeter Tiedemann 			dev_kfree_skb_any(new_skb);
1418293d984fSPeter Tiedemann 			break;
1419293d984fSPeter Tiedemann 		case MPCG_STATE_FLOWC:
1420293d984fSPeter Tiedemann 		case MPCG_STATE_READY:
142159ae1d12SJohannes Berg 			skb_put_data(new_skb, skb->data, block_len);
1422293d984fSPeter Tiedemann 			skb_queue_tail(&ch->io_queue, new_skb);
1423293d984fSPeter Tiedemann 			tasklet_schedule(&ch->ch_tasklet);
1424293d984fSPeter Tiedemann 			break;
1425293d984fSPeter Tiedemann 		default:
142659ae1d12SJohannes Berg 			skb_put_data(new_skb, skb->data, len);
1427293d984fSPeter Tiedemann 			skb_queue_tail(&ch->io_queue, new_skb);
1428293d984fSPeter Tiedemann 			tasklet_hi_schedule(&ch->ch_tasklet);
1429293d984fSPeter Tiedemann 			break;
1430293d984fSPeter Tiedemann 		}
1431293d984fSPeter Tiedemann 	}
1432293d984fSPeter Tiedemann 
1433293d984fSPeter Tiedemann again:
1434293d984fSPeter Tiedemann 	switch (fsm_getstate(grp->fsm)) {
1435293d984fSPeter Tiedemann 	int rc, dolock;
1436293d984fSPeter Tiedemann 	case MPCG_STATE_FLOWC:
1437293d984fSPeter Tiedemann 	case MPCG_STATE_READY:
1438293d984fSPeter Tiedemann 		if (ctcm_checkalloc_buffer(ch))
1439293d984fSPeter Tiedemann 			break;
1440293d984fSPeter Tiedemann 		ch->trans_skb->data = ch->trans_skb_data;
1441293d984fSPeter Tiedemann 		skb_reset_tail_pointer(ch->trans_skb);
1442293d984fSPeter Tiedemann 		ch->trans_skb->len = 0;
1443293d984fSPeter Tiedemann 		ch->ccw[1].count = ch->max_bufsize;
1444293d984fSPeter Tiedemann 		if (do_debug_ccw)
1445293d984fSPeter Tiedemann 			ctcmpc_dumpit((char *)&ch->ccw[0],
1446293d984fSPeter Tiedemann 				      sizeof(struct ccw1) * 3);
1447e871ee69SChangbin Du 		dolock = !in_hardirq();
1448293d984fSPeter Tiedemann 		if (dolock)
1449293d984fSPeter Tiedemann 			spin_lock_irqsave(
1450293d984fSPeter Tiedemann 				get_ccwdev_lock(ch->cdev), saveflags);
14514f6e01f3SJulian Wiedmann 		rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
1452293d984fSPeter Tiedemann 		if (dolock) /* see remark about conditional locking */
1453293d984fSPeter Tiedemann 			spin_unlock_irqrestore(
1454293d984fSPeter Tiedemann 				get_ccwdev_lock(ch->cdev), saveflags);
1455293d984fSPeter Tiedemann 		if (rc != 0)
1456293d984fSPeter Tiedemann 			ctcm_ccw_check_rc(ch, rc, "normal RX");
1457d4e81342SGustavo A. R. Silva 		break;
1458293d984fSPeter Tiedemann 	default:
1459293d984fSPeter Tiedemann 		break;
1460293d984fSPeter Tiedemann 	}
1461293d984fSPeter Tiedemann 
1462aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("Exit %s: %s, ch=0x%p, id=%s\n",
1463aa3f2cb6SPeter Tiedemann 			__func__, dev->name, ch, ch->id);
1464293d984fSPeter Tiedemann 
1465293d984fSPeter Tiedemann }
1466293d984fSPeter Tiedemann 
1467a962cc4bSHeiko Carstens /*
1468293d984fSPeter Tiedemann  * Initialize connection by sending a __u16 of value 0.
1469293d984fSPeter Tiedemann  *
1470293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
1471293d984fSPeter Tiedemann  * event	The event, just happened.
1472293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
1473293d984fSPeter Tiedemann  */
ctcmpc_chx_firstio(fsm_instance * fi,int event,void * arg)1474293d984fSPeter Tiedemann static void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg)
1475293d984fSPeter Tiedemann {
1476293d984fSPeter Tiedemann 	struct channel		*ch = arg;
1477293d984fSPeter Tiedemann 	struct net_device	*dev = ch->netdev;
1478261893d3SPeter Tiedemann 	struct ctcm_priv	*priv = dev->ml_priv;
1479293d984fSPeter Tiedemann 	struct mpc_group	*gptr = priv->mpcg;
1480aa3f2cb6SPeter Tiedemann 
1481aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("Enter %s: id=%s, ch=0x%p\n",
1482aa3f2cb6SPeter Tiedemann 				__func__, ch->id, ch);
1483aa3f2cb6SPeter Tiedemann 
1484aa3f2cb6SPeter Tiedemann 	CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_INFO,
1485aa3f2cb6SPeter Tiedemann 			"%s: %s: chstate:%i, grpstate:%i, prot:%i\n",
1486aa3f2cb6SPeter Tiedemann 			CTCM_FUNTAIL, ch->id, fsm_getstate(fi),
1487293d984fSPeter Tiedemann 			fsm_getstate(gptr->fsm), ch->protocol);
1488aa3f2cb6SPeter Tiedemann 
1489293d984fSPeter Tiedemann 	if (fsm_getstate(fi) == CTC_STATE_TXIDLE)
1490293d984fSPeter Tiedemann 		MPC_DBF_DEV_NAME(TRACE, dev, "remote side issued READ? ");
1491293d984fSPeter Tiedemann 
1492293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
1493293d984fSPeter Tiedemann 	if (ctcm_checkalloc_buffer(ch))
1494293d984fSPeter Tiedemann 				goto done;
1495293d984fSPeter Tiedemann 
1496293d984fSPeter Tiedemann 	switch (fsm_getstate(fi)) {
1497293d984fSPeter Tiedemann 	case CTC_STATE_STARTRETRY:
1498293d984fSPeter Tiedemann 	case CTC_STATE_SETUPWAIT:
14993c09e264SUrsula Braun 		if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
1500293d984fSPeter Tiedemann 			ctcmpc_chx_rxidle(fi, event, arg);
1501293d984fSPeter Tiedemann 		} else {
1502293d984fSPeter Tiedemann 			fsm_newstate(fi, CTC_STATE_TXIDLE);
1503293d984fSPeter Tiedemann 			fsm_event(priv->fsm, DEV_EVENT_TXUP, dev);
1504293d984fSPeter Tiedemann 		}
1505293d984fSPeter Tiedemann 				goto done;
1506293d984fSPeter Tiedemann 	default:
1507293d984fSPeter Tiedemann 		break;
15086531084cSPeter Senna Tschudin 	}
1509293d984fSPeter Tiedemann 
15103c09e264SUrsula Braun 	fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)
1511293d984fSPeter Tiedemann 		     ? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
1512293d984fSPeter Tiedemann 
1513293d984fSPeter Tiedemann done:
1514aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("Exit %s: id=%s, ch=0x%p\n",
1515aa3f2cb6SPeter Tiedemann 				__func__, ch->id, ch);
1516293d984fSPeter Tiedemann 	return;
1517293d984fSPeter Tiedemann }
1518293d984fSPeter Tiedemann 
1519a962cc4bSHeiko Carstens /*
1520293d984fSPeter Tiedemann  * Got initial data, check it. If OK,
1521293d984fSPeter Tiedemann  * notify device statemachine that we are up and
1522293d984fSPeter Tiedemann  * running.
1523293d984fSPeter Tiedemann  *
1524293d984fSPeter Tiedemann  * fi		An instance of a channel statemachine.
1525293d984fSPeter Tiedemann  * event	The event, just happened.
1526293d984fSPeter Tiedemann  * arg		Generic pointer, casted from channel * upon call.
1527293d984fSPeter Tiedemann  */
ctcmpc_chx_rxidle(fsm_instance * fi,int event,void * arg)1528293d984fSPeter Tiedemann void ctcmpc_chx_rxidle(fsm_instance *fi, int event, void *arg)
1529293d984fSPeter Tiedemann {
1530293d984fSPeter Tiedemann 	struct channel *ch = arg;
1531293d984fSPeter Tiedemann 	struct net_device *dev = ch->netdev;
1532261893d3SPeter Tiedemann 	struct ctcm_priv  *priv = dev->ml_priv;
1533293d984fSPeter Tiedemann 	struct mpc_group  *grp = priv->mpcg;
1534293d984fSPeter Tiedemann 	int rc;
1535293d984fSPeter Tiedemann 	unsigned long saveflags = 0;	/* avoids compiler warning */
1536293d984fSPeter Tiedemann 
1537293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
1538aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("%s: %s: %s: cp:%i, chstate:%i grpstate:%i\n",
1539aa3f2cb6SPeter Tiedemann 			__func__, ch->id, dev->name, smp_processor_id(),
1540293d984fSPeter Tiedemann 				fsm_getstate(fi), fsm_getstate(grp->fsm));
1541293d984fSPeter Tiedemann 
1542293d984fSPeter Tiedemann 	fsm_newstate(fi, CTC_STATE_RXIDLE);
1543293d984fSPeter Tiedemann 	/* XID processing complete */
1544293d984fSPeter Tiedemann 
1545293d984fSPeter Tiedemann 	switch (fsm_getstate(grp->fsm)) {
1546293d984fSPeter Tiedemann 	case MPCG_STATE_FLOWC:
1547293d984fSPeter Tiedemann 	case MPCG_STATE_READY:
1548293d984fSPeter Tiedemann 		if (ctcm_checkalloc_buffer(ch))
1549293d984fSPeter Tiedemann 				goto done;
1550293d984fSPeter Tiedemann 		ch->trans_skb->data = ch->trans_skb_data;
1551293d984fSPeter Tiedemann 		skb_reset_tail_pointer(ch->trans_skb);
1552293d984fSPeter Tiedemann 		ch->trans_skb->len = 0;
1553293d984fSPeter Tiedemann 		ch->ccw[1].count = ch->max_bufsize;
1554aa3f2cb6SPeter Tiedemann 		CTCM_CCW_DUMP((char *)&ch->ccw[0], sizeof(struct ccw1) * 3);
1555293d984fSPeter Tiedemann 		if (event == CTC_EVENT_START)
1556293d984fSPeter Tiedemann 			/* see remark about conditional locking */
1557293d984fSPeter Tiedemann 			spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
15584f6e01f3SJulian Wiedmann 		rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
1559293d984fSPeter Tiedemann 		if (event == CTC_EVENT_START)
1560293d984fSPeter Tiedemann 			spin_unlock_irqrestore(
1561293d984fSPeter Tiedemann 					get_ccwdev_lock(ch->cdev), saveflags);
1562293d984fSPeter Tiedemann 		if (rc != 0) {
1563293d984fSPeter Tiedemann 			fsm_newstate(fi, CTC_STATE_RXINIT);
1564293d984fSPeter Tiedemann 			ctcm_ccw_check_rc(ch, rc, "initial RX");
1565293d984fSPeter Tiedemann 			goto done;
1566293d984fSPeter Tiedemann 		}
1567293d984fSPeter Tiedemann 		break;
1568293d984fSPeter Tiedemann 	default:
1569293d984fSPeter Tiedemann 		break;
1570293d984fSPeter Tiedemann 	}
1571293d984fSPeter Tiedemann 
1572293d984fSPeter Tiedemann 	fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
1573293d984fSPeter Tiedemann done:
1574293d984fSPeter Tiedemann 	return;
1575293d984fSPeter Tiedemann }
1576293d984fSPeter Tiedemann 
1577293d984fSPeter Tiedemann /*
1578293d984fSPeter Tiedemann  * ctcmpc channel FSM action
1579293d984fSPeter Tiedemann  * called from several points in ctcmpc_ch_fsm
1580293d984fSPeter Tiedemann  * ctcmpc only
1581293d984fSPeter Tiedemann  */
ctcmpc_chx_attn(fsm_instance * fsm,int event,void * arg)1582293d984fSPeter Tiedemann static void ctcmpc_chx_attn(fsm_instance *fsm, int event, void *arg)
1583293d984fSPeter Tiedemann {
1584293d984fSPeter Tiedemann 	struct channel	  *ch     = arg;
1585293d984fSPeter Tiedemann 	struct net_device *dev    = ch->netdev;
1586261893d3SPeter Tiedemann 	struct ctcm_priv  *priv   = dev->ml_priv;
1587293d984fSPeter Tiedemann 	struct mpc_group  *grp = priv->mpcg;
1588293d984fSPeter Tiedemann 
1589aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("%s(%s): %s(ch=0x%p), cp=%i, ChStat:%s, GrpStat:%s\n",
1590aa3f2cb6SPeter Tiedemann 		__func__, dev->name, ch->id, ch, smp_processor_id(),
1591aa3f2cb6SPeter Tiedemann 			fsm_getstate_str(ch->fsm), fsm_getstate_str(grp->fsm));
1592293d984fSPeter Tiedemann 
1593293d984fSPeter Tiedemann 	switch (fsm_getstate(grp->fsm)) {
1594293d984fSPeter Tiedemann 	case MPCG_STATE_XID2INITW:
1595293d984fSPeter Tiedemann 		/* ok..start yside xid exchanges */
1596293d984fSPeter Tiedemann 		if (!ch->in_mpcgroup)
1597293d984fSPeter Tiedemann 			break;
1598293d984fSPeter Tiedemann 		if (fsm_getstate(ch->fsm) ==  CH_XID0_PENDING) {
1599293d984fSPeter Tiedemann 			fsm_deltimer(&grp->timer);
1600293d984fSPeter Tiedemann 			fsm_addtimer(&grp->timer,
1601293d984fSPeter Tiedemann 				MPC_XID_TIMEOUT_VALUE,
1602293d984fSPeter Tiedemann 				MPCG_EVENT_TIMER, dev);
1603293d984fSPeter Tiedemann 			fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
1604293d984fSPeter Tiedemann 
1605293d984fSPeter Tiedemann 		} else if (fsm_getstate(ch->fsm) < CH_XID7_PENDING1)
1606293d984fSPeter Tiedemann 			/* attn rcvd before xid0 processed via bh */
1607293d984fSPeter Tiedemann 			fsm_newstate(ch->fsm, CH_XID7_PENDING1);
1608293d984fSPeter Tiedemann 		break;
1609293d984fSPeter Tiedemann 	case MPCG_STATE_XID2INITX:
1610293d984fSPeter Tiedemann 	case MPCG_STATE_XID0IOWAIT:
1611293d984fSPeter Tiedemann 	case MPCG_STATE_XID0IOWAIX:
1612293d984fSPeter Tiedemann 		/* attn rcvd before xid0 processed on ch
1613293d984fSPeter Tiedemann 		but mid-xid0 processing for group    */
1614293d984fSPeter Tiedemann 		if (fsm_getstate(ch->fsm) < CH_XID7_PENDING1)
1615293d984fSPeter Tiedemann 			fsm_newstate(ch->fsm, CH_XID7_PENDING1);
1616293d984fSPeter Tiedemann 		break;
1617293d984fSPeter Tiedemann 	case MPCG_STATE_XID7INITW:
1618293d984fSPeter Tiedemann 	case MPCG_STATE_XID7INITX:
1619293d984fSPeter Tiedemann 	case MPCG_STATE_XID7INITI:
1620293d984fSPeter Tiedemann 	case MPCG_STATE_XID7INITZ:
1621293d984fSPeter Tiedemann 		switch (fsm_getstate(ch->fsm)) {
1622293d984fSPeter Tiedemann 		case CH_XID7_PENDING:
1623293d984fSPeter Tiedemann 			fsm_newstate(ch->fsm, CH_XID7_PENDING1);
1624293d984fSPeter Tiedemann 			break;
1625293d984fSPeter Tiedemann 		case CH_XID7_PENDING2:
1626293d984fSPeter Tiedemann 			fsm_newstate(ch->fsm, CH_XID7_PENDING3);
1627293d984fSPeter Tiedemann 			break;
1628293d984fSPeter Tiedemann 		}
1629293d984fSPeter Tiedemann 		fsm_event(grp->fsm, MPCG_EVENT_XID7DONE, dev);
1630293d984fSPeter Tiedemann 		break;
1631293d984fSPeter Tiedemann 	}
1632293d984fSPeter Tiedemann 
1633293d984fSPeter Tiedemann 	return;
1634293d984fSPeter Tiedemann }
1635293d984fSPeter Tiedemann 
1636293d984fSPeter Tiedemann /*
1637293d984fSPeter Tiedemann  * ctcmpc channel FSM action
1638293d984fSPeter Tiedemann  * called from one point in ctcmpc_ch_fsm
1639293d984fSPeter Tiedemann  * ctcmpc only
1640293d984fSPeter Tiedemann  */
ctcmpc_chx_attnbusy(fsm_instance * fsm,int event,void * arg)1641293d984fSPeter Tiedemann static void ctcmpc_chx_attnbusy(fsm_instance *fsm, int event, void *arg)
1642293d984fSPeter Tiedemann {
1643293d984fSPeter Tiedemann 	struct channel	  *ch     = arg;
1644293d984fSPeter Tiedemann 	struct net_device *dev    = ch->netdev;
1645261893d3SPeter Tiedemann 	struct ctcm_priv  *priv   = dev->ml_priv;
1646293d984fSPeter Tiedemann 	struct mpc_group  *grp    = priv->mpcg;
1647293d984fSPeter Tiedemann 
1648aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("%s(%s): %s\n  ChState:%s GrpState:%s\n",
1649aa3f2cb6SPeter Tiedemann 			__func__, dev->name, ch->id,
1650aa3f2cb6SPeter Tiedemann 			fsm_getstate_str(ch->fsm), fsm_getstate_str(grp->fsm));
1651293d984fSPeter Tiedemann 
1652293d984fSPeter Tiedemann 	fsm_deltimer(&ch->timer);
1653293d984fSPeter Tiedemann 
1654293d984fSPeter Tiedemann 	switch (fsm_getstate(grp->fsm)) {
1655293d984fSPeter Tiedemann 	case MPCG_STATE_XID0IOWAIT:
1656293d984fSPeter Tiedemann 		/* vtam wants to be primary.start yside xid exchanges*/
1657293d984fSPeter Tiedemann 		/* only receive one attn-busy at a time so must not  */
1658293d984fSPeter Tiedemann 		/* change state each time			     */
1659293d984fSPeter Tiedemann 		grp->changed_side = 1;
1660293d984fSPeter Tiedemann 		fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);
1661293d984fSPeter Tiedemann 		break;
1662293d984fSPeter Tiedemann 	case MPCG_STATE_XID2INITW:
1663293d984fSPeter Tiedemann 		if (grp->changed_side == 1) {
1664293d984fSPeter Tiedemann 			grp->changed_side = 2;
1665293d984fSPeter Tiedemann 			break;
1666293d984fSPeter Tiedemann 		}
1667293d984fSPeter Tiedemann 		/* process began via call to establish_conn	 */
1668293d984fSPeter Tiedemann 		/* so must report failure instead of reverting	 */
1669293d984fSPeter Tiedemann 		/* back to ready-for-xid passive state		 */
1670293d984fSPeter Tiedemann 		if (grp->estconnfunc)
1671293d984fSPeter Tiedemann 				goto done;
1672293d984fSPeter Tiedemann 		/* this attnbusy is NOT the result of xside xid  */
1673293d984fSPeter Tiedemann 		/* collisions so yside must have been triggered  */
1674293d984fSPeter Tiedemann 		/* by an ATTN that was not intended to start XID */
1675293d984fSPeter Tiedemann 		/* processing. Revert back to ready-for-xid and  */
1676293d984fSPeter Tiedemann 		/* wait for ATTN interrupt to signal xid start	 */
1677293d984fSPeter Tiedemann 		if (fsm_getstate(ch->fsm) == CH_XID0_INPROGRESS) {
1678293d984fSPeter Tiedemann 			fsm_newstate(ch->fsm, CH_XID0_PENDING) ;
1679293d984fSPeter Tiedemann 			fsm_deltimer(&grp->timer);
1680293d984fSPeter Tiedemann 			goto done;
1681293d984fSPeter Tiedemann 		}
1682293d984fSPeter Tiedemann 		fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
1683293d984fSPeter Tiedemann 		goto done;
1684293d984fSPeter Tiedemann 	case MPCG_STATE_XID2INITX:
1685293d984fSPeter Tiedemann 		/* XID2 was received before ATTN Busy for second
1686293d984fSPeter Tiedemann 		   channel.Send yside xid for second channel.
1687293d984fSPeter Tiedemann 		*/
1688293d984fSPeter Tiedemann 		if (grp->changed_side == 1) {
1689293d984fSPeter Tiedemann 			grp->changed_side = 2;
1690293d984fSPeter Tiedemann 			break;
1691293d984fSPeter Tiedemann 		}
1692df561f66SGustavo A. R. Silva 		fallthrough;
1693293d984fSPeter Tiedemann 	case MPCG_STATE_XID0IOWAIX:
1694293d984fSPeter Tiedemann 	case MPCG_STATE_XID7INITW:
1695293d984fSPeter Tiedemann 	case MPCG_STATE_XID7INITX:
1696293d984fSPeter Tiedemann 	case MPCG_STATE_XID7INITI:
1697293d984fSPeter Tiedemann 	case MPCG_STATE_XID7INITZ:
1698293d984fSPeter Tiedemann 	default:
1699293d984fSPeter Tiedemann 		/* multiple attn-busy indicates too out-of-sync      */
1700293d984fSPeter Tiedemann 		/* and they are certainly not being received as part */
1701293d984fSPeter Tiedemann 		/* of valid mpc group negotiations..		     */
1702293d984fSPeter Tiedemann 		fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
1703293d984fSPeter Tiedemann 				goto done;
1704293d984fSPeter Tiedemann 	}
1705293d984fSPeter Tiedemann 
1706293d984fSPeter Tiedemann 	if (grp->changed_side == 1) {
1707293d984fSPeter Tiedemann 		fsm_deltimer(&grp->timer);
1708293d984fSPeter Tiedemann 		fsm_addtimer(&grp->timer, MPC_XID_TIMEOUT_VALUE,
1709293d984fSPeter Tiedemann 			     MPCG_EVENT_TIMER, dev);
1710293d984fSPeter Tiedemann 	}
1711293d984fSPeter Tiedemann 	if (ch->in_mpcgroup)
1712293d984fSPeter Tiedemann 		fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
1713293d984fSPeter Tiedemann 	else
1714aa3f2cb6SPeter Tiedemann 		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
1715aa3f2cb6SPeter Tiedemann 			"%s(%s): channel %s not added to group",
1716aa3f2cb6SPeter Tiedemann 				CTCM_FUNTAIL, dev->name, ch->id);
1717293d984fSPeter Tiedemann 
1718293d984fSPeter Tiedemann done:
1719293d984fSPeter Tiedemann 	return;
1720293d984fSPeter Tiedemann }
1721293d984fSPeter Tiedemann 
1722293d984fSPeter Tiedemann /*
1723293d984fSPeter Tiedemann  * ctcmpc channel FSM action
1724293d984fSPeter Tiedemann  * called from several points in ctcmpc_ch_fsm
1725293d984fSPeter Tiedemann  * ctcmpc only
1726293d984fSPeter Tiedemann  */
ctcmpc_chx_resend(fsm_instance * fsm,int event,void * arg)1727293d984fSPeter Tiedemann static void ctcmpc_chx_resend(fsm_instance *fsm, int event, void *arg)
1728293d984fSPeter Tiedemann {
1729293d984fSPeter Tiedemann 	struct channel	   *ch	   = arg;
1730293d984fSPeter Tiedemann 	struct net_device  *dev    = ch->netdev;
1731261893d3SPeter Tiedemann 	struct ctcm_priv   *priv   = dev->ml_priv;
1732293d984fSPeter Tiedemann 	struct mpc_group   *grp    = priv->mpcg;
1733293d984fSPeter Tiedemann 
1734293d984fSPeter Tiedemann 	fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
1735293d984fSPeter Tiedemann 	return;
1736293d984fSPeter Tiedemann }
1737293d984fSPeter Tiedemann 
1738293d984fSPeter Tiedemann /*
1739293d984fSPeter Tiedemann  * ctcmpc channel FSM action
1740293d984fSPeter Tiedemann  * called from several points in ctcmpc_ch_fsm
1741293d984fSPeter Tiedemann  * ctcmpc only
1742293d984fSPeter Tiedemann  */
ctcmpc_chx_send_sweep(fsm_instance * fsm,int event,void * arg)1743293d984fSPeter Tiedemann static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg)
1744293d984fSPeter Tiedemann {
1745293d984fSPeter Tiedemann 	struct channel *ach = arg;
1746293d984fSPeter Tiedemann 	struct net_device *dev = ach->netdev;
1747261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
1748293d984fSPeter Tiedemann 	struct mpc_group *grp = priv->mpcg;
17493c09e264SUrsula Braun 	struct channel *wch = priv->channel[CTCM_WRITE];
17503c09e264SUrsula Braun 	struct channel *rch = priv->channel[CTCM_READ];
1751293d984fSPeter Tiedemann 	struct sk_buff *skb;
1752293d984fSPeter Tiedemann 	struct th_sweep *header;
1753293d984fSPeter Tiedemann 	int rc = 0;
1754293d984fSPeter Tiedemann 	unsigned long saveflags = 0;
1755293d984fSPeter Tiedemann 
1756aa3f2cb6SPeter Tiedemann 	CTCM_PR_DEBUG("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
1757aa3f2cb6SPeter Tiedemann 			__func__, smp_processor_id(), ach, ach->id);
1758293d984fSPeter Tiedemann 
1759293d984fSPeter Tiedemann 	if (grp->in_sweep == 0)
1760293d984fSPeter Tiedemann 				goto done;
1761293d984fSPeter Tiedemann 
1762aa3f2cb6SPeter Tiedemann 	CTCM_PR_DBGDATA("%s: 1: ToVTAM_th_seq= %08x\n" ,
1763aa3f2cb6SPeter Tiedemann 				__func__, wch->th_seq_num);
1764aa3f2cb6SPeter Tiedemann 	CTCM_PR_DBGDATA("%s: 1: FromVTAM_th_seq= %08x\n" ,
1765aa3f2cb6SPeter Tiedemann 				__func__, rch->th_seq_num);
1766293d984fSPeter Tiedemann 
1767293d984fSPeter Tiedemann 	if (fsm_getstate(wch->fsm) != CTC_STATE_TXIDLE) {
1768293d984fSPeter Tiedemann 		/* give the previous IO time to complete */
1769293d984fSPeter Tiedemann 		fsm_addtimer(&wch->sweep_timer,
1770293d984fSPeter Tiedemann 			200, CTC_EVENT_RSWEEP_TIMER, wch);
1771293d984fSPeter Tiedemann 		goto done;
1772293d984fSPeter Tiedemann 	}
1773293d984fSPeter Tiedemann 
1774293d984fSPeter Tiedemann 	skb = skb_dequeue(&wch->sweep_queue);
1775293d984fSPeter Tiedemann 	if (!skb)
1776293d984fSPeter Tiedemann 				goto done;
1777293d984fSPeter Tiedemann 
1778293d984fSPeter Tiedemann 	if (set_normalized_cda(&wch->ccw[4], skb->data)) {
1779293d984fSPeter Tiedemann 		grp->in_sweep = 0;
1780293d984fSPeter Tiedemann 		ctcm_clear_busy_do(dev);
1781293d984fSPeter Tiedemann 		dev_kfree_skb_any(skb);
1782293d984fSPeter Tiedemann 		fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
1783293d984fSPeter Tiedemann 		goto done;
1784293d984fSPeter Tiedemann 	} else {
1785bba5850cSDavid S. Miller 		refcount_inc(&skb->users);
1786293d984fSPeter Tiedemann 		skb_queue_tail(&wch->io_queue, skb);
1787293d984fSPeter Tiedemann 	}
1788293d984fSPeter Tiedemann 
1789293d984fSPeter Tiedemann 	/* send out the sweep */
1790293d984fSPeter Tiedemann 	wch->ccw[4].count = skb->len;
1791293d984fSPeter Tiedemann 
1792293d984fSPeter Tiedemann 	header = (struct th_sweep *)skb->data;
1793293d984fSPeter Tiedemann 	switch (header->th.th_ch_flag) {
1794293d984fSPeter Tiedemann 	case TH_SWEEP_REQ:
1795293d984fSPeter Tiedemann 		grp->sweep_req_pend_num--;
1796293d984fSPeter Tiedemann 		break;
1797293d984fSPeter Tiedemann 	case TH_SWEEP_RESP:
1798293d984fSPeter Tiedemann 		grp->sweep_rsp_pend_num--;
1799293d984fSPeter Tiedemann 		break;
1800293d984fSPeter Tiedemann 	}
1801293d984fSPeter Tiedemann 
1802293d984fSPeter Tiedemann 	header->sw.th_last_seq = wch->th_seq_num;
1803293d984fSPeter Tiedemann 
1804aa3f2cb6SPeter Tiedemann 	CTCM_CCW_DUMP((char *)&wch->ccw[3], sizeof(struct ccw1) * 3);
1805aa3f2cb6SPeter Tiedemann 	CTCM_PR_DBGDATA("%s: sweep packet\n", __func__);
1806aa3f2cb6SPeter Tiedemann 	CTCM_D3_DUMP((char *)header, TH_SWEEP_LENGTH);
1807293d984fSPeter Tiedemann 
1808293d984fSPeter Tiedemann 	fsm_addtimer(&wch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, wch);
1809293d984fSPeter Tiedemann 	fsm_newstate(wch->fsm, CTC_STATE_TX);
1810293d984fSPeter Tiedemann 
1811293d984fSPeter Tiedemann 	spin_lock_irqsave(get_ccwdev_lock(wch->cdev), saveflags);
1812ee6edb97SAya Mahfouz 	wch->prof.send_stamp = jiffies;
18134f6e01f3SJulian Wiedmann 	rc = ccw_device_start(wch->cdev, &wch->ccw[3], 0, 0xff, 0);
1814293d984fSPeter Tiedemann 	spin_unlock_irqrestore(get_ccwdev_lock(wch->cdev), saveflags);
1815293d984fSPeter Tiedemann 
1816293d984fSPeter Tiedemann 	if ((grp->sweep_req_pend_num == 0) &&
1817293d984fSPeter Tiedemann 	   (grp->sweep_rsp_pend_num == 0)) {
1818293d984fSPeter Tiedemann 		grp->in_sweep = 0;
1819293d984fSPeter Tiedemann 		rch->th_seq_num = 0x00;
1820293d984fSPeter Tiedemann 		wch->th_seq_num = 0x00;
1821293d984fSPeter Tiedemann 		ctcm_clear_busy_do(dev);
1822293d984fSPeter Tiedemann 	}
1823293d984fSPeter Tiedemann 
1824aa3f2cb6SPeter Tiedemann 	CTCM_PR_DBGDATA("%s: To-/From-VTAM_th_seq = %08x/%08x\n" ,
1825aa3f2cb6SPeter Tiedemann 			__func__, wch->th_seq_num, rch->th_seq_num);
1826293d984fSPeter Tiedemann 
1827293d984fSPeter Tiedemann 	if (rc != 0)
1828293d984fSPeter Tiedemann 		ctcm_ccw_check_rc(wch, rc, "send sweep");
1829293d984fSPeter Tiedemann 
1830293d984fSPeter Tiedemann done:
1831293d984fSPeter Tiedemann 	return;
1832293d984fSPeter Tiedemann }
1833293d984fSPeter Tiedemann 
1834293d984fSPeter Tiedemann 
1835293d984fSPeter Tiedemann /*
1836293d984fSPeter Tiedemann  * The ctcmpc statemachine for a channel.
1837293d984fSPeter Tiedemann  */
1838293d984fSPeter Tiedemann 
1839293d984fSPeter Tiedemann const fsm_node ctcmpc_ch_fsm[] = {
1840293d984fSPeter Tiedemann 	{ CTC_STATE_STOPPED,	CTC_EVENT_STOP,		ctcm_action_nop  },
1841293d984fSPeter Tiedemann 	{ CTC_STATE_STOPPED,	CTC_EVENT_START,	ctcm_chx_start  },
1842293d984fSPeter Tiedemann 	{ CTC_STATE_STOPPED,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1843293d984fSPeter Tiedemann 	{ CTC_STATE_STOPPED,	CTC_EVENT_FINSTAT,	ctcm_action_nop  },
1844293d984fSPeter Tiedemann 	{ CTC_STATE_STOPPED,	CTC_EVENT_MC_FAIL,	ctcm_action_nop  },
1845293d984fSPeter Tiedemann 
1846293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_STOP,		ctcm_chx_stop  },
1847293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_START,	ctcm_action_nop  },
1848293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_FINSTAT,	ctcm_action_nop  },
1849293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_MC_FAIL,	ctcm_action_nop  },
1850293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_MC_GOOD,	ctcm_chx_start  },
1851293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_UC_RCRESET,	ctcm_chx_stop  },
1852293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_UC_RSRESET,	ctcm_chx_stop  },
1853293d984fSPeter Tiedemann 	{ CTC_STATE_NOTOP,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1854293d984fSPeter Tiedemann 
1855293d984fSPeter Tiedemann 	{ CTC_STATE_STARTWAIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1856293d984fSPeter Tiedemann 	{ CTC_STATE_STARTWAIT,	CTC_EVENT_START,	ctcm_action_nop  },
1857293d984fSPeter Tiedemann 	{ CTC_STATE_STARTWAIT,	CTC_EVENT_FINSTAT,	ctcm_chx_setmode  },
1858293d984fSPeter Tiedemann 	{ CTC_STATE_STARTWAIT,	CTC_EVENT_TIMER,	ctcm_chx_setuperr  },
1859293d984fSPeter Tiedemann 	{ CTC_STATE_STARTWAIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1860293d984fSPeter Tiedemann 	{ CTC_STATE_STARTWAIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1861293d984fSPeter Tiedemann 
1862293d984fSPeter Tiedemann 	{ CTC_STATE_STARTRETRY,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1863293d984fSPeter Tiedemann 	{ CTC_STATE_STARTRETRY,	CTC_EVENT_TIMER,	ctcm_chx_setmode  },
1864293d984fSPeter Tiedemann 	{ CTC_STATE_STARTRETRY,	CTC_EVENT_FINSTAT,	ctcm_chx_setmode  },
1865293d984fSPeter Tiedemann 	{ CTC_STATE_STARTRETRY,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1866293d984fSPeter Tiedemann 	{ CTC_STATE_STARTRETRY,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1867293d984fSPeter Tiedemann 
1868293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1869293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_START,	ctcm_action_nop  },
1870293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_FINSTAT,	ctcmpc_chx_firstio  },
1871293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
1872293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
1873293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_TIMER,	ctcm_chx_setmode  },
1874293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1875293d984fSPeter Tiedemann 	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1876293d984fSPeter Tiedemann 
1877293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1878293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_START,	ctcm_action_nop  },
1879293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rxidle  },
1880293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_UC_RCRESET,	ctcm_chx_rxiniterr  },
1881293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_UC_RSRESET,	ctcm_chx_rxiniterr  },
1882293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_TIMER,	ctcm_chx_rxiniterr  },
1883293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_ATTNBUSY,	ctcm_chx_rxinitfail  },
1884293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1885293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_firstio  },
1886293d984fSPeter Tiedemann 	{ CTC_STATE_RXINIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1887293d984fSPeter Tiedemann 
1888293d984fSPeter Tiedemann 	{ CH_XID0_PENDING,	CTC_EVENT_FINSTAT,	ctcm_action_nop  },
1889293d984fSPeter Tiedemann 	{ CH_XID0_PENDING,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
1890293d984fSPeter Tiedemann 	{ CH_XID0_PENDING,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1891293d984fSPeter Tiedemann 	{ CH_XID0_PENDING,	CTC_EVENT_START,	ctcm_action_nop  },
1892293d984fSPeter Tiedemann 	{ CH_XID0_PENDING,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1893293d984fSPeter Tiedemann 	{ CH_XID0_PENDING,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1894293d984fSPeter Tiedemann 	{ CH_XID0_PENDING,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
1895293d984fSPeter Tiedemann 	{ CH_XID0_PENDING,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
1896293d984fSPeter Tiedemann 	{ CH_XID0_PENDING,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
1897293d984fSPeter Tiedemann 	{ CH_XID0_PENDING,	CTC_EVENT_ATTNBUSY,	ctcm_chx_iofatal  },
1898293d984fSPeter Tiedemann 
1899293d984fSPeter Tiedemann 	{ CH_XID0_INPROGRESS,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
1900293d984fSPeter Tiedemann 	{ CH_XID0_INPROGRESS,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
1901293d984fSPeter Tiedemann 	{ CH_XID0_INPROGRESS,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1902293d984fSPeter Tiedemann 	{ CH_XID0_INPROGRESS,	CTC_EVENT_START,	ctcm_action_nop  },
1903293d984fSPeter Tiedemann 	{ CH_XID0_INPROGRESS,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1904293d984fSPeter Tiedemann 	{ CH_XID0_INPROGRESS,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1905293d984fSPeter Tiedemann 	{ CH_XID0_INPROGRESS,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
1906293d984fSPeter Tiedemann 	{ CH_XID0_INPROGRESS,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr },
1907293d984fSPeter Tiedemann 	{ CH_XID0_INPROGRESS,	CTC_EVENT_ATTNBUSY,	ctcmpc_chx_attnbusy  },
1908293d984fSPeter Tiedemann 	{ CH_XID0_INPROGRESS,	CTC_EVENT_TIMER,	ctcmpc_chx_resend  },
1909293d984fSPeter Tiedemann 	{ CH_XID0_INPROGRESS,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
1910293d984fSPeter Tiedemann 
1911293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
1912293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
1913293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1914293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_START,	ctcm_action_nop  },
1915293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1916293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1917293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
1918293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
1919293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
1920293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
1921293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_ATTNBUSY,	ctcm_chx_iofatal  },
1922293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_TIMER,	ctcmpc_chx_resend  },
1923293d984fSPeter Tiedemann 	{ CH_XID7_PENDING,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
1924293d984fSPeter Tiedemann 
1925293d984fSPeter Tiedemann 	{ CH_XID7_PENDING1,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
1926293d984fSPeter Tiedemann 	{ CH_XID7_PENDING1,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
1927293d984fSPeter Tiedemann 	{ CH_XID7_PENDING1,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1928293d984fSPeter Tiedemann 	{ CH_XID7_PENDING1,	CTC_EVENT_START,	ctcm_action_nop  },
1929293d984fSPeter Tiedemann 	{ CH_XID7_PENDING1,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1930293d984fSPeter Tiedemann 	{ CH_XID7_PENDING1,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1931293d984fSPeter Tiedemann 	{ CH_XID7_PENDING1,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
1932293d984fSPeter Tiedemann 	{ CH_XID7_PENDING1,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
1933293d984fSPeter Tiedemann 	{ CH_XID7_PENDING1,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
1934293d984fSPeter Tiedemann 	{ CH_XID7_PENDING1,	CTC_EVENT_ATTNBUSY,	ctcm_chx_iofatal  },
1935293d984fSPeter Tiedemann 	{ CH_XID7_PENDING1,	CTC_EVENT_TIMER,	ctcmpc_chx_resend  },
1936293d984fSPeter Tiedemann 	{ CH_XID7_PENDING1,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
1937293d984fSPeter Tiedemann 
1938293d984fSPeter Tiedemann 	{ CH_XID7_PENDING2,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
1939293d984fSPeter Tiedemann 	{ CH_XID7_PENDING2,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
1940293d984fSPeter Tiedemann 	{ CH_XID7_PENDING2,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1941293d984fSPeter Tiedemann 	{ CH_XID7_PENDING2,	CTC_EVENT_START,	ctcm_action_nop  },
1942293d984fSPeter Tiedemann 	{ CH_XID7_PENDING2,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1943293d984fSPeter Tiedemann 	{ CH_XID7_PENDING2,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1944293d984fSPeter Tiedemann 	{ CH_XID7_PENDING2,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
1945293d984fSPeter Tiedemann 	{ CH_XID7_PENDING2,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
1946293d984fSPeter Tiedemann 	{ CH_XID7_PENDING2,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
1947293d984fSPeter Tiedemann 	{ CH_XID7_PENDING2,	CTC_EVENT_ATTNBUSY,	ctcm_chx_iofatal  },
1948293d984fSPeter Tiedemann 	{ CH_XID7_PENDING2,	CTC_EVENT_TIMER,	ctcmpc_chx_resend  },
1949293d984fSPeter Tiedemann 	{ CH_XID7_PENDING2,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
1950293d984fSPeter Tiedemann 
1951293d984fSPeter Tiedemann 	{ CH_XID7_PENDING3,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
1952293d984fSPeter Tiedemann 	{ CH_XID7_PENDING3,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
1953293d984fSPeter Tiedemann 	{ CH_XID7_PENDING3,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1954293d984fSPeter Tiedemann 	{ CH_XID7_PENDING3,	CTC_EVENT_START,	ctcm_action_nop  },
1955293d984fSPeter Tiedemann 	{ CH_XID7_PENDING3,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1956293d984fSPeter Tiedemann 	{ CH_XID7_PENDING3,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1957293d984fSPeter Tiedemann 	{ CH_XID7_PENDING3,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
1958293d984fSPeter Tiedemann 	{ CH_XID7_PENDING3,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
1959293d984fSPeter Tiedemann 	{ CH_XID7_PENDING3,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
1960293d984fSPeter Tiedemann 	{ CH_XID7_PENDING3,	CTC_EVENT_ATTNBUSY,	ctcm_chx_iofatal  },
1961293d984fSPeter Tiedemann 	{ CH_XID7_PENDING3,	CTC_EVENT_TIMER,	ctcmpc_chx_resend  },
1962293d984fSPeter Tiedemann 	{ CH_XID7_PENDING3,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
1963293d984fSPeter Tiedemann 
1964293d984fSPeter Tiedemann 	{ CH_XID7_PENDING4,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
1965293d984fSPeter Tiedemann 	{ CH_XID7_PENDING4,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
1966293d984fSPeter Tiedemann 	{ CH_XID7_PENDING4,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1967293d984fSPeter Tiedemann 	{ CH_XID7_PENDING4,	CTC_EVENT_START,	ctcm_action_nop  },
1968293d984fSPeter Tiedemann 	{ CH_XID7_PENDING4,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1969293d984fSPeter Tiedemann 	{ CH_XID7_PENDING4,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1970293d984fSPeter Tiedemann 	{ CH_XID7_PENDING4,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
1971293d984fSPeter Tiedemann 	{ CH_XID7_PENDING4,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
1972293d984fSPeter Tiedemann 	{ CH_XID7_PENDING4,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
1973293d984fSPeter Tiedemann 	{ CH_XID7_PENDING4,	CTC_EVENT_ATTNBUSY,	ctcm_chx_iofatal  },
1974293d984fSPeter Tiedemann 	{ CH_XID7_PENDING4,	CTC_EVENT_TIMER,	ctcmpc_chx_resend  },
1975293d984fSPeter Tiedemann 	{ CH_XID7_PENDING4,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
1976293d984fSPeter Tiedemann 
1977293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1978293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_START,	ctcm_action_nop  },
1979293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
1980293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_UC_RCRESET,	ctcm_chx_rxdisc  },
1981293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_UC_RSRESET,	ctcm_chx_fail  },
1982293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1983293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1984293d984fSPeter Tiedemann 	{ CTC_STATE_RXIDLE,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
1985293d984fSPeter Tiedemann 
1986293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1987293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_START,	ctcm_action_nop  },
1988293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_FINSTAT,	ctcm_chx_txidle  },
1989293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_UC_RCRESET,	ctcm_chx_txiniterr  },
1990293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_UC_RSRESET,	ctcm_chx_txiniterr  },
1991293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_TIMER,	ctcm_chx_txiniterr  },
1992293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
1993293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
1994293d984fSPeter Tiedemann 	{ CTC_STATE_TXINIT,	CTC_EVENT_RSWEEP_TIMER,	ctcmpc_chx_send_sweep },
1995293d984fSPeter Tiedemann 
1996293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
1997293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_START,	ctcm_action_nop  },
1998293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_FINSTAT,	ctcmpc_chx_firstio  },
1999293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_UC_RCRESET,	ctcm_chx_fail  },
2000293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_UC_RSRESET,	ctcm_chx_fail  },
2001293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
2002293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
2003293d984fSPeter Tiedemann 	{ CTC_STATE_TXIDLE,	CTC_EVENT_RSWEEP_TIMER,	ctcmpc_chx_send_sweep },
2004293d984fSPeter Tiedemann 
2005293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_STOP,		ctcm_action_nop  },
2006293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_START,	ctcm_chx_restart  },
2007293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_FINSTAT,	ctcm_chx_stopped  },
2008293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_UC_RCRESET,	ctcm_action_nop  },
2009293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_UC_RSRESET,	ctcm_action_nop  },
2010293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
2011293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
2012293d984fSPeter Tiedemann 	{ CTC_STATE_TERM,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
2013293d984fSPeter Tiedemann 
2014293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
2015293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_START,	ctcm_chx_restart  },
2016293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_FINSTAT,	ctcm_chx_setmode  },
2017293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_UC_RCRESET,	ctcm_action_nop  },
2018293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_UC_RSRESET,	ctcm_action_nop  },
2019293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
2020293d984fSPeter Tiedemann 	{ CTC_STATE_DTERM,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
2021293d984fSPeter Tiedemann 
2022293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_STOP,		ctcm_chx_haltio  },
2023293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_START,	ctcm_action_nop  },
2024293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_FINSTAT,	ctcmpc_chx_txdone  },
2025293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_UC_RCRESET,	ctcm_chx_fail  },
2026293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_UC_RSRESET,	ctcm_chx_fail  },
2027293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_TIMER,	ctcm_chx_txretry  },
2028293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
2029293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
2030293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_RSWEEP_TIMER,	ctcmpc_chx_send_sweep },
2031293d984fSPeter Tiedemann 	{ CTC_STATE_TX,		CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
2032293d984fSPeter Tiedemann 
2033293d984fSPeter Tiedemann 	{ CTC_STATE_RXERR,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
2034293d984fSPeter Tiedemann 	{ CTC_STATE_TXERR,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
2035293d984fSPeter Tiedemann 	{ CTC_STATE_TXERR,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
2036293d984fSPeter Tiedemann 	{ CTC_STATE_TXERR,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
2037293d984fSPeter Tiedemann 	{ CTC_STATE_RXERR,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
2038293d984fSPeter Tiedemann };
2039293d984fSPeter Tiedemann 
2040293d984fSPeter Tiedemann int mpc_ch_fsm_len = ARRAY_SIZE(ctcmpc_ch_fsm);
2041293d984fSPeter Tiedemann 
2042293d984fSPeter Tiedemann /*
2043293d984fSPeter Tiedemann  * Actions for interface - statemachine.
2044293d984fSPeter Tiedemann  */
2045293d984fSPeter Tiedemann 
2046a962cc4bSHeiko Carstens /*
2047293d984fSPeter Tiedemann  * Startup channels by sending CTC_EVENT_START to each channel.
2048293d984fSPeter Tiedemann  *
2049293d984fSPeter Tiedemann  * fi		An instance of an interface statemachine.
2050293d984fSPeter Tiedemann  * event	The event, just happened.
2051293d984fSPeter Tiedemann  * arg		Generic pointer, casted from struct net_device * upon call.
2052293d984fSPeter Tiedemann  */
dev_action_start(fsm_instance * fi,int event,void * arg)2053293d984fSPeter Tiedemann static void dev_action_start(fsm_instance *fi, int event, void *arg)
2054293d984fSPeter Tiedemann {
2055293d984fSPeter Tiedemann 	struct net_device *dev = arg;
2056261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
2057293d984fSPeter Tiedemann 	int direction;
2058293d984fSPeter Tiedemann 
2059293d984fSPeter Tiedemann 	CTCMY_DBF_DEV_NAME(SETUP, dev, "");
2060293d984fSPeter Tiedemann 
2061293d984fSPeter Tiedemann 	fsm_deltimer(&priv->restart_timer);
2062293d984fSPeter Tiedemann 	fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
2063293d984fSPeter Tiedemann 	if (IS_MPC(priv))
2064293d984fSPeter Tiedemann 		priv->mpcg->channels_terminating = 0;
20653c09e264SUrsula Braun 	for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
2066293d984fSPeter Tiedemann 		struct channel *ch = priv->channel[direction];
2067293d984fSPeter Tiedemann 		fsm_event(ch->fsm, CTC_EVENT_START, ch);
2068293d984fSPeter Tiedemann 	}
2069293d984fSPeter Tiedemann }
2070293d984fSPeter Tiedemann 
2071a962cc4bSHeiko Carstens /*
2072293d984fSPeter Tiedemann  * Shutdown channels by sending CTC_EVENT_STOP to each channel.
2073293d984fSPeter Tiedemann  *
2074293d984fSPeter Tiedemann  * fi		An instance of an interface statemachine.
2075293d984fSPeter Tiedemann  * event	The event, just happened.
2076293d984fSPeter Tiedemann  * arg		Generic pointer, casted from struct net_device * upon call.
2077293d984fSPeter Tiedemann  */
dev_action_stop(fsm_instance * fi,int event,void * arg)2078293d984fSPeter Tiedemann static void dev_action_stop(fsm_instance *fi, int event, void *arg)
2079293d984fSPeter Tiedemann {
2080293d984fSPeter Tiedemann 	int direction;
2081293d984fSPeter Tiedemann 	struct net_device *dev = arg;
2082261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
2083293d984fSPeter Tiedemann 
2084293d984fSPeter Tiedemann 	CTCMY_DBF_DEV_NAME(SETUP, dev, "");
2085293d984fSPeter Tiedemann 
2086293d984fSPeter Tiedemann 	fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
20873c09e264SUrsula Braun 	for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
2088293d984fSPeter Tiedemann 		struct channel *ch = priv->channel[direction];
2089293d984fSPeter Tiedemann 		fsm_event(ch->fsm, CTC_EVENT_STOP, ch);
2090293d984fSPeter Tiedemann 		ch->th_seq_num = 0x00;
2091aa3f2cb6SPeter Tiedemann 		CTCM_PR_DEBUG("%s: CH_th_seq= %08x\n",
2092aa3f2cb6SPeter Tiedemann 				__func__, ch->th_seq_num);
2093293d984fSPeter Tiedemann 	}
2094293d984fSPeter Tiedemann 	if (IS_MPC(priv))
2095293d984fSPeter Tiedemann 		fsm_newstate(priv->mpcg->fsm, MPCG_STATE_RESET);
2096293d984fSPeter Tiedemann }
2097293d984fSPeter Tiedemann 
dev_action_restart(fsm_instance * fi,int event,void * arg)2098293d984fSPeter Tiedemann static void dev_action_restart(fsm_instance *fi, int event, void *arg)
2099293d984fSPeter Tiedemann {
2100293d984fSPeter Tiedemann 	int restart_timer;
2101293d984fSPeter Tiedemann 	struct net_device *dev = arg;
2102261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
2103293d984fSPeter Tiedemann 
2104293d984fSPeter Tiedemann 	CTCMY_DBF_DEV_NAME(TRACE, dev, "");
2105293d984fSPeter Tiedemann 
2106293d984fSPeter Tiedemann 	if (IS_MPC(priv)) {
2107293d984fSPeter Tiedemann 		restart_timer = CTCM_TIME_1_SEC;
2108293d984fSPeter Tiedemann 	} else {
2109293d984fSPeter Tiedemann 		restart_timer = CTCM_TIME_5_SEC;
2110293d984fSPeter Tiedemann 	}
21112a7c6f2cSPeter Tiedemann 	dev_info(&dev->dev, "Restarting device\n");
2112293d984fSPeter Tiedemann 
2113293d984fSPeter Tiedemann 	dev_action_stop(fi, event, arg);
2114293d984fSPeter Tiedemann 	fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
2115293d984fSPeter Tiedemann 	if (IS_MPC(priv))
2116293d984fSPeter Tiedemann 		fsm_newstate(priv->mpcg->fsm, MPCG_STATE_RESET);
2117293d984fSPeter Tiedemann 
2118293d984fSPeter Tiedemann 	/* going back into start sequence too quickly can	  */
2119293d984fSPeter Tiedemann 	/* result in the other side becoming unreachable   due	  */
2120293d984fSPeter Tiedemann 	/* to sense reported when IO is aborted			  */
2121293d984fSPeter Tiedemann 	fsm_addtimer(&priv->restart_timer, restart_timer,
2122293d984fSPeter Tiedemann 			DEV_EVENT_START, dev);
2123293d984fSPeter Tiedemann }
2124293d984fSPeter Tiedemann 
2125a962cc4bSHeiko Carstens /*
2126293d984fSPeter Tiedemann  * Called from channel statemachine
2127293d984fSPeter Tiedemann  * when a channel is up and running.
2128293d984fSPeter Tiedemann  *
2129293d984fSPeter Tiedemann  * fi		An instance of an interface statemachine.
2130293d984fSPeter Tiedemann  * event	The event, just happened.
2131293d984fSPeter Tiedemann  * arg		Generic pointer, casted from struct net_device * upon call.
2132293d984fSPeter Tiedemann  */
dev_action_chup(fsm_instance * fi,int event,void * arg)2133293d984fSPeter Tiedemann static void dev_action_chup(fsm_instance *fi, int event, void *arg)
2134293d984fSPeter Tiedemann {
2135293d984fSPeter Tiedemann 	struct net_device *dev = arg;
2136261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
2137aa3f2cb6SPeter Tiedemann 	int dev_stat = fsm_getstate(fi);
2138293d984fSPeter Tiedemann 
2139aa3f2cb6SPeter Tiedemann 	CTCM_DBF_TEXT_(SETUP, CTC_DBF_NOTICE,
2140aa3f2cb6SPeter Tiedemann 			"%s(%s): priv = %p [%d,%d]\n ",	CTCM_FUNTAIL,
2141261893d3SPeter Tiedemann 				dev->name, dev->ml_priv, dev_stat, event);
2142293d984fSPeter Tiedemann 
2143293d984fSPeter Tiedemann 	switch (fsm_getstate(fi)) {
2144293d984fSPeter Tiedemann 	case DEV_STATE_STARTWAIT_RXTX:
2145293d984fSPeter Tiedemann 		if (event == DEV_EVENT_RXUP)
2146293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
2147293d984fSPeter Tiedemann 		else
2148293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
2149293d984fSPeter Tiedemann 		break;
2150293d984fSPeter Tiedemann 	case DEV_STATE_STARTWAIT_RX:
2151293d984fSPeter Tiedemann 		if (event == DEV_EVENT_RXUP) {
2152293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_RUNNING);
21532a7c6f2cSPeter Tiedemann 			dev_info(&dev->dev,
21542a7c6f2cSPeter Tiedemann 				"Connected with remote side\n");
2155293d984fSPeter Tiedemann 			ctcm_clear_busy(dev);
2156293d984fSPeter Tiedemann 		}
2157293d984fSPeter Tiedemann 		break;
2158293d984fSPeter Tiedemann 	case DEV_STATE_STARTWAIT_TX:
2159293d984fSPeter Tiedemann 		if (event == DEV_EVENT_TXUP) {
2160293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_RUNNING);
21612a7c6f2cSPeter Tiedemann 			dev_info(&dev->dev,
21622a7c6f2cSPeter Tiedemann 				"Connected with remote side\n");
2163293d984fSPeter Tiedemann 			ctcm_clear_busy(dev);
2164293d984fSPeter Tiedemann 		}
2165293d984fSPeter Tiedemann 		break;
2166293d984fSPeter Tiedemann 	case DEV_STATE_STOPWAIT_TX:
2167293d984fSPeter Tiedemann 		if (event == DEV_EVENT_RXUP)
2168293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
2169293d984fSPeter Tiedemann 		break;
2170293d984fSPeter Tiedemann 	case DEV_STATE_STOPWAIT_RX:
2171293d984fSPeter Tiedemann 		if (event == DEV_EVENT_TXUP)
2172293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
2173293d984fSPeter Tiedemann 		break;
2174293d984fSPeter Tiedemann 	}
2175293d984fSPeter Tiedemann 
2176293d984fSPeter Tiedemann 	if (IS_MPC(priv)) {
2177293d984fSPeter Tiedemann 		if (event == DEV_EVENT_RXUP)
21783c09e264SUrsula Braun 			mpc_channel_action(priv->channel[CTCM_READ],
21793c09e264SUrsula Braun 				CTCM_READ, MPC_CHANNEL_ADD);
2180293d984fSPeter Tiedemann 		else
21813c09e264SUrsula Braun 			mpc_channel_action(priv->channel[CTCM_WRITE],
21823c09e264SUrsula Braun 				CTCM_WRITE, MPC_CHANNEL_ADD);
2183293d984fSPeter Tiedemann 	}
2184293d984fSPeter Tiedemann }
2185293d984fSPeter Tiedemann 
2186a962cc4bSHeiko Carstens /*
2187293d984fSPeter Tiedemann  * Called from device statemachine
2188293d984fSPeter Tiedemann  * when a channel has been shutdown.
2189293d984fSPeter Tiedemann  *
2190293d984fSPeter Tiedemann  * fi		An instance of an interface statemachine.
2191293d984fSPeter Tiedemann  * event	The event, just happened.
2192293d984fSPeter Tiedemann  * arg		Generic pointer, casted from struct net_device * upon call.
2193293d984fSPeter Tiedemann  */
dev_action_chdown(fsm_instance * fi,int event,void * arg)2194293d984fSPeter Tiedemann static void dev_action_chdown(fsm_instance *fi, int event, void *arg)
2195293d984fSPeter Tiedemann {
2196293d984fSPeter Tiedemann 
2197293d984fSPeter Tiedemann 	struct net_device *dev = arg;
2198261893d3SPeter Tiedemann 	struct ctcm_priv *priv = dev->ml_priv;
2199293d984fSPeter Tiedemann 
2200293d984fSPeter Tiedemann 	CTCMY_DBF_DEV_NAME(SETUP, dev, "");
2201293d984fSPeter Tiedemann 
2202293d984fSPeter Tiedemann 	switch (fsm_getstate(fi)) {
2203293d984fSPeter Tiedemann 	case DEV_STATE_RUNNING:
2204293d984fSPeter Tiedemann 		if (event == DEV_EVENT_TXDOWN)
2205293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
2206293d984fSPeter Tiedemann 		else
2207293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
2208293d984fSPeter Tiedemann 		break;
2209293d984fSPeter Tiedemann 	case DEV_STATE_STARTWAIT_RX:
2210293d984fSPeter Tiedemann 		if (event == DEV_EVENT_TXDOWN)
2211293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
2212293d984fSPeter Tiedemann 		break;
2213293d984fSPeter Tiedemann 	case DEV_STATE_STARTWAIT_TX:
2214293d984fSPeter Tiedemann 		if (event == DEV_EVENT_RXDOWN)
2215293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
2216293d984fSPeter Tiedemann 		break;
2217293d984fSPeter Tiedemann 	case DEV_STATE_STOPWAIT_RXTX:
2218293d984fSPeter Tiedemann 		if (event == DEV_EVENT_TXDOWN)
2219293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_STOPWAIT_RX);
2220293d984fSPeter Tiedemann 		else
2221293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_STOPWAIT_TX);
2222293d984fSPeter Tiedemann 		break;
2223293d984fSPeter Tiedemann 	case DEV_STATE_STOPWAIT_RX:
2224293d984fSPeter Tiedemann 		if (event == DEV_EVENT_RXDOWN)
2225293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_STOPPED);
2226293d984fSPeter Tiedemann 		break;
2227293d984fSPeter Tiedemann 	case DEV_STATE_STOPWAIT_TX:
2228293d984fSPeter Tiedemann 		if (event == DEV_EVENT_TXDOWN)
2229293d984fSPeter Tiedemann 			fsm_newstate(fi, DEV_STATE_STOPPED);
2230293d984fSPeter Tiedemann 		break;
2231293d984fSPeter Tiedemann 	}
2232293d984fSPeter Tiedemann 	if (IS_MPC(priv)) {
2233293d984fSPeter Tiedemann 		if (event == DEV_EVENT_RXDOWN)
22343c09e264SUrsula Braun 			mpc_channel_action(priv->channel[CTCM_READ],
22353c09e264SUrsula Braun 				CTCM_READ, MPC_CHANNEL_REMOVE);
2236293d984fSPeter Tiedemann 		else
22373c09e264SUrsula Braun 			mpc_channel_action(priv->channel[CTCM_WRITE],
22383c09e264SUrsula Braun 				CTCM_WRITE, MPC_CHANNEL_REMOVE);
2239293d984fSPeter Tiedemann 	}
2240293d984fSPeter Tiedemann }
2241293d984fSPeter Tiedemann 
2242293d984fSPeter Tiedemann const fsm_node dev_fsm[] = {
2243293d984fSPeter Tiedemann 	{ DEV_STATE_STOPPED,        DEV_EVENT_START,   dev_action_start   },
2244293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_START,   dev_action_start   },
2245293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_RXDOWN,  dev_action_chdown  },
2246293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_TXDOWN,  dev_action_chdown  },
2247293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_RESTART, dev_action_restart },
2248293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_RX,    DEV_EVENT_START,   dev_action_start   },
2249293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_RX,    DEV_EVENT_RXUP,    dev_action_chup    },
2250293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_RX,    DEV_EVENT_TXUP,    dev_action_chup    },
2251293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_RX,    DEV_EVENT_RXDOWN,  dev_action_chdown  },
2252293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_RX,    DEV_EVENT_RESTART, dev_action_restart },
2253293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_TX,    DEV_EVENT_START,   dev_action_start   },
2254293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_TX,    DEV_EVENT_RXUP,    dev_action_chup    },
2255293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_TX,    DEV_EVENT_TXUP,    dev_action_chup    },
2256293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_TX,    DEV_EVENT_TXDOWN,  dev_action_chdown  },
2257293d984fSPeter Tiedemann 	{ DEV_STATE_STOPWAIT_TX,    DEV_EVENT_RESTART, dev_action_restart },
2258293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP,    dev_action_stop    },
2259293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP,    dev_action_chup    },
2260293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP,    dev_action_chup    },
2261293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN,  dev_action_chdown  },
2262293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN,  dev_action_chdown  },
2263293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart },
2264293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_TX,   DEV_EVENT_STOP,    dev_action_stop    },
2265293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_TX,   DEV_EVENT_RXUP,    dev_action_chup    },
2266293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_TX,   DEV_EVENT_TXUP,    dev_action_chup    },
2267293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_TX,   DEV_EVENT_RXDOWN,  dev_action_chdown  },
2268293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_TX,   DEV_EVENT_RESTART, dev_action_restart },
2269293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_RX,   DEV_EVENT_STOP,    dev_action_stop    },
2270293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_RX,   DEV_EVENT_RXUP,    dev_action_chup    },
2271293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_RX,   DEV_EVENT_TXUP,    dev_action_chup    },
2272293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_RX,   DEV_EVENT_TXDOWN,  dev_action_chdown  },
2273293d984fSPeter Tiedemann 	{ DEV_STATE_STARTWAIT_RX,   DEV_EVENT_RESTART, dev_action_restart },
2274293d984fSPeter Tiedemann 	{ DEV_STATE_RUNNING,        DEV_EVENT_STOP,    dev_action_stop    },
2275293d984fSPeter Tiedemann 	{ DEV_STATE_RUNNING,        DEV_EVENT_RXDOWN,  dev_action_chdown  },
2276293d984fSPeter Tiedemann 	{ DEV_STATE_RUNNING,        DEV_EVENT_TXDOWN,  dev_action_chdown  },
2277293d984fSPeter Tiedemann 	{ DEV_STATE_RUNNING,        DEV_EVENT_TXUP,    ctcm_action_nop    },
2278293d984fSPeter Tiedemann 	{ DEV_STATE_RUNNING,        DEV_EVENT_RXUP,    ctcm_action_nop    },
2279293d984fSPeter Tiedemann 	{ DEV_STATE_RUNNING,        DEV_EVENT_RESTART, dev_action_restart },
2280293d984fSPeter Tiedemann };
2281293d984fSPeter Tiedemann 
2282293d984fSPeter Tiedemann int dev_fsm_len = ARRAY_SIZE(dev_fsm);
2283293d984fSPeter Tiedemann 
2284293d984fSPeter Tiedemann /* --- This is the END my friend --- */
2285293d984fSPeter Tiedemann 
2286