📄 ctcmain.c
字号:
{ struct channel *ch = (struct channel *) arg; struct channel *ch2; struct net_device *dev = ch->netdev; DBF_TEXT(trace, 3, __FUNCTION__); fsm_deltimer(&ch->timer); ctc_pr_debug("%s: Got remote disconnect, re-initializing ...\n", dev->name); /** * Notify device statemachine */ fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXDOWN, dev); fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); fsm_newstate(fi, CH_STATE_DTERM); ch2 = ((struct ctc_priv *) dev->priv)->channel[WRITE]; fsm_newstate(ch2->fsm, CH_STATE_DTERM); ccw_device_halt(ch->cdev, (unsigned long) ch); ccw_device_halt(ch2->cdev, (unsigned long) ch2);}/** * 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 voidch_action_txiniterr(fsm_instance * fi, int event, void *arg){ struct channel *ch = (struct channel *) arg; struct net_device *dev = ch->netdev; DBF_TEXT(setup, 2, __FUNCTION__); if (event == CH_EVENT_TIMER) { fsm_deltimer(&ch->timer); ctc_pr_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(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); } } else ctc_pr_warn("%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 voidch_action_txretry(fsm_instance * fi, int event, void *arg){ struct channel *ch = (struct channel *) arg; struct net_device *dev = ch->netdev; unsigned long saveflags; DBF_TEXT(trace, 4, __FUNCTION__); fsm_deltimer(&ch->timer); if (ch->retry++ > 3) { ctc_pr_debug("%s: TX retry failed, restarting channel\n", dev->name); fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); ch_action_restart(fi, event, arg); } else { struct sk_buff *skb; ctc_pr_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], skb->data)) { ctc_pr_debug( "%s: IDAL alloc failed, chan restart\n", dev->name); fsm_event(((struct 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); saveflags = 0; /* avoids compiler warning with spin_unlock_irqrestore */ if (event == CH_EVENT_TIMER) // only for TIMER not yet locked spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); rc = ccw_device_start(ch->cdev, &ch->ccw[3], (unsigned long) ch, 0xff, 0); if (event == CH_EVENT_TIMER) spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); if (rc != 0) { fsm_deltimer(&ch->timer); ccw_check_return_code(ch, rc, "TX in ch_action_txretry"); 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 voidch_action_iofatal(fsm_instance * fi, int event, void *arg){ struct channel *ch = (struct channel *) arg; struct net_device *dev = ch->netdev; DBF_TEXT(trace, 3, __FUNCTION__); fsm_deltimer(&ch->timer); if (CHANNEL_DIRECTION(ch->flags) == READ) { ctc_pr_debug("%s: RX I/O error\n", dev->name); fsm_newstate(fi, CH_STATE_RXERR); fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXDOWN, dev); } else { ctc_pr_debug("%s: TX I/O error\n", dev->name); fsm_newstate(fi, CH_STATE_TXERR); fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); }}static void ch_action_reinit(fsm_instance *fi, int event, void *arg){ struct channel *ch = (struct channel *)arg; struct net_device *dev = ch->netdev; struct ctc_priv *privptr = dev->priv; DBF_TEXT(trace, 4, __FUNCTION__); ch_action_iofatal(fi, event, arg); fsm_addtimer(&privptr->restart_timer, 1000, DEV_EVENT_RESTART, 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_reinit }, {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_reinit }, {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_reinit }, {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_reinit }, {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_reinit }, {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_reinit }, {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_reinit }, {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. *****************************************************************************/static inline intless_than(char *id1, char *id2){ int dev1, dev2, i; for (i = 0; i < 5; i++) { id1++; id2++; } dev1 = simple_strtoul(id1, &id1, 16); dev2 = simple_strtoul(id2, &id2, 16); return (dev1 < dev2);}/** * Add a new channel to the list of channels. * Keeps the channel list sorted. * * @param cdev The ccw_device to be added. * @param type The type class of the new channel. * * @return 0 on success, !0 on error. */static intadd_channel(struct ccw_device *cdev, enum channel_types type){ struct channel **c = &channels; struct channel *ch; DBF_TEXT(trace, 2, __FUNCTION__); if ((ch = (struct channel *) kmalloc(sizeof (struct channel), GFP_KERNEL)) == NULL) { ctc_pr_warn("ctc: Out of memory in add_channel\n"); return -1; } memset(ch, 0, sizeof (struct channel)); if ((ch->ccw = (struct ccw1 *) kmalloc(8*sizeof(struct ccw1), GFP_KERNEL | GFP_DMA)) == NULL) { kfree(ch); ctc_pr_warn("ctc: Out of memory in add_channel\n"); return -1; } memset(ch->ccw, 0, 8*sizeof(struct ccw1)); // assure all flags and counters are reset /** * "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): * 6: set extended mode * 7: 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[7].cmd_code = CCW_CMD_NOOP; ch->ccw[7].flags = CCW_FLAG_SLI; ch->cdev = cdev; snprintf(ch->id, CTC_ID_SIZE, "ch-%s", cdev->dev.bus_id); ch->type = type; ch->fsm = init_fsm(ch->id, ch_state_names, ch_event_names, NR_CH_STATES, NR_CH_EVENTS, ch_fsm, CH_FSM_LEN, GFP_KERNEL); if (ch->fsm == NULL) { ctc_pr_warn("ctc: Could not create FSM in add_channel\n"); kfree(ch->ccw); kfree(ch); return -1; } fsm_newstate(ch->fsm, CH_STATE_IDLE); if ((ch->irb = (struct irb *) kmalloc(sizeof (struct irb), GFP_KERNEL)) == NULL) { ctc_pr_warn("ctc: Out of memory in add_channel\n"); kfree_fsm(ch->fsm); kfree(ch->ccw); kfree(ch); return -1; } memset(ch->irb, 0, sizeof (struct irb)); while (*c && less_than((*c)->id, ch->id)) c = &(*c)->next; if (*c && (!strncmp((*c)->id, ch->id, CTC_ID_SIZE))) { ctc_pr_debug( "ctc: add_channel: device %s already in list, " "using old entry\n", (*c)->id); kfree(ch->irb); kfree_fsm(ch->fsm); kfree(ch->ccw); kfree(ch); return 0; } fsm_settimer(ch->fsm, &ch->timer); skb_queue_head_init(&ch->io_queue); skb_queue_head_init(&ch->collect_queue); ch->next = *c; *c = ch; return 0;}/** * Release a specific channel in the channel list. * * @param ch Pointer to channel struct to be released. */static voidchannel_free(struct channel *ch){ ch->flags &= ~CHANNEL_FLAGS_INUSE; fsm_newstate(ch->fsm, CH_STATE_IDLE);}/** * Remove a specific channel in the channel list. * * @param ch Pointer to channel struct to be released. */static voidchannel_remove(struct channel *ch){ struct channel **c = &channels; DBF_TEXT(trace, 2, __FUNCTION__); if (ch == NULL) return; channel_free(ch); while (*c) { if (*c == ch) { *c = ch->next; fsm_deltimer(&ch->timer); kfree_fsm(ch->fsm); clear_normalized_cda(&ch->ccw[4]); if (ch->trans_skb != NULL) { clear_normalized_cda(&ch->ccw[1]); dev_kfree_skb(ch->trans_skb); } kfree(ch->ccw); kfree(ch->irb); kfree(ch); return; } c = &((*c)->next); }}/** * Get a specific channel from the channel list. * * @param type Type of channel we are interested in. * @param id Id of channel we are interested in. * @param direction Direction we want to use this channel for. * * @return Pointer to a channel or NULL if no matching channel available. */static struct channel*channel_get(enum channel_types type, char *id, int direction){ struct channel *ch = channels; DBF_TEXT(trace, 3, __FUNCTION__);#ifdef DEBUG ctc_pr_debug("ctc: %s(): searching for ch with id %s and type %d\n", __func__, id, type);#endif while (ch && ((strncmp(ch->id, id, CTC_ID_SIZE)) || (ch->type != type))) {#ifdef DEBUG ctc_pr_debug("ctc: %s(): ch=0x%p (id=%s, type=%d\n", __func__, ch, ch->id, ch->type);#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -