⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 qeth_main.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * * 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 + -