📄 qeth_main.c
字号:
/* * * linux/drivers/s390/net/qeth_main.c ($Revision: 1.251 $) * * Linux on zSeries OSA Express and HiperSockets support * * Copyright 2000,2003 IBM Corporation * * Author(s): Original Code written by * Utz Bacher (utz.bacher@de.ibm.com) * Rewritten by * Frank Pavlic (fpavlic@de.ibm.com) and * Thomas Spatzier <tspat@de.ibm.com> * * $Revision: 1.251 $ $Date: 2005/05/04 20:19:18 $ * * 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/config.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/mm.h>#include <linux/ip.h>#include <linux/inetdevice.h>#include <linux/netdevice.h>#include <linux/sched.h>#include <linux/workqueue.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/tcp.h>#include <linux/icmp.h>#include <linux/skbuff.h>#include <linux/in.h>#include <linux/igmp.h>#include <linux/init.h>#include <linux/reboot.h>#include <linux/mii.h>#include <linux/rcupdate.h>#include <linux/ethtool.h>#include <net/arp.h>#include <net/ip.h>#include <net/route.h>#include <asm/ebcdic.h>#include <asm/io.h>#include <asm/qeth.h>#include <asm/timex.h>#include <asm/semaphore.h>#include <asm/uaccess.h>#include "qeth.h"#include "qeth_mpc.h"#include "qeth_fs.h"#include "qeth_eddp.h"#include "qeth_tso.h"#define VERSION_QETH_C "$Revision: 1.251 $"static const char *version = "qeth S/390 OSA-Express driver";/** * Debug Facility Stuff */static debug_info_t *qeth_dbf_setup = NULL;static debug_info_t *qeth_dbf_data = NULL;static debug_info_t *qeth_dbf_misc = NULL;static debug_info_t *qeth_dbf_control = NULL;debug_info_t *qeth_dbf_trace = NULL;static debug_info_t *qeth_dbf_sense = NULL;static debug_info_t *qeth_dbf_qerr = NULL;DEFINE_PER_CPU(char[256], qeth_dbf_txt_buf);/** * some more definitions and declarations */static unsigned int known_devices[][10] = QETH_MODELLIST_ARRAY;/* list of our cards */struct qeth_card_list_struct qeth_card_list;/*process list want to be notified*/spinlock_t qeth_notify_lock;struct list_head qeth_notify_list;static void qeth_send_control_data_cb(struct qeth_channel *, struct qeth_cmd_buffer *);/** * here we go with function implementation */static voidqeth_init_qdio_info(struct qeth_card *card);static intqeth_init_qdio_queues(struct qeth_card *card);static intqeth_alloc_qdio_buffers(struct qeth_card *card);static voidqeth_free_qdio_buffers(struct qeth_card *);static voidqeth_clear_qdio_buffers(struct qeth_card *);static voidqeth_clear_ip_list(struct qeth_card *, int, int);static voidqeth_clear_ipacmd_list(struct qeth_card *);static intqeth_qdio_clear_card(struct qeth_card *, int);static voidqeth_clear_working_pool_list(struct qeth_card *);static voidqeth_clear_cmd_buffers(struct qeth_channel *);static intqeth_stop(struct net_device *);static voidqeth_clear_ipato_list(struct qeth_card *);static intqeth_is_addr_covered_by_ipato(struct qeth_card *, struct qeth_ipaddr *);static voidqeth_irq_tasklet(unsigned long);static intqeth_set_online(struct ccwgroup_device *);static int__qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode);static struct qeth_ipaddr *qeth_get_addr_buffer(enum qeth_prot_versions);static voidqeth_set_multicast_list(struct net_device *);static voidqeth_setadp_promisc_mode(struct qeth_card *);static voidqeth_notify_processes(void){ /*notify all registered processes */ struct qeth_notify_list_struct *n_entry; QETH_DBF_TEXT(trace,3,"procnoti"); spin_lock(&qeth_notify_lock); list_for_each_entry(n_entry, &qeth_notify_list, list) { send_sig(n_entry->signum, n_entry->task, 1); } spin_unlock(&qeth_notify_lock);}intqeth_notifier_unregister(struct task_struct *p){ struct qeth_notify_list_struct *n_entry, *tmp; QETH_DBF_TEXT(trace, 2, "notunreg"); spin_lock(&qeth_notify_lock); list_for_each_entry_safe(n_entry, tmp, &qeth_notify_list, list) { if (n_entry->task == p) { list_del(&n_entry->list); kfree(n_entry); goto out; } }out: spin_unlock(&qeth_notify_lock); return 0;}intqeth_notifier_register(struct task_struct *p, int signum){ struct qeth_notify_list_struct *n_entry; /*check first if entry already exists*/ spin_lock(&qeth_notify_lock); list_for_each_entry(n_entry, &qeth_notify_list, list) { if (n_entry->task == p) { n_entry->signum = signum; spin_unlock(&qeth_notify_lock); return 0; } } spin_unlock(&qeth_notify_lock); n_entry = (struct qeth_notify_list_struct *) kmalloc(sizeof(struct qeth_notify_list_struct),GFP_KERNEL); if (!n_entry) return -ENOMEM; n_entry->task = p; n_entry->signum = signum; spin_lock(&qeth_notify_lock); list_add(&n_entry->list,&qeth_notify_list); spin_unlock(&qeth_notify_lock); return 0;}/** * free channel command buffers */static voidqeth_clean_channel(struct qeth_channel *channel){ int cnt; QETH_DBF_TEXT(setup, 2, "freech"); for (cnt = 0; cnt < QETH_CMD_BUFFER_NO; cnt++) kfree(channel->iob[cnt].data);}/** * free card */static voidqeth_free_card(struct qeth_card *card){ QETH_DBF_TEXT(setup, 2, "freecrd"); QETH_DBF_HEX(setup, 2, &card, sizeof(void *)); qeth_clean_channel(&card->read); qeth_clean_channel(&card->write); if (card->dev) free_netdev(card->dev); qeth_clear_ip_list(card, 0, 0); qeth_clear_ipato_list(card); kfree(card->ip_tbd_list); qeth_free_qdio_buffers(card); kfree(card);}/** * alloc memory for command buffer per channel */static intqeth_setup_channel(struct qeth_channel *channel){ int cnt; QETH_DBF_TEXT(setup, 2, "setupch"); for (cnt=0; cnt < QETH_CMD_BUFFER_NO; cnt++) { channel->iob[cnt].data = (char *) kmalloc(QETH_BUFSIZE, GFP_DMA|GFP_KERNEL); if (channel->iob[cnt].data == NULL) break; channel->iob[cnt].state = BUF_STATE_FREE; channel->iob[cnt].channel = channel; channel->iob[cnt].callback = qeth_send_control_data_cb; channel->iob[cnt].rc = 0; } if (cnt < QETH_CMD_BUFFER_NO) { while (cnt-- > 0) kfree(channel->iob[cnt].data); return -ENOMEM; } channel->buf_no = 0; channel->io_buf_no = 0; atomic_set(&channel->irq_pending, 0); spin_lock_init(&channel->iob_lock); init_waitqueue_head(&channel->wait_q); channel->irq_tasklet.data = (unsigned long) channel; channel->irq_tasklet.func = qeth_irq_tasklet; return 0;}/** * alloc memory for card structure */static struct qeth_card *qeth_alloc_card(void){ struct qeth_card *card; QETH_DBF_TEXT(setup, 2, "alloccrd"); card = (struct qeth_card *) kmalloc(sizeof(struct qeth_card), GFP_DMA|GFP_KERNEL); if (!card) return NULL; QETH_DBF_HEX(setup, 2, &card, sizeof(void *)); memset(card, 0, sizeof(struct qeth_card)); if (qeth_setup_channel(&card->read)) { kfree(card); return NULL; } if (qeth_setup_channel(&card->write)) { qeth_clean_channel(&card->read); kfree(card); return NULL; } return card;}static long__qeth_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); QETH_DBF_TEXT(trace, 2, "ckirberr"); QETH_DBF_TEXT_(trace, 2, " rc%d", -EIO); break; case -ETIMEDOUT: PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id); QETH_DBF_TEXT(trace, 2, "ckirberr"); QETH_DBF_TEXT_(trace, 2, " rc%d", -ETIMEDOUT); break; default: PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb), cdev->dev.bus_id); QETH_DBF_TEXT(trace, 2, "ckirberr"); QETH_DBF_TEXT(trace, 2, " rc???"); } return PTR_ERR(irb);}static intqeth_get_problem(struct ccw_device *cdev, struct irb *irb){ int dstat,cstat; char *sense; sense = (char *) irb->ecw; cstat = irb->scsw.cstat; dstat = irb->scsw.dstat; if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK | SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK | SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK)) { QETH_DBF_TEXT(trace,2, "CGENCHK"); PRINT_WARN("check on device %s, dstat=x%x, cstat=x%x ", cdev->dev.bus_id, dstat, cstat); HEXDUMP16(WARN, "irb: ", irb); HEXDUMP16(WARN, "irb: ", ((char *) irb) + 32); return 1; } if (dstat & DEV_STAT_UNIT_CHECK) { if (sense[SENSE_RESETTING_EVENT_BYTE] & SENSE_RESETTING_EVENT_FLAG) { QETH_DBF_TEXT(trace,2,"REVIND"); return 1; } if (sense[SENSE_COMMAND_REJECT_BYTE] & SENSE_COMMAND_REJECT_FLAG) { QETH_DBF_TEXT(trace,2,"CMDREJi"); return 0; } if ((sense[2] == 0xaf) && (sense[3] == 0xfe)) { QETH_DBF_TEXT(trace,2,"AFFE"); return 1; } if ((!sense[0]) && (!sense[1]) && (!sense[2]) && (!sense[3])) { QETH_DBF_TEXT(trace,2,"ZEROSEN"); return 0; } QETH_DBF_TEXT(trace,2,"DGENCHK"); return 1; } return 0;}static int qeth_issue_next_read(struct qeth_card *);/** * interrupt handler */static voidqeth_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb){ int rc; int cstat,dstat; struct qeth_cmd_buffer *buffer; struct qeth_channel *channel; struct qeth_card *card; QETH_DBF_TEXT(trace,5,"irq"); if (__qeth_check_irb_error(cdev, irb)) return; cstat = irb->scsw.cstat; dstat = irb->scsw.dstat; card = CARD_FROM_CDEV(cdev); if (!card) return; if (card->read.ccwdev == cdev){ channel = &card->read; QETH_DBF_TEXT(trace,5,"read"); } else if (card->write.ccwdev == cdev) { channel = &card->write; QETH_DBF_TEXT(trace,5,"write"); } else { channel = &card->data; QETH_DBF_TEXT(trace,5,"data"); } atomic_set(&channel->irq_pending, 0); if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC)) channel->state = CH_STATE_STOPPED; if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC)) channel->state = CH_STATE_HALTED; /*let's wake up immediately on data channel*/ if ((channel == &card->data) && (intparm != 0)) goto out; if (intparm == QETH_CLEAR_CHANNEL_PARM) { QETH_DBF_TEXT(trace, 6, "clrchpar"); /* we don't have to handle this further */ intparm = 0; } if (intparm == QETH_HALT_CHANNEL_PARM) { QETH_DBF_TEXT(trace, 6, "hltchpar"); /* we don't have to handle this further */ intparm = 0; } if ((dstat & DEV_STAT_UNIT_EXCEP) || (dstat & DEV_STAT_UNIT_CHECK) || (cstat)) { if (irb->esw.esw0.erw.cons) { /* TODO: we should make this s390dbf */ PRINT_WARN("sense data available on channel %s.\n", CHANNEL_ID(channel)); PRINT_WARN(" cstat 0x%X\n dstat 0x%X\n", cstat, dstat); HEXDUMP16(WARN,"irb: ",irb); HEXDUMP16(WARN,"sense data: ",irb->ecw); } rc = qeth_get_problem(cdev,irb); if (rc) { qeth_schedule_recovery(card); goto out; } } if (intparm) { buffer = (struct qeth_cmd_buffer *) __va((addr_t)intparm); buffer->state = BUF_STATE_PROCESSED; } if (channel == &card->data) return; if (channel == &card->read && channel->state == CH_STATE_UP) qeth_issue_next_read(card); tasklet_schedule(&channel->irq_tasklet); return;out: wake_up(&card->wait_q);}/** * tasklet function scheduled from irq handler */static voidqeth_irq_tasklet(unsigned long data){ struct qeth_card *card; struct qeth_channel *channel; struct qeth_cmd_buffer *iob; __u8 index;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -