dasd_eckd.c

来自「linux 内核源代码」· C语言 代码 · 共 1,829 行 · 第 1/4 页

C
1,829
字号
/* * File...........: linux/drivers/s390/block/dasd_eckd.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> *		    Horst Hummel <Horst.Hummel@de.ibm.com> *		    Carsten Otte <Cotte@de.ibm.com> *		    Martin Schwidefsky <schwidefsky@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * */#include <linux/stddef.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/hdreg.h>	/* HDIO_GETGEO			    */#include <linux/bio.h>#include <linux/module.h>#include <linux/init.h>#include <asm/debug.h>#include <asm/idals.h>#include <asm/ebcdic.h>#include <asm/io.h>#include <asm/todclk.h>#include <asm/uaccess.h>#include <asm/cio.h>#include <asm/ccwdev.h>#include "dasd_int.h"#include "dasd_eckd.h"#ifdef PRINTK_HEADER#undef PRINTK_HEADER#endif				/* PRINTK_HEADER */#define PRINTK_HEADER "dasd(eckd):"#define ECKD_C0(i) (i->home_bytes)#define ECKD_F(i) (i->formula)#define ECKD_F1(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f1):\		    (i->factors.f_0x02.f1))#define ECKD_F2(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f2):\		    (i->factors.f_0x02.f2))#define ECKD_F3(i) (ECKD_F(i)==0x01?(i->factors.f_0x01.f3):\		    (i->factors.f_0x02.f3))#define ECKD_F4(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f4):0)#define ECKD_F5(i) (ECKD_F(i)==0x02?(i->factors.f_0x02.f5):0)#define ECKD_F6(i) (i->factor6)#define ECKD_F7(i) (i->factor7)#define ECKD_F8(i) (i->factor8)MODULE_LICENSE("GPL");static struct dasd_discipline dasd_eckd_discipline;struct dasd_eckd_private {	struct dasd_eckd_characteristics rdc_data;	struct dasd_eckd_confdata conf_data;	struct dasd_eckd_path path_data;	struct eckd_count count_area[5];	int init_cqr_status;	int uses_cdl;	struct attrib_data_t attrib;	/* e.g. cache operations */};/* The ccw bus type uses this table to find devices that it sends to * dasd_eckd_probe */static struct ccw_device_id dasd_eckd_ids[] = {	{ CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), .driver_info = 0x1},	{ CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), .driver_info = 0x2},	{ CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3390, 0), .driver_info = 0x3},	{ CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), .driver_info = 0x4},	{ CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), .driver_info = 0x5},	{ CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), .driver_info = 0x6},	{ CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), .driver_info = 0x7},	{ CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), .driver_info = 0x8},	{ CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), .driver_info = 0x9},	{ CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), .driver_info = 0xa},	{ /* end of list */ },};MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);static struct ccw_driver dasd_eckd_driver; /* see below *//* initial attempt at a probe function. this can be simplified once * the other detection code is gone */static intdasd_eckd_probe (struct ccw_device *cdev){	int ret;	/* set ECKD specific ccw-device options */	ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE);	if (ret) {		printk(KERN_WARNING		       "dasd_eckd_probe: could not set ccw-device options "		       "for %s\n", cdev->dev.bus_id);		return ret;	}	ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);	return ret;}static intdasd_eckd_set_online(struct ccw_device *cdev){	return dasd_generic_set_online(cdev, &dasd_eckd_discipline);}static struct ccw_driver dasd_eckd_driver = {	.name        = "dasd-eckd",	.owner       = THIS_MODULE,	.ids         = dasd_eckd_ids,	.probe       = dasd_eckd_probe,	.remove      = dasd_generic_remove,	.set_offline = dasd_generic_set_offline,	.set_online  = dasd_eckd_set_online,	.notify      = dasd_generic_notify,};static const int sizes_trk0[] = { 28, 148, 84 };#define LABEL_SIZE 140static inline unsigned intround_up_multiple(unsigned int no, unsigned int mult){	int rem = no % mult;	return (rem ? no - rem + mult : no);}static inline unsigned intceil_quot(unsigned int d1, unsigned int d2){	return (d1 + (d2 - 1)) / d2;}static unsigned intrecs_per_track(struct dasd_eckd_characteristics * rdc,	       unsigned int kl, unsigned int dl){	int dn, kn;	switch (rdc->dev_type) {	case 0x3380:		if (kl)			return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) +				       ceil_quot(dl + 12, 32));		else			return 1499 / (15 + ceil_quot(dl + 12, 32));	case 0x3390:		dn = ceil_quot(dl + 6, 232) + 1;		if (kl) {			kn = ceil_quot(kl + 6, 232) + 1;			return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) +				       9 + ceil_quot(dl + 6 * dn, 34));		} else			return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34));	case 0x9345:		dn = ceil_quot(dl + 6, 232) + 1;		if (kl) {			kn = ceil_quot(kl + 6, 232) + 1;			return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) +				       ceil_quot(dl + 6 * dn, 34));		} else			return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34));	}	return 0;}static intcheck_XRC (struct ccw1         *de_ccw,           struct DE_eckd_data *data,           struct dasd_device  *device){        struct dasd_eckd_private *private;	int rc;        private = (struct dasd_eckd_private *) device->private;	if (!private->rdc_data.facilities.XRC_supported)		return 0;        /* switch on System Time Stamp - needed for XRC Support */	data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid'   */	data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */	rc = get_sync_clock(&data->ep_sys_time);	/* Ignore return code if sync clock is switched off. */	if (rc == -ENOSYS || rc == -EACCES)		rc = 0;	de_ccw->count = sizeof (struct DE_eckd_data);	de_ccw->flags |= CCW_FLAG_SLI;	return rc;}static intdefine_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,	      int totrk, int cmd, struct dasd_device * device){	struct dasd_eckd_private *private;	struct ch_t geo, beg, end;	int rc = 0;	private = (struct dasd_eckd_private *) device->private;	ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT;	ccw->flags = 0;	ccw->count = 16;	ccw->cda = (__u32) __pa(data);	memset(data, 0, sizeof (struct DE_eckd_data));	switch (cmd) {	case DASD_ECKD_CCW_READ_HOME_ADDRESS:	case DASD_ECKD_CCW_READ_RECORD_ZERO:	case DASD_ECKD_CCW_READ:	case DASD_ECKD_CCW_READ_MT:	case DASD_ECKD_CCW_READ_CKD:	case DASD_ECKD_CCW_READ_CKD_MT:	case DASD_ECKD_CCW_READ_KD:	case DASD_ECKD_CCW_READ_KD_MT:	case DASD_ECKD_CCW_READ_COUNT:		data->mask.perm = 0x1;		data->attributes.operation = private->attrib.operation;		break;	case DASD_ECKD_CCW_WRITE:	case DASD_ECKD_CCW_WRITE_MT:	case DASD_ECKD_CCW_WRITE_KD:	case DASD_ECKD_CCW_WRITE_KD_MT:		data->mask.perm = 0x02;		data->attributes.operation = private->attrib.operation;		rc = check_XRC (ccw, data, device);		break;	case DASD_ECKD_CCW_WRITE_CKD:	case DASD_ECKD_CCW_WRITE_CKD_MT:		data->attributes.operation = DASD_BYPASS_CACHE;		rc = check_XRC (ccw, data, device);		break;	case DASD_ECKD_CCW_ERASE:	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:	case DASD_ECKD_CCW_WRITE_RECORD_ZERO:		data->mask.perm = 0x3;		data->mask.auth = 0x1;		data->attributes.operation = DASD_BYPASS_CACHE;		rc = check_XRC (ccw, data, device);		break;	default:		DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd);		break;	}	data->attributes.mode = 0x3;	/* ECKD */	if ((private->rdc_data.cu_type == 0x2105 ||	     private->rdc_data.cu_type == 0x2107 ||	     private->rdc_data.cu_type == 0x1750)	    && !(private->uses_cdl && trk < 2))		data->ga_extended |= 0x40; /* Regular Data Format Mode */	geo.cyl = private->rdc_data.no_cyl;	geo.head = private->rdc_data.trk_per_cyl;	beg.cyl = trk / geo.head;	beg.head = trk % geo.head;	end.cyl = totrk / geo.head;	end.head = totrk % geo.head;	/* check for sequential prestage - enhance cylinder range */	if (data->attributes.operation == DASD_SEQ_PRESTAGE ||	    data->attributes.operation == DASD_SEQ_ACCESS) {		if (end.cyl + private->attrib.nr_cyl < geo.cyl)			end.cyl += private->attrib.nr_cyl;		else			end.cyl = (geo.cyl - 1);	}	data->beg_ext.cyl = beg.cyl;	data->beg_ext.head = beg.head;	data->end_ext.cyl = end.cyl;	data->end_ext.head = end.head;	return rc;}static voidlocate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,	      int rec_on_trk, int no_rec, int cmd,	      struct dasd_device * device, int reclen){	struct dasd_eckd_private *private;	int sector;	int dn, d;	private = (struct dasd_eckd_private *) device->private;	DBF_DEV_EVENT(DBF_INFO, device,		  "Locate: trk %d, rec %d, no_rec %d, cmd %d, reclen %d",		  trk, rec_on_trk, no_rec, cmd, reclen);	ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD;	ccw->flags = 0;	ccw->count = 16;	ccw->cda = (__u32) __pa(data);	memset(data, 0, sizeof (struct LO_eckd_data));	sector = 0;	if (rec_on_trk) {		switch (private->rdc_data.dev_type) {		case 0x3390:			dn = ceil_quot(reclen + 6, 232);			d = 9 + ceil_quot(reclen + 6 * (dn + 1), 34);			sector = (49 + (rec_on_trk - 1) * (10 + d)) / 8;			break;		case 0x3380:			d = 7 + ceil_quot(reclen + 12, 32);			sector = (39 + (rec_on_trk - 1) * (8 + d)) / 7;			break;		}	}	data->sector = sector;	data->count = no_rec;	switch (cmd) {	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:		data->operation.orientation = 0x3;		data->operation.operation = 0x03;		break;	case DASD_ECKD_CCW_READ_HOME_ADDRESS:		data->operation.orientation = 0x3;		data->operation.operation = 0x16;		break;	case DASD_ECKD_CCW_WRITE_RECORD_ZERO:		data->operation.orientation = 0x1;		data->operation.operation = 0x03;		data->count++;		break;	case DASD_ECKD_CCW_READ_RECORD_ZERO:		data->operation.orientation = 0x3;		data->operation.operation = 0x16;		data->count++;		break;	case DASD_ECKD_CCW_WRITE:	case DASD_ECKD_CCW_WRITE_MT:	case DASD_ECKD_CCW_WRITE_KD:	case DASD_ECKD_CCW_WRITE_KD_MT:		data->auxiliary.last_bytes_used = 0x1;		data->length = reclen;		data->operation.operation = 0x01;		break;	case DASD_ECKD_CCW_WRITE_CKD:	case DASD_ECKD_CCW_WRITE_CKD_MT:		data->auxiliary.last_bytes_used = 0x1;		data->length = reclen;		data->operation.operation = 0x03;		break;	case DASD_ECKD_CCW_READ:	case DASD_ECKD_CCW_READ_MT:	case DASD_ECKD_CCW_READ_KD:	case DASD_ECKD_CCW_READ_KD_MT:		data->auxiliary.last_bytes_used = 0x1;		data->length = reclen;		data->operation.operation = 0x06;		break;	case DASD_ECKD_CCW_READ_CKD:	case DASD_ECKD_CCW_READ_CKD_MT:		data->auxiliary.last_bytes_used = 0x1;		data->length = reclen;		data->operation.operation = 0x16;		break;	case DASD_ECKD_CCW_READ_COUNT:		data->operation.operation = 0x06;		break;	case DASD_ECKD_CCW_ERASE:		data->length = reclen;		data->auxiliary.last_bytes_used = 0x1;		data->operation.operation = 0x0b;		break;	default:		DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd);	}	data->seek_addr.cyl = data->search_arg.cyl =		trk / private->rdc_data.trk_per_cyl;	data->seek_addr.head = data->search_arg.head =		trk % private->rdc_data.trk_per_cyl;	data->search_arg.record = rec_on_trk;}/* * Returns 1 if the block is one of the special blocks that needs * to get read/written with the KD variant of the command. * That is DASD_ECKD_READ_KD_MT instead of DASD_ECKD_READ_MT and * DASD_ECKD_WRITE_KD_MT instead of DASD_ECKD_WRITE_MT. * Luckily the KD variants differ only by one bit (0x08) from the * normal variant. So don't wonder about code like: * if (dasd_eckd_cdl_special(blk_per_trk, recid)) *         ccw->cmd_code |= 0x8; */static inline intdasd_eckd_cdl_special(int blk_per_trk, int recid){	if (recid < 3)		return 1;	if (recid < blk_per_trk)		return 0;	if (recid < 2 * blk_per_trk)		return 1;	return 0;}/* * Returns the record size for the special blocks of the cdl format. * Only returns something useful if dasd_eckd_cdl_special is true * for the recid. */static inline intdasd_eckd_cdl_reclen(int recid){	if (recid < 3)		return sizes_trk0[recid];	return LABEL_SIZE;}/* * Generate device unique id that specifies the physical device. */static intdasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid){	struct dasd_eckd_private *private;	struct dasd_eckd_confdata *confdata;	private = (struct dasd_eckd_private *) device->private;	if (!private)		return -ENODEV;	confdata = &private->conf_data;	if (!confdata)		return -ENODEV;	memset(uid, 0, sizeof(struct dasd_uid));	memcpy(uid->vendor, confdata->ned1.HDA_manufacturer,	       sizeof(uid->vendor) - 1);	EBCASC(uid->vendor, sizeof(uid->vendor) - 1);	memcpy(uid->serial, confdata->ned1.HDA_location,	       sizeof(uid->serial) - 1);	EBCASC(uid->serial, sizeof(uid->serial) - 1);	uid->ssid = confdata->neq.subsystemID;	if (confdata->ned2.sneq.flags == 0x40) {		uid->alias = 1;		uid->unit_addr = confdata->ned2.sneq.base_unit_addr;	} else		uid->unit_addr = confdata->ned1.unit_addr;	return 0;}static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device,						    void *rcd_buffer,						    struct ciw *ciw, __u8 lpm){	struct dasd_ccw_req *cqr;	struct ccw1 *ccw;

⌨️ 快捷键说明

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