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

📄 device_ops.c

📁 linux-2.6.15.6
💻 C
字号:
/* *  drivers/s390/cio/device_ops.c * *   $Revision: 1.57 $ * *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, *			 IBM Corporation *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) *               Cornelia Huck (cohuck@de.ibm.com) */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/list.h>#include <linux/device.h>#include <linux/delay.h>#include <asm/ccwdev.h>#include <asm/idals.h>#include "cio.h"#include "cio_debug.h"#include "css.h"#include "chsc.h"#include "device.h"intccw_device_set_options(struct ccw_device *cdev, unsigned long flags){       /*	* The flag usage is mutal exclusive ...	*/	if ((flags & CCWDEV_EARLY_NOTIFICATION) &&	    (flags & CCWDEV_REPORT_ALL))		return -EINVAL;	cdev->private->options.fast = (flags & CCWDEV_EARLY_NOTIFICATION) != 0;	cdev->private->options.repall = (flags & CCWDEV_REPORT_ALL) != 0;	cdev->private->options.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0;	cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0;	return 0;}intccw_device_clear(struct ccw_device *cdev, unsigned long intparm){	struct subchannel *sch;	int ret;	if (!cdev)		return -ENODEV;	if (cdev->private->state == DEV_STATE_NOT_OPER)		return -ENODEV;	if (cdev->private->state != DEV_STATE_ONLINE &&	    cdev->private->state != DEV_STATE_WAIT4IO &&	    cdev->private->state != DEV_STATE_W4SENSE)		return -EINVAL;	sch = to_subchannel(cdev->dev.parent);	if (!sch)		return -ENODEV;	ret = cio_clear(sch);	if (ret == 0)		cdev->private->intparm = intparm;	return ret;}intccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,		     unsigned long intparm, __u8 lpm, __u8 key,		     unsigned long flags){	struct subchannel *sch;	int ret;	if (!cdev)		return -ENODEV;	sch = to_subchannel(cdev->dev.parent);	if (!sch)		return -ENODEV;	if (cdev->private->state == DEV_STATE_NOT_OPER)		return -ENODEV;	if (cdev->private->state == DEV_STATE_VERIFY) {		/* Remember to fake irb when finished. */		if (!cdev->private->flags.fake_irb) {			cdev->private->flags.fake_irb = 1;			cdev->private->intparm = intparm;			return 0;		} else			/* There's already a fake I/O around. */			return -EBUSY;	}	if (cdev->private->state != DEV_STATE_ONLINE ||	    ((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&	     !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) ||	    cdev->private->flags.doverify)		return -EBUSY;	ret = cio_set_options (sch, flags);	if (ret)		return ret;	ret = cio_start_key (sch, cpa, lpm, key);	if (ret == 0)		cdev->private->intparm = intparm;	return ret;}intccw_device_start_timeout_key(struct ccw_device *cdev, struct ccw1 *cpa,			     unsigned long intparm, __u8 lpm, __u8 key,			     unsigned long flags, int expires){	int ret;	if (!cdev)		return -ENODEV;	ccw_device_set_timeout(cdev, expires);	ret = ccw_device_start_key(cdev, cpa, intparm, lpm, key, flags);	if (ret != 0)		ccw_device_set_timeout(cdev, 0);	return ret;}intccw_device_start(struct ccw_device *cdev, struct ccw1 *cpa,		 unsigned long intparm, __u8 lpm, unsigned long flags){	return ccw_device_start_key(cdev, cpa, intparm, lpm,				    PAGE_DEFAULT_KEY, flags);}intccw_device_start_timeout(struct ccw_device *cdev, struct ccw1 *cpa,			 unsigned long intparm, __u8 lpm, unsigned long flags,			 int expires){	return ccw_device_start_timeout_key(cdev, cpa, intparm, lpm,					    PAGE_DEFAULT_KEY, flags,					    expires);}intccw_device_halt(struct ccw_device *cdev, unsigned long intparm){	struct subchannel *sch;	int ret;	if (!cdev)		return -ENODEV;	if (cdev->private->state == DEV_STATE_NOT_OPER)		return -ENODEV;	if (cdev->private->state != DEV_STATE_ONLINE &&	    cdev->private->state != DEV_STATE_WAIT4IO &&	    cdev->private->state != DEV_STATE_W4SENSE)		return -EINVAL;	sch = to_subchannel(cdev->dev.parent);	if (!sch)		return -ENODEV;	ret = cio_halt(sch);	if (ret == 0)		cdev->private->intparm = intparm;	return ret;}intccw_device_resume(struct ccw_device *cdev){	struct subchannel *sch;	if (!cdev)		return -ENODEV;	sch = to_subchannel(cdev->dev.parent);	if (!sch)		return -ENODEV;	if (cdev->private->state == DEV_STATE_NOT_OPER)		return -ENODEV;	if (cdev->private->state != DEV_STATE_ONLINE ||	    !(sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED))		return -EINVAL;	return cio_resume(sch);}/* * Pass interrupt to device driver. */intccw_device_call_handler(struct ccw_device *cdev){	struct subchannel *sch;	unsigned int stctl;	int ending_status;	sch = to_subchannel(cdev->dev.parent);	/*	 * we allow for the device action handler if .	 *  - we received ending status	 *  - the action handler requested to see all interrupts	 *  - we received an intermediate status	 *  - fast notification was requested (primary status)	 *  - unsolicited interrupts	 */	stctl = cdev->private->irb.scsw.stctl;	ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||		(stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||		(stctl == SCSW_STCTL_STATUS_PEND);	if (!ending_status &&	    !cdev->private->options.repall &&	    !(stctl & SCSW_STCTL_INTER_STATUS) &&	    !(cdev->private->options.fast &&	      (stctl & SCSW_STCTL_PRIM_STATUS)))		return 0;	/*	 * Now we are ready to call the device driver interrupt handler.	 */	if (cdev->handler)		cdev->handler(cdev, cdev->private->intparm,			      &cdev->private->irb);	/*	 * Clear the old and now useless interrupt response block.	 */	memset(&cdev->private->irb, 0, sizeof(struct irb));	return 1;}/* * Search for CIW command in extended sense data. */struct ciw *ccw_device_get_ciw(struct ccw_device *cdev, __u32 ct){	int ciw_cnt;	if (cdev->private->flags.esid == 0)		return NULL;	for (ciw_cnt = 0; ciw_cnt < MAX_CIWS; ciw_cnt++)		if (cdev->private->senseid.ciw[ciw_cnt].ct == ct)			return cdev->private->senseid.ciw + ciw_cnt;	return NULL;}__u8ccw_device_get_path_mask(struct ccw_device *cdev){	struct subchannel *sch;	sch = to_subchannel(cdev->dev.parent);	if (!sch)		return 0;	else		return sch->vpm;}static voidccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb){	if (!ip)		/* unsolicited interrupt */		return;	/* Abuse intparm for error reporting. */	if (IS_ERR(irb))		cdev->private->intparm = -EIO;	else if ((irb->scsw.dstat !=		  (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||		 (irb->scsw.cstat != 0)) {		/*		 * We didn't get channel end / device end. Check if path		 * verification has been started; we can retry after it has		 * finished. We also retry unit checks except for command reject		 * or intervention required.		 */		 if (cdev->private->flags.doverify ||			 cdev->private->state == DEV_STATE_VERIFY)			 cdev->private->intparm = -EAGAIN;		 if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&		     !(irb->ecw[0] &		       (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ)))			 cdev->private->intparm = -EAGAIN;		 else			 cdev->private->intparm = -EIO;			 	} else		cdev->private->intparm = 0;	wake_up(&cdev->private->wait_q);}static inline int__ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, __u8 lpm){	int ret;	struct subchannel *sch;	sch = to_subchannel(cdev->dev.parent);	do {		ret = cio_start (sch, ccw, lpm);		if ((ret == -EBUSY) || (ret == -EACCES)) {			/* Try again later. */			spin_unlock_irq(&sch->lock);			msleep(10);			spin_lock_irq(&sch->lock);			continue;		}		if (ret != 0)			/* Non-retryable error. */			break;		/* Wait for end of request. */		cdev->private->intparm = magic;		spin_unlock_irq(&sch->lock);		wait_event(cdev->private->wait_q,			   (cdev->private->intparm == -EIO) ||			   (cdev->private->intparm == -EAGAIN) ||			   (cdev->private->intparm == 0));		spin_lock_irq(&sch->lock);		/* Check at least for channel end / device end */		if (cdev->private->intparm == -EIO) {			/* Non-retryable error. */			ret = -EIO;			break;		}		if (cdev->private->intparm == 0)			/* Success. */			break;		/* Try again later. */		spin_unlock_irq(&sch->lock);		msleep(10);		spin_lock_irq(&sch->lock);	} while (1);	return ret;}/** * read_dev_chars() - read device characteristics * @param cdev   target ccw device * @param buffer pointer to buffer for rdc data * @param length size of rdc data * @returns 0 for success, negative error value on failure * * Context: *   called for online device, lock not held **/intread_dev_chars (struct ccw_device *cdev, void **buffer, int length){	void (*handler)(struct ccw_device *, unsigned long, struct irb *);	struct subchannel *sch;	int ret;	struct ccw1 *rdc_ccw;	if (!cdev)		return -ENODEV;	if (!buffer || !length)		return -EINVAL;	sch = to_subchannel(cdev->dev.parent);	CIO_TRACE_EVENT (4, "rddevch");	CIO_TRACE_EVENT (4, sch->dev.bus_id);	rdc_ccw = kmalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);	if (!rdc_ccw)		return -ENOMEM;	memset(rdc_ccw, 0, sizeof(struct ccw1));	rdc_ccw->cmd_code = CCW_CMD_RDC;	rdc_ccw->count = length;	rdc_ccw->flags = CCW_FLAG_SLI;	ret = set_normalized_cda (rdc_ccw, (*buffer));	if (ret != 0) {		kfree(rdc_ccw);		return ret;	}	spin_lock_irq(&sch->lock);	/* Save interrupt handler. */	handler = cdev->handler;	/* Temporarily install own handler. */	cdev->handler = ccw_device_wake_up;	if (cdev->private->state != DEV_STATE_ONLINE)		ret = -ENODEV;	else if (((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&		  !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) ||		 cdev->private->flags.doverify)		ret = -EBUSY;	else		/* 0x00D9C4C3 == ebcdic "RDC" */		ret = __ccw_device_retry_loop(cdev, rdc_ccw, 0x00D9C4C3, 0);	/* Restore interrupt handler. */	cdev->handler = handler;	spin_unlock_irq(&sch->lock);	clear_normalized_cda (rdc_ccw);	kfree(rdc_ccw);	return ret;}/* *  Read Configuration data using path mask */intread_conf_data_lpm (struct ccw_device *cdev, void **buffer, int *length, __u8 lpm){	void (*handler)(struct ccw_device *, unsigned long, struct irb *);	struct subchannel *sch;	struct ciw *ciw;	char *rcd_buf;	int ret;	struct ccw1 *rcd_ccw;	if (!cdev)		return -ENODEV;	if (!buffer || !length)		return -EINVAL;	sch = to_subchannel(cdev->dev.parent);	CIO_TRACE_EVENT (4, "rdconf");	CIO_TRACE_EVENT (4, sch->dev.bus_id);	/*	 * scan for RCD command in extended SenseID data	 */	ciw = ccw_device_get_ciw(cdev, CIW_TYPE_RCD);	if (!ciw || ciw->cmd == 0)		return -EOPNOTSUPP;	rcd_ccw = kmalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);	if (!rcd_ccw)		return -ENOMEM;	memset(rcd_ccw, 0, sizeof(struct ccw1));	rcd_buf = kmalloc(ciw->count, GFP_KERNEL | GFP_DMA); 	if (!rcd_buf) {		kfree(rcd_ccw);		return -ENOMEM;	} 	memset (rcd_buf, 0, ciw->count);	rcd_ccw->cmd_code = ciw->cmd;	rcd_ccw->cda = (__u32) __pa (rcd_buf);	rcd_ccw->count = ciw->count;	rcd_ccw->flags = CCW_FLAG_SLI;	spin_lock_irq(&sch->lock);	/* Save interrupt handler. */	handler = cdev->handler;	/* Temporarily install own handler. */	cdev->handler = ccw_device_wake_up;	if (cdev->private->state != DEV_STATE_ONLINE)		ret = -ENODEV;	else if (((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&		  !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) ||		 cdev->private->flags.doverify)		ret = -EBUSY;	else		/* 0x00D9C3C4 == ebcdic "RCD" */		ret = __ccw_device_retry_loop(cdev, rcd_ccw, 0x00D9C3C4, lpm);	/* Restore interrupt handler. */	cdev->handler = handler;	spin_unlock_irq(&sch->lock); 	/* 	 * on success we update the user input parms 	 */ 	if (ret) { 		kfree (rcd_buf); 		*buffer = NULL; 		*length = 0; 	} else {		*length = ciw->count;		*buffer = rcd_buf;	}	kfree(rcd_ccw);	return ret;}/* *  Read Configuration data */intread_conf_data (struct ccw_device *cdev, void **buffer, int *length){	return read_conf_data_lpm (cdev, buffer, length, 0);}/* * Try to break the lock on a boxed device. */intccw_device_stlck(struct ccw_device *cdev){	void *buf, *buf2;	unsigned long flags;	struct subchannel *sch;	int ret;	if (!cdev)		return -ENODEV;	if (cdev->drv && !cdev->private->options.force)		return -EINVAL;	sch = to_subchannel(cdev->dev.parent);		CIO_TRACE_EVENT(2, "stl lock");	CIO_TRACE_EVENT(2, cdev->dev.bus_id);	buf = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);	if (!buf)		return -ENOMEM;	buf2 = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);	if (!buf2) {		kfree(buf);		return -ENOMEM;	}	spin_lock_irqsave(&sch->lock, flags);	ret = cio_enable_subchannel(sch, 3);	if (ret)		goto out_unlock;	/*	 * Setup ccw. We chain an unconditional reserve and a release so we	 * only break the lock.	 */	cdev->private->iccws[0].cmd_code = CCW_CMD_STLCK;	cdev->private->iccws[0].cda = (__u32) __pa(buf);	cdev->private->iccws[0].count = 32;	cdev->private->iccws[0].flags = CCW_FLAG_CC;	cdev->private->iccws[1].cmd_code = CCW_CMD_RELEASE;	cdev->private->iccws[1].cda = (__u32) __pa(buf2);	cdev->private->iccws[1].count = 32;	cdev->private->iccws[1].flags = 0;	ret = cio_start(sch, cdev->private->iccws, 0);	if (ret) {		cio_disable_subchannel(sch); //FIXME: return code?		goto out_unlock;	}	cdev->private->irb.scsw.actl |= SCSW_ACTL_START_PEND;	spin_unlock_irqrestore(&sch->lock, flags);	wait_event(cdev->private->wait_q, cdev->private->irb.scsw.actl == 0);	spin_lock_irqsave(&sch->lock, flags);	cio_disable_subchannel(sch); //FIXME: return code?	if ((cdev->private->irb.scsw.dstat !=	     (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||	    (cdev->private->irb.scsw.cstat != 0))		ret = -EIO;	/* Clear irb. */	memset(&cdev->private->irb, 0, sizeof(struct irb));out_unlock:	kfree(buf);	kfree(buf2);	spin_unlock_irqrestore(&sch->lock, flags);	return ret;}void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no){	struct subchannel *sch;	sch = to_subchannel(cdev->dev.parent);	return chsc_get_chp_desc(sch, chp_no);}// FIXME: these have to go:int_ccw_device_get_subchannel_number(struct ccw_device *cdev){	return cdev->private->irq;}int_ccw_device_get_device_number(struct ccw_device *cdev){	return cdev->private->devno;}MODULE_LICENSE("GPL");EXPORT_SYMBOL(ccw_device_set_options);EXPORT_SYMBOL(ccw_device_clear);EXPORT_SYMBOL(ccw_device_halt);EXPORT_SYMBOL(ccw_device_resume);EXPORT_SYMBOL(ccw_device_start_timeout);EXPORT_SYMBOL(ccw_device_start);EXPORT_SYMBOL(ccw_device_start_timeout_key);EXPORT_SYMBOL(ccw_device_start_key);EXPORT_SYMBOL(ccw_device_get_ciw);EXPORT_SYMBOL(ccw_device_get_path_mask);EXPORT_SYMBOL(read_conf_data);EXPORT_SYMBOL(read_dev_chars);EXPORT_SYMBOL(_ccw_device_get_subchannel_number);EXPORT_SYMBOL(_ccw_device_get_device_number);EXPORT_SYMBOL_GPL(ccw_device_get_chp_desc);EXPORT_SYMBOL_GPL(read_conf_data_lpm);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -