📄 lcs.c
字号:
/* * linux/drivers/s390/net/lcs.c * * Linux for S/390 Lan Channel Station Network Driver * * Copyright (C) 1999-2001 IBM Deutschland Entwicklung GmbH, * IBM Corporation * Author(s): Original Code written by * DJ Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) * Rewritten by * Frank Pavlic (fpavlic@de.ibm.com) and * Martin Schwidefsky <schwidefsky@de.ibm.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/module.h>#include <linux/if.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/trdevice.h>#include <linux/fddidevice.h>#include <linux/inetdevice.h>#include <linux/in.h>#include <linux/igmp.h>#include <linux/delay.h>#include <net/arp.h>#include <net/ip.h>#include <asm/debug.h>#include <asm/idals.h>#include <asm/timex.h>#include <linux/device.h>#include <asm/ccwgroup.h>#include "lcs.h"#include "cu3088.h"#if !defined(CONFIG_NET_ETHERNET) && \ !defined(CONFIG_TR) && !defined(CONFIG_FDDI)#error Cannot compile lcs.c without some net devices switched on.#endif/** * initialization string for output */static char version[] __initdata = "LCS driver";static char debug_buffer[255];/** * Some prototypes. */static void lcs_tasklet(unsigned long);static void lcs_start_kernel_thread(struct lcs_card *card);static void lcs_get_frames_cb(struct lcs_channel *, struct lcs_buffer *);static int lcs_send_delipm(struct lcs_card *, struct lcs_ipm_list *);static int lcs_recovery(void *ptr);/** * Debug Facility Stuff */static debug_info_t *lcs_dbf_setup;static debug_info_t *lcs_dbf_trace;/** * LCS Debug Facility functions */static voidlcs_unregister_debug_facility(void){ if (lcs_dbf_setup) debug_unregister(lcs_dbf_setup); if (lcs_dbf_trace) debug_unregister(lcs_dbf_trace);}static intlcs_register_debug_facility(void){ lcs_dbf_setup = debug_register("lcs_setup", 2, 1, 8); lcs_dbf_trace = debug_register("lcs_trace", 2, 2, 8); if (lcs_dbf_setup == NULL || lcs_dbf_trace == NULL) { PRINT_ERR("Not enough memory for debug facility.\n"); lcs_unregister_debug_facility(); return -ENOMEM; } debug_register_view(lcs_dbf_setup, &debug_hex_ascii_view); debug_set_level(lcs_dbf_setup, 2); debug_register_view(lcs_dbf_trace, &debug_hex_ascii_view); debug_set_level(lcs_dbf_trace, 2); return 0;}/** * Allocate io buffers. */static intlcs_alloc_channel(struct lcs_channel *channel){ int cnt; LCS_DBF_TEXT(2, setup, "ichalloc"); for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) { /* alloc memory fo iobuffer */ channel->iob[cnt].data = kzalloc(LCS_IOBUFFERSIZE, GFP_DMA | GFP_KERNEL); if (channel->iob[cnt].data == NULL) break; channel->iob[cnt].state = BUF_STATE_EMPTY; } if (cnt < LCS_NUM_BUFFS) { /* Not all io buffers could be allocated. */ LCS_DBF_TEXT(2, setup, "echalloc"); while (cnt-- > 0) kfree(channel->iob[cnt].data); return -ENOMEM; } return 0;}/** * Free io buffers. */static voidlcs_free_channel(struct lcs_channel *channel){ int cnt; LCS_DBF_TEXT(2, setup, "ichfree"); for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) { kfree(channel->iob[cnt].data); channel->iob[cnt].data = NULL; }}/* * Cleanup channel. */static voidlcs_cleanup_channel(struct lcs_channel *channel){ LCS_DBF_TEXT(3, setup, "cleanch"); /* Kill write channel tasklets. */ tasklet_kill(&channel->irq_tasklet); /* Free channel buffers. */ lcs_free_channel(channel);}/** * LCS free memory for card and channels. */static voidlcs_free_card(struct lcs_card *card){ LCS_DBF_TEXT(2, setup, "remcard"); LCS_DBF_HEX(2, setup, &card, sizeof(void*)); kfree(card);}/** * LCS alloc memory for card and channels */static struct lcs_card *lcs_alloc_card(void){ struct lcs_card *card; int rc; LCS_DBF_TEXT(2, setup, "alloclcs"); card = kzalloc(sizeof(struct lcs_card), GFP_KERNEL | GFP_DMA); if (card == NULL) return NULL; card->lan_type = LCS_FRAME_TYPE_AUTO; card->pkt_seq = 0; card->lancmd_timeout = LCS_LANCMD_TIMEOUT_DEFAULT; /* Allocate io buffers for the read channel. */ rc = lcs_alloc_channel(&card->read); if (rc){ LCS_DBF_TEXT(2, setup, "iccwerr"); lcs_free_card(card); return NULL; } /* Allocate io buffers for the write channel. */ rc = lcs_alloc_channel(&card->write); if (rc) { LCS_DBF_TEXT(2, setup, "iccwerr"); lcs_cleanup_channel(&card->read); lcs_free_card(card); return NULL; }#ifdef CONFIG_IP_MULTICAST INIT_LIST_HEAD(&card->ipm_list);#endif LCS_DBF_HEX(2, setup, &card, sizeof(void*)); return card;}/* * Setup read channel. */static voidlcs_setup_read_ccws(struct lcs_card *card){ int cnt; LCS_DBF_TEXT(2, setup, "ireadccw"); /* Setup read ccws. */ memset(card->read.ccws, 0, sizeof (struct ccw1) * (LCS_NUM_BUFFS + 1)); for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) { card->read.ccws[cnt].cmd_code = LCS_CCW_READ; card->read.ccws[cnt].count = LCS_IOBUFFERSIZE; card->read.ccws[cnt].flags = CCW_FLAG_CC | CCW_FLAG_SLI | CCW_FLAG_PCI; /* * Note: we have allocated the buffer with GFP_DMA, so * we do not need to do set_normalized_cda. */ card->read.ccws[cnt].cda = (__u32) __pa(card->read.iob[cnt].data); ((struct lcs_header *) card->read.iob[cnt].data)->offset = LCS_ILLEGAL_OFFSET; card->read.iob[cnt].callback = lcs_get_frames_cb; card->read.iob[cnt].state = BUF_STATE_READY; card->read.iob[cnt].count = LCS_IOBUFFERSIZE; } card->read.ccws[0].flags &= ~CCW_FLAG_PCI; card->read.ccws[LCS_NUM_BUFFS - 1].flags &= ~CCW_FLAG_PCI; card->read.ccws[LCS_NUM_BUFFS - 1].flags |= CCW_FLAG_SUSPEND; /* Last ccw is a tic (transfer in channel). */ card->read.ccws[LCS_NUM_BUFFS].cmd_code = LCS_CCW_TRANSFER; card->read.ccws[LCS_NUM_BUFFS].cda = (__u32) __pa(card->read.ccws); /* Setg initial state of the read channel. */ card->read.state = CH_STATE_INIT; card->read.io_idx = 0; card->read.buf_idx = 0;}static voidlcs_setup_read(struct lcs_card *card){ LCS_DBF_TEXT(3, setup, "initread"); lcs_setup_read_ccws(card); /* Initialize read channel tasklet. */ card->read.irq_tasklet.data = (unsigned long) &card->read; card->read.irq_tasklet.func = lcs_tasklet; /* Initialize waitqueue. */ init_waitqueue_head(&card->read.wait_q);}/* * Setup write channel. */static voidlcs_setup_write_ccws(struct lcs_card *card){ int cnt; LCS_DBF_TEXT(3, setup, "iwritccw"); /* Setup write ccws. */ memset(card->write.ccws, 0, sizeof(struct ccw1) * LCS_NUM_BUFFS + 1); for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) { card->write.ccws[cnt].cmd_code = LCS_CCW_WRITE; card->write.ccws[cnt].count = 0; card->write.ccws[cnt].flags = CCW_FLAG_SUSPEND | CCW_FLAG_CC | CCW_FLAG_SLI; /* * Note: we have allocated the buffer with GFP_DMA, so * we do not need to do set_normalized_cda. */ card->write.ccws[cnt].cda = (__u32) __pa(card->write.iob[cnt].data); } /* Last ccw is a tic (transfer in channel). */ card->write.ccws[LCS_NUM_BUFFS].cmd_code = LCS_CCW_TRANSFER; card->write.ccws[LCS_NUM_BUFFS].cda = (__u32) __pa(card->write.ccws); /* Set initial state of the write channel. */ card->read.state = CH_STATE_INIT; card->write.io_idx = 0; card->write.buf_idx = 0;}static voidlcs_setup_write(struct lcs_card *card){ LCS_DBF_TEXT(3, setup, "initwrit"); lcs_setup_write_ccws(card); /* Initialize write channel tasklet. */ card->write.irq_tasklet.data = (unsigned long) &card->write; card->write.irq_tasklet.func = lcs_tasklet; /* Initialize waitqueue. */ init_waitqueue_head(&card->write.wait_q);}static voidlcs_set_allowed_threads(struct lcs_card *card, unsigned long threads){ unsigned long flags; spin_lock_irqsave(&card->mask_lock, flags); card->thread_allowed_mask = threads; spin_unlock_irqrestore(&card->mask_lock, flags); wake_up(&card->wait_q);}static inline intlcs_threads_running(struct lcs_card *card, unsigned long threads){ unsigned long flags; int rc = 0; spin_lock_irqsave(&card->mask_lock, flags); rc = (card->thread_running_mask & threads); spin_unlock_irqrestore(&card->mask_lock, flags); return rc;}static intlcs_wait_for_threads(struct lcs_card *card, unsigned long threads){ return wait_event_interruptible(card->wait_q, lcs_threads_running(card, threads) == 0);}static inline intlcs_set_thread_start_bit(struct lcs_card *card, unsigned long thread){ unsigned long flags; spin_lock_irqsave(&card->mask_lock, flags); if ( !(card->thread_allowed_mask & thread) || (card->thread_start_mask & thread) ) { spin_unlock_irqrestore(&card->mask_lock, flags); return -EPERM; } card->thread_start_mask |= thread; spin_unlock_irqrestore(&card->mask_lock, flags); return 0;}static voidlcs_clear_thread_running_bit(struct lcs_card *card, unsigned long thread){ unsigned long flags; spin_lock_irqsave(&card->mask_lock, flags); card->thread_running_mask &= ~thread; spin_unlock_irqrestore(&card->mask_lock, flags); wake_up(&card->wait_q);}static inline int__lcs_do_run_thread(struct lcs_card *card, unsigned long thread){ unsigned long flags; int rc = 0; spin_lock_irqsave(&card->mask_lock, flags); if (card->thread_start_mask & thread){ if ((card->thread_allowed_mask & thread) && !(card->thread_running_mask & thread)){ rc = 1; card->thread_start_mask &= ~thread; card->thread_running_mask |= thread; } else rc = -EPERM; } spin_unlock_irqrestore(&card->mask_lock, flags); return rc;}static intlcs_do_run_thread(struct lcs_card *card, unsigned long thread){ int rc = 0; wait_event(card->wait_q, (rc = __lcs_do_run_thread(card, thread)) >= 0); return rc;}static intlcs_do_start_thread(struct lcs_card *card, unsigned long thread){ unsigned long flags; int rc = 0; spin_lock_irqsave(&card->mask_lock, flags); LCS_DBF_TEXT_(4, trace, " %02x%02x%02x", (u8) card->thread_start_mask, (u8) card->thread_allowed_mask, (u8) card->thread_running_mask); rc = (card->thread_start_mask & thread); spin_unlock_irqrestore(&card->mask_lock, flags); return rc;}/** * Initialize channels,card and state machines. */static voidlcs_setup_card(struct lcs_card *card){ LCS_DBF_TEXT(2, setup, "initcard"); LCS_DBF_HEX(2, setup, &card, sizeof(void*)); lcs_setup_read(card); lcs_setup_write(card); /* Set cards initial state. */ card->state = DEV_STATE_DOWN; card->tx_buffer = NULL; card->tx_emitted = 0; init_waitqueue_head(&card->wait_q); spin_lock_init(&card->lock); spin_lock_init(&card->ipm_lock); spin_lock_init(&card->mask_lock);#ifdef CONFIG_IP_MULTICAST INIT_LIST_HEAD(&card->ipm_list);#endif INIT_LIST_HEAD(&card->lancmd_waiters);}static inline voidlcs_clear_multicast_list(struct lcs_card *card){#ifdef CONFIG_IP_MULTICAST struct lcs_ipm_list *ipm; unsigned long flags; /* Free multicast list. */ LCS_DBF_TEXT(3, setup, "clmclist"); spin_lock_irqsave(&card->ipm_lock, flags); while (!list_empty(&card->ipm_list)){ ipm = list_entry(card->ipm_list.next, struct lcs_ipm_list, list); list_del(&ipm->list); if (ipm->ipm_state != LCS_IPM_STATE_SET_REQUIRED){ spin_unlock_irqrestore(&card->ipm_lock, flags); lcs_send_delipm(card, ipm); spin_lock_irqsave(&card->ipm_lock, flags); } kfree(ipm); } spin_unlock_irqrestore(&card->ipm_lock, flags);#endif}/** * Cleanup channels,card and state machines. */static voidlcs_cleanup_card(struct lcs_card *card){ LCS_DBF_TEXT(3, setup, "cleancrd"); LCS_DBF_HEX(2,setup,&card,sizeof(void*)); if (card->dev != NULL) free_netdev(card->dev); /* Cleanup channels. */ lcs_cleanup_channel(&card->write); lcs_cleanup_channel(&card->read);}/** * Start channel. */static intlcs_start_channel(struct lcs_channel *channel){ unsigned long flags; int rc; LCS_DBF_TEXT_(4,trace,"ssch%s", channel->ccwdev->dev.bus_id); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); rc = ccw_device_start(channel->ccwdev, channel->ccws + channel->io_idx, 0, 0, DOIO_DENY_PREFETCH | DOIO_ALLOW_SUSPEND); if (rc == 0) channel->state = CH_STATE_RUNNING; spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); if (rc) { LCS_DBF_TEXT_(4,trace,"essh%s", channel->ccwdev->dev.bus_id); PRINT_ERR("Error in starting channel, rc=%d!\n", rc); } return rc;}static intlcs_clear_channel(struct lcs_channel *channel){ unsigned long flags; int rc; LCS_DBF_TEXT(4,trace,"clearch"); LCS_DBF_TEXT_(4,trace,"%s", channel->ccwdev->dev.bus_id); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); rc = ccw_device_clear(channel->ccwdev, (addr_t) channel); spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); if (rc) { LCS_DBF_TEXT_(4,trace,"ecsc%s", channel->ccwdev->dev.bus_id); return rc; } wait_event(channel->wait_q, (channel->state == CH_STATE_CLEARED)); channel->state = CH_STATE_STOPPED; return rc;}/** * Stop channel. */static intlcs_stop_channel(struct lcs_channel *channel){ unsigned long flags; int rc; if (channel->state == CH_STATE_STOPPED) return 0; LCS_DBF_TEXT(4,trace,"haltsch"); LCS_DBF_TEXT_(4,trace,"%s", channel->ccwdev->dev.bus_id); channel->state = CH_STATE_INIT; spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); rc = ccw_device_halt(channel->ccwdev, (addr_t) channel); spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); if (rc) { LCS_DBF_TEXT_(4,trace,"ehsc%s", channel->ccwdev->dev.bus_id); return rc; } /* Asynchronous halt initialted. Wait for its completion. */ wait_event(channel->wait_q, (channel->state == CH_STATE_HALTED)); lcs_clear_channel(channel); return 0;}/** * start read and write channel */static intlcs_start_channels(struct lcs_card *card){ int rc; LCS_DBF_TEXT(2, trace, "chstart"); /* start read channel */ rc = lcs_start_channel(&card->read); if (rc) return rc; /* start write channel */ rc = lcs_start_channel(&card->write); if (rc) lcs_stop_channel(&card->read); return rc;}/** * stop read and write channel */static intlcs_stop_channels(struct lcs_card *card){ LCS_DBF_TEXT(2, trace, "chhalt"); lcs_stop_channel(&card->read); lcs_stop_channel(&card->write); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -