|
@@ -313,8 +313,8 @@ static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, int trk,
|
|
|
memset(pfxdata, 0, sizeof(*pfxdata));
|
|
|
/* prefix data */
|
|
|
pfxdata->format = 0;
|
|
|
- pfxdata->base_address = basepriv->conf_data.ned1.unit_addr;
|
|
|
- pfxdata->base_lss = basepriv->conf_data.ned1.ID;
|
|
|
+ pfxdata->base_address = basepriv->ned->unit_addr;
|
|
|
+ pfxdata->base_lss = basepriv->ned->ID;
|
|
|
pfxdata->validity.define_extend = 1;
|
|
|
|
|
|
/* private uid is kept up to date, conf_data may be outdated */
|
|
@@ -536,36 +536,40 @@ dasd_eckd_cdl_reclen(int recid)
|
|
|
/*
|
|
|
* Generate device unique id that specifies the physical device.
|
|
|
*/
|
|
|
-static int
|
|
|
-dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid)
|
|
|
+static int dasd_eckd_generate_uid(struct dasd_device *device,
|
|
|
+ struct dasd_uid *uid)
|
|
|
{
|
|
|
struct dasd_eckd_private *private;
|
|
|
- struct dasd_eckd_confdata *confdata;
|
|
|
+ int count;
|
|
|
|
|
|
private = (struct dasd_eckd_private *) device->private;
|
|
|
if (!private)
|
|
|
return -ENODEV;
|
|
|
- confdata = &private->conf_data;
|
|
|
- if (!confdata)
|
|
|
+ if (!private->ned || !private->gneq)
|
|
|
return -ENODEV;
|
|
|
|
|
|
memset(uid, 0, sizeof(struct dasd_uid));
|
|
|
- memcpy(uid->vendor, confdata->ned1.HDA_manufacturer,
|
|
|
+ memcpy(uid->vendor, private->ned->HDA_manufacturer,
|
|
|
sizeof(uid->vendor) - 1);
|
|
|
EBCASC(uid->vendor, sizeof(uid->vendor) - 1);
|
|
|
- memcpy(uid->serial, confdata->ned1.HDA_location,
|
|
|
+ memcpy(uid->serial, private->ned->HDA_location,
|
|
|
sizeof(uid->serial) - 1);
|
|
|
EBCASC(uid->serial, sizeof(uid->serial) - 1);
|
|
|
- uid->ssid = confdata->neq.subsystemID;
|
|
|
- uid->real_unit_addr = confdata->ned1.unit_addr;
|
|
|
- if (confdata->ned2.sneq.flags == 0x40 &&
|
|
|
- confdata->ned2.sneq.format == 0x0001) {
|
|
|
- uid->type = confdata->ned2.sneq.sua_flags;
|
|
|
+ uid->ssid = private->gneq->subsystemID;
|
|
|
+ uid->real_unit_addr = private->ned->unit_addr;;
|
|
|
+ if (private->sneq) {
|
|
|
+ uid->type = private->sneq->sua_flags;
|
|
|
if (uid->type == UA_BASE_PAV_ALIAS)
|
|
|
- uid->base_unit_addr = confdata->ned2.sneq.base_unit_addr;
|
|
|
+ uid->base_unit_addr = private->sneq->base_unit_addr;
|
|
|
} else {
|
|
|
uid->type = UA_BASE_DEVICE;
|
|
|
}
|
|
|
+ if (private->vdsneq) {
|
|
|
+ for (count = 0; count < 16; count++) {
|
|
|
+ sprintf(uid->vduit+2*count, "%02x",
|
|
|
+ private->vdsneq->uit[count]);
|
|
|
+ }
|
|
|
+ }
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -623,6 +627,15 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
|
|
|
ret = -ENOMEM;
|
|
|
goto out_error;
|
|
|
}
|
|
|
+
|
|
|
+ /*
|
|
|
+ * buffer has to start with EBCDIC "V1.0" to show
|
|
|
+ * support for virtual device SNEQ
|
|
|
+ */
|
|
|
+ rcd_buf[0] = 0xE5;
|
|
|
+ rcd_buf[1] = 0xF1;
|
|
|
+ rcd_buf[2] = 0x4B;
|
|
|
+ rcd_buf[3] = 0xF0;
|
|
|
cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm);
|
|
|
if (IS_ERR(cqr)) {
|
|
|
ret = PTR_ERR(cqr);
|
|
@@ -646,8 +659,62 @@ out_error:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-dasd_eckd_read_conf(struct dasd_device *device)
|
|
|
+static int dasd_eckd_identify_conf_parts(struct dasd_eckd_private *private)
|
|
|
+{
|
|
|
+
|
|
|
+ struct dasd_sneq *sneq;
|
|
|
+ int i, count;
|
|
|
+
|
|
|
+ private->ned = NULL;
|
|
|
+ private->sneq = NULL;
|
|
|
+ private->vdsneq = NULL;
|
|
|
+ private->gneq = NULL;
|
|
|
+ count = private->conf_len / sizeof(struct dasd_sneq);
|
|
|
+ sneq = (struct dasd_sneq *)private->conf_data;
|
|
|
+ for (i = 0; i < count; ++i) {
|
|
|
+ if (sneq->flags.identifier == 1 && sneq->format == 1)
|
|
|
+ private->sneq = sneq;
|
|
|
+ else if (sneq->flags.identifier == 1 && sneq->format == 4)
|
|
|
+ private->vdsneq = (struct vd_sneq *)sneq;
|
|
|
+ else if (sneq->flags.identifier == 2)
|
|
|
+ private->gneq = (struct dasd_gneq *)sneq;
|
|
|
+ else if (sneq->flags.identifier == 3 && sneq->res1 == 1)
|
|
|
+ private->ned = (struct dasd_ned *)sneq;
|
|
|
+ sneq++;
|
|
|
+ }
|
|
|
+ if (!private->ned || !private->gneq) {
|
|
|
+ private->ned = NULL;
|
|
|
+ private->sneq = NULL;
|
|
|
+ private->vdsneq = NULL;
|
|
|
+ private->gneq = NULL;
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+static unsigned char dasd_eckd_path_access(void *conf_data, int conf_len)
|
|
|
+{
|
|
|
+ struct dasd_gneq *gneq;
|
|
|
+ int i, count, found;
|
|
|
+
|
|
|
+ count = conf_len / sizeof(*gneq);
|
|
|
+ gneq = (struct dasd_gneq *)conf_data;
|
|
|
+ found = 0;
|
|
|
+ for (i = 0; i < count; ++i) {
|
|
|
+ if (gneq->flags.identifier == 2) {
|
|
|
+ found = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ gneq++;
|
|
|
+ }
|
|
|
+ if (found)
|
|
|
+ return ((char *)gneq)[18] & 0x07;
|
|
|
+ else
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int dasd_eckd_read_conf(struct dasd_device *device)
|
|
|
{
|
|
|
void *conf_data;
|
|
|
int conf_len, conf_data_saved;
|
|
@@ -661,7 +728,6 @@ dasd_eckd_read_conf(struct dasd_device *device)
|
|
|
path_data->opm = ccw_device_get_path_mask(device->cdev);
|
|
|
lpm = 0x80;
|
|
|
conf_data_saved = 0;
|
|
|
-
|
|
|
/* get configuration data per operational path */
|
|
|
for (lpm = 0x80; lpm; lpm>>= 1) {
|
|
|
if (lpm & path_data->opm){
|
|
@@ -678,22 +744,20 @@ dasd_eckd_read_conf(struct dasd_device *device)
|
|
|
"data retrieved");
|
|
|
continue; /* no error */
|
|
|
}
|
|
|
- if (conf_len != sizeof(struct dasd_eckd_confdata)) {
|
|
|
- MESSAGE(KERN_WARNING,
|
|
|
- "sizes of configuration data mismatch"
|
|
|
- "%d (read) vs %ld (expected)",
|
|
|
- conf_len,
|
|
|
- sizeof(struct dasd_eckd_confdata));
|
|
|
- kfree(conf_data);
|
|
|
- continue; /* no error */
|
|
|
- }
|
|
|
/* save first valid configuration data */
|
|
|
- if (!conf_data_saved){
|
|
|
- memcpy(&private->conf_data, conf_data,
|
|
|
- sizeof(struct dasd_eckd_confdata));
|
|
|
+ if (!conf_data_saved) {
|
|
|
+ kfree(private->conf_data);
|
|
|
+ private->conf_data = conf_data;
|
|
|
+ private->conf_len = conf_len;
|
|
|
+ if (dasd_eckd_identify_conf_parts(private)) {
|
|
|
+ private->conf_data = NULL;
|
|
|
+ private->conf_len = 0;
|
|
|
+ kfree(conf_data);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
conf_data_saved++;
|
|
|
}
|
|
|
- switch (((char *)conf_data)[242] & 0x07){
|
|
|
+ switch (dasd_eckd_path_access(conf_data, conf_len)) {
|
|
|
case 0x02:
|
|
|
path_data->npm |= lpm;
|
|
|
break;
|
|
@@ -701,7 +765,8 @@ dasd_eckd_read_conf(struct dasd_device *device)
|
|
|
path_data->ppm |= lpm;
|
|
|
break;
|
|
|
}
|
|
|
- kfree(conf_data);
|
|
|
+ if (conf_data != private->conf_data)
|
|
|
+ kfree(conf_data);
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
@@ -952,6 +1017,7 @@ out_err2:
|
|
|
dasd_free_block(device->block);
|
|
|
device->block = NULL;
|
|
|
out_err1:
|
|
|
+ kfree(private->conf_data);
|
|
|
kfree(device->private);
|
|
|
device->private = NULL;
|
|
|
return rc;
|
|
@@ -959,7 +1025,17 @@ out_err1:
|
|
|
|
|
|
static void dasd_eckd_uncheck_device(struct dasd_device *device)
|
|
|
{
|
|
|
+ struct dasd_eckd_private *private;
|
|
|
+
|
|
|
+ private = (struct dasd_eckd_private *) device->private;
|
|
|
dasd_alias_disconnect_device_from_lcu(device);
|
|
|
+ private->ned = NULL;
|
|
|
+ private->sneq = NULL;
|
|
|
+ private->vdsneq = NULL;
|
|
|
+ private->gneq = NULL;
|
|
|
+ private->conf_len = 0;
|
|
|
+ kfree(private->conf_data);
|
|
|
+ private->conf_data = NULL;
|
|
|
}
|
|
|
|
|
|
static struct dasd_ccw_req *
|
|
@@ -1746,9 +1822,10 @@ dasd_eckd_fill_info(struct dasd_device * device,
|
|
|
info->characteristics_size = sizeof(struct dasd_eckd_characteristics);
|
|
|
memcpy(info->characteristics, &private->rdc_data,
|
|
|
sizeof(struct dasd_eckd_characteristics));
|
|
|
- info->confdata_size = sizeof(struct dasd_eckd_confdata);
|
|
|
- memcpy(info->configuration_data, &private->conf_data,
|
|
|
- sizeof(struct dasd_eckd_confdata));
|
|
|
+ info->confdata_size = min((unsigned long)private->conf_len,
|
|
|
+ sizeof(info->configuration_data));
|
|
|
+ memcpy(info->configuration_data, private->conf_data,
|
|
|
+ info->confdata_size);
|
|
|
return 0;
|
|
|
}
|
|
|
|