📄 ctcmain.c
字号:
ch_event_names, NR_CH_STATES, NR_CH_EVENTS, ch_fsm, CH_FSM_LEN, GFP_KERNEL); if (ch->fsm == NULL) { printk(KERN_WARNING "ctc: Could not create FSM in add_channel\n"); kfree(ch); return -1; } fsm_newstate(ch->fsm, CH_STATE_IDLE); if ((ch->devstat = (devstat_t*)kmalloc(sizeof(devstat_t), GFP_KERNEL)) == NULL) { printk(KERN_WARNING "ctc: Out of memory in add_channel\n"); kfree_fsm(ch->fsm); kfree(ch); return -1; } memset(ch->devstat, 0, sizeof(devstat_t)); while (*c && ((*c)->devno < devno)) c = &(*c)->next; if ((*c)->devno == devno) { printk(KERN_DEBUG "ctc: add_channel: device %04x already in list, " "using old entry\n", (*c)->devno); kfree(ch->devstat); kfree_fsm(ch->fsm); 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;}#ifndef CTC_CHANDEV/** * scan for all channels and create an entry in the channels list * for every supported channel. */static void channel_scan(void){ static int print_result = 1; int irq; int nr_escon = 0; int nr_ctca = 0; s390_dev_info_t di; for (irq = 0; irq < NR_IRQS; irq++) { if (get_dev_info_by_irq(irq, &di) == 0) { if ((di.status == DEVSTAT_NOT_OPER) || (di.status == DEVSTAT_DEVICE_OWNED)) continue; switch (channel_type(&di.sid_data)) { case channel_type_ctca: /* CTC/A */ if (!add_channel(irq, di.devno, channel_type_ctca)) nr_ctca++; break; case channel_type_escon: /* ESCON */ if (!add_channel(irq, di.devno, channel_type_escon)) nr_escon++; break; default: } } } if (print_result) { if (nr_escon + nr_ctca) printk(KERN_INFO "ctc: %d CTC/A channel%s and %d ESCON " "channel%s found.\n", nr_ctca, (nr_ctca == 1) ? "s" : "", nr_escon, (nr_escon == 1) ? "s" : ""); else printk(KERN_INFO "ctc: No channel devices found.\n"); } print_result = 0;}#endif/** * Release a specific channel in the channel list. * * @param ch Pointer to channel struct to be released. */static void channel_free(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 void channel_remove(channel *ch){ channel **c = &channels; if (ch == NULL) return;#ifndef CTC_CHANDEV if (ch->flags & CHANNEL_FLAGS_INUSE) FREE_IRQ(ch->irq, ch->devstat);#endif 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); return; } c = &((*c)->next); }}/** * Get a specific channel from the channel list. * * @param type Type of channel we are interested in. * @param devno Device number 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 channel *channel_get(channel_type_t type, int devno, int direction){ channel *ch = channels;#ifdef DEBUG printk(KERN_DEBUG "ctc: %s(): searching for ch with devno %d and type %d\n", __FUNCTION__, devno, type);#endif while (ch && ((ch->devno != devno) || (ch->type != type))) {#ifdef DEBUG printk(KERN_DEBUG "ctc: %s(): ch=0x%p (devno=%d, type=%d\n", __FUNCTION__, ch, ch->devno, ch->type);#endif ch = ch->next; }#ifdef DEBUG printk(KERN_DEBUG "ctc: %s(): ch=0x%pq (devno=%d, type=%d\n", __FUNCTION__, ch, ch->devno, ch->type);#endif if (!ch) { printk(KERN_WARNING "ctc: %s(): channel with devno %d " "and type %d not found in channel list\n", __FUNCTION__, devno, 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;}#ifndef CTC_CHANDEV/** * Get the next free channel from the channel list * * @param type Type 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 channel *channel_get_next(channel_type_t type, int direction){ channel *ch = channels; while (ch && (ch->type != type || (ch->flags & CHANNEL_FLAGS_INUSE))) ch = ch->next; if (ch) { 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;}#endif/** * Return the channel type by name. * * @param name Name of network interface. * * @return Type class of channel to be used for that interface. */static channel_type_t inline extract_channel_media(char *name){ channel_type_t ret = channel_type_unknown; if (name != NULL) { if (strncmp(name, "ctc", 3) == 0) ret = channel_type_ctca; if (strncmp(name, "escon", 5) == 0) ret = channel_type_escon; } return ret;}/** * Find a channel in the list by its IRQ. * * @param irq IRQ to search for. * * @return Pointer to channel or NULL if no matching channel found. */static channel *find_channel_by_irq(int irq){ channel *ch = channels; while (ch && (ch->irq != irq)) ch = ch->next; return ch;}/** * Main IRQ handler. * * @param irq The IRQ to handle. * @param intparm IRQ params. * @param regs CPU registers. */static void ctc_irq_handler (int irq, void *intparm, struct pt_regs *regs){ devstat_t *devstat = (devstat_t *)intparm; channel *ch = (channel *)devstat->intparm; net_device *dev; /** * Check for unsolicited interrupts. * If intparm is NULL, then loop over all our known * channels and try matching the irq number. */ if (ch == NULL) { if ((ch = find_channel_by_irq(irq)) == NULL) { printk(KERN_WARNING "ctc: Got unsolicited irq: %04x c-%02x d-%02x" "f-%02x\n", devstat->devno, devstat->cstat, devstat->dstat, devstat->flag); goto done; } } dev = (net_device *)(ch->netdev); if (dev == NULL) { printk(KERN_CRIT "ctc: ctc_irq_handler dev = NULL irq=%d, ch=0x%p\n", irq, ch); goto done; } if (intparm == NULL) printk(KERN_DEBUG "%s: Channel %04x found by IRQ %d\n", dev->name, ch->devno, irq);#ifdef DEBUG printk(KERN_DEBUG "%s: interrupt for device: %04x received c-%02x d-%02x " "f-%02x\n", dev->name, devstat->devno, devstat->cstat, devstat->dstat, devstat->flag);#endif /* Check for good subchannel return code, otherwise error message */ if (devstat->cstat) { fsm_event(ch->fsm, CH_EVENT_SC_UNKNOWN, ch); printk(KERN_WARNING "%s: subchannel check for device: %04x - %02x %02x " "%02x\n", dev->name, ch->devno, devstat->cstat, devstat->dstat, devstat->flag); goto done; } /* Check the reason-code of a unit check */ if (devstat->dstat & DEV_STAT_UNIT_CHECK) { ccw_unit_check(ch, devstat->ii.sense.data[0]); goto done; } if (devstat->dstat & DEV_STAT_BUSY) { if (devstat->dstat & DEV_STAT_ATTENTION) fsm_event(ch->fsm, CH_EVENT_ATTNBUSY, ch); else fsm_event(ch->fsm, CH_EVENT_BUSY, ch); goto done; } if (devstat->dstat & DEV_STAT_ATTENTION) { fsm_event(ch->fsm, CH_EVENT_ATTN, ch); goto done; } if (devstat->flag & DEVSTAT_FINAL_STATUS) fsm_event(ch->fsm, CH_EVENT_FINSTAT, ch); else fsm_event(ch->fsm, CH_EVENT_IRQ, ch); done:}/** * 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 net_device * upon call. */static void dev_action_start(fsm_instance *fi, int event, void *arg){ net_device *dev = (net_device *)arg; ctc_priv *privptr = dev->priv; int direction; fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX); for (direction = READ; direction <= WRITE; direction++) { 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 net_device * upon call. */static void dev_action_stop(fsm_instance *fi, int event, void *arg){ net_device *dev = (net_device *)arg; ctc_priv *privptr = dev->priv; int direction; fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX); for (direction = READ; direction <= WRITE; direction++) { channel *ch = privptr->channel[direction]; fsm_event(ch->fsm, CH_EVENT_STOP, ch); }}/** * 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 net_device * upon call. */static void dev_action_chup(fsm_instance *fi, int event, void *arg){ net_device *dev = (net_device *)arg; ctc_priv *privptr = dev->priv; 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); printk(KERN_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); printk(KERN_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 net_device * upon call. */static void dev_action_chdown(fsm_instance *fi, int event, void *arg){ net_device *dev = (net_device *)arg; ctc_priv *privptr = dev->priv; 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_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_TX, DEV_EVENT_START, dev_action_start }, { DEV_STATE_STOPWAIT_TX, DEV_EVENT_RXUP, dev_action_chup }, { DEV_STATE_STOPWA
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -