162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright IBM Corp. 2001, 2007
462306a36Sopenharmony_ci * Authors:	Fritz Elfert (felfert@millenux.com)
562306a36Sopenharmony_ci * 		Peter Tiedemann (ptiedem@de.ibm.com)
662306a36Sopenharmony_ci *	MPC additions :
762306a36Sopenharmony_ci *		Belinda Thompson (belindat@us.ibm.com)
862306a36Sopenharmony_ci *		Andy Richter (richtera@us.ibm.com)
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#undef DEBUG
1262306a36Sopenharmony_ci#undef DEBUGDATA
1362306a36Sopenharmony_ci#undef DEBUGCCW
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define KMSG_COMPONENT "ctcm"
1662306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/init.h>
2062306a36Sopenharmony_ci#include <linux/kernel.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/errno.h>
2362306a36Sopenharmony_ci#include <linux/types.h>
2462306a36Sopenharmony_ci#include <linux/interrupt.h>
2562306a36Sopenharmony_ci#include <linux/timer.h>
2662306a36Sopenharmony_ci#include <linux/bitops.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/signal.h>
2962306a36Sopenharmony_ci#include <linux/string.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <linux/ip.h>
3262306a36Sopenharmony_ci#include <linux/if_arp.h>
3362306a36Sopenharmony_ci#include <linux/tcp.h>
3462306a36Sopenharmony_ci#include <linux/skbuff.h>
3562306a36Sopenharmony_ci#include <linux/ctype.h>
3662306a36Sopenharmony_ci#include <net/dst.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <linux/io.h>
3962306a36Sopenharmony_ci#include <asm/ccwdev.h>
4062306a36Sopenharmony_ci#include <asm/ccwgroup.h>
4162306a36Sopenharmony_ci#include <linux/uaccess.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include <asm/idals.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include "fsm.h"
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#include "ctcm_dbug.h"
4862306a36Sopenharmony_ci#include "ctcm_main.h"
4962306a36Sopenharmony_ci#include "ctcm_fsms.h"
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ciconst char *dev_state_names[] = {
5262306a36Sopenharmony_ci	[DEV_STATE_STOPPED]		= "Stopped",
5362306a36Sopenharmony_ci	[DEV_STATE_STARTWAIT_RXTX]	= "StartWait RXTX",
5462306a36Sopenharmony_ci	[DEV_STATE_STARTWAIT_RX]	= "StartWait RX",
5562306a36Sopenharmony_ci	[DEV_STATE_STARTWAIT_TX]	= "StartWait TX",
5662306a36Sopenharmony_ci	[DEV_STATE_STOPWAIT_RXTX]	= "StopWait RXTX",
5762306a36Sopenharmony_ci	[DEV_STATE_STOPWAIT_RX]		= "StopWait RX",
5862306a36Sopenharmony_ci	[DEV_STATE_STOPWAIT_TX]		= "StopWait TX",
5962306a36Sopenharmony_ci	[DEV_STATE_RUNNING]		= "Running",
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciconst char *dev_event_names[] = {
6362306a36Sopenharmony_ci	[DEV_EVENT_START]	= "Start",
6462306a36Sopenharmony_ci	[DEV_EVENT_STOP]	= "Stop",
6562306a36Sopenharmony_ci	[DEV_EVENT_RXUP]	= "RX up",
6662306a36Sopenharmony_ci	[DEV_EVENT_TXUP]	= "TX up",
6762306a36Sopenharmony_ci	[DEV_EVENT_RXDOWN]	= "RX down",
6862306a36Sopenharmony_ci	[DEV_EVENT_TXDOWN]	= "TX down",
6962306a36Sopenharmony_ci	[DEV_EVENT_RESTART]	= "Restart",
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciconst char *ctc_ch_event_names[] = {
7362306a36Sopenharmony_ci	[CTC_EVENT_IO_SUCCESS]	= "ccw_device success",
7462306a36Sopenharmony_ci	[CTC_EVENT_IO_EBUSY]	= "ccw_device busy",
7562306a36Sopenharmony_ci	[CTC_EVENT_IO_ENODEV]	= "ccw_device enodev",
7662306a36Sopenharmony_ci	[CTC_EVENT_IO_UNKNOWN]	= "ccw_device unknown",
7762306a36Sopenharmony_ci	[CTC_EVENT_ATTNBUSY]	= "Status ATTN & BUSY",
7862306a36Sopenharmony_ci	[CTC_EVENT_ATTN]	= "Status ATTN",
7962306a36Sopenharmony_ci	[CTC_EVENT_BUSY]	= "Status BUSY",
8062306a36Sopenharmony_ci	[CTC_EVENT_UC_RCRESET]	= "Unit check remote reset",
8162306a36Sopenharmony_ci	[CTC_EVENT_UC_RSRESET]	= "Unit check remote system reset",
8262306a36Sopenharmony_ci	[CTC_EVENT_UC_TXTIMEOUT] = "Unit check TX timeout",
8362306a36Sopenharmony_ci	[CTC_EVENT_UC_TXPARITY]	= "Unit check TX parity",
8462306a36Sopenharmony_ci	[CTC_EVENT_UC_HWFAIL]	= "Unit check Hardware failure",
8562306a36Sopenharmony_ci	[CTC_EVENT_UC_RXPARITY]	= "Unit check RX parity",
8662306a36Sopenharmony_ci	[CTC_EVENT_UC_ZERO]	= "Unit check ZERO",
8762306a36Sopenharmony_ci	[CTC_EVENT_UC_UNKNOWN]	= "Unit check Unknown",
8862306a36Sopenharmony_ci	[CTC_EVENT_SC_UNKNOWN]	= "SubChannel check Unknown",
8962306a36Sopenharmony_ci	[CTC_EVENT_MC_FAIL]	= "Machine check failure",
9062306a36Sopenharmony_ci	[CTC_EVENT_MC_GOOD]	= "Machine check operational",
9162306a36Sopenharmony_ci	[CTC_EVENT_IRQ]		= "IRQ normal",
9262306a36Sopenharmony_ci	[CTC_EVENT_FINSTAT]	= "IRQ final",
9362306a36Sopenharmony_ci	[CTC_EVENT_TIMER]	= "Timer",
9462306a36Sopenharmony_ci	[CTC_EVENT_START]	= "Start",
9562306a36Sopenharmony_ci	[CTC_EVENT_STOP]	= "Stop",
9662306a36Sopenharmony_ci	/*
9762306a36Sopenharmony_ci	* additional MPC events
9862306a36Sopenharmony_ci	*/
9962306a36Sopenharmony_ci	[CTC_EVENT_SEND_XID]	= "XID Exchange",
10062306a36Sopenharmony_ci	[CTC_EVENT_RSWEEP_TIMER] = "MPC Group Sweep Timer",
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ciconst char *ctc_ch_state_names[] = {
10462306a36Sopenharmony_ci	[CTC_STATE_IDLE]	= "Idle",
10562306a36Sopenharmony_ci	[CTC_STATE_STOPPED]	= "Stopped",
10662306a36Sopenharmony_ci	[CTC_STATE_STARTWAIT]	= "StartWait",
10762306a36Sopenharmony_ci	[CTC_STATE_STARTRETRY]	= "StartRetry",
10862306a36Sopenharmony_ci	[CTC_STATE_SETUPWAIT]	= "SetupWait",
10962306a36Sopenharmony_ci	[CTC_STATE_RXINIT]	= "RX init",
11062306a36Sopenharmony_ci	[CTC_STATE_TXINIT]	= "TX init",
11162306a36Sopenharmony_ci	[CTC_STATE_RX]		= "RX",
11262306a36Sopenharmony_ci	[CTC_STATE_TX]		= "TX",
11362306a36Sopenharmony_ci	[CTC_STATE_RXIDLE]	= "RX idle",
11462306a36Sopenharmony_ci	[CTC_STATE_TXIDLE]	= "TX idle",
11562306a36Sopenharmony_ci	[CTC_STATE_RXERR]	= "RX error",
11662306a36Sopenharmony_ci	[CTC_STATE_TXERR]	= "TX error",
11762306a36Sopenharmony_ci	[CTC_STATE_TERM]	= "Terminating",
11862306a36Sopenharmony_ci	[CTC_STATE_DTERM]	= "Restarting",
11962306a36Sopenharmony_ci	[CTC_STATE_NOTOP]	= "Not operational",
12062306a36Sopenharmony_ci	/*
12162306a36Sopenharmony_ci	* additional MPC states
12262306a36Sopenharmony_ci	*/
12362306a36Sopenharmony_ci	[CH_XID0_PENDING]	= "Pending XID0 Start",
12462306a36Sopenharmony_ci	[CH_XID0_INPROGRESS]	= "In XID0 Negotiations ",
12562306a36Sopenharmony_ci	[CH_XID7_PENDING]	= "Pending XID7 P1 Start",
12662306a36Sopenharmony_ci	[CH_XID7_PENDING1]	= "Active XID7 P1 Exchange ",
12762306a36Sopenharmony_ci	[CH_XID7_PENDING2]	= "Pending XID7 P2 Start ",
12862306a36Sopenharmony_ci	[CH_XID7_PENDING3]	= "Active XID7 P2 Exchange ",
12962306a36Sopenharmony_ci	[CH_XID7_PENDING4]	= "XID7 Complete - Pending READY ",
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void ctcm_action_nop(fsm_instance *fi, int event, void *arg);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/*
13562306a36Sopenharmony_ci * ----- static ctcm actions for channel statemachine -----
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci*/
13862306a36Sopenharmony_cistatic void chx_txdone(fsm_instance *fi, int event, void *arg);
13962306a36Sopenharmony_cistatic void chx_rx(fsm_instance *fi, int event, void *arg);
14062306a36Sopenharmony_cistatic void chx_rxidle(fsm_instance *fi, int event, void *arg);
14162306a36Sopenharmony_cistatic void chx_firstio(fsm_instance *fi, int event, void *arg);
14262306a36Sopenharmony_cistatic void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg);
14362306a36Sopenharmony_cistatic void ctcm_chx_start(fsm_instance *fi, int event, void *arg);
14462306a36Sopenharmony_cistatic void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg);
14562306a36Sopenharmony_cistatic void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg);
14662306a36Sopenharmony_cistatic void ctcm_chx_stop(fsm_instance *fi, int event, void *arg);
14762306a36Sopenharmony_cistatic void ctcm_chx_fail(fsm_instance *fi, int event, void *arg);
14862306a36Sopenharmony_cistatic void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg);
14962306a36Sopenharmony_cistatic void ctcm_chx_restart(fsm_instance *fi, int event, void *arg);
15062306a36Sopenharmony_cistatic void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg);
15162306a36Sopenharmony_cistatic void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg);
15262306a36Sopenharmony_cistatic void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg);
15362306a36Sopenharmony_cistatic void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg);
15462306a36Sopenharmony_cistatic void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg);
15562306a36Sopenharmony_cistatic void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/*
15862306a36Sopenharmony_ci * ----- static ctcmpc actions for ctcmpc channel statemachine -----
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci*/
16162306a36Sopenharmony_cistatic void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg);
16262306a36Sopenharmony_cistatic void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg);
16362306a36Sopenharmony_cistatic void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg);
16462306a36Sopenharmony_ci/* shared :
16562306a36Sopenharmony_cistatic void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg);
16662306a36Sopenharmony_cistatic void ctcm_chx_start(fsm_instance *fi, int event, void *arg);
16762306a36Sopenharmony_cistatic void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg);
16862306a36Sopenharmony_cistatic void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg);
16962306a36Sopenharmony_cistatic void ctcm_chx_stop(fsm_instance *fi, int event, void *arg);
17062306a36Sopenharmony_cistatic void ctcm_chx_fail(fsm_instance *fi, int event, void *arg);
17162306a36Sopenharmony_cistatic void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg);
17262306a36Sopenharmony_cistatic void ctcm_chx_restart(fsm_instance *fi, int event, void *arg);
17362306a36Sopenharmony_cistatic void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg);
17462306a36Sopenharmony_cistatic void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg);
17562306a36Sopenharmony_cistatic void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg);
17662306a36Sopenharmony_cistatic void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg);
17762306a36Sopenharmony_cistatic void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg);
17862306a36Sopenharmony_cistatic void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg);
17962306a36Sopenharmony_ci*/
18062306a36Sopenharmony_cistatic void ctcmpc_chx_attn(fsm_instance *fsm, int event, void *arg);
18162306a36Sopenharmony_cistatic void ctcmpc_chx_attnbusy(fsm_instance *, int, void *);
18262306a36Sopenharmony_cistatic void ctcmpc_chx_resend(fsm_instance *, int, void *);
18362306a36Sopenharmony_cistatic void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/*
18662306a36Sopenharmony_ci * Check return code of a preceding ccw_device call, halt_IO etc...
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci * ch	:	The channel, the error belongs to.
18962306a36Sopenharmony_ci * Returns the error code (!= 0) to inspect.
19062306a36Sopenharmony_ci */
19162306a36Sopenharmony_civoid ctcm_ccw_check_rc(struct channel *ch, int rc, char *msg)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
19462306a36Sopenharmony_ci		"%s(%s): %s: %04x\n",
19562306a36Sopenharmony_ci		CTCM_FUNTAIL, ch->id, msg, rc);
19662306a36Sopenharmony_ci	switch (rc) {
19762306a36Sopenharmony_ci	case -EBUSY:
19862306a36Sopenharmony_ci		pr_info("%s: The communication peer is busy\n",
19962306a36Sopenharmony_ci			ch->id);
20062306a36Sopenharmony_ci		fsm_event(ch->fsm, CTC_EVENT_IO_EBUSY, ch);
20162306a36Sopenharmony_ci		break;
20262306a36Sopenharmony_ci	case -ENODEV:
20362306a36Sopenharmony_ci		pr_err("%s: The specified target device is not valid\n",
20462306a36Sopenharmony_ci		       ch->id);
20562306a36Sopenharmony_ci		fsm_event(ch->fsm, CTC_EVENT_IO_ENODEV, ch);
20662306a36Sopenharmony_ci		break;
20762306a36Sopenharmony_ci	default:
20862306a36Sopenharmony_ci		pr_err("An I/O operation resulted in error %04x\n",
20962306a36Sopenharmony_ci		       rc);
21062306a36Sopenharmony_ci		fsm_event(ch->fsm, CTC_EVENT_IO_UNKNOWN, ch);
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_civoid ctcm_purge_skb_queue(struct sk_buff_head *q)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct sk_buff *skb;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	CTCM_DBF_TEXT(TRACE, CTC_DBF_DEBUG, __func__);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	while ((skb = skb_dequeue(q))) {
22162306a36Sopenharmony_ci		refcount_dec(&skb->users);
22262306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/*
22762306a36Sopenharmony_ci * NOP action for statemachines
22862306a36Sopenharmony_ci */
22962306a36Sopenharmony_cistatic void ctcm_action_nop(fsm_instance *fi, int event, void *arg)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/*
23462306a36Sopenharmony_ci * Actions for channel - statemachines.
23562306a36Sopenharmony_ci */
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci/*
23862306a36Sopenharmony_ci * Normal data has been send. Free the corresponding
23962306a36Sopenharmony_ci * skb (it's in io_queue), reset dev->tbusy and
24062306a36Sopenharmony_ci * revert to idle state.
24162306a36Sopenharmony_ci *
24262306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
24362306a36Sopenharmony_ci * event	The event, just happened.
24462306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
24562306a36Sopenharmony_ci */
24662306a36Sopenharmony_cistatic void chx_txdone(fsm_instance *fi, int event, void *arg)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct channel *ch = arg;
24962306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
25062306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
25162306a36Sopenharmony_ci	struct sk_buff *skb;
25262306a36Sopenharmony_ci	int first = 1;
25362306a36Sopenharmony_ci	int i;
25462306a36Sopenharmony_ci	unsigned long duration;
25562306a36Sopenharmony_ci	unsigned long done_stamp = jiffies;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	duration = done_stamp - ch->prof.send_stamp;
26062306a36Sopenharmony_ci	if (duration > ch->prof.tx_time)
26162306a36Sopenharmony_ci		ch->prof.tx_time = duration;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (ch->irb->scsw.cmd.count != 0)
26462306a36Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
26562306a36Sopenharmony_ci			"%s(%s): TX not complete, remaining %d bytes",
26662306a36Sopenharmony_ci			     CTCM_FUNTAIL, dev->name, ch->irb->scsw.cmd.count);
26762306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
26862306a36Sopenharmony_ci	while ((skb = skb_dequeue(&ch->io_queue))) {
26962306a36Sopenharmony_ci		priv->stats.tx_packets++;
27062306a36Sopenharmony_ci		priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
27162306a36Sopenharmony_ci		if (first) {
27262306a36Sopenharmony_ci			priv->stats.tx_bytes += 2;
27362306a36Sopenharmony_ci			first = 0;
27462306a36Sopenharmony_ci		}
27562306a36Sopenharmony_ci		refcount_dec(&skb->users);
27662306a36Sopenharmony_ci		dev_kfree_skb_irq(skb);
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci	spin_lock(&ch->collect_lock);
27962306a36Sopenharmony_ci	clear_normalized_cda(&ch->ccw[4]);
28062306a36Sopenharmony_ci	if (ch->collect_len > 0) {
28162306a36Sopenharmony_ci		int rc;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		if (ctcm_checkalloc_buffer(ch)) {
28462306a36Sopenharmony_ci			spin_unlock(&ch->collect_lock);
28562306a36Sopenharmony_ci			return;
28662306a36Sopenharmony_ci		}
28762306a36Sopenharmony_ci		ch->trans_skb->data = ch->trans_skb_data;
28862306a36Sopenharmony_ci		skb_reset_tail_pointer(ch->trans_skb);
28962306a36Sopenharmony_ci		ch->trans_skb->len = 0;
29062306a36Sopenharmony_ci		if (ch->prof.maxmulti < (ch->collect_len + 2))
29162306a36Sopenharmony_ci			ch->prof.maxmulti = ch->collect_len + 2;
29262306a36Sopenharmony_ci		if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
29362306a36Sopenharmony_ci			ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
29462306a36Sopenharmony_ci		*((__u16 *)skb_put(ch->trans_skb, 2)) = ch->collect_len + 2;
29562306a36Sopenharmony_ci		i = 0;
29662306a36Sopenharmony_ci		while ((skb = skb_dequeue(&ch->collect_queue))) {
29762306a36Sopenharmony_ci			skb_copy_from_linear_data(skb,
29862306a36Sopenharmony_ci				skb_put(ch->trans_skb, skb->len), skb->len);
29962306a36Sopenharmony_ci			priv->stats.tx_packets++;
30062306a36Sopenharmony_ci			priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
30162306a36Sopenharmony_ci			refcount_dec(&skb->users);
30262306a36Sopenharmony_ci			dev_kfree_skb_irq(skb);
30362306a36Sopenharmony_ci			i++;
30462306a36Sopenharmony_ci		}
30562306a36Sopenharmony_ci		ch->collect_len = 0;
30662306a36Sopenharmony_ci		spin_unlock(&ch->collect_lock);
30762306a36Sopenharmony_ci		ch->ccw[1].count = ch->trans_skb->len;
30862306a36Sopenharmony_ci		fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
30962306a36Sopenharmony_ci		ch->prof.send_stamp = jiffies;
31062306a36Sopenharmony_ci		rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
31162306a36Sopenharmony_ci		ch->prof.doios_multi++;
31262306a36Sopenharmony_ci		if (rc != 0) {
31362306a36Sopenharmony_ci			priv->stats.tx_dropped += i;
31462306a36Sopenharmony_ci			priv->stats.tx_errors += i;
31562306a36Sopenharmony_ci			fsm_deltimer(&ch->timer);
31662306a36Sopenharmony_ci			ctcm_ccw_check_rc(ch, rc, "chained TX");
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci	} else {
31962306a36Sopenharmony_ci		spin_unlock(&ch->collect_lock);
32062306a36Sopenharmony_ci		fsm_newstate(fi, CTC_STATE_TXIDLE);
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci	ctcm_clear_busy_do(dev);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/*
32662306a36Sopenharmony_ci * Initial data is sent.
32762306a36Sopenharmony_ci * Notify device statemachine that we are up and
32862306a36Sopenharmony_ci * running.
32962306a36Sopenharmony_ci *
33062306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
33162306a36Sopenharmony_ci * event	The event, just happened.
33262306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
33362306a36Sopenharmony_ci */
33462306a36Sopenharmony_civoid ctcm_chx_txidle(fsm_instance *fi, int event, void *arg)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct channel *ch = arg;
33762306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
33862306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
34362306a36Sopenharmony_ci	fsm_newstate(fi, CTC_STATE_TXIDLE);
34462306a36Sopenharmony_ci	fsm_event(priv->fsm, DEV_EVENT_TXUP, ch->netdev);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci/*
34862306a36Sopenharmony_ci * Got normal data, check for sanity, queue it up, allocate new buffer
34962306a36Sopenharmony_ci * trigger bottom half, and initiate next read.
35062306a36Sopenharmony_ci *
35162306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
35262306a36Sopenharmony_ci * event	The event, just happened.
35362306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
35462306a36Sopenharmony_ci */
35562306a36Sopenharmony_cistatic void chx_rx(fsm_instance *fi, int event, void *arg)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct channel *ch = arg;
35862306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
35962306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
36062306a36Sopenharmony_ci	int len = ch->max_bufsize - ch->irb->scsw.cmd.count;
36162306a36Sopenharmony_ci	struct sk_buff *skb = ch->trans_skb;
36262306a36Sopenharmony_ci	__u16 block_len = *((__u16 *)skb->data);
36362306a36Sopenharmony_ci	int check_len;
36462306a36Sopenharmony_ci	int rc;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
36762306a36Sopenharmony_ci	if (len < 8) {
36862306a36Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
36962306a36Sopenharmony_ci			"%s(%s): got packet with length %d < 8\n",
37062306a36Sopenharmony_ci					CTCM_FUNTAIL, dev->name, len);
37162306a36Sopenharmony_ci		priv->stats.rx_dropped++;
37262306a36Sopenharmony_ci		priv->stats.rx_length_errors++;
37362306a36Sopenharmony_ci		goto again;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci	if (len > ch->max_bufsize) {
37662306a36Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
37762306a36Sopenharmony_ci			"%s(%s): got packet with length %d > %d\n",
37862306a36Sopenharmony_ci				CTCM_FUNTAIL, dev->name, len, ch->max_bufsize);
37962306a36Sopenharmony_ci		priv->stats.rx_dropped++;
38062306a36Sopenharmony_ci		priv->stats.rx_length_errors++;
38162306a36Sopenharmony_ci		goto again;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	/*
38562306a36Sopenharmony_ci	 * VM TCP seems to have a bug sending 2 trailing bytes of garbage.
38662306a36Sopenharmony_ci	 */
38762306a36Sopenharmony_ci	switch (ch->protocol) {
38862306a36Sopenharmony_ci	case CTCM_PROTO_S390:
38962306a36Sopenharmony_ci	case CTCM_PROTO_OS390:
39062306a36Sopenharmony_ci		check_len = block_len + 2;
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	default:
39362306a36Sopenharmony_ci		check_len = block_len;
39462306a36Sopenharmony_ci		break;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci	if ((len < block_len) || (len > check_len)) {
39762306a36Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
39862306a36Sopenharmony_ci			"%s(%s): got block length %d != rx length %d\n",
39962306a36Sopenharmony_ci				CTCM_FUNTAIL, dev->name, block_len, len);
40062306a36Sopenharmony_ci		if (do_debug)
40162306a36Sopenharmony_ci			ctcmpc_dump_skb(skb, 0);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		*((__u16 *)skb->data) = len;
40462306a36Sopenharmony_ci		priv->stats.rx_dropped++;
40562306a36Sopenharmony_ci		priv->stats.rx_length_errors++;
40662306a36Sopenharmony_ci		goto again;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci	if (block_len > 2) {
40962306a36Sopenharmony_ci		*((__u16 *)skb->data) = block_len - 2;
41062306a36Sopenharmony_ci		ctcm_unpack_skb(ch, skb);
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci again:
41362306a36Sopenharmony_ci	skb->data = ch->trans_skb_data;
41462306a36Sopenharmony_ci	skb_reset_tail_pointer(skb);
41562306a36Sopenharmony_ci	skb->len = 0;
41662306a36Sopenharmony_ci	if (ctcm_checkalloc_buffer(ch))
41762306a36Sopenharmony_ci		return;
41862306a36Sopenharmony_ci	ch->ccw[1].count = ch->max_bufsize;
41962306a36Sopenharmony_ci	rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
42062306a36Sopenharmony_ci	if (rc != 0)
42162306a36Sopenharmony_ci		ctcm_ccw_check_rc(ch, rc, "normal RX");
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci/*
42562306a36Sopenharmony_ci * Initialize connection by sending a __u16 of value 0.
42662306a36Sopenharmony_ci *
42762306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
42862306a36Sopenharmony_ci * event	The event, just happened.
42962306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
43062306a36Sopenharmony_ci */
43162306a36Sopenharmony_cistatic void chx_firstio(fsm_instance *fi, int event, void *arg)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	int rc;
43462306a36Sopenharmony_ci	struct channel *ch = arg;
43562306a36Sopenharmony_ci	int fsmstate = fsm_getstate(fi);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
43862306a36Sopenharmony_ci		"%s(%s) : %02x",
43962306a36Sopenharmony_ci		CTCM_FUNTAIL, ch->id, fsmstate);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	ch->sense_rc = 0;	/* reset unit check report control */
44262306a36Sopenharmony_ci	if (fsmstate == CTC_STATE_TXIDLE)
44362306a36Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
44462306a36Sopenharmony_ci			"%s(%s): remote side issued READ?, init.\n",
44562306a36Sopenharmony_ci				CTCM_FUNTAIL, ch->id);
44662306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
44762306a36Sopenharmony_ci	if (ctcm_checkalloc_buffer(ch))
44862306a36Sopenharmony_ci		return;
44962306a36Sopenharmony_ci	if ((fsmstate == CTC_STATE_SETUPWAIT) &&
45062306a36Sopenharmony_ci	    (ch->protocol == CTCM_PROTO_OS390)) {
45162306a36Sopenharmony_ci		/* OS/390 resp. z/OS */
45262306a36Sopenharmony_ci		if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
45362306a36Sopenharmony_ci			*((__u16 *)ch->trans_skb->data) = CTCM_INITIAL_BLOCKLEN;
45462306a36Sopenharmony_ci			fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC,
45562306a36Sopenharmony_ci				     CTC_EVENT_TIMER, ch);
45662306a36Sopenharmony_ci			chx_rxidle(fi, event, arg);
45762306a36Sopenharmony_ci		} else {
45862306a36Sopenharmony_ci			struct net_device *dev = ch->netdev;
45962306a36Sopenharmony_ci			struct ctcm_priv *priv = dev->ml_priv;
46062306a36Sopenharmony_ci			fsm_newstate(fi, CTC_STATE_TXIDLE);
46162306a36Sopenharmony_ci			fsm_event(priv->fsm, DEV_EVENT_TXUP, dev);
46262306a36Sopenharmony_ci		}
46362306a36Sopenharmony_ci		return;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci	/*
46662306a36Sopenharmony_ci	 * Don't setup a timer for receiving the initial RX frame
46762306a36Sopenharmony_ci	 * if in compatibility mode, since VM TCP delays the initial
46862306a36Sopenharmony_ci	 * frame until it has some data to send.
46962306a36Sopenharmony_ci	 */
47062306a36Sopenharmony_ci	if ((CHANNEL_DIRECTION(ch->flags) == CTCM_WRITE) ||
47162306a36Sopenharmony_ci	    (ch->protocol != CTCM_PROTO_S390))
47262306a36Sopenharmony_ci		fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	*((__u16 *)ch->trans_skb->data) = CTCM_INITIAL_BLOCKLEN;
47562306a36Sopenharmony_ci	ch->ccw[1].count = 2;	/* Transfer only length */
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)
47862306a36Sopenharmony_ci		     ? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
47962306a36Sopenharmony_ci	rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
48062306a36Sopenharmony_ci	if (rc != 0) {
48162306a36Sopenharmony_ci		fsm_deltimer(&ch->timer);
48262306a36Sopenharmony_ci		fsm_newstate(fi, CTC_STATE_SETUPWAIT);
48362306a36Sopenharmony_ci		ctcm_ccw_check_rc(ch, rc, "init IO");
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci	/*
48662306a36Sopenharmony_ci	 * If in compatibility mode since we don't setup a timer, we
48762306a36Sopenharmony_ci	 * also signal RX channel up immediately. This enables us
48862306a36Sopenharmony_ci	 * to send packets early which in turn usually triggers some
48962306a36Sopenharmony_ci	 * reply from VM TCP which brings up the RX channel to it's
49062306a36Sopenharmony_ci	 * final state.
49162306a36Sopenharmony_ci	 */
49262306a36Sopenharmony_ci	if ((CHANNEL_DIRECTION(ch->flags) == CTCM_READ) &&
49362306a36Sopenharmony_ci	    (ch->protocol == CTCM_PROTO_S390)) {
49462306a36Sopenharmony_ci		struct net_device *dev = ch->netdev;
49562306a36Sopenharmony_ci		struct ctcm_priv *priv = dev->ml_priv;
49662306a36Sopenharmony_ci		fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/*
50162306a36Sopenharmony_ci * Got initial data, check it. If OK,
50262306a36Sopenharmony_ci * notify device statemachine that we are up and
50362306a36Sopenharmony_ci * running.
50462306a36Sopenharmony_ci *
50562306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
50662306a36Sopenharmony_ci * event	The event, just happened.
50762306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
50862306a36Sopenharmony_ci */
50962306a36Sopenharmony_cistatic void chx_rxidle(fsm_instance *fi, int event, void *arg)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	struct channel *ch = arg;
51262306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
51362306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
51462306a36Sopenharmony_ci	__u16 buflen;
51562306a36Sopenharmony_ci	int rc;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
51862306a36Sopenharmony_ci	buflen = *((__u16 *)ch->trans_skb->data);
51962306a36Sopenharmony_ci	CTCM_PR_DEBUG("%s: %s: Initial RX count = %d\n",
52062306a36Sopenharmony_ci			__func__, dev->name, buflen);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (buflen >= CTCM_INITIAL_BLOCKLEN) {
52362306a36Sopenharmony_ci		if (ctcm_checkalloc_buffer(ch))
52462306a36Sopenharmony_ci			return;
52562306a36Sopenharmony_ci		ch->ccw[1].count = ch->max_bufsize;
52662306a36Sopenharmony_ci		fsm_newstate(fi, CTC_STATE_RXIDLE);
52762306a36Sopenharmony_ci		rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
52862306a36Sopenharmony_ci		if (rc != 0) {
52962306a36Sopenharmony_ci			fsm_newstate(fi, CTC_STATE_RXINIT);
53062306a36Sopenharmony_ci			ctcm_ccw_check_rc(ch, rc, "initial RX");
53162306a36Sopenharmony_ci		} else
53262306a36Sopenharmony_ci			fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
53362306a36Sopenharmony_ci	} else {
53462306a36Sopenharmony_ci		CTCM_PR_DEBUG("%s: %s: Initial RX count %d not %d\n",
53562306a36Sopenharmony_ci				__func__, dev->name,
53662306a36Sopenharmony_ci					buflen, CTCM_INITIAL_BLOCKLEN);
53762306a36Sopenharmony_ci		chx_firstio(fi, event, arg);
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci/*
54262306a36Sopenharmony_ci * Set channel into extended mode.
54362306a36Sopenharmony_ci *
54462306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
54562306a36Sopenharmony_ci * event	The event, just happened.
54662306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
54762306a36Sopenharmony_ci */
54862306a36Sopenharmony_cistatic void ctcm_chx_setmode(fsm_instance *fi, int event, void *arg)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	struct channel *ch = arg;
55162306a36Sopenharmony_ci	int rc;
55262306a36Sopenharmony_ci	unsigned long saveflags = 0;
55362306a36Sopenharmony_ci	int timeout = CTCM_TIME_5_SEC;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
55662306a36Sopenharmony_ci	if (IS_MPC(ch)) {
55762306a36Sopenharmony_ci		timeout = 1500;
55862306a36Sopenharmony_ci		CTCM_PR_DEBUG("enter %s: cp=%i ch=0x%p id=%s\n",
55962306a36Sopenharmony_ci				__func__, smp_processor_id(), ch, ch->id);
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci	fsm_addtimer(&ch->timer, timeout, CTC_EVENT_TIMER, ch);
56262306a36Sopenharmony_ci	fsm_newstate(fi, CTC_STATE_SETUPWAIT);
56362306a36Sopenharmony_ci	CTCM_CCW_DUMP((char *)&ch->ccw[6], sizeof(struct ccw1) * 2);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (event == CTC_EVENT_TIMER)	/* only for timer not yet locked */
56662306a36Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
56762306a36Sopenharmony_ci			/* Such conditional locking is undeterministic in
56862306a36Sopenharmony_ci			 * static view. => ignore sparse warnings here. */
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	rc = ccw_device_start(ch->cdev, &ch->ccw[6], 0, 0xff, 0);
57162306a36Sopenharmony_ci	if (event == CTC_EVENT_TIMER)	/* see above comments */
57262306a36Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
57362306a36Sopenharmony_ci	if (rc != 0) {
57462306a36Sopenharmony_ci		fsm_deltimer(&ch->timer);
57562306a36Sopenharmony_ci		fsm_newstate(fi, CTC_STATE_STARTWAIT);
57662306a36Sopenharmony_ci		ctcm_ccw_check_rc(ch, rc, "set Mode");
57762306a36Sopenharmony_ci	} else
57862306a36Sopenharmony_ci		ch->retry = 0;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci/*
58262306a36Sopenharmony_ci * Setup channel.
58362306a36Sopenharmony_ci *
58462306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
58562306a36Sopenharmony_ci * event	The event, just happened.
58662306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
58762306a36Sopenharmony_ci */
58862306a36Sopenharmony_cistatic void ctcm_chx_start(fsm_instance *fi, int event, void *arg)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct channel *ch	= arg;
59162306a36Sopenharmony_ci	unsigned long saveflags;
59262306a36Sopenharmony_ci	int rc;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s): %s",
59562306a36Sopenharmony_ci		CTCM_FUNTAIL, ch->id,
59662306a36Sopenharmony_ci		(CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? "RX" : "TX");
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	if (ch->trans_skb != NULL) {
59962306a36Sopenharmony_ci		clear_normalized_cda(&ch->ccw[1]);
60062306a36Sopenharmony_ci		dev_kfree_skb(ch->trans_skb);
60162306a36Sopenharmony_ci		ch->trans_skb = NULL;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci	if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
60462306a36Sopenharmony_ci		ch->ccw[1].cmd_code = CCW_CMD_READ;
60562306a36Sopenharmony_ci		ch->ccw[1].flags = CCW_FLAG_SLI;
60662306a36Sopenharmony_ci		ch->ccw[1].count = 0;
60762306a36Sopenharmony_ci	} else {
60862306a36Sopenharmony_ci		ch->ccw[1].cmd_code = CCW_CMD_WRITE;
60962306a36Sopenharmony_ci		ch->ccw[1].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
61062306a36Sopenharmony_ci		ch->ccw[1].count = 0;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci	if (ctcm_checkalloc_buffer(ch)) {
61362306a36Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
61462306a36Sopenharmony_ci			"%s(%s): %s trans_skb alloc delayed "
61562306a36Sopenharmony_ci			"until first transfer",
61662306a36Sopenharmony_ci			CTCM_FUNTAIL, ch->id,
61762306a36Sopenharmony_ci			(CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ?
61862306a36Sopenharmony_ci				"RX" : "TX");
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci	ch->ccw[0].cmd_code = CCW_CMD_PREPARE;
62162306a36Sopenharmony_ci	ch->ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
62262306a36Sopenharmony_ci	ch->ccw[0].count = 0;
62362306a36Sopenharmony_ci	ch->ccw[0].cda = 0;
62462306a36Sopenharmony_ci	ch->ccw[2].cmd_code = CCW_CMD_NOOP;	/* jointed CE + DE */
62562306a36Sopenharmony_ci	ch->ccw[2].flags = CCW_FLAG_SLI;
62662306a36Sopenharmony_ci	ch->ccw[2].count = 0;
62762306a36Sopenharmony_ci	ch->ccw[2].cda = 0;
62862306a36Sopenharmony_ci	memcpy(&ch->ccw[3], &ch->ccw[0], sizeof(struct ccw1) * 3);
62962306a36Sopenharmony_ci	ch->ccw[4].cda = 0;
63062306a36Sopenharmony_ci	ch->ccw[4].flags &= ~CCW_FLAG_IDA;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	fsm_newstate(fi, CTC_STATE_STARTWAIT);
63362306a36Sopenharmony_ci	fsm_addtimer(&ch->timer, 1000, CTC_EVENT_TIMER, ch);
63462306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
63562306a36Sopenharmony_ci	rc = ccw_device_halt(ch->cdev, 0);
63662306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
63762306a36Sopenharmony_ci	if (rc != 0) {
63862306a36Sopenharmony_ci		if (rc != -EBUSY)
63962306a36Sopenharmony_ci			fsm_deltimer(&ch->timer);
64062306a36Sopenharmony_ci		ctcm_ccw_check_rc(ch, rc, "initial HaltIO");
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci/*
64562306a36Sopenharmony_ci * Shutdown a channel.
64662306a36Sopenharmony_ci *
64762306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
64862306a36Sopenharmony_ci * event	The event, just happened.
64962306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
65062306a36Sopenharmony_ci */
65162306a36Sopenharmony_cistatic void ctcm_chx_haltio(fsm_instance *fi, int event, void *arg)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	struct channel *ch = arg;
65462306a36Sopenharmony_ci	unsigned long saveflags = 0;
65562306a36Sopenharmony_ci	int rc;
65662306a36Sopenharmony_ci	int oldstate;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
65962306a36Sopenharmony_ci	if (IS_MPC(ch))
66062306a36Sopenharmony_ci		fsm_deltimer(&ch->sweep_timer);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	if (event == CTC_EVENT_STOP)	/* only for STOP not yet locked */
66562306a36Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
66662306a36Sopenharmony_ci			/* Such conditional locking is undeterministic in
66762306a36Sopenharmony_ci			 * static view. => ignore sparse warnings here. */
66862306a36Sopenharmony_ci	oldstate = fsm_getstate(fi);
66962306a36Sopenharmony_ci	fsm_newstate(fi, CTC_STATE_TERM);
67062306a36Sopenharmony_ci	rc = ccw_device_halt(ch->cdev, 0);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (event == CTC_EVENT_STOP)
67362306a36Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
67462306a36Sopenharmony_ci			/* see remark above about conditional locking */
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	if (rc != 0 && rc != -EBUSY) {
67762306a36Sopenharmony_ci		fsm_deltimer(&ch->timer);
67862306a36Sopenharmony_ci		if (event != CTC_EVENT_STOP) {
67962306a36Sopenharmony_ci			fsm_newstate(fi, oldstate);
68062306a36Sopenharmony_ci			ctcm_ccw_check_rc(ch, rc, (char *)__func__);
68162306a36Sopenharmony_ci		}
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci/*
68662306a36Sopenharmony_ci * Cleanup helper for chx_fail and chx_stopped
68762306a36Sopenharmony_ci * cleanup channels queue and notify interface statemachine.
68862306a36Sopenharmony_ci *
68962306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
69062306a36Sopenharmony_ci * state	The next state (depending on caller).
69162306a36Sopenharmony_ci * ch		The channel to operate on.
69262306a36Sopenharmony_ci */
69362306a36Sopenharmony_cistatic void ctcm_chx_cleanup(fsm_instance *fi, int state,
69462306a36Sopenharmony_ci		struct channel *ch)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
69762306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	CTCM_DBF_TEXT_(SETUP, CTC_DBF_NOTICE,
70062306a36Sopenharmony_ci			"%s(%s): %s[%d]\n",
70162306a36Sopenharmony_ci			CTCM_FUNTAIL, dev->name, ch->id, state);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
70462306a36Sopenharmony_ci	if (IS_MPC(ch))
70562306a36Sopenharmony_ci		fsm_deltimer(&ch->sweep_timer);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	fsm_newstate(fi, state);
70862306a36Sopenharmony_ci	if (state == CTC_STATE_STOPPED && ch->trans_skb != NULL) {
70962306a36Sopenharmony_ci		clear_normalized_cda(&ch->ccw[1]);
71062306a36Sopenharmony_ci		dev_kfree_skb_any(ch->trans_skb);
71162306a36Sopenharmony_ci		ch->trans_skb = NULL;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	ch->th_seg = 0x00;
71562306a36Sopenharmony_ci	ch->th_seq_num = 0x00;
71662306a36Sopenharmony_ci	if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
71762306a36Sopenharmony_ci		skb_queue_purge(&ch->io_queue);
71862306a36Sopenharmony_ci		fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
71962306a36Sopenharmony_ci	} else {
72062306a36Sopenharmony_ci		ctcm_purge_skb_queue(&ch->io_queue);
72162306a36Sopenharmony_ci		if (IS_MPC(ch))
72262306a36Sopenharmony_ci			ctcm_purge_skb_queue(&ch->sweep_queue);
72362306a36Sopenharmony_ci		spin_lock(&ch->collect_lock);
72462306a36Sopenharmony_ci		ctcm_purge_skb_queue(&ch->collect_queue);
72562306a36Sopenharmony_ci		ch->collect_len = 0;
72662306a36Sopenharmony_ci		spin_unlock(&ch->collect_lock);
72762306a36Sopenharmony_ci		fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci/*
73262306a36Sopenharmony_ci * A channel has successfully been halted.
73362306a36Sopenharmony_ci * Cleanup it's queue and notify interface statemachine.
73462306a36Sopenharmony_ci *
73562306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
73662306a36Sopenharmony_ci * event	The event, just happened.
73762306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
73862306a36Sopenharmony_ci */
73962306a36Sopenharmony_cistatic void ctcm_chx_stopped(fsm_instance *fi, int event, void *arg)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	ctcm_chx_cleanup(fi, CTC_STATE_STOPPED, arg);
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci/*
74562306a36Sopenharmony_ci * A stop command from device statemachine arrived and we are in
74662306a36Sopenharmony_ci * not operational mode. Set state to stopped.
74762306a36Sopenharmony_ci *
74862306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
74962306a36Sopenharmony_ci * event	The event, just happened.
75062306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
75162306a36Sopenharmony_ci */
75262306a36Sopenharmony_cistatic void ctcm_chx_stop(fsm_instance *fi, int event, void *arg)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	fsm_newstate(fi, CTC_STATE_STOPPED);
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci/*
75862306a36Sopenharmony_ci * A machine check for no path, not operational status or gone device has
75962306a36Sopenharmony_ci * happened.
76062306a36Sopenharmony_ci * Cleanup queue and notify interface statemachine.
76162306a36Sopenharmony_ci *
76262306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
76362306a36Sopenharmony_ci * event	The event, just happened.
76462306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
76562306a36Sopenharmony_ci */
76662306a36Sopenharmony_cistatic void ctcm_chx_fail(fsm_instance *fi, int event, void *arg)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	ctcm_chx_cleanup(fi, CTC_STATE_NOTOP, arg);
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci/*
77262306a36Sopenharmony_ci * Handle error during setup of channel.
77362306a36Sopenharmony_ci *
77462306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
77562306a36Sopenharmony_ci * event	The event, just happened.
77662306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
77762306a36Sopenharmony_ci */
77862306a36Sopenharmony_cistatic void ctcm_chx_setuperr(fsm_instance *fi, int event, void *arg)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	struct channel *ch = arg;
78162306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
78262306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	/*
78562306a36Sopenharmony_ci	 * Special case: Got UC_RCRESET on setmode.
78662306a36Sopenharmony_ci	 * This means that remote side isn't setup. In this case
78762306a36Sopenharmony_ci	 * simply retry after some 10 secs...
78862306a36Sopenharmony_ci	 */
78962306a36Sopenharmony_ci	if ((fsm_getstate(fi) == CTC_STATE_SETUPWAIT) &&
79062306a36Sopenharmony_ci	    ((event == CTC_EVENT_UC_RCRESET) ||
79162306a36Sopenharmony_ci	     (event == CTC_EVENT_UC_RSRESET))) {
79262306a36Sopenharmony_ci		fsm_newstate(fi, CTC_STATE_STARTRETRY);
79362306a36Sopenharmony_ci		fsm_deltimer(&ch->timer);
79462306a36Sopenharmony_ci		fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
79562306a36Sopenharmony_ci		if (!IS_MPC(ch) &&
79662306a36Sopenharmony_ci		    (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)) {
79762306a36Sopenharmony_ci			int rc = ccw_device_halt(ch->cdev, 0);
79862306a36Sopenharmony_ci			if (rc != 0)
79962306a36Sopenharmony_ci				ctcm_ccw_check_rc(ch, rc,
80062306a36Sopenharmony_ci					"HaltIO in chx_setuperr");
80162306a36Sopenharmony_ci		}
80262306a36Sopenharmony_ci		return;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT,
80662306a36Sopenharmony_ci		"%s(%s) : %s error during %s channel setup state=%s\n",
80762306a36Sopenharmony_ci		CTCM_FUNTAIL, dev->name, ctc_ch_event_names[event],
80862306a36Sopenharmony_ci		(CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? "RX" : "TX",
80962306a36Sopenharmony_ci		fsm_getstate_str(fi));
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
81262306a36Sopenharmony_ci		fsm_newstate(fi, CTC_STATE_RXERR);
81362306a36Sopenharmony_ci		fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
81462306a36Sopenharmony_ci	} else {
81562306a36Sopenharmony_ci		fsm_newstate(fi, CTC_STATE_TXERR);
81662306a36Sopenharmony_ci		fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci/*
82162306a36Sopenharmony_ci * Restart a channel after an error.
82262306a36Sopenharmony_ci *
82362306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
82462306a36Sopenharmony_ci * event	The event, just happened.
82562306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
82662306a36Sopenharmony_ci */
82762306a36Sopenharmony_cistatic void ctcm_chx_restart(fsm_instance *fi, int event, void *arg)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	struct channel *ch = arg;
83062306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
83162306a36Sopenharmony_ci	unsigned long saveflags = 0;
83262306a36Sopenharmony_ci	int oldstate;
83362306a36Sopenharmony_ci	int rc;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
83662306a36Sopenharmony_ci		"%s: %s[%d] of %s\n",
83762306a36Sopenharmony_ci			CTCM_FUNTAIL, ch->id, event, dev->name);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
84262306a36Sopenharmony_ci	oldstate = fsm_getstate(fi);
84362306a36Sopenharmony_ci	fsm_newstate(fi, CTC_STATE_STARTWAIT);
84462306a36Sopenharmony_ci	if (event == CTC_EVENT_TIMER)	/* only for timer not yet locked */
84562306a36Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
84662306a36Sopenharmony_ci			/* Such conditional locking is a known problem for
84762306a36Sopenharmony_ci			 * sparse because its undeterministic in static view.
84862306a36Sopenharmony_ci			 * Warnings should be ignored here. */
84962306a36Sopenharmony_ci	rc = ccw_device_halt(ch->cdev, 0);
85062306a36Sopenharmony_ci	if (event == CTC_EVENT_TIMER)
85162306a36Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
85262306a36Sopenharmony_ci	if (rc != 0) {
85362306a36Sopenharmony_ci		if (rc != -EBUSY) {
85462306a36Sopenharmony_ci		    fsm_deltimer(&ch->timer);
85562306a36Sopenharmony_ci		    fsm_newstate(fi, oldstate);
85662306a36Sopenharmony_ci		}
85762306a36Sopenharmony_ci		ctcm_ccw_check_rc(ch, rc, "HaltIO in ctcm_chx_restart");
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci/*
86262306a36Sopenharmony_ci * Handle error during RX initial handshake (exchange of
86362306a36Sopenharmony_ci * 0-length block header)
86462306a36Sopenharmony_ci *
86562306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
86662306a36Sopenharmony_ci * event	The event, just happened.
86762306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
86862306a36Sopenharmony_ci */
86962306a36Sopenharmony_cistatic void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg)
87062306a36Sopenharmony_ci{
87162306a36Sopenharmony_ci	struct channel *ch = arg;
87262306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
87362306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (event == CTC_EVENT_TIMER) {
87662306a36Sopenharmony_ci		if (!IS_MPCDEV(dev))
87762306a36Sopenharmony_ci			/* TODO : check if MPC deletes timer somewhere */
87862306a36Sopenharmony_ci			fsm_deltimer(&ch->timer);
87962306a36Sopenharmony_ci		if (ch->retry++ < 3)
88062306a36Sopenharmony_ci			ctcm_chx_restart(fi, event, arg);
88162306a36Sopenharmony_ci		else {
88262306a36Sopenharmony_ci			fsm_newstate(fi, CTC_STATE_RXERR);
88362306a36Sopenharmony_ci			fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
88462306a36Sopenharmony_ci		}
88562306a36Sopenharmony_ci	} else {
88662306a36Sopenharmony_ci		CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
88762306a36Sopenharmony_ci			"%s(%s): %s in %s", CTCM_FUNTAIL, ch->id,
88862306a36Sopenharmony_ci			ctc_ch_event_names[event], fsm_getstate_str(fi));
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci		dev_warn(&dev->dev,
89162306a36Sopenharmony_ci			"Initialization failed with RX/TX init handshake "
89262306a36Sopenharmony_ci			"error %s\n", ctc_ch_event_names[event]);
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci/*
89762306a36Sopenharmony_ci * Notify device statemachine if we gave up initialization
89862306a36Sopenharmony_ci * of RX channel.
89962306a36Sopenharmony_ci *
90062306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
90162306a36Sopenharmony_ci * event	The event, just happened.
90262306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
90362306a36Sopenharmony_ci */
90462306a36Sopenharmony_cistatic void ctcm_chx_rxinitfail(fsm_instance *fi, int event, void *arg)
90562306a36Sopenharmony_ci{
90662306a36Sopenharmony_ci	struct channel *ch = arg;
90762306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
90862306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
91162306a36Sopenharmony_ci			"%s(%s): RX %s busy, init. fail",
91262306a36Sopenharmony_ci				CTCM_FUNTAIL, dev->name, ch->id);
91362306a36Sopenharmony_ci	fsm_newstate(fi, CTC_STATE_RXERR);
91462306a36Sopenharmony_ci	fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci/*
91862306a36Sopenharmony_ci * Handle RX Unit check remote reset (remote disconnected)
91962306a36Sopenharmony_ci *
92062306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
92162306a36Sopenharmony_ci * event	The event, just happened.
92262306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
92362306a36Sopenharmony_ci */
92462306a36Sopenharmony_cistatic void ctcm_chx_rxdisc(fsm_instance *fi, int event, void *arg)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	struct channel *ch = arg;
92762306a36Sopenharmony_ci	struct channel *ch2;
92862306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
92962306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
93262306a36Sopenharmony_ci			"%s: %s: remote disconnect - re-init ...",
93362306a36Sopenharmony_ci				CTCM_FUNTAIL, dev->name);
93462306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
93562306a36Sopenharmony_ci	/*
93662306a36Sopenharmony_ci	 * Notify device statemachine
93762306a36Sopenharmony_ci	 */
93862306a36Sopenharmony_ci	fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
93962306a36Sopenharmony_ci	fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	fsm_newstate(fi, CTC_STATE_DTERM);
94262306a36Sopenharmony_ci	ch2 = priv->channel[CTCM_WRITE];
94362306a36Sopenharmony_ci	fsm_newstate(ch2->fsm, CTC_STATE_DTERM);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	ccw_device_halt(ch->cdev, 0);
94662306a36Sopenharmony_ci	ccw_device_halt(ch2->cdev, 0);
94762306a36Sopenharmony_ci}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci/*
95062306a36Sopenharmony_ci * Handle error during TX channel initialization.
95162306a36Sopenharmony_ci *
95262306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
95362306a36Sopenharmony_ci * event	The event, just happened.
95462306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
95562306a36Sopenharmony_ci */
95662306a36Sopenharmony_cistatic void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	struct channel *ch = arg;
95962306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
96062306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if (event == CTC_EVENT_TIMER) {
96362306a36Sopenharmony_ci		fsm_deltimer(&ch->timer);
96462306a36Sopenharmony_ci		if (ch->retry++ < 3)
96562306a36Sopenharmony_ci			ctcm_chx_restart(fi, event, arg);
96662306a36Sopenharmony_ci		else {
96762306a36Sopenharmony_ci			fsm_newstate(fi, CTC_STATE_TXERR);
96862306a36Sopenharmony_ci			fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
96962306a36Sopenharmony_ci		}
97062306a36Sopenharmony_ci	} else {
97162306a36Sopenharmony_ci		CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
97262306a36Sopenharmony_ci			"%s(%s): %s in %s", CTCM_FUNTAIL, ch->id,
97362306a36Sopenharmony_ci			ctc_ch_event_names[event], fsm_getstate_str(fi));
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		dev_warn(&dev->dev,
97662306a36Sopenharmony_ci			"Initialization failed with RX/TX init handshake "
97762306a36Sopenharmony_ci			"error %s\n", ctc_ch_event_names[event]);
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci/*
98262306a36Sopenharmony_ci * Handle TX timeout by retrying operation.
98362306a36Sopenharmony_ci *
98462306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
98562306a36Sopenharmony_ci * event	The event, just happened.
98662306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
98762306a36Sopenharmony_ci */
98862306a36Sopenharmony_cistatic void ctcm_chx_txretry(fsm_instance *fi, int event, void *arg)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	struct channel *ch = arg;
99162306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
99262306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
99362306a36Sopenharmony_ci	struct sk_buff *skb;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	CTCM_PR_DEBUG("Enter: %s: cp=%i ch=0x%p id=%s\n",
99662306a36Sopenharmony_ci			__func__, smp_processor_id(), ch, ch->id);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
99962306a36Sopenharmony_ci	if (ch->retry++ > 3) {
100062306a36Sopenharmony_ci		struct mpc_group *gptr = priv->mpcg;
100162306a36Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO,
100262306a36Sopenharmony_ci				"%s: %s: retries exceeded",
100362306a36Sopenharmony_ci					CTCM_FUNTAIL, ch->id);
100462306a36Sopenharmony_ci		fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
100562306a36Sopenharmony_ci		/* call restart if not MPC or if MPC and mpcg fsm is ready.
100662306a36Sopenharmony_ci			use gptr as mpc indicator */
100762306a36Sopenharmony_ci		if (!(gptr && (fsm_getstate(gptr->fsm) != MPCG_STATE_READY)))
100862306a36Sopenharmony_ci			ctcm_chx_restart(fi, event, arg);
100962306a36Sopenharmony_ci		goto done;
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
101362306a36Sopenharmony_ci			"%s : %s: retry %d",
101462306a36Sopenharmony_ci				CTCM_FUNTAIL, ch->id, ch->retry);
101562306a36Sopenharmony_ci	skb = skb_peek(&ch->io_queue);
101662306a36Sopenharmony_ci	if (skb) {
101762306a36Sopenharmony_ci		int rc = 0;
101862306a36Sopenharmony_ci		unsigned long saveflags = 0;
101962306a36Sopenharmony_ci		clear_normalized_cda(&ch->ccw[4]);
102062306a36Sopenharmony_ci		ch->ccw[4].count = skb->len;
102162306a36Sopenharmony_ci		if (set_normalized_cda(&ch->ccw[4], skb->data)) {
102262306a36Sopenharmony_ci			CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO,
102362306a36Sopenharmony_ci				"%s: %s: IDAL alloc failed",
102462306a36Sopenharmony_ci						CTCM_FUNTAIL, ch->id);
102562306a36Sopenharmony_ci			fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
102662306a36Sopenharmony_ci			ctcm_chx_restart(fi, event, arg);
102762306a36Sopenharmony_ci			goto done;
102862306a36Sopenharmony_ci		}
102962306a36Sopenharmony_ci		fsm_addtimer(&ch->timer, 1000, CTC_EVENT_TIMER, ch);
103062306a36Sopenharmony_ci		if (event == CTC_EVENT_TIMER) /* for TIMER not yet locked */
103162306a36Sopenharmony_ci			spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
103262306a36Sopenharmony_ci			/* Such conditional locking is a known problem for
103362306a36Sopenharmony_ci			 * sparse because its undeterministic in static view.
103462306a36Sopenharmony_ci			 * Warnings should be ignored here. */
103562306a36Sopenharmony_ci		if (do_debug_ccw)
103662306a36Sopenharmony_ci			ctcmpc_dumpit((char *)&ch->ccw[3],
103762306a36Sopenharmony_ci					sizeof(struct ccw1) * 3);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci		rc = ccw_device_start(ch->cdev, &ch->ccw[3], 0, 0xff, 0);
104062306a36Sopenharmony_ci		if (event == CTC_EVENT_TIMER)
104162306a36Sopenharmony_ci			spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev),
104262306a36Sopenharmony_ci					saveflags);
104362306a36Sopenharmony_ci		if (rc != 0) {
104462306a36Sopenharmony_ci			fsm_deltimer(&ch->timer);
104562306a36Sopenharmony_ci			ctcm_ccw_check_rc(ch, rc, "TX in chx_txretry");
104662306a36Sopenharmony_ci			ctcm_purge_skb_queue(&ch->io_queue);
104762306a36Sopenharmony_ci		}
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_cidone:
105062306a36Sopenharmony_ci	return;
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci/*
105462306a36Sopenharmony_ci * Handle fatal errors during an I/O command.
105562306a36Sopenharmony_ci *
105662306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
105762306a36Sopenharmony_ci * event	The event, just happened.
105862306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
105962306a36Sopenharmony_ci */
106062306a36Sopenharmony_cistatic void ctcm_chx_iofatal(fsm_instance *fi, int event, void *arg)
106162306a36Sopenharmony_ci{
106262306a36Sopenharmony_ci	struct channel *ch = arg;
106362306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
106462306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
106562306a36Sopenharmony_ci	int rd = CHANNEL_DIRECTION(ch->flags);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
106862306a36Sopenharmony_ci	CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
106962306a36Sopenharmony_ci		"%s: %s: %s unrecoverable channel error",
107062306a36Sopenharmony_ci			CTCM_FUNTAIL, ch->id, rd == CTCM_READ ? "RX" : "TX");
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	if (IS_MPC(ch)) {
107362306a36Sopenharmony_ci		priv->stats.tx_dropped++;
107462306a36Sopenharmony_ci		priv->stats.tx_errors++;
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci	if (rd == CTCM_READ) {
107762306a36Sopenharmony_ci		fsm_newstate(fi, CTC_STATE_RXERR);
107862306a36Sopenharmony_ci		fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
107962306a36Sopenharmony_ci	} else {
108062306a36Sopenharmony_ci		fsm_newstate(fi, CTC_STATE_TXERR);
108162306a36Sopenharmony_ci		fsm_event(priv->fsm, DEV_EVENT_TXDOWN, dev);
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci/*
108662306a36Sopenharmony_ci * The ctcm statemachine for a channel.
108762306a36Sopenharmony_ci */
108862306a36Sopenharmony_ciconst fsm_node ch_fsm[] = {
108962306a36Sopenharmony_ci	{ CTC_STATE_STOPPED,	CTC_EVENT_STOP,		ctcm_action_nop  },
109062306a36Sopenharmony_ci	{ CTC_STATE_STOPPED,	CTC_EVENT_START,	ctcm_chx_start  },
109162306a36Sopenharmony_ci	{ CTC_STATE_STOPPED,	CTC_EVENT_FINSTAT,	ctcm_action_nop  },
109262306a36Sopenharmony_ci	{ CTC_STATE_STOPPED,	CTC_EVENT_MC_FAIL,	ctcm_action_nop  },
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_STOP,		ctcm_chx_stop  },
109562306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_START,	ctcm_action_nop  },
109662306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_FINSTAT,	ctcm_action_nop  },
109762306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_MC_FAIL,	ctcm_action_nop  },
109862306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_MC_GOOD,	ctcm_chx_start  },
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	{ CTC_STATE_STARTWAIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
110162306a36Sopenharmony_ci	{ CTC_STATE_STARTWAIT,	CTC_EVENT_START,	ctcm_action_nop  },
110262306a36Sopenharmony_ci	{ CTC_STATE_STARTWAIT,	CTC_EVENT_FINSTAT,	ctcm_chx_setmode  },
110362306a36Sopenharmony_ci	{ CTC_STATE_STARTWAIT,	CTC_EVENT_TIMER,	ctcm_chx_setuperr  },
110462306a36Sopenharmony_ci	{ CTC_STATE_STARTWAIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
110562306a36Sopenharmony_ci	{ CTC_STATE_STARTWAIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	{ CTC_STATE_STARTRETRY,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
110862306a36Sopenharmony_ci	{ CTC_STATE_STARTRETRY,	CTC_EVENT_TIMER,	ctcm_chx_setmode  },
110962306a36Sopenharmony_ci	{ CTC_STATE_STARTRETRY,	CTC_EVENT_FINSTAT,	ctcm_action_nop  },
111062306a36Sopenharmony_ci	{ CTC_STATE_STARTRETRY,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
111362306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_START,	ctcm_action_nop  },
111462306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_FINSTAT,	chx_firstio  },
111562306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
111662306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
111762306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_TIMER,	ctcm_chx_setmode  },
111862306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
111962306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
112262306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_START,	ctcm_action_nop  },
112362306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_FINSTAT,	chx_rxidle  },
112462306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_UC_RCRESET,	ctcm_chx_rxiniterr  },
112562306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_UC_RSRESET,	ctcm_chx_rxiniterr  },
112662306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_TIMER,	ctcm_chx_rxiniterr  },
112762306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_ATTNBUSY,	ctcm_chx_rxinitfail  },
112862306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
112962306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_UC_ZERO,	chx_firstio  },
113062306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
113362306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_START,	ctcm_action_nop  },
113462306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_FINSTAT,	chx_rx  },
113562306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_UC_RCRESET,	ctcm_chx_rxdisc  },
113662306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
113762306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
113862306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_UC_ZERO,	chx_rx  },
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
114162306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_START,	ctcm_action_nop  },
114262306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_FINSTAT,	ctcm_chx_txidle  },
114362306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_UC_RCRESET,	ctcm_chx_txiniterr  },
114462306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_UC_RSRESET,	ctcm_chx_txiniterr  },
114562306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_TIMER,	ctcm_chx_txiniterr  },
114662306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
114762306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
115062306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_START,	ctcm_action_nop  },
115162306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_FINSTAT,	chx_firstio  },
115262306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_UC_RCRESET,	ctcm_action_nop  },
115362306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_UC_RSRESET,	ctcm_action_nop  },
115462306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
115562306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_STOP,		ctcm_action_nop  },
115862306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_START,	ctcm_chx_restart  },
115962306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_FINSTAT,	ctcm_chx_stopped  },
116062306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_UC_RCRESET,	ctcm_action_nop  },
116162306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_UC_RSRESET,	ctcm_action_nop  },
116262306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
116562306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_START,	ctcm_chx_restart  },
116662306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_FINSTAT,	ctcm_chx_setmode  },
116762306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_UC_RCRESET,	ctcm_action_nop  },
116862306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_UC_RSRESET,	ctcm_action_nop  },
116962306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_STOP,		ctcm_chx_haltio  },
117262306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_START,	ctcm_action_nop  },
117362306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_FINSTAT,	chx_txdone  },
117462306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_UC_RCRESET,	ctcm_chx_txretry  },
117562306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_UC_RSRESET,	ctcm_chx_txretry  },
117662306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_TIMER,	ctcm_chx_txretry  },
117762306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
117862306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	{ CTC_STATE_RXERR,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
118162306a36Sopenharmony_ci	{ CTC_STATE_TXERR,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
118262306a36Sopenharmony_ci	{ CTC_STATE_TXERR,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
118362306a36Sopenharmony_ci	{ CTC_STATE_RXERR,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
118462306a36Sopenharmony_ci};
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ciint ch_fsm_len = ARRAY_SIZE(ch_fsm);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci/*
118962306a36Sopenharmony_ci * MPC actions for mpc channel statemachine
119062306a36Sopenharmony_ci * handling of MPC protocol requires extra
119162306a36Sopenharmony_ci * statemachine and actions which are prefixed ctcmpc_ .
119262306a36Sopenharmony_ci * The ctc_ch_states and ctc_ch_state_names,
119362306a36Sopenharmony_ci * ctc_ch_events and ctc_ch_event_names share the ctcm definitions
119462306a36Sopenharmony_ci * which are expanded by some elements.
119562306a36Sopenharmony_ci */
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci/*
119862306a36Sopenharmony_ci * Actions for mpc channel statemachine.
119962306a36Sopenharmony_ci */
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci/*
120262306a36Sopenharmony_ci * Normal data has been send. Free the corresponding
120362306a36Sopenharmony_ci * skb (it's in io_queue), reset dev->tbusy and
120462306a36Sopenharmony_ci * revert to idle state.
120562306a36Sopenharmony_ci *
120662306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
120762306a36Sopenharmony_ci * event	The event, just happened.
120862306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
120962306a36Sopenharmony_ci */
121062306a36Sopenharmony_cistatic void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	struct channel		*ch = arg;
121362306a36Sopenharmony_ci	struct net_device	*dev = ch->netdev;
121462306a36Sopenharmony_ci	struct ctcm_priv	*priv = dev->ml_priv;
121562306a36Sopenharmony_ci	struct mpc_group	*grp = priv->mpcg;
121662306a36Sopenharmony_ci	struct sk_buff		*skb;
121762306a36Sopenharmony_ci	int		first = 1;
121862306a36Sopenharmony_ci	int		i;
121962306a36Sopenharmony_ci	__u32		data_space;
122062306a36Sopenharmony_ci	unsigned long	duration;
122162306a36Sopenharmony_ci	struct sk_buff	*peekskb;
122262306a36Sopenharmony_ci	int		rc;
122362306a36Sopenharmony_ci	struct th_header *header;
122462306a36Sopenharmony_ci	struct pdu	*p_header;
122562306a36Sopenharmony_ci	unsigned long done_stamp = jiffies;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	CTCM_PR_DEBUG("Enter %s: %s cp:%i\n",
122862306a36Sopenharmony_ci			__func__, dev->name, smp_processor_id());
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	duration = done_stamp - ch->prof.send_stamp;
123162306a36Sopenharmony_ci	if (duration > ch->prof.tx_time)
123262306a36Sopenharmony_ci		ch->prof.tx_time = duration;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	if (ch->irb->scsw.cmd.count != 0)
123562306a36Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
123662306a36Sopenharmony_ci			"%s(%s): TX not complete, remaining %d bytes",
123762306a36Sopenharmony_ci			     CTCM_FUNTAIL, dev->name, ch->irb->scsw.cmd.count);
123862306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
123962306a36Sopenharmony_ci	while ((skb = skb_dequeue(&ch->io_queue))) {
124062306a36Sopenharmony_ci		priv->stats.tx_packets++;
124162306a36Sopenharmony_ci		priv->stats.tx_bytes += skb->len - TH_HEADER_LENGTH;
124262306a36Sopenharmony_ci		if (first) {
124362306a36Sopenharmony_ci			priv->stats.tx_bytes += 2;
124462306a36Sopenharmony_ci			first = 0;
124562306a36Sopenharmony_ci		}
124662306a36Sopenharmony_ci		refcount_dec(&skb->users);
124762306a36Sopenharmony_ci		dev_kfree_skb_irq(skb);
124862306a36Sopenharmony_ci	}
124962306a36Sopenharmony_ci	spin_lock(&ch->collect_lock);
125062306a36Sopenharmony_ci	clear_normalized_cda(&ch->ccw[4]);
125162306a36Sopenharmony_ci	if ((ch->collect_len <= 0) || (grp->in_sweep != 0)) {
125262306a36Sopenharmony_ci		spin_unlock(&ch->collect_lock);
125362306a36Sopenharmony_ci		fsm_newstate(fi, CTC_STATE_TXIDLE);
125462306a36Sopenharmony_ci		goto done;
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	if (ctcm_checkalloc_buffer(ch)) {
125862306a36Sopenharmony_ci		spin_unlock(&ch->collect_lock);
125962306a36Sopenharmony_ci		goto done;
126062306a36Sopenharmony_ci	}
126162306a36Sopenharmony_ci	ch->trans_skb->data = ch->trans_skb_data;
126262306a36Sopenharmony_ci	skb_reset_tail_pointer(ch->trans_skb);
126362306a36Sopenharmony_ci	ch->trans_skb->len = 0;
126462306a36Sopenharmony_ci	if (ch->prof.maxmulti < (ch->collect_len + TH_HEADER_LENGTH))
126562306a36Sopenharmony_ci		ch->prof.maxmulti = ch->collect_len + TH_HEADER_LENGTH;
126662306a36Sopenharmony_ci	if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
126762306a36Sopenharmony_ci		ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
126862306a36Sopenharmony_ci	i = 0;
126962306a36Sopenharmony_ci	p_header = NULL;
127062306a36Sopenharmony_ci	data_space = grp->group_max_buflen - TH_HEADER_LENGTH;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	CTCM_PR_DBGDATA("%s: building trans_skb from collect_q"
127362306a36Sopenharmony_ci		       " data_space:%04x\n",
127462306a36Sopenharmony_ci		       __func__, data_space);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	while ((skb = skb_dequeue(&ch->collect_queue))) {
127762306a36Sopenharmony_ci		skb_put_data(ch->trans_skb, skb->data, skb->len);
127862306a36Sopenharmony_ci		p_header = (struct pdu *)
127962306a36Sopenharmony_ci			(skb_tail_pointer(ch->trans_skb) - skb->len);
128062306a36Sopenharmony_ci		p_header->pdu_flag = 0x00;
128162306a36Sopenharmony_ci		if (be16_to_cpu(skb->protocol) == ETH_P_SNAP)
128262306a36Sopenharmony_ci			p_header->pdu_flag |= 0x60;
128362306a36Sopenharmony_ci		else
128462306a36Sopenharmony_ci			p_header->pdu_flag |= 0x20;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci		CTCM_PR_DBGDATA("%s: trans_skb len:%04x \n",
128762306a36Sopenharmony_ci				__func__, ch->trans_skb->len);
128862306a36Sopenharmony_ci		CTCM_PR_DBGDATA("%s: pdu header and data for up"
128962306a36Sopenharmony_ci				" to 32 bytes sent to vtam\n", __func__);
129062306a36Sopenharmony_ci		CTCM_D3_DUMP((char *)p_header, min_t(int, skb->len, 32));
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci		ch->collect_len -= skb->len;
129362306a36Sopenharmony_ci		data_space -= skb->len;
129462306a36Sopenharmony_ci		priv->stats.tx_packets++;
129562306a36Sopenharmony_ci		priv->stats.tx_bytes += skb->len;
129662306a36Sopenharmony_ci		refcount_dec(&skb->users);
129762306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
129862306a36Sopenharmony_ci		peekskb = skb_peek(&ch->collect_queue);
129962306a36Sopenharmony_ci		if (peekskb->len > data_space)
130062306a36Sopenharmony_ci			break;
130162306a36Sopenharmony_ci		i++;
130262306a36Sopenharmony_ci	}
130362306a36Sopenharmony_ci	/* p_header points to the last one we handled */
130462306a36Sopenharmony_ci	if (p_header)
130562306a36Sopenharmony_ci		p_header->pdu_flag |= PDU_LAST;	/*Say it's the last one*/
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	header = skb_push(ch->trans_skb, TH_HEADER_LENGTH);
130862306a36Sopenharmony_ci	memset(header, 0, TH_HEADER_LENGTH);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	header->th_ch_flag = TH_HAS_PDU;  /* Normal data */
131162306a36Sopenharmony_ci	ch->th_seq_num++;
131262306a36Sopenharmony_ci	header->th_seq_num = ch->th_seq_num;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	CTCM_PR_DBGDATA("%s: ToVTAM_th_seq= %08x\n" ,
131562306a36Sopenharmony_ci					__func__, ch->th_seq_num);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	CTCM_PR_DBGDATA("%s: trans_skb len:%04x \n",
131862306a36Sopenharmony_ci		       __func__, ch->trans_skb->len);
131962306a36Sopenharmony_ci	CTCM_PR_DBGDATA("%s: up-to-50 bytes of trans_skb "
132062306a36Sopenharmony_ci			"data to vtam from collect_q\n", __func__);
132162306a36Sopenharmony_ci	CTCM_D3_DUMP((char *)ch->trans_skb->data,
132262306a36Sopenharmony_ci				min_t(int, ch->trans_skb->len, 50));
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	spin_unlock(&ch->collect_lock);
132562306a36Sopenharmony_ci	clear_normalized_cda(&ch->ccw[1]);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	CTCM_PR_DBGDATA("ccwcda=0x%p data=0x%p\n",
132862306a36Sopenharmony_ci			(void *)(unsigned long)ch->ccw[1].cda,
132962306a36Sopenharmony_ci			ch->trans_skb->data);
133062306a36Sopenharmony_ci	ch->ccw[1].count = ch->max_bufsize;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {
133362306a36Sopenharmony_ci		dev_kfree_skb_any(ch->trans_skb);
133462306a36Sopenharmony_ci		ch->trans_skb = NULL;
133562306a36Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR,
133662306a36Sopenharmony_ci			"%s: %s: IDAL alloc failed",
133762306a36Sopenharmony_ci				CTCM_FUNTAIL, ch->id);
133862306a36Sopenharmony_ci		fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
133962306a36Sopenharmony_ci		return;
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	CTCM_PR_DBGDATA("ccwcda=0x%p data=0x%p\n",
134362306a36Sopenharmony_ci			(void *)(unsigned long)ch->ccw[1].cda,
134462306a36Sopenharmony_ci			ch->trans_skb->data);
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	ch->ccw[1].count = ch->trans_skb->len;
134762306a36Sopenharmony_ci	fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
134862306a36Sopenharmony_ci	ch->prof.send_stamp = jiffies;
134962306a36Sopenharmony_ci	if (do_debug_ccw)
135062306a36Sopenharmony_ci		ctcmpc_dumpit((char *)&ch->ccw[0], sizeof(struct ccw1) * 3);
135162306a36Sopenharmony_ci	rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
135262306a36Sopenharmony_ci	ch->prof.doios_multi++;
135362306a36Sopenharmony_ci	if (rc != 0) {
135462306a36Sopenharmony_ci		priv->stats.tx_dropped += i;
135562306a36Sopenharmony_ci		priv->stats.tx_errors += i;
135662306a36Sopenharmony_ci		fsm_deltimer(&ch->timer);
135762306a36Sopenharmony_ci		ctcm_ccw_check_rc(ch, rc, "chained TX");
135862306a36Sopenharmony_ci	}
135962306a36Sopenharmony_cidone:
136062306a36Sopenharmony_ci	ctcm_clear_busy(dev);
136162306a36Sopenharmony_ci	return;
136262306a36Sopenharmony_ci}
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci/*
136562306a36Sopenharmony_ci * Got normal data, check for sanity, queue it up, allocate new buffer
136662306a36Sopenharmony_ci * trigger bottom half, and initiate next read.
136762306a36Sopenharmony_ci *
136862306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
136962306a36Sopenharmony_ci * event	The event, just happened.
137062306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
137162306a36Sopenharmony_ci */
137262306a36Sopenharmony_cistatic void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg)
137362306a36Sopenharmony_ci{
137462306a36Sopenharmony_ci	struct channel		*ch = arg;
137562306a36Sopenharmony_ci	struct net_device	*dev = ch->netdev;
137662306a36Sopenharmony_ci	struct ctcm_priv	*priv = dev->ml_priv;
137762306a36Sopenharmony_ci	struct mpc_group	*grp = priv->mpcg;
137862306a36Sopenharmony_ci	struct sk_buff		*skb = ch->trans_skb;
137962306a36Sopenharmony_ci	struct sk_buff		*new_skb;
138062306a36Sopenharmony_ci	unsigned long		saveflags = 0;	/* avoids compiler warning */
138162306a36Sopenharmony_ci	int len	= ch->max_bufsize - ch->irb->scsw.cmd.count;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	CTCM_PR_DEBUG("%s: %s: cp:%i %s maxbuf : %04x, len: %04x\n",
138462306a36Sopenharmony_ci			CTCM_FUNTAIL, dev->name, smp_processor_id(),
138562306a36Sopenharmony_ci				ch->id, ch->max_bufsize, len);
138662306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	if (skb == NULL) {
138962306a36Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
139062306a36Sopenharmony_ci			"%s(%s): TRANS_SKB = NULL",
139162306a36Sopenharmony_ci				CTCM_FUNTAIL, dev->name);
139262306a36Sopenharmony_ci		goto again;
139362306a36Sopenharmony_ci	}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	if (len < TH_HEADER_LENGTH) {
139662306a36Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
139762306a36Sopenharmony_ci				"%s(%s): packet length %d too short",
139862306a36Sopenharmony_ci					CTCM_FUNTAIL, dev->name, len);
139962306a36Sopenharmony_ci		priv->stats.rx_dropped++;
140062306a36Sopenharmony_ci		priv->stats.rx_length_errors++;
140162306a36Sopenharmony_ci	} else {
140262306a36Sopenharmony_ci		/* must have valid th header or game over */
140362306a36Sopenharmony_ci		__u32	block_len = len;
140462306a36Sopenharmony_ci		len = TH_HEADER_LENGTH + XID2_LENGTH + 4;
140562306a36Sopenharmony_ci		new_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC);
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci		if (new_skb == NULL) {
140862306a36Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
140962306a36Sopenharmony_ci				"%s(%s): skb allocation failed",
141062306a36Sopenharmony_ci						CTCM_FUNTAIL, dev->name);
141162306a36Sopenharmony_ci			fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
141262306a36Sopenharmony_ci			goto again;
141362306a36Sopenharmony_ci		}
141462306a36Sopenharmony_ci		switch (fsm_getstate(grp->fsm)) {
141562306a36Sopenharmony_ci		case MPCG_STATE_RESET:
141662306a36Sopenharmony_ci		case MPCG_STATE_INOP:
141762306a36Sopenharmony_ci			dev_kfree_skb_any(new_skb);
141862306a36Sopenharmony_ci			break;
141962306a36Sopenharmony_ci		case MPCG_STATE_FLOWC:
142062306a36Sopenharmony_ci		case MPCG_STATE_READY:
142162306a36Sopenharmony_ci			skb_put_data(new_skb, skb->data, block_len);
142262306a36Sopenharmony_ci			skb_queue_tail(&ch->io_queue, new_skb);
142362306a36Sopenharmony_ci			tasklet_schedule(&ch->ch_tasklet);
142462306a36Sopenharmony_ci			break;
142562306a36Sopenharmony_ci		default:
142662306a36Sopenharmony_ci			skb_put_data(new_skb, skb->data, len);
142762306a36Sopenharmony_ci			skb_queue_tail(&ch->io_queue, new_skb);
142862306a36Sopenharmony_ci			tasklet_hi_schedule(&ch->ch_tasklet);
142962306a36Sopenharmony_ci			break;
143062306a36Sopenharmony_ci		}
143162306a36Sopenharmony_ci	}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ciagain:
143462306a36Sopenharmony_ci	switch (fsm_getstate(grp->fsm)) {
143562306a36Sopenharmony_ci	int rc, dolock;
143662306a36Sopenharmony_ci	case MPCG_STATE_FLOWC:
143762306a36Sopenharmony_ci	case MPCG_STATE_READY:
143862306a36Sopenharmony_ci		if (ctcm_checkalloc_buffer(ch))
143962306a36Sopenharmony_ci			break;
144062306a36Sopenharmony_ci		ch->trans_skb->data = ch->trans_skb_data;
144162306a36Sopenharmony_ci		skb_reset_tail_pointer(ch->trans_skb);
144262306a36Sopenharmony_ci		ch->trans_skb->len = 0;
144362306a36Sopenharmony_ci		ch->ccw[1].count = ch->max_bufsize;
144462306a36Sopenharmony_ci		if (do_debug_ccw)
144562306a36Sopenharmony_ci			ctcmpc_dumpit((char *)&ch->ccw[0],
144662306a36Sopenharmony_ci				      sizeof(struct ccw1) * 3);
144762306a36Sopenharmony_ci		dolock = !in_hardirq();
144862306a36Sopenharmony_ci		if (dolock)
144962306a36Sopenharmony_ci			spin_lock_irqsave(
145062306a36Sopenharmony_ci				get_ccwdev_lock(ch->cdev), saveflags);
145162306a36Sopenharmony_ci		rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
145262306a36Sopenharmony_ci		if (dolock) /* see remark about conditional locking */
145362306a36Sopenharmony_ci			spin_unlock_irqrestore(
145462306a36Sopenharmony_ci				get_ccwdev_lock(ch->cdev), saveflags);
145562306a36Sopenharmony_ci		if (rc != 0)
145662306a36Sopenharmony_ci			ctcm_ccw_check_rc(ch, rc, "normal RX");
145762306a36Sopenharmony_ci		break;
145862306a36Sopenharmony_ci	default:
145962306a36Sopenharmony_ci		break;
146062306a36Sopenharmony_ci	}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	CTCM_PR_DEBUG("Exit %s: %s, ch=0x%p, id=%s\n",
146362306a36Sopenharmony_ci			__func__, dev->name, ch, ch->id);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci/*
146862306a36Sopenharmony_ci * Initialize connection by sending a __u16 of value 0.
146962306a36Sopenharmony_ci *
147062306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
147162306a36Sopenharmony_ci * event	The event, just happened.
147262306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
147362306a36Sopenharmony_ci */
147462306a36Sopenharmony_cistatic void ctcmpc_chx_firstio(fsm_instance *fi, int event, void *arg)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	struct channel		*ch = arg;
147762306a36Sopenharmony_ci	struct net_device	*dev = ch->netdev;
147862306a36Sopenharmony_ci	struct ctcm_priv	*priv = dev->ml_priv;
147962306a36Sopenharmony_ci	struct mpc_group	*gptr = priv->mpcg;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	CTCM_PR_DEBUG("Enter %s: id=%s, ch=0x%p\n",
148262306a36Sopenharmony_ci				__func__, ch->id, ch);
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_INFO,
148562306a36Sopenharmony_ci			"%s: %s: chstate:%i, grpstate:%i, prot:%i\n",
148662306a36Sopenharmony_ci			CTCM_FUNTAIL, ch->id, fsm_getstate(fi),
148762306a36Sopenharmony_ci			fsm_getstate(gptr->fsm), ch->protocol);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	if (fsm_getstate(fi) == CTC_STATE_TXIDLE)
149062306a36Sopenharmony_ci		MPC_DBF_DEV_NAME(TRACE, dev, "remote side issued READ? ");
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
149362306a36Sopenharmony_ci	if (ctcm_checkalloc_buffer(ch))
149462306a36Sopenharmony_ci				goto done;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	switch (fsm_getstate(fi)) {
149762306a36Sopenharmony_ci	case CTC_STATE_STARTRETRY:
149862306a36Sopenharmony_ci	case CTC_STATE_SETUPWAIT:
149962306a36Sopenharmony_ci		if (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) {
150062306a36Sopenharmony_ci			ctcmpc_chx_rxidle(fi, event, arg);
150162306a36Sopenharmony_ci		} else {
150262306a36Sopenharmony_ci			fsm_newstate(fi, CTC_STATE_TXIDLE);
150362306a36Sopenharmony_ci			fsm_event(priv->fsm, DEV_EVENT_TXUP, dev);
150462306a36Sopenharmony_ci		}
150562306a36Sopenharmony_ci				goto done;
150662306a36Sopenharmony_ci	default:
150762306a36Sopenharmony_ci		break;
150862306a36Sopenharmony_ci	}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == CTCM_READ)
151162306a36Sopenharmony_ci		     ? CTC_STATE_RXINIT : CTC_STATE_TXINIT);
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cidone:
151462306a36Sopenharmony_ci	CTCM_PR_DEBUG("Exit %s: id=%s, ch=0x%p\n",
151562306a36Sopenharmony_ci				__func__, ch->id, ch);
151662306a36Sopenharmony_ci	return;
151762306a36Sopenharmony_ci}
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci/*
152062306a36Sopenharmony_ci * Got initial data, check it. If OK,
152162306a36Sopenharmony_ci * notify device statemachine that we are up and
152262306a36Sopenharmony_ci * running.
152362306a36Sopenharmony_ci *
152462306a36Sopenharmony_ci * fi		An instance of a channel statemachine.
152562306a36Sopenharmony_ci * event	The event, just happened.
152662306a36Sopenharmony_ci * arg		Generic pointer, casted from channel * upon call.
152762306a36Sopenharmony_ci */
152862306a36Sopenharmony_civoid ctcmpc_chx_rxidle(fsm_instance *fi, int event, void *arg)
152962306a36Sopenharmony_ci{
153062306a36Sopenharmony_ci	struct channel *ch = arg;
153162306a36Sopenharmony_ci	struct net_device *dev = ch->netdev;
153262306a36Sopenharmony_ci	struct ctcm_priv  *priv = dev->ml_priv;
153362306a36Sopenharmony_ci	struct mpc_group  *grp = priv->mpcg;
153462306a36Sopenharmony_ci	int rc;
153562306a36Sopenharmony_ci	unsigned long saveflags = 0;	/* avoids compiler warning */
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
153862306a36Sopenharmony_ci	CTCM_PR_DEBUG("%s: %s: %s: cp:%i, chstate:%i grpstate:%i\n",
153962306a36Sopenharmony_ci			__func__, ch->id, dev->name, smp_processor_id(),
154062306a36Sopenharmony_ci				fsm_getstate(fi), fsm_getstate(grp->fsm));
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	fsm_newstate(fi, CTC_STATE_RXIDLE);
154362306a36Sopenharmony_ci	/* XID processing complete */
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	switch (fsm_getstate(grp->fsm)) {
154662306a36Sopenharmony_ci	case MPCG_STATE_FLOWC:
154762306a36Sopenharmony_ci	case MPCG_STATE_READY:
154862306a36Sopenharmony_ci		if (ctcm_checkalloc_buffer(ch))
154962306a36Sopenharmony_ci				goto done;
155062306a36Sopenharmony_ci		ch->trans_skb->data = ch->trans_skb_data;
155162306a36Sopenharmony_ci		skb_reset_tail_pointer(ch->trans_skb);
155262306a36Sopenharmony_ci		ch->trans_skb->len = 0;
155362306a36Sopenharmony_ci		ch->ccw[1].count = ch->max_bufsize;
155462306a36Sopenharmony_ci		CTCM_CCW_DUMP((char *)&ch->ccw[0], sizeof(struct ccw1) * 3);
155562306a36Sopenharmony_ci		if (event == CTC_EVENT_START)
155662306a36Sopenharmony_ci			/* see remark about conditional locking */
155762306a36Sopenharmony_ci			spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
155862306a36Sopenharmony_ci		rc = ccw_device_start(ch->cdev, &ch->ccw[0], 0, 0xff, 0);
155962306a36Sopenharmony_ci		if (event == CTC_EVENT_START)
156062306a36Sopenharmony_ci			spin_unlock_irqrestore(
156162306a36Sopenharmony_ci					get_ccwdev_lock(ch->cdev), saveflags);
156262306a36Sopenharmony_ci		if (rc != 0) {
156362306a36Sopenharmony_ci			fsm_newstate(fi, CTC_STATE_RXINIT);
156462306a36Sopenharmony_ci			ctcm_ccw_check_rc(ch, rc, "initial RX");
156562306a36Sopenharmony_ci			goto done;
156662306a36Sopenharmony_ci		}
156762306a36Sopenharmony_ci		break;
156862306a36Sopenharmony_ci	default:
156962306a36Sopenharmony_ci		break;
157062306a36Sopenharmony_ci	}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	fsm_event(priv->fsm, DEV_EVENT_RXUP, dev);
157362306a36Sopenharmony_cidone:
157462306a36Sopenharmony_ci	return;
157562306a36Sopenharmony_ci}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci/*
157862306a36Sopenharmony_ci * ctcmpc channel FSM action
157962306a36Sopenharmony_ci * called from several points in ctcmpc_ch_fsm
158062306a36Sopenharmony_ci * ctcmpc only
158162306a36Sopenharmony_ci */
158262306a36Sopenharmony_cistatic void ctcmpc_chx_attn(fsm_instance *fsm, int event, void *arg)
158362306a36Sopenharmony_ci{
158462306a36Sopenharmony_ci	struct channel	  *ch     = arg;
158562306a36Sopenharmony_ci	struct net_device *dev    = ch->netdev;
158662306a36Sopenharmony_ci	struct ctcm_priv  *priv   = dev->ml_priv;
158762306a36Sopenharmony_ci	struct mpc_group  *grp = priv->mpcg;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	CTCM_PR_DEBUG("%s(%s): %s(ch=0x%p), cp=%i, ChStat:%s, GrpStat:%s\n",
159062306a36Sopenharmony_ci		__func__, dev->name, ch->id, ch, smp_processor_id(),
159162306a36Sopenharmony_ci			fsm_getstate_str(ch->fsm), fsm_getstate_str(grp->fsm));
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	switch (fsm_getstate(grp->fsm)) {
159462306a36Sopenharmony_ci	case MPCG_STATE_XID2INITW:
159562306a36Sopenharmony_ci		/* ok..start yside xid exchanges */
159662306a36Sopenharmony_ci		if (!ch->in_mpcgroup)
159762306a36Sopenharmony_ci			break;
159862306a36Sopenharmony_ci		if (fsm_getstate(ch->fsm) ==  CH_XID0_PENDING) {
159962306a36Sopenharmony_ci			fsm_deltimer(&grp->timer);
160062306a36Sopenharmony_ci			fsm_addtimer(&grp->timer,
160162306a36Sopenharmony_ci				MPC_XID_TIMEOUT_VALUE,
160262306a36Sopenharmony_ci				MPCG_EVENT_TIMER, dev);
160362306a36Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci		} else if (fsm_getstate(ch->fsm) < CH_XID7_PENDING1)
160662306a36Sopenharmony_ci			/* attn rcvd before xid0 processed via bh */
160762306a36Sopenharmony_ci			fsm_newstate(ch->fsm, CH_XID7_PENDING1);
160862306a36Sopenharmony_ci		break;
160962306a36Sopenharmony_ci	case MPCG_STATE_XID2INITX:
161062306a36Sopenharmony_ci	case MPCG_STATE_XID0IOWAIT:
161162306a36Sopenharmony_ci	case MPCG_STATE_XID0IOWAIX:
161262306a36Sopenharmony_ci		/* attn rcvd before xid0 processed on ch
161362306a36Sopenharmony_ci		but mid-xid0 processing for group    */
161462306a36Sopenharmony_ci		if (fsm_getstate(ch->fsm) < CH_XID7_PENDING1)
161562306a36Sopenharmony_ci			fsm_newstate(ch->fsm, CH_XID7_PENDING1);
161662306a36Sopenharmony_ci		break;
161762306a36Sopenharmony_ci	case MPCG_STATE_XID7INITW:
161862306a36Sopenharmony_ci	case MPCG_STATE_XID7INITX:
161962306a36Sopenharmony_ci	case MPCG_STATE_XID7INITI:
162062306a36Sopenharmony_ci	case MPCG_STATE_XID7INITZ:
162162306a36Sopenharmony_ci		switch (fsm_getstate(ch->fsm)) {
162262306a36Sopenharmony_ci		case CH_XID7_PENDING:
162362306a36Sopenharmony_ci			fsm_newstate(ch->fsm, CH_XID7_PENDING1);
162462306a36Sopenharmony_ci			break;
162562306a36Sopenharmony_ci		case CH_XID7_PENDING2:
162662306a36Sopenharmony_ci			fsm_newstate(ch->fsm, CH_XID7_PENDING3);
162762306a36Sopenharmony_ci			break;
162862306a36Sopenharmony_ci		}
162962306a36Sopenharmony_ci		fsm_event(grp->fsm, MPCG_EVENT_XID7DONE, dev);
163062306a36Sopenharmony_ci		break;
163162306a36Sopenharmony_ci	}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	return;
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci/*
163762306a36Sopenharmony_ci * ctcmpc channel FSM action
163862306a36Sopenharmony_ci * called from one point in ctcmpc_ch_fsm
163962306a36Sopenharmony_ci * ctcmpc only
164062306a36Sopenharmony_ci */
164162306a36Sopenharmony_cistatic void ctcmpc_chx_attnbusy(fsm_instance *fsm, int event, void *arg)
164262306a36Sopenharmony_ci{
164362306a36Sopenharmony_ci	struct channel	  *ch     = arg;
164462306a36Sopenharmony_ci	struct net_device *dev    = ch->netdev;
164562306a36Sopenharmony_ci	struct ctcm_priv  *priv   = dev->ml_priv;
164662306a36Sopenharmony_ci	struct mpc_group  *grp    = priv->mpcg;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	CTCM_PR_DEBUG("%s(%s): %s\n  ChState:%s GrpState:%s\n",
164962306a36Sopenharmony_ci			__func__, dev->name, ch->id,
165062306a36Sopenharmony_ci			fsm_getstate_str(ch->fsm), fsm_getstate_str(grp->fsm));
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	fsm_deltimer(&ch->timer);
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	switch (fsm_getstate(grp->fsm)) {
165562306a36Sopenharmony_ci	case MPCG_STATE_XID0IOWAIT:
165662306a36Sopenharmony_ci		/* vtam wants to be primary.start yside xid exchanges*/
165762306a36Sopenharmony_ci		/* only receive one attn-busy at a time so must not  */
165862306a36Sopenharmony_ci		/* change state each time			     */
165962306a36Sopenharmony_ci		grp->changed_side = 1;
166062306a36Sopenharmony_ci		fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);
166162306a36Sopenharmony_ci		break;
166262306a36Sopenharmony_ci	case MPCG_STATE_XID2INITW:
166362306a36Sopenharmony_ci		if (grp->changed_side == 1) {
166462306a36Sopenharmony_ci			grp->changed_side = 2;
166562306a36Sopenharmony_ci			break;
166662306a36Sopenharmony_ci		}
166762306a36Sopenharmony_ci		/* process began via call to establish_conn	 */
166862306a36Sopenharmony_ci		/* so must report failure instead of reverting	 */
166962306a36Sopenharmony_ci		/* back to ready-for-xid passive state		 */
167062306a36Sopenharmony_ci		if (grp->estconnfunc)
167162306a36Sopenharmony_ci				goto done;
167262306a36Sopenharmony_ci		/* this attnbusy is NOT the result of xside xid  */
167362306a36Sopenharmony_ci		/* collisions so yside must have been triggered  */
167462306a36Sopenharmony_ci		/* by an ATTN that was not intended to start XID */
167562306a36Sopenharmony_ci		/* processing. Revert back to ready-for-xid and  */
167662306a36Sopenharmony_ci		/* wait for ATTN interrupt to signal xid start	 */
167762306a36Sopenharmony_ci		if (fsm_getstate(ch->fsm) == CH_XID0_INPROGRESS) {
167862306a36Sopenharmony_ci			fsm_newstate(ch->fsm, CH_XID0_PENDING) ;
167962306a36Sopenharmony_ci			fsm_deltimer(&grp->timer);
168062306a36Sopenharmony_ci			goto done;
168162306a36Sopenharmony_ci		}
168262306a36Sopenharmony_ci		fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
168362306a36Sopenharmony_ci		goto done;
168462306a36Sopenharmony_ci	case MPCG_STATE_XID2INITX:
168562306a36Sopenharmony_ci		/* XID2 was received before ATTN Busy for second
168662306a36Sopenharmony_ci		   channel.Send yside xid for second channel.
168762306a36Sopenharmony_ci		*/
168862306a36Sopenharmony_ci		if (grp->changed_side == 1) {
168962306a36Sopenharmony_ci			grp->changed_side = 2;
169062306a36Sopenharmony_ci			break;
169162306a36Sopenharmony_ci		}
169262306a36Sopenharmony_ci		fallthrough;
169362306a36Sopenharmony_ci	case MPCG_STATE_XID0IOWAIX:
169462306a36Sopenharmony_ci	case MPCG_STATE_XID7INITW:
169562306a36Sopenharmony_ci	case MPCG_STATE_XID7INITX:
169662306a36Sopenharmony_ci	case MPCG_STATE_XID7INITI:
169762306a36Sopenharmony_ci	case MPCG_STATE_XID7INITZ:
169862306a36Sopenharmony_ci	default:
169962306a36Sopenharmony_ci		/* multiple attn-busy indicates too out-of-sync      */
170062306a36Sopenharmony_ci		/* and they are certainly not being received as part */
170162306a36Sopenharmony_ci		/* of valid mpc group negotiations..		     */
170262306a36Sopenharmony_ci		fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
170362306a36Sopenharmony_ci				goto done;
170462306a36Sopenharmony_ci	}
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	if (grp->changed_side == 1) {
170762306a36Sopenharmony_ci		fsm_deltimer(&grp->timer);
170862306a36Sopenharmony_ci		fsm_addtimer(&grp->timer, MPC_XID_TIMEOUT_VALUE,
170962306a36Sopenharmony_ci			     MPCG_EVENT_TIMER, dev);
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci	if (ch->in_mpcgroup)
171262306a36Sopenharmony_ci		fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
171362306a36Sopenharmony_ci	else
171462306a36Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
171562306a36Sopenharmony_ci			"%s(%s): channel %s not added to group",
171662306a36Sopenharmony_ci				CTCM_FUNTAIL, dev->name, ch->id);
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_cidone:
171962306a36Sopenharmony_ci	return;
172062306a36Sopenharmony_ci}
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci/*
172362306a36Sopenharmony_ci * ctcmpc channel FSM action
172462306a36Sopenharmony_ci * called from several points in ctcmpc_ch_fsm
172562306a36Sopenharmony_ci * ctcmpc only
172662306a36Sopenharmony_ci */
172762306a36Sopenharmony_cistatic void ctcmpc_chx_resend(fsm_instance *fsm, int event, void *arg)
172862306a36Sopenharmony_ci{
172962306a36Sopenharmony_ci	struct channel	   *ch	   = arg;
173062306a36Sopenharmony_ci	struct net_device  *dev    = ch->netdev;
173162306a36Sopenharmony_ci	struct ctcm_priv   *priv   = dev->ml_priv;
173262306a36Sopenharmony_ci	struct mpc_group   *grp    = priv->mpcg;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	fsm_event(grp->fsm, MPCG_EVENT_XID0DO, ch);
173562306a36Sopenharmony_ci	return;
173662306a36Sopenharmony_ci}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci/*
173962306a36Sopenharmony_ci * ctcmpc channel FSM action
174062306a36Sopenharmony_ci * called from several points in ctcmpc_ch_fsm
174162306a36Sopenharmony_ci * ctcmpc only
174262306a36Sopenharmony_ci */
174362306a36Sopenharmony_cistatic void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg)
174462306a36Sopenharmony_ci{
174562306a36Sopenharmony_ci	struct channel *ach = arg;
174662306a36Sopenharmony_ci	struct net_device *dev = ach->netdev;
174762306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
174862306a36Sopenharmony_ci	struct mpc_group *grp = priv->mpcg;
174962306a36Sopenharmony_ci	struct channel *wch = priv->channel[CTCM_WRITE];
175062306a36Sopenharmony_ci	struct channel *rch = priv->channel[CTCM_READ];
175162306a36Sopenharmony_ci	struct sk_buff *skb;
175262306a36Sopenharmony_ci	struct th_sweep *header;
175362306a36Sopenharmony_ci	int rc = 0;
175462306a36Sopenharmony_ci	unsigned long saveflags = 0;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	CTCM_PR_DEBUG("ctcmpc enter: %s(): cp=%i ch=0x%p id=%s\n",
175762306a36Sopenharmony_ci			__func__, smp_processor_id(), ach, ach->id);
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	if (grp->in_sweep == 0)
176062306a36Sopenharmony_ci				goto done;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	CTCM_PR_DBGDATA("%s: 1: ToVTAM_th_seq= %08x\n" ,
176362306a36Sopenharmony_ci				__func__, wch->th_seq_num);
176462306a36Sopenharmony_ci	CTCM_PR_DBGDATA("%s: 1: FromVTAM_th_seq= %08x\n" ,
176562306a36Sopenharmony_ci				__func__, rch->th_seq_num);
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	if (fsm_getstate(wch->fsm) != CTC_STATE_TXIDLE) {
176862306a36Sopenharmony_ci		/* give the previous IO time to complete */
176962306a36Sopenharmony_ci		fsm_addtimer(&wch->sweep_timer,
177062306a36Sopenharmony_ci			200, CTC_EVENT_RSWEEP_TIMER, wch);
177162306a36Sopenharmony_ci		goto done;
177262306a36Sopenharmony_ci	}
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	skb = skb_dequeue(&wch->sweep_queue);
177562306a36Sopenharmony_ci	if (!skb)
177662306a36Sopenharmony_ci				goto done;
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	if (set_normalized_cda(&wch->ccw[4], skb->data)) {
177962306a36Sopenharmony_ci		grp->in_sweep = 0;
178062306a36Sopenharmony_ci		ctcm_clear_busy_do(dev);
178162306a36Sopenharmony_ci		dev_kfree_skb_any(skb);
178262306a36Sopenharmony_ci		fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
178362306a36Sopenharmony_ci		goto done;
178462306a36Sopenharmony_ci	} else {
178562306a36Sopenharmony_ci		refcount_inc(&skb->users);
178662306a36Sopenharmony_ci		skb_queue_tail(&wch->io_queue, skb);
178762306a36Sopenharmony_ci	}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	/* send out the sweep */
179062306a36Sopenharmony_ci	wch->ccw[4].count = skb->len;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	header = (struct th_sweep *)skb->data;
179362306a36Sopenharmony_ci	switch (header->th.th_ch_flag) {
179462306a36Sopenharmony_ci	case TH_SWEEP_REQ:
179562306a36Sopenharmony_ci		grp->sweep_req_pend_num--;
179662306a36Sopenharmony_ci		break;
179762306a36Sopenharmony_ci	case TH_SWEEP_RESP:
179862306a36Sopenharmony_ci		grp->sweep_rsp_pend_num--;
179962306a36Sopenharmony_ci		break;
180062306a36Sopenharmony_ci	}
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	header->sw.th_last_seq = wch->th_seq_num;
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	CTCM_CCW_DUMP((char *)&wch->ccw[3], sizeof(struct ccw1) * 3);
180562306a36Sopenharmony_ci	CTCM_PR_DBGDATA("%s: sweep packet\n", __func__);
180662306a36Sopenharmony_ci	CTCM_D3_DUMP((char *)header, TH_SWEEP_LENGTH);
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	fsm_addtimer(&wch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, wch);
180962306a36Sopenharmony_ci	fsm_newstate(wch->fsm, CTC_STATE_TX);
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(wch->cdev), saveflags);
181262306a36Sopenharmony_ci	wch->prof.send_stamp = jiffies;
181362306a36Sopenharmony_ci	rc = ccw_device_start(wch->cdev, &wch->ccw[3], 0, 0xff, 0);
181462306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(wch->cdev), saveflags);
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	if ((grp->sweep_req_pend_num == 0) &&
181762306a36Sopenharmony_ci	   (grp->sweep_rsp_pend_num == 0)) {
181862306a36Sopenharmony_ci		grp->in_sweep = 0;
181962306a36Sopenharmony_ci		rch->th_seq_num = 0x00;
182062306a36Sopenharmony_ci		wch->th_seq_num = 0x00;
182162306a36Sopenharmony_ci		ctcm_clear_busy_do(dev);
182262306a36Sopenharmony_ci	}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	CTCM_PR_DBGDATA("%s: To-/From-VTAM_th_seq = %08x/%08x\n" ,
182562306a36Sopenharmony_ci			__func__, wch->th_seq_num, rch->th_seq_num);
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	if (rc != 0)
182862306a36Sopenharmony_ci		ctcm_ccw_check_rc(wch, rc, "send sweep");
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_cidone:
183162306a36Sopenharmony_ci	return;
183262306a36Sopenharmony_ci}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci/*
183662306a36Sopenharmony_ci * The ctcmpc statemachine for a channel.
183762306a36Sopenharmony_ci */
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ciconst fsm_node ctcmpc_ch_fsm[] = {
184062306a36Sopenharmony_ci	{ CTC_STATE_STOPPED,	CTC_EVENT_STOP,		ctcm_action_nop  },
184162306a36Sopenharmony_ci	{ CTC_STATE_STOPPED,	CTC_EVENT_START,	ctcm_chx_start  },
184262306a36Sopenharmony_ci	{ CTC_STATE_STOPPED,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
184362306a36Sopenharmony_ci	{ CTC_STATE_STOPPED,	CTC_EVENT_FINSTAT,	ctcm_action_nop  },
184462306a36Sopenharmony_ci	{ CTC_STATE_STOPPED,	CTC_EVENT_MC_FAIL,	ctcm_action_nop  },
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_STOP,		ctcm_chx_stop  },
184762306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_START,	ctcm_action_nop  },
184862306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_FINSTAT,	ctcm_action_nop  },
184962306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_MC_FAIL,	ctcm_action_nop  },
185062306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_MC_GOOD,	ctcm_chx_start  },
185162306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_UC_RCRESET,	ctcm_chx_stop  },
185262306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_UC_RSRESET,	ctcm_chx_stop  },
185362306a36Sopenharmony_ci	{ CTC_STATE_NOTOP,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	{ CTC_STATE_STARTWAIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
185662306a36Sopenharmony_ci	{ CTC_STATE_STARTWAIT,	CTC_EVENT_START,	ctcm_action_nop  },
185762306a36Sopenharmony_ci	{ CTC_STATE_STARTWAIT,	CTC_EVENT_FINSTAT,	ctcm_chx_setmode  },
185862306a36Sopenharmony_ci	{ CTC_STATE_STARTWAIT,	CTC_EVENT_TIMER,	ctcm_chx_setuperr  },
185962306a36Sopenharmony_ci	{ CTC_STATE_STARTWAIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
186062306a36Sopenharmony_ci	{ CTC_STATE_STARTWAIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	{ CTC_STATE_STARTRETRY,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
186362306a36Sopenharmony_ci	{ CTC_STATE_STARTRETRY,	CTC_EVENT_TIMER,	ctcm_chx_setmode  },
186462306a36Sopenharmony_ci	{ CTC_STATE_STARTRETRY,	CTC_EVENT_FINSTAT,	ctcm_chx_setmode  },
186562306a36Sopenharmony_ci	{ CTC_STATE_STARTRETRY,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
186662306a36Sopenharmony_ci	{ CTC_STATE_STARTRETRY,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
186962306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_START,	ctcm_action_nop  },
187062306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_FINSTAT,	ctcmpc_chx_firstio  },
187162306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
187262306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
187362306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_TIMER,	ctcm_chx_setmode  },
187462306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
187562306a36Sopenharmony_ci	{ CTC_STATE_SETUPWAIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
187862306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_START,	ctcm_action_nop  },
187962306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rxidle  },
188062306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_UC_RCRESET,	ctcm_chx_rxiniterr  },
188162306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_UC_RSRESET,	ctcm_chx_rxiniterr  },
188262306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_TIMER,	ctcm_chx_rxiniterr  },
188362306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_ATTNBUSY,	ctcm_chx_rxinitfail  },
188462306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
188562306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_firstio  },
188662306a36Sopenharmony_ci	{ CTC_STATE_RXINIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	{ CH_XID0_PENDING,	CTC_EVENT_FINSTAT,	ctcm_action_nop  },
188962306a36Sopenharmony_ci	{ CH_XID0_PENDING,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
189062306a36Sopenharmony_ci	{ CH_XID0_PENDING,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
189162306a36Sopenharmony_ci	{ CH_XID0_PENDING,	CTC_EVENT_START,	ctcm_action_nop  },
189262306a36Sopenharmony_ci	{ CH_XID0_PENDING,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
189362306a36Sopenharmony_ci	{ CH_XID0_PENDING,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
189462306a36Sopenharmony_ci	{ CH_XID0_PENDING,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
189562306a36Sopenharmony_ci	{ CH_XID0_PENDING,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
189662306a36Sopenharmony_ci	{ CH_XID0_PENDING,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
189762306a36Sopenharmony_ci	{ CH_XID0_PENDING,	CTC_EVENT_ATTNBUSY,	ctcm_chx_iofatal  },
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	{ CH_XID0_INPROGRESS,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
190062306a36Sopenharmony_ci	{ CH_XID0_INPROGRESS,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
190162306a36Sopenharmony_ci	{ CH_XID0_INPROGRESS,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
190262306a36Sopenharmony_ci	{ CH_XID0_INPROGRESS,	CTC_EVENT_START,	ctcm_action_nop  },
190362306a36Sopenharmony_ci	{ CH_XID0_INPROGRESS,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
190462306a36Sopenharmony_ci	{ CH_XID0_INPROGRESS,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
190562306a36Sopenharmony_ci	{ CH_XID0_INPROGRESS,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
190662306a36Sopenharmony_ci	{ CH_XID0_INPROGRESS,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr },
190762306a36Sopenharmony_ci	{ CH_XID0_INPROGRESS,	CTC_EVENT_ATTNBUSY,	ctcmpc_chx_attnbusy  },
190862306a36Sopenharmony_ci	{ CH_XID0_INPROGRESS,	CTC_EVENT_TIMER,	ctcmpc_chx_resend  },
190962306a36Sopenharmony_ci	{ CH_XID0_INPROGRESS,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
191262306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
191362306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
191462306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_START,	ctcm_action_nop  },
191562306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
191662306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
191762306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
191862306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
191962306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
192062306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
192162306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_ATTNBUSY,	ctcm_chx_iofatal  },
192262306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_TIMER,	ctcmpc_chx_resend  },
192362306a36Sopenharmony_ci	{ CH_XID7_PENDING,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci	{ CH_XID7_PENDING1,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
192662306a36Sopenharmony_ci	{ CH_XID7_PENDING1,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
192762306a36Sopenharmony_ci	{ CH_XID7_PENDING1,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
192862306a36Sopenharmony_ci	{ CH_XID7_PENDING1,	CTC_EVENT_START,	ctcm_action_nop  },
192962306a36Sopenharmony_ci	{ CH_XID7_PENDING1,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
193062306a36Sopenharmony_ci	{ CH_XID7_PENDING1,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
193162306a36Sopenharmony_ci	{ CH_XID7_PENDING1,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
193262306a36Sopenharmony_ci	{ CH_XID7_PENDING1,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
193362306a36Sopenharmony_ci	{ CH_XID7_PENDING1,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
193462306a36Sopenharmony_ci	{ CH_XID7_PENDING1,	CTC_EVENT_ATTNBUSY,	ctcm_chx_iofatal  },
193562306a36Sopenharmony_ci	{ CH_XID7_PENDING1,	CTC_EVENT_TIMER,	ctcmpc_chx_resend  },
193662306a36Sopenharmony_ci	{ CH_XID7_PENDING1,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	{ CH_XID7_PENDING2,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
193962306a36Sopenharmony_ci	{ CH_XID7_PENDING2,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
194062306a36Sopenharmony_ci	{ CH_XID7_PENDING2,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
194162306a36Sopenharmony_ci	{ CH_XID7_PENDING2,	CTC_EVENT_START,	ctcm_action_nop  },
194262306a36Sopenharmony_ci	{ CH_XID7_PENDING2,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
194362306a36Sopenharmony_ci	{ CH_XID7_PENDING2,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
194462306a36Sopenharmony_ci	{ CH_XID7_PENDING2,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
194562306a36Sopenharmony_ci	{ CH_XID7_PENDING2,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
194662306a36Sopenharmony_ci	{ CH_XID7_PENDING2,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
194762306a36Sopenharmony_ci	{ CH_XID7_PENDING2,	CTC_EVENT_ATTNBUSY,	ctcm_chx_iofatal  },
194862306a36Sopenharmony_ci	{ CH_XID7_PENDING2,	CTC_EVENT_TIMER,	ctcmpc_chx_resend  },
194962306a36Sopenharmony_ci	{ CH_XID7_PENDING2,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	{ CH_XID7_PENDING3,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
195262306a36Sopenharmony_ci	{ CH_XID7_PENDING3,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
195362306a36Sopenharmony_ci	{ CH_XID7_PENDING3,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
195462306a36Sopenharmony_ci	{ CH_XID7_PENDING3,	CTC_EVENT_START,	ctcm_action_nop  },
195562306a36Sopenharmony_ci	{ CH_XID7_PENDING3,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
195662306a36Sopenharmony_ci	{ CH_XID7_PENDING3,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
195762306a36Sopenharmony_ci	{ CH_XID7_PENDING3,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
195862306a36Sopenharmony_ci	{ CH_XID7_PENDING3,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
195962306a36Sopenharmony_ci	{ CH_XID7_PENDING3,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
196062306a36Sopenharmony_ci	{ CH_XID7_PENDING3,	CTC_EVENT_ATTNBUSY,	ctcm_chx_iofatal  },
196162306a36Sopenharmony_ci	{ CH_XID7_PENDING3,	CTC_EVENT_TIMER,	ctcmpc_chx_resend  },
196262306a36Sopenharmony_ci	{ CH_XID7_PENDING3,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	{ CH_XID7_PENDING4,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
196562306a36Sopenharmony_ci	{ CH_XID7_PENDING4,	CTC_EVENT_ATTN,		ctcmpc_chx_attn  },
196662306a36Sopenharmony_ci	{ CH_XID7_PENDING4,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
196762306a36Sopenharmony_ci	{ CH_XID7_PENDING4,	CTC_EVENT_START,	ctcm_action_nop  },
196862306a36Sopenharmony_ci	{ CH_XID7_PENDING4,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
196962306a36Sopenharmony_ci	{ CH_XID7_PENDING4,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
197062306a36Sopenharmony_ci	{ CH_XID7_PENDING4,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
197162306a36Sopenharmony_ci	{ CH_XID7_PENDING4,	CTC_EVENT_UC_RCRESET,	ctcm_chx_setuperr  },
197262306a36Sopenharmony_ci	{ CH_XID7_PENDING4,	CTC_EVENT_UC_RSRESET,	ctcm_chx_setuperr  },
197362306a36Sopenharmony_ci	{ CH_XID7_PENDING4,	CTC_EVENT_ATTNBUSY,	ctcm_chx_iofatal  },
197462306a36Sopenharmony_ci	{ CH_XID7_PENDING4,	CTC_EVENT_TIMER,	ctcmpc_chx_resend  },
197562306a36Sopenharmony_ci	{ CH_XID7_PENDING4,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
197862306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_START,	ctcm_action_nop  },
197962306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_FINSTAT,	ctcmpc_chx_rx  },
198062306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_UC_RCRESET,	ctcm_chx_rxdisc  },
198162306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_UC_RSRESET,	ctcm_chx_fail  },
198262306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
198362306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
198462306a36Sopenharmony_ci	{ CTC_STATE_RXIDLE,	CTC_EVENT_UC_ZERO,	ctcmpc_chx_rx  },
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
198762306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_START,	ctcm_action_nop  },
198862306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_FINSTAT,	ctcm_chx_txidle  },
198962306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_UC_RCRESET,	ctcm_chx_txiniterr  },
199062306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_UC_RSRESET,	ctcm_chx_txiniterr  },
199162306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_TIMER,	ctcm_chx_txiniterr  },
199262306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
199362306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
199462306a36Sopenharmony_ci	{ CTC_STATE_TXINIT,	CTC_EVENT_RSWEEP_TIMER,	ctcmpc_chx_send_sweep },
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
199762306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_START,	ctcm_action_nop  },
199862306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_FINSTAT,	ctcmpc_chx_firstio  },
199962306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_UC_RCRESET,	ctcm_chx_fail  },
200062306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_UC_RSRESET,	ctcm_chx_fail  },
200162306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
200262306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
200362306a36Sopenharmony_ci	{ CTC_STATE_TXIDLE,	CTC_EVENT_RSWEEP_TIMER,	ctcmpc_chx_send_sweep },
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_STOP,		ctcm_action_nop  },
200662306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_START,	ctcm_chx_restart  },
200762306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_FINSTAT,	ctcm_chx_stopped  },
200862306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_UC_RCRESET,	ctcm_action_nop  },
200962306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_UC_RSRESET,	ctcm_action_nop  },
201062306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
201162306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
201262306a36Sopenharmony_ci	{ CTC_STATE_TERM,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
201562306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_START,	ctcm_chx_restart  },
201662306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_FINSTAT,	ctcm_chx_setmode  },
201762306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_UC_RCRESET,	ctcm_action_nop  },
201862306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_UC_RSRESET,	ctcm_action_nop  },
201962306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
202062306a36Sopenharmony_ci	{ CTC_STATE_DTERM,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_STOP,		ctcm_chx_haltio  },
202362306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_START,	ctcm_action_nop  },
202462306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_FINSTAT,	ctcmpc_chx_txdone  },
202562306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_UC_RCRESET,	ctcm_chx_fail  },
202662306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_UC_RSRESET,	ctcm_chx_fail  },
202762306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_TIMER,	ctcm_chx_txretry  },
202862306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
202962306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
203062306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_RSWEEP_TIMER,	ctcmpc_chx_send_sweep },
203162306a36Sopenharmony_ci	{ CTC_STATE_TX,		CTC_EVENT_IO_EBUSY,	ctcm_chx_fail  },
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci	{ CTC_STATE_RXERR,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
203462306a36Sopenharmony_ci	{ CTC_STATE_TXERR,	CTC_EVENT_STOP,		ctcm_chx_haltio  },
203562306a36Sopenharmony_ci	{ CTC_STATE_TXERR,	CTC_EVENT_IO_ENODEV,	ctcm_chx_iofatal  },
203662306a36Sopenharmony_ci	{ CTC_STATE_TXERR,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
203762306a36Sopenharmony_ci	{ CTC_STATE_RXERR,	CTC_EVENT_MC_FAIL,	ctcm_chx_fail  },
203862306a36Sopenharmony_ci};
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ciint mpc_ch_fsm_len = ARRAY_SIZE(ctcmpc_ch_fsm);
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci/*
204362306a36Sopenharmony_ci * Actions for interface - statemachine.
204462306a36Sopenharmony_ci */
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci/*
204762306a36Sopenharmony_ci * Startup channels by sending CTC_EVENT_START to each channel.
204862306a36Sopenharmony_ci *
204962306a36Sopenharmony_ci * fi		An instance of an interface statemachine.
205062306a36Sopenharmony_ci * event	The event, just happened.
205162306a36Sopenharmony_ci * arg		Generic pointer, casted from struct net_device * upon call.
205262306a36Sopenharmony_ci */
205362306a36Sopenharmony_cistatic void dev_action_start(fsm_instance *fi, int event, void *arg)
205462306a36Sopenharmony_ci{
205562306a36Sopenharmony_ci	struct net_device *dev = arg;
205662306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
205762306a36Sopenharmony_ci	int direction;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	CTCMY_DBF_DEV_NAME(SETUP, dev, "");
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	fsm_deltimer(&priv->restart_timer);
206262306a36Sopenharmony_ci	fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
206362306a36Sopenharmony_ci	if (IS_MPC(priv))
206462306a36Sopenharmony_ci		priv->mpcg->channels_terminating = 0;
206562306a36Sopenharmony_ci	for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
206662306a36Sopenharmony_ci		struct channel *ch = priv->channel[direction];
206762306a36Sopenharmony_ci		fsm_event(ch->fsm, CTC_EVENT_START, ch);
206862306a36Sopenharmony_ci	}
206962306a36Sopenharmony_ci}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci/*
207262306a36Sopenharmony_ci * Shutdown channels by sending CTC_EVENT_STOP to each channel.
207362306a36Sopenharmony_ci *
207462306a36Sopenharmony_ci * fi		An instance of an interface statemachine.
207562306a36Sopenharmony_ci * event	The event, just happened.
207662306a36Sopenharmony_ci * arg		Generic pointer, casted from struct net_device * upon call.
207762306a36Sopenharmony_ci */
207862306a36Sopenharmony_cistatic void dev_action_stop(fsm_instance *fi, int event, void *arg)
207962306a36Sopenharmony_ci{
208062306a36Sopenharmony_ci	int direction;
208162306a36Sopenharmony_ci	struct net_device *dev = arg;
208262306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	CTCMY_DBF_DEV_NAME(SETUP, dev, "");
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
208762306a36Sopenharmony_ci	for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
208862306a36Sopenharmony_ci		struct channel *ch = priv->channel[direction];
208962306a36Sopenharmony_ci		fsm_event(ch->fsm, CTC_EVENT_STOP, ch);
209062306a36Sopenharmony_ci		ch->th_seq_num = 0x00;
209162306a36Sopenharmony_ci		CTCM_PR_DEBUG("%s: CH_th_seq= %08x\n",
209262306a36Sopenharmony_ci				__func__, ch->th_seq_num);
209362306a36Sopenharmony_ci	}
209462306a36Sopenharmony_ci	if (IS_MPC(priv))
209562306a36Sopenharmony_ci		fsm_newstate(priv->mpcg->fsm, MPCG_STATE_RESET);
209662306a36Sopenharmony_ci}
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_cistatic void dev_action_restart(fsm_instance *fi, int event, void *arg)
209962306a36Sopenharmony_ci{
210062306a36Sopenharmony_ci	int restart_timer;
210162306a36Sopenharmony_ci	struct net_device *dev = arg;
210262306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	CTCMY_DBF_DEV_NAME(TRACE, dev, "");
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	if (IS_MPC(priv)) {
210762306a36Sopenharmony_ci		restart_timer = CTCM_TIME_1_SEC;
210862306a36Sopenharmony_ci	} else {
210962306a36Sopenharmony_ci		restart_timer = CTCM_TIME_5_SEC;
211062306a36Sopenharmony_ci	}
211162306a36Sopenharmony_ci	dev_info(&dev->dev, "Restarting device\n");
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	dev_action_stop(fi, event, arg);
211462306a36Sopenharmony_ci	fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
211562306a36Sopenharmony_ci	if (IS_MPC(priv))
211662306a36Sopenharmony_ci		fsm_newstate(priv->mpcg->fsm, MPCG_STATE_RESET);
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	/* going back into start sequence too quickly can	  */
211962306a36Sopenharmony_ci	/* result in the other side becoming unreachable   due	  */
212062306a36Sopenharmony_ci	/* to sense reported when IO is aborted			  */
212162306a36Sopenharmony_ci	fsm_addtimer(&priv->restart_timer, restart_timer,
212262306a36Sopenharmony_ci			DEV_EVENT_START, dev);
212362306a36Sopenharmony_ci}
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci/*
212662306a36Sopenharmony_ci * Called from channel statemachine
212762306a36Sopenharmony_ci * when a channel is up and running.
212862306a36Sopenharmony_ci *
212962306a36Sopenharmony_ci * fi		An instance of an interface statemachine.
213062306a36Sopenharmony_ci * event	The event, just happened.
213162306a36Sopenharmony_ci * arg		Generic pointer, casted from struct net_device * upon call.
213262306a36Sopenharmony_ci */
213362306a36Sopenharmony_cistatic void dev_action_chup(fsm_instance *fi, int event, void *arg)
213462306a36Sopenharmony_ci{
213562306a36Sopenharmony_ci	struct net_device *dev = arg;
213662306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
213762306a36Sopenharmony_ci	int dev_stat = fsm_getstate(fi);
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	CTCM_DBF_TEXT_(SETUP, CTC_DBF_NOTICE,
214062306a36Sopenharmony_ci			"%s(%s): priv = %p [%d,%d]\n ",	CTCM_FUNTAIL,
214162306a36Sopenharmony_ci				dev->name, dev->ml_priv, dev_stat, event);
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	switch (fsm_getstate(fi)) {
214462306a36Sopenharmony_ci	case DEV_STATE_STARTWAIT_RXTX:
214562306a36Sopenharmony_ci		if (event == DEV_EVENT_RXUP)
214662306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
214762306a36Sopenharmony_ci		else
214862306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
214962306a36Sopenharmony_ci		break;
215062306a36Sopenharmony_ci	case DEV_STATE_STARTWAIT_RX:
215162306a36Sopenharmony_ci		if (event == DEV_EVENT_RXUP) {
215262306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_RUNNING);
215362306a36Sopenharmony_ci			dev_info(&dev->dev,
215462306a36Sopenharmony_ci				"Connected with remote side\n");
215562306a36Sopenharmony_ci			ctcm_clear_busy(dev);
215662306a36Sopenharmony_ci		}
215762306a36Sopenharmony_ci		break;
215862306a36Sopenharmony_ci	case DEV_STATE_STARTWAIT_TX:
215962306a36Sopenharmony_ci		if (event == DEV_EVENT_TXUP) {
216062306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_RUNNING);
216162306a36Sopenharmony_ci			dev_info(&dev->dev,
216262306a36Sopenharmony_ci				"Connected with remote side\n");
216362306a36Sopenharmony_ci			ctcm_clear_busy(dev);
216462306a36Sopenharmony_ci		}
216562306a36Sopenharmony_ci		break;
216662306a36Sopenharmony_ci	case DEV_STATE_STOPWAIT_TX:
216762306a36Sopenharmony_ci		if (event == DEV_EVENT_RXUP)
216862306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
216962306a36Sopenharmony_ci		break;
217062306a36Sopenharmony_ci	case DEV_STATE_STOPWAIT_RX:
217162306a36Sopenharmony_ci		if (event == DEV_EVENT_TXUP)
217262306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
217362306a36Sopenharmony_ci		break;
217462306a36Sopenharmony_ci	}
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	if (IS_MPC(priv)) {
217762306a36Sopenharmony_ci		if (event == DEV_EVENT_RXUP)
217862306a36Sopenharmony_ci			mpc_channel_action(priv->channel[CTCM_READ],
217962306a36Sopenharmony_ci				CTCM_READ, MPC_CHANNEL_ADD);
218062306a36Sopenharmony_ci		else
218162306a36Sopenharmony_ci			mpc_channel_action(priv->channel[CTCM_WRITE],
218262306a36Sopenharmony_ci				CTCM_WRITE, MPC_CHANNEL_ADD);
218362306a36Sopenharmony_ci	}
218462306a36Sopenharmony_ci}
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci/*
218762306a36Sopenharmony_ci * Called from device statemachine
218862306a36Sopenharmony_ci * when a channel has been shutdown.
218962306a36Sopenharmony_ci *
219062306a36Sopenharmony_ci * fi		An instance of an interface statemachine.
219162306a36Sopenharmony_ci * event	The event, just happened.
219262306a36Sopenharmony_ci * arg		Generic pointer, casted from struct net_device * upon call.
219362306a36Sopenharmony_ci */
219462306a36Sopenharmony_cistatic void dev_action_chdown(fsm_instance *fi, int event, void *arg)
219562306a36Sopenharmony_ci{
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci	struct net_device *dev = arg;
219862306a36Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	CTCMY_DBF_DEV_NAME(SETUP, dev, "");
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	switch (fsm_getstate(fi)) {
220362306a36Sopenharmony_ci	case DEV_STATE_RUNNING:
220462306a36Sopenharmony_ci		if (event == DEV_EVENT_TXDOWN)
220562306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
220662306a36Sopenharmony_ci		else
220762306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
220862306a36Sopenharmony_ci		break;
220962306a36Sopenharmony_ci	case DEV_STATE_STARTWAIT_RX:
221062306a36Sopenharmony_ci		if (event == DEV_EVENT_TXDOWN)
221162306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
221262306a36Sopenharmony_ci		break;
221362306a36Sopenharmony_ci	case DEV_STATE_STARTWAIT_TX:
221462306a36Sopenharmony_ci		if (event == DEV_EVENT_RXDOWN)
221562306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
221662306a36Sopenharmony_ci		break;
221762306a36Sopenharmony_ci	case DEV_STATE_STOPWAIT_RXTX:
221862306a36Sopenharmony_ci		if (event == DEV_EVENT_TXDOWN)
221962306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_STOPWAIT_RX);
222062306a36Sopenharmony_ci		else
222162306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_STOPWAIT_TX);
222262306a36Sopenharmony_ci		break;
222362306a36Sopenharmony_ci	case DEV_STATE_STOPWAIT_RX:
222462306a36Sopenharmony_ci		if (event == DEV_EVENT_RXDOWN)
222562306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_STOPPED);
222662306a36Sopenharmony_ci		break;
222762306a36Sopenharmony_ci	case DEV_STATE_STOPWAIT_TX:
222862306a36Sopenharmony_ci		if (event == DEV_EVENT_TXDOWN)
222962306a36Sopenharmony_ci			fsm_newstate(fi, DEV_STATE_STOPPED);
223062306a36Sopenharmony_ci		break;
223162306a36Sopenharmony_ci	}
223262306a36Sopenharmony_ci	if (IS_MPC(priv)) {
223362306a36Sopenharmony_ci		if (event == DEV_EVENT_RXDOWN)
223462306a36Sopenharmony_ci			mpc_channel_action(priv->channel[CTCM_READ],
223562306a36Sopenharmony_ci				CTCM_READ, MPC_CHANNEL_REMOVE);
223662306a36Sopenharmony_ci		else
223762306a36Sopenharmony_ci			mpc_channel_action(priv->channel[CTCM_WRITE],
223862306a36Sopenharmony_ci				CTCM_WRITE, MPC_CHANNEL_REMOVE);
223962306a36Sopenharmony_ci	}
224062306a36Sopenharmony_ci}
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ciconst fsm_node dev_fsm[] = {
224362306a36Sopenharmony_ci	{ DEV_STATE_STOPPED,        DEV_EVENT_START,   dev_action_start   },
224462306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_START,   dev_action_start   },
224562306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_RXDOWN,  dev_action_chdown  },
224662306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_TXDOWN,  dev_action_chdown  },
224762306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_RESTART, dev_action_restart },
224862306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_RX,    DEV_EVENT_START,   dev_action_start   },
224962306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_RX,    DEV_EVENT_RXUP,    dev_action_chup    },
225062306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_RX,    DEV_EVENT_TXUP,    dev_action_chup    },
225162306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_RX,    DEV_EVENT_RXDOWN,  dev_action_chdown  },
225262306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_RX,    DEV_EVENT_RESTART, dev_action_restart },
225362306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_TX,    DEV_EVENT_START,   dev_action_start   },
225462306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_TX,    DEV_EVENT_RXUP,    dev_action_chup    },
225562306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_TX,    DEV_EVENT_TXUP,    dev_action_chup    },
225662306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_TX,    DEV_EVENT_TXDOWN,  dev_action_chdown  },
225762306a36Sopenharmony_ci	{ DEV_STATE_STOPWAIT_TX,    DEV_EVENT_RESTART, dev_action_restart },
225862306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP,    dev_action_stop    },
225962306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP,    dev_action_chup    },
226062306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP,    dev_action_chup    },
226162306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN,  dev_action_chdown  },
226262306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN,  dev_action_chdown  },
226362306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart },
226462306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_TX,   DEV_EVENT_STOP,    dev_action_stop    },
226562306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_TX,   DEV_EVENT_RXUP,    dev_action_chup    },
226662306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_TX,   DEV_EVENT_TXUP,    dev_action_chup    },
226762306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_TX,   DEV_EVENT_RXDOWN,  dev_action_chdown  },
226862306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_TX,   DEV_EVENT_RESTART, dev_action_restart },
226962306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_RX,   DEV_EVENT_STOP,    dev_action_stop    },
227062306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_RX,   DEV_EVENT_RXUP,    dev_action_chup    },
227162306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_RX,   DEV_EVENT_TXUP,    dev_action_chup    },
227262306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_RX,   DEV_EVENT_TXDOWN,  dev_action_chdown  },
227362306a36Sopenharmony_ci	{ DEV_STATE_STARTWAIT_RX,   DEV_EVENT_RESTART, dev_action_restart },
227462306a36Sopenharmony_ci	{ DEV_STATE_RUNNING,        DEV_EVENT_STOP,    dev_action_stop    },
227562306a36Sopenharmony_ci	{ DEV_STATE_RUNNING,        DEV_EVENT_RXDOWN,  dev_action_chdown  },
227662306a36Sopenharmony_ci	{ DEV_STATE_RUNNING,        DEV_EVENT_TXDOWN,  dev_action_chdown  },
227762306a36Sopenharmony_ci	{ DEV_STATE_RUNNING,        DEV_EVENT_TXUP,    ctcm_action_nop    },
227862306a36Sopenharmony_ci	{ DEV_STATE_RUNNING,        DEV_EVENT_RXUP,    ctcm_action_nop    },
227962306a36Sopenharmony_ci	{ DEV_STATE_RUNNING,        DEV_EVENT_RESTART, dev_action_restart },
228062306a36Sopenharmony_ci};
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ciint dev_fsm_len = ARRAY_SIZE(dev_fsm);
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci/* --- This is the END my friend --- */
228562306a36Sopenharmony_ci
2286