📄 ctcmain.c
字号:
}/** * A stop command from device statemachine arrived and we are in * not operational mode. Set state to stopped. * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static void ch_action_stop(fsm_instance *fi, int event, void *arg){ fsm_newstate(fi, CH_STATE_STOPPED);}/** * A machine check for no path, not operational status or gone device has * happened. * Cleanup queue and notify interface statemachine. * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static void ch_action_fail(fsm_instance *fi, int event, void *arg){ channel *ch = (channel *)arg; net_device *dev = ch->netdev; fsm_deltimer(&ch->timer); fsm_newstate(fi, CH_STATE_NOTOP); if (CHANNEL_DIRECTION(ch->flags) == READ) { skb_queue_purge(&ch->io_queue); fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev); } else { ctc_purge_skb_queue(&ch->io_queue); spin_lock(&ch->collect_lock); ctc_purge_skb_queue(&ch->collect_queue); ch->collect_len = 0; spin_unlock(&ch->collect_lock); fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); }}/** * Handle error during setup of channel. * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static void ch_action_setuperr(fsm_instance *fi, int event, void *arg){ channel *ch = (channel *)arg; net_device *dev = ch->netdev; /** * Special case: Got UC_RCRESET on setmode. * This means that remote side isn't setup. In this case * simply retry after some 10 secs... */ if ((fsm_getstate(fi) == CH_STATE_SETUPWAIT) && ((event == CH_EVENT_UC_RCRESET) || (event == CH_EVENT_UC_RSRESET) ) ) { fsm_newstate(fi, CH_STATE_STARTRETRY); fsm_deltimer(&ch->timer); fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); if (CHANNEL_DIRECTION(ch->flags) == READ) { int rc = halt_IO (ch->irq, (intparm_t)ch, 0); if (rc != 0) ccw_check_return_code(ch, rc); } return; } printk(KERN_DEBUG "%s: Error %s during %s channel setup state=%s\n", dev->name, ch_event_names[event], (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX", fsm_getstate_str(fi)); if (CHANNEL_DIRECTION(ch->flags) == READ) { fsm_newstate(fi, CH_STATE_RXERR); fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev); } else { fsm_newstate(fi, CH_STATE_TXERR); fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); }}/** * Restart a channel after an error. * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static void ch_action_restart(fsm_instance *fi, int event, void *arg){ unsigned long saveflags; int oldstate; int rc; channel *ch = (channel *)arg; net_device *dev = ch->netdev; fsm_deltimer(&ch->timer); printk(KERN_DEBUG "%s: %s channel restart\n", dev->name, (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); oldstate = fsm_getstate(fi); fsm_newstate(fi, CH_STATE_STARTWAIT); if (event == CH_EVENT_TIMER) s390irq_spin_lock_irqsave(ch->irq, saveflags); rc = halt_IO (ch->irq, (intparm_t)ch, 0); if (event == CH_EVENT_TIMER) s390irq_spin_unlock_irqrestore(ch->irq, saveflags); if (rc != 0) { fsm_deltimer(&ch->timer); fsm_newstate(fi, oldstate); ccw_check_return_code(ch, rc); }}/** * Handle error during RX initial handshake (exchange of * 0-length block header) * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static void ch_action_rxiniterr(fsm_instance *fi, int event, void *arg){ channel *ch = (channel *)arg; net_device *dev = ch->netdev; if (event == CH_EVENT_TIMER) { fsm_deltimer(&ch->timer); printk(KERN_DEBUG "%s: Timeout during RX init handshake\n", dev->name); if (ch->retry++ < 3) ch_action_restart(fi, event, arg); else { fsm_newstate(fi, CH_STATE_RXERR); fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev); } } else printk(KERN_WARNING "%s: Error during RX init handshake\n", dev->name);}/** * Notify device statemachine if we gave up initialization * of RX channel. * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static void ch_action_rxinitfail(fsm_instance *fi, int event, void *arg){ channel *ch = (channel *)arg; net_device *dev = ch->netdev; fsm_newstate(fi, CH_STATE_RXERR); printk(KERN_WARNING "%s: RX initialization failed\n", dev->name); printk(KERN_WARNING "%s: RX <-> RX connection detected\n", dev->name); fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);}/** * Handle RX Unit check remote reset (remote disconnected) * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static void ch_action_rxdisc(fsm_instance *fi, int event, void *arg){ channel *ch = (channel *)arg; channel *ch2; net_device *dev = ch->netdev; fsm_deltimer(&ch->timer); printk(KERN_DEBUG "%s: Got remote disconnect, re-initializing ...\n", dev->name); /** * Notify device statemachine */ fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev); fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); fsm_newstate(fi, CH_STATE_DTERM); ch2 = ((ctc_priv *)dev->priv)->channel[WRITE]; fsm_newstate(ch2->fsm, CH_STATE_DTERM); halt_IO(ch->irq, (intparm_t)ch, 0); halt_IO(ch2->irq, (intparm_t)ch2, 0);}/** * Handle error during TX channel initialization. * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static void ch_action_txiniterr(fsm_instance *fi, int event, void *arg){ channel *ch = (channel *)arg; net_device *dev = ch->netdev; if (event == CH_EVENT_TIMER) { fsm_deltimer(&ch->timer); printk(KERN_DEBUG "%s: Timeout during TX init handshake\n", dev->name); if (ch->retry++ < 3) ch_action_restart(fi, event, arg); else { fsm_newstate(fi, CH_STATE_TXERR); fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); } } else printk(KERN_WARNING "%s: Error during TX init handshake\n", dev->name);}/** * Handle TX timeout by retrying operation. * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static void ch_action_txretry(fsm_instance *fi, int event, void *arg){ channel *ch = (channel *)arg; net_device *dev = ch->netdev; unsigned long saveflags; fsm_deltimer(&ch->timer); if (ch->retry++ > 3) { printk(KERN_DEBUG "%s: TX retry failed, restarting channel\n", dev->name); fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); ch_action_restart(fi, event, arg); } else { struct sk_buff *skb; printk(KERN_DEBUG "%s: TX retry %d\n", dev->name, ch->retry); if ((skb = skb_peek(&ch->io_queue))) { int rc = 0; clear_normalized_cda(&ch->ccw[4]); ch->ccw[4].count = skb->len; if (set_normalized_cda(&ch->ccw[4], virt_to_phys(skb->data))) { printk(KERN_DEBUG "%s: IDAL alloc failed, " "restarting channel\n", dev->name); fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); ch_action_restart(fi, event, arg); return; } fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch); if (event == CH_EVENT_TIMER) s390irq_spin_lock_irqsave(ch->irq, saveflags); rc = do_IO(ch->irq, &ch->ccw[3], (intparm_t)ch, 0xff, 0); if (event == CH_EVENT_TIMER) s390irq_spin_unlock_irqrestore(ch->irq, saveflags); if (rc != 0) { fsm_deltimer(&ch->timer); ccw_check_return_code(ch, rc); ctc_purge_skb_queue(&ch->io_queue); } } }}/** * Handle fatal errors during an I/O command. * * @param fi An instance of a channel statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from channel * upon call. */static void ch_action_iofatal(fsm_instance *fi, int event, void *arg){ channel *ch = (channel *)arg; net_device *dev = ch->netdev; fsm_deltimer(&ch->timer); if (CHANNEL_DIRECTION(ch->flags) == READ) { printk(KERN_DEBUG "%s: RX I/O error\n", dev->name); fsm_newstate(fi, CH_STATE_RXERR); fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev); } else { printk(KERN_DEBUG "%s: TX I/O error\n", dev->name); fsm_newstate(fi, CH_STATE_TXERR); fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); }}/** * The statemachine for a channel. */static const fsm_node ch_fsm[] = { { CH_STATE_STOPPED, CH_EVENT_STOP, fsm_action_nop }, { CH_STATE_STOPPED, CH_EVENT_START, ch_action_start }, { CH_STATE_STOPPED, CH_EVENT_FINSTAT, fsm_action_nop }, { CH_STATE_STOPPED, CH_EVENT_MC_FAIL, fsm_action_nop }, { CH_STATE_NOTOP, CH_EVENT_STOP, ch_action_stop }, { CH_STATE_NOTOP, CH_EVENT_START, fsm_action_nop }, { CH_STATE_NOTOP, CH_EVENT_FINSTAT, fsm_action_nop }, { CH_STATE_NOTOP, CH_EVENT_MC_FAIL, fsm_action_nop }, { CH_STATE_NOTOP, CH_EVENT_MC_GOOD, ch_action_start }, { CH_STATE_STARTWAIT, CH_EVENT_STOP, ch_action_haltio }, { CH_STATE_STARTWAIT, CH_EVENT_START, fsm_action_nop }, { CH_STATE_STARTWAIT, CH_EVENT_FINSTAT, ch_action_setmode }, { CH_STATE_STARTWAIT, CH_EVENT_TIMER, ch_action_setuperr }, { CH_STATE_STARTWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, { CH_STATE_STARTWAIT, CH_EVENT_IO_EIO, ch_action_iofatal }, { CH_STATE_STARTWAIT, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_STARTRETRY, CH_EVENT_STOP, ch_action_haltio }, { CH_STATE_STARTRETRY, CH_EVENT_TIMER, ch_action_setmode }, { CH_STATE_STARTRETRY, CH_EVENT_FINSTAT, fsm_action_nop }, { CH_STATE_STARTRETRY, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_SETUPWAIT, CH_EVENT_STOP, ch_action_haltio }, { CH_STATE_SETUPWAIT, CH_EVENT_START, fsm_action_nop }, { CH_STATE_SETUPWAIT, CH_EVENT_FINSTAT, ch_action_firstio }, { CH_STATE_SETUPWAIT, CH_EVENT_UC_RCRESET, ch_action_setuperr }, { CH_STATE_SETUPWAIT, CH_EVENT_UC_RSRESET, ch_action_setuperr }, { CH_STATE_SETUPWAIT, CH_EVENT_TIMER, ch_action_setmode }, { CH_STATE_SETUPWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, { CH_STATE_SETUPWAIT, CH_EVENT_IO_EIO, ch_action_iofatal }, { CH_STATE_SETUPWAIT, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_RXINIT, CH_EVENT_STOP, ch_action_haltio }, { CH_STATE_RXINIT, CH_EVENT_START, fsm_action_nop }, { CH_STATE_RXINIT, CH_EVENT_FINSTAT, ch_action_rxidle }, { CH_STATE_RXINIT, CH_EVENT_UC_RCRESET, ch_action_rxiniterr }, { CH_STATE_RXINIT, CH_EVENT_UC_RSRESET, ch_action_rxiniterr }, { CH_STATE_RXINIT, CH_EVENT_TIMER, ch_action_rxiniterr }, { CH_STATE_RXINIT, CH_EVENT_ATTNBUSY, ch_action_rxinitfail }, { CH_STATE_RXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, { CH_STATE_RXINIT, CH_EVENT_IO_EIO, ch_action_iofatal }, { CH_STATE_RXINIT, CH_EVENT_UC_ZERO, ch_action_firstio }, { CH_STATE_RXINIT, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_RXIDLE, CH_EVENT_STOP, ch_action_haltio }, { CH_STATE_RXIDLE, CH_EVENT_START, fsm_action_nop }, { CH_STATE_RXIDLE, CH_EVENT_FINSTAT, ch_action_rx }, { CH_STATE_RXIDLE, CH_EVENT_UC_RCRESET, ch_action_rxdisc },// { CH_STATE_RXIDLE, CH_EVENT_UC_RSRESET, ch_action_rxretry }, { CH_STATE_RXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal }, { CH_STATE_RXIDLE, CH_EVENT_IO_EIO, ch_action_iofatal }, { CH_STATE_RXIDLE, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_RXIDLE, CH_EVENT_UC_ZERO, ch_action_rx }, { CH_STATE_TXINIT, CH_EVENT_STOP, ch_action_haltio }, { CH_STATE_TXINIT, CH_EVENT_START, fsm_action_nop }, { CH_STATE_TXINIT, CH_EVENT_FINSTAT, ch_action_txidle }, { CH_STATE_TXINIT, CH_EVENT_UC_RCRESET, ch_action_txiniterr }, { CH_STATE_TXINIT, CH_EVENT_UC_RSRESET, ch_action_txiniterr }, { CH_STATE_TXINIT, CH_EVENT_TIMER, ch_action_txiniterr }, { CH_STATE_TXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, { CH_STATE_TXINIT, CH_EVENT_IO_EIO, ch_action_iofatal }, { CH_STATE_TXINIT, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_TXIDLE, CH_EVENT_STOP, ch_action_haltio }, { CH_STATE_TXIDLE, CH_EVENT_START, fsm_action_nop }, { CH_STATE_TXIDLE, CH_EVENT_FINSTAT, ch_action_firstio }, { CH_STATE_TXIDLE, CH_EVENT_UC_RCRESET, fsm_action_nop }, { CH_STATE_TXIDLE, CH_EVENT_UC_RSRESET, fsm_action_nop }, { CH_STATE_TXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal }, { CH_STATE_TXIDLE, CH_EVENT_IO_EIO, ch_action_iofatal }, { CH_STATE_TXIDLE, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_TERM, CH_EVENT_STOP, fsm_action_nop }, { CH_STATE_TERM, CH_EVENT_START, ch_action_restart }, { CH_STATE_TERM, CH_EVENT_FINSTAT, ch_action_stopped }, { CH_STATE_TERM, CH_EVENT_UC_RCRESET, fsm_action_nop }, { CH_STATE_TERM, CH_EVENT_UC_RSRESET, fsm_action_nop }, { CH_STATE_TERM, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_DTERM, CH_EVENT_STOP, ch_action_haltio }, { CH_STATE_DTERM, CH_EVENT_START, ch_action_restart }, { CH_STATE_DTERM, CH_EVENT_FINSTAT, ch_action_setmode }, { CH_STATE_DTERM, CH_EVENT_UC_RCRESET, fsm_action_nop }, { CH_STATE_DTERM, CH_EVENT_UC_RSRESET, fsm_action_nop }, { CH_STATE_DTERM, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_TX, CH_EVENT_STOP, ch_action_haltio }, { CH_STATE_TX, CH_EVENT_START, fsm_action_nop }, { CH_STATE_TX, CH_EVENT_FINSTAT, ch_action_txdone }, { CH_STATE_TX, CH_EVENT_UC_RCRESET, ch_action_txretry }, { CH_STATE_TX, CH_EVENT_UC_RSRESET, ch_action_txretry }, { CH_STATE_TX, CH_EVENT_TIMER, ch_action_txretry }, { CH_STATE_TX, CH_EVENT_IO_ENODEV, ch_action_iofatal }, { CH_STATE_TX, CH_EVENT_IO_EIO, ch_action_iofatal }, { CH_STATE_TX, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_RXERR, CH_EVENT_STOP, ch_action_haltio }, { CH_STATE_TXERR, CH_EVENT_STOP, ch_action_haltio }, { CH_STATE_TXERR, CH_EVENT_MC_FAIL, ch_action_fail }, { CH_STATE_RXERR, CH_EVENT_MC_FAIL, ch_action_fail },};static const int CH_FSM_LEN = sizeof(ch_fsm) / sizeof(fsm_node);/** * Functions related to setup and device detection. *****************************************************************************//** * Add a new channel to the list of channels. * Keeps the channel list sorted. * * @param irq The IRQ to be used by the new channel. * @param devno The device number of the new channel. * @param type The type class of the new channel. * * @return 0 on success, !0 on error. */static int add_channel(int irq, __u16 devno, channel_type_t type){ channel **c = &channels; channel *ch; char name[10]; if ((ch = (channel *)kmalloc(sizeof(channel), GFP_KERNEL)) == NULL) { printk(KERN_WARNING "ctc: Out of memory in add_channel\n"); return -1; } memset(ch, 0, sizeof(channel)); if ((ch->ccw = (ccw1_t *)kmalloc(sizeof(ccw1_t) * 8, GFP_KERNEL|GFP_DMA)) == NULL) { kfree(ch); printk(KERN_WARNING "ctc: Out of memory in add_channel\n"); return -1; } /** * "static" ccws are used in the following way: * * ccw[0..2] (Channel program for generic I/O): * 0: prepare * 1: read or write (depending on direction) with fixed * buffer (idal allocated once when buffer is allocated) * 2: nop * ccw[3..5] (Channel program for direct write of packets) * 3: prepare * 4: write (idal allocated on every write). * 5: nop * ccw[6..7] (Channel program for initial channel setup): * 3: set extended mode * 4: nop * * ch->ccw[0..5] are initialized in ch_action_start because * the channel's direction is yet unknown here. */ ch->ccw[6].cmd_code = CCW_CMD_SET_EXTENDED; ch->ccw[6].flags = CCW_FLAG_SLI; ch->ccw[6].count = 0; ch->ccw[6].cda = 0; ch->ccw[7].cmd_code = CCW_CMD_NOOP; ch->ccw[7].flags = CCW_FLAG_SLI; ch->ccw[7].count = 0; ch->ccw[7].cda = 0; ch->irq = irq; ch->devno = devno; ch->type = type; sprintf(name, "ch-%04x", devno); ch->fsm = init_fsm(name, ch_state_names,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -