📄 ctcmain.c
字号:
ch = ch->next; }#ifdef DEBUG ctc_pr_debug("ctc: %s(): ch=0x%pq (id=%s, type=%d\n", __func__, ch, ch->id, ch->type);#endif if (!ch) { ctc_pr_warn("ctc: %s(): channel with id %s " "and type %d not found in channel list\n", __func__, id, type); } else { if (ch->flags & CHANNEL_FLAGS_INUSE) ch = NULL; else { ch->flags |= CHANNEL_FLAGS_INUSE; ch->flags &= ~CHANNEL_FLAGS_RWMASK; ch->flags |= (direction == WRITE) ? CHANNEL_FLAGS_WRITE : CHANNEL_FLAGS_READ; fsm_newstate(ch->fsm, CH_STATE_STOPPED); } } return ch;}/** * Return the channel type by name. * * @param name Name of network interface. * * @return Type class of channel to be used for that interface. */static enum channel_types inlineextract_channel_media(char *name){ enum channel_types ret = channel_type_unknown; if (name != NULL) { if (strncmp(name, "ctc", 3) == 0) ret = channel_type_parallel; if (strncmp(name, "escon", 5) == 0) ret = channel_type_escon; } return ret;}static long__ctc_check_irb_error(struct ccw_device *cdev, struct irb *irb){ if (!IS_ERR(irb)) return 0; switch (PTR_ERR(irb)) { case -EIO: ctc_pr_warn("i/o-error on device %s\n", cdev->dev.bus_id);// CTC_DBF_TEXT(trace, 2, "ckirberr");// CTC_DBF_TEXT_(trace, 2, " rc%d", -EIO); break; case -ETIMEDOUT: ctc_pr_warn("timeout on device %s\n", cdev->dev.bus_id);// CTC_DBF_TEXT(trace, 2, "ckirberr");// CTC_DBF_TEXT_(trace, 2, " rc%d", -ETIMEDOUT); break; default: ctc_pr_warn("unknown error %ld on device %s\n", PTR_ERR(irb), cdev->dev.bus_id);// CTC_DBF_TEXT(trace, 2, "ckirberr");// CTC_DBF_TEXT(trace, 2, " rc???"); } return PTR_ERR(irb);}/** * Main IRQ handler. * * @param cdev The ccw_device the interrupt is for. * @param intparm interruption parameter. * @param irb interruption response block. */static voidctc_irq_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb){ struct channel *ch; struct net_device *dev; struct ctc_priv *priv; DBF_TEXT(trace, 5, __FUNCTION__); if (__ctc_check_irb_error(cdev, irb)) return; /* Check for unsolicited interrupts. */ if (!cdev->dev.driver_data) { ctc_pr_warn("ctc: Got unsolicited irq: %s c-%02x d-%02x\n", cdev->dev.bus_id, irb->scsw.cstat, irb->scsw.dstat); return; } priv = ((struct ccwgroup_device *)cdev->dev.driver_data) ->dev.driver_data; /* Try to extract channel from driver data. */ if (priv->channel[READ]->cdev == cdev) ch = priv->channel[READ]; else if (priv->channel[WRITE]->cdev == cdev) ch = priv->channel[WRITE]; else { ctc_pr_err("ctc: Can't determine channel for interrupt, " "device %s\n", cdev->dev.bus_id); return; } dev = (struct net_device *) (ch->netdev); if (dev == NULL) { ctc_pr_crit("ctc: ctc_irq_handler dev=NULL bus_id=%s, ch=0x%p\n", cdev->dev.bus_id, ch); return; }#ifdef DEBUG ctc_pr_debug("%s: interrupt for device: %s received c-%02x d-%02x\n", dev->name, ch->id, irb->scsw.cstat, irb->scsw.dstat);#endif /* Copy interruption response block. */ memcpy(ch->irb, irb, sizeof(struct irb)); /* Check for good subchannel return code, otherwise error message */ if (ch->irb->scsw.cstat) { fsm_event(ch->fsm, CH_EVENT_SC_UNKNOWN, ch); ctc_pr_warn("%s: subchannel check for device: %s - %02x %02x\n", dev->name, ch->id, ch->irb->scsw.cstat, ch->irb->scsw.dstat); return; } /* Check the reason-code of a unit check */ if (ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) { ccw_unit_check(ch, ch->irb->ecw[0]); return; } if (ch->irb->scsw.dstat & DEV_STAT_BUSY) { if (ch->irb->scsw.dstat & DEV_STAT_ATTENTION) fsm_event(ch->fsm, CH_EVENT_ATTNBUSY, ch); else fsm_event(ch->fsm, CH_EVENT_BUSY, ch); return; } if (ch->irb->scsw.dstat & DEV_STAT_ATTENTION) { fsm_event(ch->fsm, CH_EVENT_ATTN, ch); return; } if ((ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) || (ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) || (ch->irb->scsw.stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))) fsm_event(ch->fsm, CH_EVENT_FINSTAT, ch); else fsm_event(ch->fsm, CH_EVENT_IRQ, ch);}/** * Actions for interface - statemachine. *****************************************************************************//** * Startup channels by sending CH_EVENT_START to each channel. * * @param fi An instance of an interface statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from struct net_device * upon call. */static voiddev_action_start(fsm_instance * fi, int event, void *arg){ struct net_device *dev = (struct net_device *) arg; struct ctc_priv *privptr = dev->priv; int direction; DBF_TEXT(setup, 3, __FUNCTION__); fsm_deltimer(&privptr->restart_timer); fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX); for (direction = READ; direction <= WRITE; direction++) { struct channel *ch = privptr->channel[direction]; fsm_event(ch->fsm, CH_EVENT_START, ch); }}/** * Shutdown channels by sending CH_EVENT_STOP to each channel. * * @param fi An instance of an interface statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from struct net_device * upon call. */static voiddev_action_stop(fsm_instance * fi, int event, void *arg){ struct net_device *dev = (struct net_device *) arg; struct ctc_priv *privptr = dev->priv; int direction; DBF_TEXT(trace, 3, __FUNCTION__); fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX); for (direction = READ; direction <= WRITE; direction++) { struct channel *ch = privptr->channel[direction]; fsm_event(ch->fsm, CH_EVENT_STOP, ch); }}static void dev_action_restart(fsm_instance *fi, int event, void *arg){ struct net_device *dev = (struct net_device *)arg; struct ctc_priv *privptr = dev->priv; DBF_TEXT(trace, 3, __FUNCTION__); ctc_pr_debug("%s: Restarting\n", dev->name); dev_action_stop(fi, event, arg); fsm_event(privptr->fsm, DEV_EVENT_STOP, dev); fsm_addtimer(&privptr->restart_timer, CTC_TIMEOUT_5SEC, DEV_EVENT_START, dev);}/** * Called from channel statemachine * when a channel is up and running. * * @param fi An instance of an interface statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from struct net_device * upon call. */static voiddev_action_chup(fsm_instance * fi, int event, void *arg){ struct net_device *dev = (struct net_device *) arg; struct ctc_priv *privptr = dev->priv; DBF_TEXT(trace, 3, __FUNCTION__); switch (fsm_getstate(fi)) { case DEV_STATE_STARTWAIT_RXTX: if (event == DEV_EVENT_RXUP) fsm_newstate(fi, DEV_STATE_STARTWAIT_TX); else fsm_newstate(fi, DEV_STATE_STARTWAIT_RX); break; case DEV_STATE_STARTWAIT_RX: if (event == DEV_EVENT_RXUP) { fsm_newstate(fi, DEV_STATE_RUNNING); ctc_pr_info("%s: connected with remote side\n", dev->name); if (privptr->protocol == CTC_PROTO_LINUX_TTY) ctc_tty_setcarrier(dev, 1); ctc_clear_busy(dev); } break; case DEV_STATE_STARTWAIT_TX: if (event == DEV_EVENT_TXUP) { fsm_newstate(fi, DEV_STATE_RUNNING); ctc_pr_info("%s: connected with remote side\n", dev->name); if (privptr->protocol == CTC_PROTO_LINUX_TTY) ctc_tty_setcarrier(dev, 1); ctc_clear_busy(dev); } break; case DEV_STATE_STOPWAIT_TX: if (event == DEV_EVENT_RXUP) fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX); break; case DEV_STATE_STOPWAIT_RX: if (event == DEV_EVENT_TXUP) fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX); break; }}/** * Called from channel statemachine * when a channel has been shutdown. * * @param fi An instance of an interface statemachine. * @param event The event, just happened. * @param arg Generic pointer, casted from struct net_device * upon call. */static voiddev_action_chdown(fsm_instance * fi, int event, void *arg){ struct net_device *dev = (struct net_device *) arg; struct ctc_priv *privptr = dev->priv; DBF_TEXT(trace, 3, __FUNCTION__); switch (fsm_getstate(fi)) { case DEV_STATE_RUNNING: if (privptr->protocol == CTC_PROTO_LINUX_TTY) ctc_tty_setcarrier(dev, 0); if (event == DEV_EVENT_TXDOWN) fsm_newstate(fi, DEV_STATE_STARTWAIT_TX); else fsm_newstate(fi, DEV_STATE_STARTWAIT_RX); break; case DEV_STATE_STARTWAIT_RX: if (event == DEV_EVENT_TXDOWN) fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX); break; case DEV_STATE_STARTWAIT_TX: if (event == DEV_EVENT_RXDOWN) fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX); break; case DEV_STATE_STOPWAIT_RXTX: if (event == DEV_EVENT_TXDOWN) fsm_newstate(fi, DEV_STATE_STOPWAIT_RX); else fsm_newstate(fi, DEV_STATE_STOPWAIT_TX); break; case DEV_STATE_STOPWAIT_RX: if (event == DEV_EVENT_RXDOWN) fsm_newstate(fi, DEV_STATE_STOPPED); break; case DEV_STATE_STOPWAIT_TX: if (event == DEV_EVENT_TXDOWN) fsm_newstate(fi, DEV_STATE_STOPPED); break; }}static const fsm_node dev_fsm[] = { {DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start}, {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_START, dev_action_start }, {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown }, {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown }, {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart }, {DEV_STATE_STOPWAIT_RX, DEV_EVENT_START, dev_action_start }, {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXUP, dev_action_chup }, {DEV_STATE_STOPWAIT_RX, DEV_EVENT_TXUP, dev_action_chup }, {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXDOWN, dev_action_chdown }, {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RESTART, dev_action_restart }, {DEV_STATE_STOPWAIT_TX, DEV_EVENT_START, dev_action_start }, {DEV_STATE_STOPWAIT_TX, DEV_EVENT_RXUP, dev_action_chup }, {DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXUP, dev_action_chup }, {DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXDOWN, dev_action_chdown }, {DEV_STATE_STOPWAIT_TX, DEV_EVENT_RESTART, dev_action_restart }, {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP, dev_action_stop }, {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP, dev_action_chup }, {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP, dev_action_chup }, {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown }, {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown }, {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart }, {DEV_STATE_STARTWAIT_TX, DEV_EVENT_STOP, dev_action_stop }, {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXUP, dev_action_chup }, {DEV_STATE_STARTWAIT_TX, DEV_EVENT_TXUP, dev_action_chup }, {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXDOWN, dev_action_chdown }, {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RESTART, dev_action_restart }, {DEV_STATE_STARTWAIT_RX, DEV_EVENT_STOP, dev_action_stop }, {DEV_STATE_STARTWAIT_RX, DEV_EVENT_RXUP, dev_action_chup }, {DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXUP, dev_action_chup }, {DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXDOWN, dev_action_chdown }, {DEV_STATE_STARTWAIT_RX, DEV_EVENT_RESTART, dev_action_restart }, {DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop }, {DEV_STATE_RUNNING, DEV_EVENT_RXDOWN, dev_action_chdown }, {DEV_STATE_RUNNING, DEV_EVENT_TXDOWN, dev_action_chdown }, {DEV_STATE_RUNNING, DEV_EVENT_TXUP, fsm_action_nop }, {DEV_STATE_RUNNING, DEV_EVENT_RXUP, fsm_action_nop }, {DEV_STATE_RUNNING, DEV_EVENT_RESTART, dev_action_restart },};static const int DEV_FSM_LEN = sizeof (dev_fsm) / sizeof (fsm_node);/** * Transmit a packet. * This is a helper function for ctc_tx(). * * @param ch Channel to be used for sending. * @param skb Pointer to struct sk_buff of packet to send. * The linklevel header has already been set up * by ctc_tx(). * * @return 0 on success, -ERRNO on failure. (Never fails.) */static inttransmit_skb(struct channel *ch, struct sk_buff *skb){ unsigned long saveflags; struct ll_header header; int rc = 0; DBF_TEXT(trace, 5, __FUNCTION__); /* we need to acquire the lock for testing the state * otherwise we can have an IRQ changing the state to * TXIDLE after the test but before acquiring the lock. */ spin_lock_irqsave(&ch->collect_lock, saveflags); if (fsm_getstate(ch->fsm) != CH_STATE_TXIDLE) { int l = skb->len + LL_HEADER_LENGTH; if (ch->collect_len + l > ch->max_bufsize - 2) { spin_unlock_irqrestore(&ch->collect_lock, saveflags); return -EBUSY; } else { atomic_inc(&skb->users); header.length = l; header.type = skb->protocol; header.unused = 0; memcpy(skb_push(skb, LL_HEADER_LENGTH), &header, LL_HEADER_LENGTH); skb_queue_tail(&ch->collect_queue, skb); ch->collect_len += l; } spin_unlock_irqrestore(&ch->collect_lock, saveflags); } else { __u16 block_len; int ccw_idx; struct sk_buff *nskb; unsigned long hi; spin_unlock_irqrestore(&ch->collect_lock, saveflags); /** * Protect skb against beeing free'd by upper * layers. */ atomic_inc(&skb->users); ch->prof.txlen += skb->len; header.length = skb->len + LL_HEADER_LENGTH; header.type = skb->protocol; header.unused = 0; memcpy(skb_push(skb, LL_HEADER_LENGTH), &header, LL_HEADER_LENGTH); block_len = skb->len + 2; *((__u16 *) skb_push(skb, 2)) = block_len; /** * IDAL support in CTC is broken, so we have to * care about skb's above 2G ourselves. */ hi = ((unsigned long) skb->tail + LL_HEADER_LENGTH) >> 31; if (hi) { nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); if (!nskb) { atomic_dec(&skb->users); skb_pull(skb, LL_HEADER_LENGTH + 2); ctc_clear_busy(ch->netdev); return -ENOMEM; } else { memcpy(skb_put(nskb, skb->len), skb->data, skb->len); atomic_inc(&nskb->users); atomic_dec(&skb->users);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -