📄 lcs.c
字号:
/** * function called by net device to handle multicast address relevant things */static inline voidlcs_remove_mc_addresses(struct lcs_card *card, struct in_device *in4_dev){ struct ip_mc_list *im4; struct list_head *l; struct lcs_ipm_list *ipm; unsigned long flags; char buf[MAX_ADDR_LEN]; LCS_DBF_TEXT(4, trace, "remmclst"); spin_lock_irqsave(&card->ipm_lock, flags); list_for_each(l, &card->ipm_list) { ipm = list_entry(l, struct lcs_ipm_list, list); for (im4 = in4_dev->mc_list; im4 != NULL; im4 = im4->next) { lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev); if ( (ipm->ipm.ip_addr == im4->multiaddr) && (memcmp(buf, &ipm->ipm.mac_addr, LCS_MAC_LENGTH) == 0) ) break; } if (im4 == NULL) ipm->ipm_state = LCS_IPM_STATE_DEL_REQUIRED; } spin_unlock_irqrestore(&card->ipm_lock, flags);}static inline struct lcs_ipm_list *lcs_check_addr_entry(struct lcs_card *card, struct ip_mc_list *im4, char *buf){ struct lcs_ipm_list *tmp, *ipm = NULL; struct list_head *l; unsigned long flags; LCS_DBF_TEXT(4, trace, "chkmcent"); spin_lock_irqsave(&card->ipm_lock, flags); list_for_each(l, &card->ipm_list) { tmp = list_entry(l, struct lcs_ipm_list, list); if ( (tmp->ipm.ip_addr == im4->multiaddr) && (memcmp(buf, &tmp->ipm.mac_addr, LCS_MAC_LENGTH) == 0) ) { ipm = tmp; break; } } spin_unlock_irqrestore(&card->ipm_lock, flags); return ipm;}static inline voidlcs_set_mc_addresses(struct lcs_card *card, struct in_device *in4_dev){ struct ip_mc_list *im4; struct lcs_ipm_list *ipm; char buf[MAX_ADDR_LEN]; unsigned long flags; LCS_DBF_TEXT(4, trace, "setmclst"); for (im4 = in4_dev->mc_list; im4; im4 = im4->next) { lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev); ipm = lcs_check_addr_entry(card, im4, buf); if (ipm != NULL) continue; /* Address already in list. */ ipm = (struct lcs_ipm_list *) kmalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC); if (ipm == NULL) { PRINT_INFO("Not enough memory to add " "new multicast entry!\n"); break; } memset(ipm, 0, sizeof(struct lcs_ipm_list)); memcpy(&ipm->ipm.mac_addr, buf, LCS_MAC_LENGTH); ipm->ipm.ip_addr = im4->multiaddr; ipm->ipm_state = LCS_IPM_STATE_SET_REQUIRED; spin_lock_irqsave(&card->ipm_lock, flags); list_add(&ipm->list, &card->ipm_list); spin_unlock_irqrestore(&card->ipm_lock, flags); }}static intlcs_register_mc_addresses(void *data){ struct lcs_card *card; struct in_device *in4_dev; card = (struct lcs_card *) data; daemonize("regipm"); if (!lcs_do_run_thread(card, LCS_SET_MC_THREAD)) return 0; LCS_DBF_TEXT(4, trace, "regmulti"); in4_dev = in_dev_get(card->dev); if (in4_dev == NULL) goto out; read_lock(&in4_dev->mc_list_lock); lcs_remove_mc_addresses(card,in4_dev); lcs_set_mc_addresses(card, in4_dev); read_unlock(&in4_dev->mc_list_lock); in_dev_put(in4_dev); lcs_fix_multicast_list(card);out: lcs_clear_thread_running_bit(card, LCS_SET_MC_THREAD); return 0;}/** * function called by net device to * handle multicast address relevant things */static voidlcs_set_multicast_list(struct net_device *dev){ struct lcs_card *card; LCS_DBF_TEXT(4, trace, "setmulti"); card = (struct lcs_card *) dev->priv; if (!lcs_set_thread_start_bit(card, LCS_SET_MC_THREAD)) { schedule_work(&card->kernel_thread_starter); }}#endif /* CONFIG_IP_MULTICAST */static longlcs_check_irb_error(struct ccw_device *cdev, struct irb *irb){ if (!IS_ERR(irb)) return 0; switch (PTR_ERR(irb)) { case -EIO: PRINT_WARN("i/o-error on device %s\n", cdev->dev.bus_id); LCS_DBF_TEXT(2, trace, "ckirberr"); LCS_DBF_TEXT_(2, trace, " rc%d", -EIO); break; case -ETIMEDOUT: PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id); LCS_DBF_TEXT(2, trace, "ckirberr"); LCS_DBF_TEXT_(2, trace, " rc%d", -ETIMEDOUT); break; default: PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb), cdev->dev.bus_id); LCS_DBF_TEXT(2, trace, "ckirberr"); LCS_DBF_TEXT(2, trace, " rc???"); } return PTR_ERR(irb);}/** * IRQ Handler for LCS channels */static voidlcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb){ struct lcs_card *card; struct lcs_channel *channel; int index; if (lcs_check_irb_error(cdev, irb)) return; card = CARD_FROM_DEV(cdev); if (card->read.ccwdev == cdev) channel = &card->read; else channel = &card->write; LCS_DBF_TEXT_(5, trace, "Rint%s",cdev->dev.bus_id); LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.cstat, irb->scsw.dstat); LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.fctl, irb->scsw.actl); /* How far in the ccw chain have we processed? */ if ((channel->state != CH_STATE_INIT) && (irb->scsw.fctl & SCSW_FCTL_START_FUNC)) { index = (struct ccw1 *) __va((addr_t) irb->scsw.cpa) - channel->ccws; if ((irb->scsw.actl & SCSW_ACTL_SUSPENDED) || (irb->scsw.cstat | SCHN_STAT_PCI)) /* Bloody io subsystem tells us lies about cpa... */ index = (index - 1) & (LCS_NUM_BUFFS - 1); while (channel->io_idx != index) { __lcs_processed_buffer(channel, channel->iob + channel->io_idx); channel->io_idx = (channel->io_idx + 1) & (LCS_NUM_BUFFS - 1); } } if ((irb->scsw.dstat & DEV_STAT_DEV_END) || (irb->scsw.dstat & DEV_STAT_CHN_END) || (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)) /* Mark channel as stopped. */ channel->state = CH_STATE_STOPPED; else if (irb->scsw.actl & SCSW_ACTL_SUSPENDED) /* CCW execution stopped on a suspend bit. */ channel->state = CH_STATE_SUSPENDED; if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) { if (irb->scsw.cc != 0) { ccw_device_halt(channel->ccwdev, (addr_t) channel); return; } /* The channel has been stopped by halt_IO. */ channel->state = CH_STATE_HALTED; } if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) { channel->state = CH_STATE_CLEARED; } /* Do the rest in the tasklet. */ tasklet_schedule(&channel->irq_tasklet);}/** * Tasklet for IRQ handler */static voidlcs_tasklet(unsigned long data){ unsigned long flags; struct lcs_channel *channel; struct lcs_buffer *iob; int buf_idx; int rc; channel = (struct lcs_channel *) data; LCS_DBF_TEXT_(5, trace, "tlet%s",channel->ccwdev->dev.bus_id); /* Check for processed buffers. */ iob = channel->iob; buf_idx = channel->buf_idx; while (iob[buf_idx].state == BUF_STATE_PROCESSED) { /* Do the callback thing. */ if (iob[buf_idx].callback != NULL) iob[buf_idx].callback(channel, iob + buf_idx); buf_idx = (buf_idx + 1) & (LCS_NUM_BUFFS - 1); } channel->buf_idx = buf_idx; if (channel->state == CH_STATE_STOPPED) // FIXME: what if rc != 0 ?? rc = lcs_start_channel(channel); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); if (channel->state == CH_STATE_SUSPENDED && channel->iob[channel->io_idx].state == BUF_STATE_READY) { // FIXME: what if rc != 0 ?? rc = __lcs_resume_channel(channel); } spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); /* Something happened on the channel. Wake up waiters. */ wake_up(&channel->wait_q);}/** * Finish current tx buffer and make it ready for transmit. */static void__lcs_emit_txbuffer(struct lcs_card *card){ LCS_DBF_TEXT(5, trace, "emittx"); *(__u16 *)(card->tx_buffer->data + card->tx_buffer->count) = 0; card->tx_buffer->count += 2; lcs_ready_buffer(&card->write, card->tx_buffer); card->tx_buffer = NULL; card->tx_emitted++;}/** * Callback for finished tx buffers. */static voidlcs_txbuffer_cb(struct lcs_channel *channel, struct lcs_buffer *buffer){ struct lcs_card *card; LCS_DBF_TEXT(5, trace, "txbuffcb"); /* Put buffer back to pool. */ lcs_release_buffer(channel, buffer); card = (struct lcs_card *) ((char *) channel - offsetof(struct lcs_card, write)); spin_lock(&card->lock); card->tx_emitted--; if (card->tx_emitted <= 0 && card->tx_buffer != NULL) /* * Last running tx buffer has finished. Submit partially * filled current buffer. */ __lcs_emit_txbuffer(card); spin_unlock(&card->lock);}/** * Packet transmit function called by network stack */static int__lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb, struct net_device *dev){ struct lcs_header *header; LCS_DBF_TEXT(5, trace, "hardxmit"); if (skb == NULL) { card->stats.tx_dropped++; card->stats.tx_errors++; return -EIO; } if (card->state != DEV_STATE_UP) { dev_kfree_skb(skb); card->stats.tx_dropped++; card->stats.tx_errors++; card->stats.tx_carrier_errors++; return 0; } if (netif_queue_stopped(dev) ) { card->stats.tx_dropped++; return -EBUSY; } if (card->tx_buffer != NULL && card->tx_buffer->count + sizeof(struct lcs_header) + skb->len + sizeof(u16) > LCS_IOBUFFERSIZE) /* skb too big for current tx buffer. */ __lcs_emit_txbuffer(card); if (card->tx_buffer == NULL) { /* Get new tx buffer */ card->tx_buffer = lcs_get_buffer(&card->write); if (card->tx_buffer == NULL) { card->stats.tx_dropped++; return -EBUSY; } card->tx_buffer->callback = lcs_txbuffer_cb; card->tx_buffer->count = 0; } header = (struct lcs_header *) (card->tx_buffer->data + card->tx_buffer->count); card->tx_buffer->count += skb->len + sizeof(struct lcs_header); header->offset = card->tx_buffer->count; header->type = card->lan_type; header->slot = card->portno; memcpy(header + 1, skb->data, skb->len); card->stats.tx_bytes += skb->len; card->stats.tx_packets++; dev_kfree_skb(skb); if (card->tx_emitted <= 0) /* If this is the first tx buffer emit it immediately. */ __lcs_emit_txbuffer(card); return 0;}static intlcs_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct lcs_card *card; int rc; LCS_DBF_TEXT(5, trace, "pktxmit"); card = (struct lcs_card *) dev->priv; spin_lock(&card->lock); rc = __lcs_start_xmit(card, skb, dev); spin_unlock(&card->lock); return rc;}/** * send startlan and lanstat command to make LCS device ready */static intlcs_startlan_auto(struct lcs_card *card){ int rc; LCS_DBF_TEXT(2, trace, "strtauto");#ifdef CONFIG_NET_ETHERNET card->lan_type = LCS_FRAME_TYPE_ENET; rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP); if (rc == 0) return 0;#endif#ifdef CONFIG_TR card->lan_type = LCS_FRAME_TYPE_TR; rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP); if (rc == 0) return 0;#endif#ifdef CONFIG_FDDI card->lan_type = LCS_FRAME_TYPE_FDDI; rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP); if (rc == 0) return 0;#endif return -EIO;}static intlcs_startlan(struct lcs_card *card){ int rc, i; LCS_DBF_TEXT(2, trace, "startlan"); rc = 0; if (card->portno != LCS_INVALID_PORT_NO) { if (card->lan_type == LCS_FRAME_TYPE_AUTO) rc = lcs_startlan_auto(card); else rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP); } else { for (i = 0; i <= 16; i++) { card->portno = i; if (card->lan_type != LCS_FRAME_TYPE_AUTO) rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP); else /* autodetecting lan type */ rc = lcs_startlan_auto(card); if (rc == 0) break; } } if (rc == 0) return lcs_send_lanstat(card); return rc;}/** * LCS detect function * setup channels and make them I/O ready */static intlcs_detect(struct lcs_card *card){ int rc = 0; LCS_DBF_TEXT(2, setup, "lcsdetct"); /* start/reset card */ if (card->dev) netif_stop_queue(card->dev); rc = lcs_stop_channels(card); if (rc == 0) { rc = lcs_start_channels(card); if (rc == 0) { rc = lcs_send_startup(card, LCS_INITIATOR_TCPIP); if (rc == 0) rc = lcs_startlan(card); } } if (rc == 0) { card->state = DEV_STATE_UP; } else { card->state = DEV_STATE_DOWN; card->write.state = CH_STATE_INIT; card->read.state = CH_STATE_INIT; } return rc;}/** * reset card */static intlcs_resetcard(struct lcs_card *card){ int retries; LCS_DBF_TEXT(2, trace, "rescard"); for (retries = 0; retries < 10; retries++) { if (lcs_detect(card) == 0) { netif_wake_queue(card->dev); card->state = DEV_STATE_UP; PRINT_INFO("LCS device %s successfully restarted!\n", card->dev->name); return 0; } msleep(3000); } PRINT_ERR("Error in Reseting LCS card!\n"); return -EIO;}/** * LCS Stop card */static intlcs_stopcard(struct lcs_card *card){ int rc; LCS_DBF_TEXT(3, setup, "stopcard"); if (card->read.state != CH_STATE_STOPPED && card->write.state != CH_STATE_STOPPED && card->state == DEV_STATE_UP) { lcs_clear_multicast_list(card); rc = lcs_send_stoplan(card,LCS_INITIATOR_TCPIP); rc = lcs_send_shutdown(card); } rc = lcs_stop_channels(card); card->state = DEV_STATE_DOWN; return rc;}/** * LGW initiated commands */static intlcs_lgw_startlan_thread(void *data){ struct lcs_card *card; card = (struct lcs_card *) data; daemonize("lgwstpln"); if (!lcs_do_run_thread(card, LCS_STARTLAN_THREAD)) return 0; LCS_DBF_TEXT(4, trace, "lgwstpln"); if (card->dev) netif_stop_queue(card->dev); if (lcs_startlan(card) == 0) { netif_wake_queue(card->dev); card->state = DEV_STATE_UP; PRINT_INFO("LCS Startlan for device %s succeeded!\n", card->dev->name); } else PRINT_ERR("LCS Startlan for device %s failed!\n", card->dev->name); lcs_clear_thread_running_bit(card, LCS_STARTLAN_THREAD); return 0;}/** * Send startup command initiated by Lan Gateway */static intlcs_lgw_startup_thread(void *data){ int rc; struct lcs_card *card; card = (struct lcs_card *) data; daemonize("lgwstaln"); if (!lcs_do_run_thread(card, LCS_STARTUP_THREAD)) return 0; LCS_DBF_TEXT(4, trace, "lgwstaln"); if (card->dev) netif_stop_queue(card->dev); rc = lcs_send_startup(card, LCS_INITIATOR_LGW); if (rc != 0) { PRINT_ERR("Startup for LCS device %s initiated " \ "by LGW failed!\nReseting card ...\n", card->dev->name); /* do a card reset */ rc = lcs_resetcard(card); if (rc == 0) goto Done; } rc = lcs_startlan(card); if (rc == 0) { netif_wake_queue(card->dev); card->state = DEV_STATE_UP; }Done: if (rc == 0) PRINT_INFO("LCS Startup for device %s succeeded!\n", card->dev->name); else PRINT_ERR("LCS Startup for device %s failed!\n", card->dev->name); lcs_clear_thread_running_bit(card, LCS_STARTUP_THREAD); return 0;}/** * send stoplan command initiated by Lan Gateway */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -