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