|
@@ -2,7 +2,7 @@
|
|
|
* drivers/s390/char/tape_3590.c
|
|
|
* tape device discipline for 3590 tapes.
|
|
|
*
|
|
|
- * Copyright (C) IBM Corp. 2001,2006
|
|
|
+ * Copyright IBM Corp. 2001,2006
|
|
|
* Author(s): Stefan Bader <shbader@de.ibm.com>
|
|
|
* Michael Holzheu <holzheu@de.ibm.com>
|
|
|
* Martin Schwidefsky <schwidefsky@de.ibm.com>
|
|
@@ -11,6 +11,7 @@
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/bio.h>
|
|
|
+#include <asm/ebcdic.h>
|
|
|
|
|
|
#define TAPE_DBF_AREA tape_3590_dbf
|
|
|
|
|
@@ -30,7 +31,7 @@ EXPORT_SYMBOL(TAPE_DBF_AREA);
|
|
|
* - Read Device (buffered) log: BRA
|
|
|
* - Read Library log: BRA
|
|
|
* - Swap Devices: BRA
|
|
|
- * - Long Busy: BRA
|
|
|
+ * - Long Busy: implemented
|
|
|
* - Special Intercept: BRA
|
|
|
* - Read Alternate: implemented
|
|
|
*******************************************************************/
|
|
@@ -94,6 +95,332 @@ static const char *tape_3590_msg[TAPE_3590_MAX_MSG] = {
|
|
|
[0xae] = "Subsystem environmental alert",
|
|
|
};
|
|
|
|
|
|
+static int crypt_supported(struct tape_device *device)
|
|
|
+{
|
|
|
+ return TAPE390_CRYPT_SUPPORTED(TAPE_3590_CRYPT_INFO(device));
|
|
|
+}
|
|
|
+
|
|
|
+static int crypt_enabled(struct tape_device *device)
|
|
|
+{
|
|
|
+ return TAPE390_CRYPT_ON(TAPE_3590_CRYPT_INFO(device));
|
|
|
+}
|
|
|
+
|
|
|
+static void ext_to_int_kekl(struct tape390_kekl *in,
|
|
|
+ struct tape3592_kekl *out)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ memset(out, 0, sizeof(*out));
|
|
|
+ if (in->type == TAPE390_KEKL_TYPE_HASH)
|
|
|
+ out->flags |= 0x40;
|
|
|
+ if (in->type_on_tape == TAPE390_KEKL_TYPE_HASH)
|
|
|
+ out->flags |= 0x80;
|
|
|
+ strncpy(out->label, in->label, 64);
|
|
|
+ for (i = strlen(in->label); i < sizeof(out->label); i++)
|
|
|
+ out->label[i] = ' ';
|
|
|
+ ASCEBC(out->label, sizeof(out->label));
|
|
|
+}
|
|
|
+
|
|
|
+static void int_to_ext_kekl(struct tape3592_kekl *in,
|
|
|
+ struct tape390_kekl *out)
|
|
|
+{
|
|
|
+ memset(out, 0, sizeof(*out));
|
|
|
+ if(in->flags & 0x40)
|
|
|
+ out->type = TAPE390_KEKL_TYPE_HASH;
|
|
|
+ else
|
|
|
+ out->type = TAPE390_KEKL_TYPE_LABEL;
|
|
|
+ if(in->flags & 0x80)
|
|
|
+ out->type_on_tape = TAPE390_KEKL_TYPE_HASH;
|
|
|
+ else
|
|
|
+ out->type_on_tape = TAPE390_KEKL_TYPE_LABEL;
|
|
|
+ memcpy(out->label, in->label, sizeof(in->label));
|
|
|
+ EBCASC(out->label, sizeof(in->label));
|
|
|
+ strstrip(out->label);
|
|
|
+}
|
|
|
+
|
|
|
+static void int_to_ext_kekl_pair(struct tape3592_kekl_pair *in,
|
|
|
+ struct tape390_kekl_pair *out)
|
|
|
+{
|
|
|
+ if (in->count == 0) {
|
|
|
+ out->kekl[0].type = TAPE390_KEKL_TYPE_NONE;
|
|
|
+ out->kekl[0].type_on_tape = TAPE390_KEKL_TYPE_NONE;
|
|
|
+ out->kekl[1].type = TAPE390_KEKL_TYPE_NONE;
|
|
|
+ out->kekl[1].type_on_tape = TAPE390_KEKL_TYPE_NONE;
|
|
|
+ } else if (in->count == 1) {
|
|
|
+ int_to_ext_kekl(&in->kekl[0], &out->kekl[0]);
|
|
|
+ out->kekl[1].type = TAPE390_KEKL_TYPE_NONE;
|
|
|
+ out->kekl[1].type_on_tape = TAPE390_KEKL_TYPE_NONE;
|
|
|
+ } else if (in->count == 2) {
|
|
|
+ int_to_ext_kekl(&in->kekl[0], &out->kekl[0]);
|
|
|
+ int_to_ext_kekl(&in->kekl[1], &out->kekl[1]);
|
|
|
+ } else {
|
|
|
+ printk("Invalid KEKL number: %d\n", in->count);
|
|
|
+ BUG();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int check_ext_kekl(struct tape390_kekl *kekl)
|
|
|
+{
|
|
|
+ if (kekl->type == TAPE390_KEKL_TYPE_NONE)
|
|
|
+ goto invalid;
|
|
|
+ if (kekl->type > TAPE390_KEKL_TYPE_HASH)
|
|
|
+ goto invalid;
|
|
|
+ if (kekl->type_on_tape == TAPE390_KEKL_TYPE_NONE)
|
|
|
+ goto invalid;
|
|
|
+ if (kekl->type_on_tape > TAPE390_KEKL_TYPE_HASH)
|
|
|
+ goto invalid;
|
|
|
+ if ((kekl->type == TAPE390_KEKL_TYPE_HASH) &&
|
|
|
+ (kekl->type_on_tape == TAPE390_KEKL_TYPE_LABEL))
|
|
|
+ goto invalid;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+invalid:
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+static int check_ext_kekl_pair(struct tape390_kekl_pair *kekls)
|
|
|
+{
|
|
|
+ if (check_ext_kekl(&kekls->kekl[0]))
|
|
|
+ goto invalid;
|
|
|
+ if (check_ext_kekl(&kekls->kekl[1]))
|
|
|
+ goto invalid;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+invalid:
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Query KEKLs
|
|
|
+ */
|
|
|
+static int tape_3592_kekl_query(struct tape_device *device,
|
|
|
+ struct tape390_kekl_pair *ext_kekls)
|
|
|
+{
|
|
|
+ struct tape_request *request;
|
|
|
+ struct tape3592_kekl_query_order *order;
|
|
|
+ struct tape3592_kekl_query_data *int_kekls;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ DBF_EVENT(6, "tape3592_kekl_query\n");
|
|
|
+ int_kekls = kmalloc(sizeof(*int_kekls), GFP_KERNEL|GFP_DMA);
|
|
|
+ if (!int_kekls)
|
|
|
+ return -ENOMEM;
|
|
|
+ request = tape_alloc_request(2, sizeof(*order));
|
|
|
+ if (IS_ERR(request)) {
|
|
|
+ rc = PTR_ERR(request);
|
|
|
+ goto fail_malloc;
|
|
|
+ }
|
|
|
+ order = request->cpdata;
|
|
|
+ memset(order,0,sizeof(*order));
|
|
|
+ order->code = 0xe2;
|
|
|
+ order->max_count = 2;
|
|
|
+ request->op = TO_KEKL_QUERY;
|
|
|
+ tape_ccw_cc(request->cpaddr, PERF_SUBSYS_FUNC, sizeof(*order), order);
|
|
|
+ tape_ccw_end(request->cpaddr + 1, READ_SS_DATA, sizeof(*int_kekls),
|
|
|
+ int_kekls);
|
|
|
+ rc = tape_do_io(device, request);
|
|
|
+ if (rc)
|
|
|
+ goto fail_request;
|
|
|
+ int_to_ext_kekl_pair(&int_kekls->kekls, ext_kekls);
|
|
|
+
|
|
|
+ rc = 0;
|
|
|
+fail_request:
|
|
|
+ tape_free_request(request);
|
|
|
+fail_malloc:
|
|
|
+ kfree(int_kekls);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * IOCTL: Query KEKLs
|
|
|
+ */
|
|
|
+static int tape_3592_ioctl_kekl_query(struct tape_device *device,
|
|
|
+ unsigned long arg)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ struct tape390_kekl_pair *ext_kekls;
|
|
|
+
|
|
|
+ DBF_EVENT(6, "tape_3592_ioctl_kekl_query\n");
|
|
|
+ if (!crypt_supported(device))
|
|
|
+ return -ENOSYS;
|
|
|
+ if (!crypt_enabled(device))
|
|
|
+ return -EUNATCH;
|
|
|
+ ext_kekls = kmalloc(sizeof(*ext_kekls), GFP_KERNEL);
|
|
|
+ if (!ext_kekls)
|
|
|
+ return -ENOMEM;
|
|
|
+ rc = tape_3592_kekl_query(device, ext_kekls);
|
|
|
+ if (rc != 0)
|
|
|
+ goto fail;
|
|
|
+ if (copy_to_user((char __user *) arg, ext_kekls, sizeof(*ext_kekls))) {
|
|
|
+ rc = -EFAULT;
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ rc = 0;
|
|
|
+fail:
|
|
|
+ kfree(ext_kekls);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static int tape_3590_mttell(struct tape_device *device, int mt_count);
|
|
|
+
|
|
|
+/*
|
|
|
+ * Set KEKLs
|
|
|
+ */
|
|
|
+static int tape_3592_kekl_set(struct tape_device *device,
|
|
|
+ struct tape390_kekl_pair *ext_kekls)
|
|
|
+{
|
|
|
+ struct tape_request *request;
|
|
|
+ struct tape3592_kekl_set_order *order;
|
|
|
+
|
|
|
+ DBF_EVENT(6, "tape3592_kekl_set\n");
|
|
|
+ if (check_ext_kekl_pair(ext_kekls)) {
|
|
|
+ DBF_EVENT(6, "invalid kekls\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ if (tape_3590_mttell(device, 0) != 0)
|
|
|
+ return -EBADSLT;
|
|
|
+ request = tape_alloc_request(1, sizeof(*order));
|
|
|
+ if (IS_ERR(request))
|
|
|
+ return PTR_ERR(request);
|
|
|
+ order = request->cpdata;
|
|
|
+ memset(order, 0, sizeof(*order));
|
|
|
+ order->code = 0xe3;
|
|
|
+ order->kekls.count = 2;
|
|
|
+ ext_to_int_kekl(&ext_kekls->kekl[0], &order->kekls.kekl[0]);
|
|
|
+ ext_to_int_kekl(&ext_kekls->kekl[1], &order->kekls.kekl[1]);
|
|
|
+ request->op = TO_KEKL_SET;
|
|
|
+ tape_ccw_end(request->cpaddr, PERF_SUBSYS_FUNC, sizeof(*order), order);
|
|
|
+
|
|
|
+ return tape_do_io_free(device, request);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * IOCTL: Set KEKLs
|
|
|
+ */
|
|
|
+static int tape_3592_ioctl_kekl_set(struct tape_device *device,
|
|
|
+ unsigned long arg)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ struct tape390_kekl_pair *ext_kekls;
|
|
|
+
|
|
|
+ DBF_EVENT(6, "tape_3592_ioctl_kekl_set\n");
|
|
|
+ if (!crypt_supported(device))
|
|
|
+ return -ENOSYS;
|
|
|
+ if (!crypt_enabled(device))
|
|
|
+ return -EUNATCH;
|
|
|
+ ext_kekls = kmalloc(sizeof(*ext_kekls), GFP_KERNEL);
|
|
|
+ if (!ext_kekls)
|
|
|
+ return -ENOMEM;
|
|
|
+ if (copy_from_user(ext_kekls, (char __user *)arg, sizeof(*ext_kekls))) {
|
|
|
+ rc = -EFAULT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ rc = tape_3592_kekl_set(device, ext_kekls);
|
|
|
+out:
|
|
|
+ kfree(ext_kekls);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Enable encryption
|
|
|
+ */
|
|
|
+static int tape_3592_enable_crypt(struct tape_device *device)
|
|
|
+{
|
|
|
+ struct tape_request *request;
|
|
|
+ char *data;
|
|
|
+
|
|
|
+ DBF_EVENT(6, "tape_3592_enable_crypt\n");
|
|
|
+ if (!crypt_supported(device))
|
|
|
+ return -ENOSYS;
|
|
|
+ request = tape_alloc_request(2, 72);
|
|
|
+ if (IS_ERR(request))
|
|
|
+ return PTR_ERR(request);
|
|
|
+ data = request->cpdata;
|
|
|
+ memset(data,0,72);
|
|
|
+
|
|
|
+ data[0] = 0x05;
|
|
|
+ data[36 + 0] = 0x03;
|
|
|
+ data[36 + 1] = 0x03;
|
|
|
+ data[36 + 4] = 0x40;
|
|
|
+ data[36 + 6] = 0x01;
|
|
|
+ data[36 + 14] = 0x2f;
|
|
|
+ data[36 + 18] = 0xc3;
|
|
|
+ data[36 + 35] = 0x72;
|
|
|
+ request->op = TO_CRYPT_ON;
|
|
|
+ tape_ccw_cc(request->cpaddr, MODE_SET_CB, 36, data);
|
|
|
+ tape_ccw_end(request->cpaddr + 1, MODE_SET_CB, 36, data + 36);
|
|
|
+ return tape_do_io_free(device, request);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Disable encryption
|
|
|
+ */
|
|
|
+static int tape_3592_disable_crypt(struct tape_device *device)
|
|
|
+{
|
|
|
+ struct tape_request *request;
|
|
|
+ char *data;
|
|
|
+
|
|
|
+ DBF_EVENT(6, "tape_3592_disable_crypt\n");
|
|
|
+ if (!crypt_supported(device))
|
|
|
+ return -ENOSYS;
|
|
|
+ request = tape_alloc_request(2, 72);
|
|
|
+ if (IS_ERR(request))
|
|
|
+ return PTR_ERR(request);
|
|
|
+ data = request->cpdata;
|
|
|
+ memset(data,0,72);
|
|
|
+
|
|
|
+ data[0] = 0x05;
|
|
|
+ data[36 + 0] = 0x03;
|
|
|
+ data[36 + 1] = 0x03;
|
|
|
+ data[36 + 35] = 0x32;
|
|
|
+
|
|
|
+ request->op = TO_CRYPT_OFF;
|
|
|
+ tape_ccw_cc(request->cpaddr, MODE_SET_CB, 36, data);
|
|
|
+ tape_ccw_end(request->cpaddr + 1, MODE_SET_CB, 36, data + 36);
|
|
|
+
|
|
|
+ return tape_do_io_free(device, request);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * IOCTL: Set encryption status
|
|
|
+ */
|
|
|
+static int tape_3592_ioctl_crypt_set(struct tape_device *device,
|
|
|
+ unsigned long arg)
|
|
|
+{
|
|
|
+ struct tape390_crypt_info info;
|
|
|
+
|
|
|
+ DBF_EVENT(6, "tape_3592_ioctl_crypt_set\n");
|
|
|
+ if (!crypt_supported(device))
|
|
|
+ return -ENOSYS;
|
|
|
+ if (copy_from_user(&info, (char __user *)arg, sizeof(info)))
|
|
|
+ return -EFAULT;
|
|
|
+ if (info.status & ~TAPE390_CRYPT_ON_MASK)
|
|
|
+ return -EINVAL;
|
|
|
+ if (info.status & TAPE390_CRYPT_ON_MASK)
|
|
|
+ return tape_3592_enable_crypt(device);
|
|
|
+ else
|
|
|
+ return tape_3592_disable_crypt(device);
|
|
|
+}
|
|
|
+
|
|
|
+static int tape_3590_sense_medium(struct tape_device *device);
|
|
|
+
|
|
|
+/*
|
|
|
+ * IOCTL: Query enryption status
|
|
|
+ */
|
|
|
+static int tape_3592_ioctl_crypt_query(struct tape_device *device,
|
|
|
+ unsigned long arg)
|
|
|
+{
|
|
|
+ DBF_EVENT(6, "tape_3592_ioctl_crypt_query\n");
|
|
|
+ if (!crypt_supported(device))
|
|
|
+ return -ENOSYS;
|
|
|
+ tape_3590_sense_medium(device);
|
|
|
+ if (copy_to_user((char __user *) arg, &TAPE_3590_CRYPT_INFO(device),
|
|
|
+ sizeof(TAPE_3590_CRYPT_INFO(device))))
|
|
|
+ return -EFAULT;
|
|
|
+ else
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* 3590 IOCTL Overload
|
|
|
*/
|
|
@@ -109,6 +436,14 @@ tape_3590_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg)
|
|
|
|
|
|
return tape_std_display(device, &disp);
|
|
|
}
|
|
|
+ case TAPE390_KEKL_SET:
|
|
|
+ return tape_3592_ioctl_kekl_set(device, arg);
|
|
|
+ case TAPE390_KEKL_QUERY:
|
|
|
+ return tape_3592_ioctl_kekl_query(device, arg);
|
|
|
+ case TAPE390_CRYPT_SET:
|
|
|
+ return tape_3592_ioctl_crypt_set(device, arg);
|
|
|
+ case TAPE390_CRYPT_QUERY:
|
|
|
+ return tape_3592_ioctl_crypt_query(device, arg);
|
|
|
default:
|
|
|
return -EINVAL; /* no additional ioctls */
|
|
|
}
|
|
@@ -248,6 +583,12 @@ tape_3590_work_handler(struct work_struct *work)
|
|
|
case TO_READ_ATTMSG:
|
|
|
tape_3590_read_attmsg(p->device);
|
|
|
break;
|
|
|
+ case TO_CRYPT_ON:
|
|
|
+ tape_3592_enable_crypt(p->device);
|
|
|
+ break;
|
|
|
+ case TO_CRYPT_OFF:
|
|
|
+ tape_3592_disable_crypt(p->device);
|
|
|
+ break;
|
|
|
default:
|
|
|
DBF_EVENT(3, "T3590: work handler undefined for "
|
|
|
"operation 0x%02x\n", p->op);
|
|
@@ -365,6 +706,33 @@ tape_3590_check_locate(struct tape_device *device, struct tape_request *request)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+static void tape_3590_med_state_set(struct tape_device *device,
|
|
|
+ struct tape_3590_med_sense *sense)
|
|
|
+{
|
|
|
+ struct tape390_crypt_info *c_info;
|
|
|
+
|
|
|
+ c_info = &TAPE_3590_CRYPT_INFO(device);
|
|
|
+
|
|
|
+ if (sense->masst == MSENSE_UNASSOCIATED) {
|
|
|
+ tape_med_state_set(device, MS_UNLOADED);
|
|
|
+ TAPE_3590_CRYPT_INFO(device).medium_status = 0;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (sense->masst != MSENSE_ASSOCIATED_MOUNT) {
|
|
|
+ PRINT_ERR("Unknown medium state: %x\n", sense->masst);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ tape_med_state_set(device, MS_LOADED);
|
|
|
+ c_info->medium_status |= TAPE390_MEDIUM_LOADED_MASK;
|
|
|
+ if (sense->flags & MSENSE_CRYPT_MASK) {
|
|
|
+ PRINT_INFO("Medium is encrypted (%04x)\n", sense->flags);
|
|
|
+ c_info->medium_status |= TAPE390_MEDIUM_ENCRYPTED_MASK;
|
|
|
+ } else {
|
|
|
+ DBF_EVENT(6, "Medium is not encrypted %04x\n", sense->flags);
|
|
|
+ c_info->medium_status &= ~TAPE390_MEDIUM_ENCRYPTED_MASK;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* The done handler is called at device/channel end and wakes up the sleeping
|
|
|
* process
|
|
@@ -372,9 +740,10 @@ tape_3590_check_locate(struct tape_device *device, struct tape_request *request)
|
|
|
static int
|
|
|
tape_3590_done(struct tape_device *device, struct tape_request *request)
|
|
|
{
|
|
|
- struct tape_3590_med_sense *sense;
|
|
|
+ struct tape_3590_disc_data *disc_data;
|
|
|
|
|
|
DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);
|
|
|
+ disc_data = device->discdata;
|
|
|
|
|
|
switch (request->op) {
|
|
|
case TO_BSB:
|
|
@@ -394,13 +763,20 @@ tape_3590_done(struct tape_device *device, struct tape_request *request)
|
|
|
break;
|
|
|
case TO_RUN:
|
|
|
tape_med_state_set(device, MS_UNLOADED);
|
|
|
+ tape_3590_schedule_work(device, TO_CRYPT_OFF);
|
|
|
break;
|
|
|
case TO_MSEN:
|
|
|
- sense = (struct tape_3590_med_sense *) request->cpdata;
|
|
|
- if (sense->masst == MSENSE_UNASSOCIATED)
|
|
|
- tape_med_state_set(device, MS_UNLOADED);
|
|
|
- if (sense->masst == MSENSE_ASSOCIATED_MOUNT)
|
|
|
- tape_med_state_set(device, MS_LOADED);
|
|
|
+ tape_3590_med_state_set(device, request->cpdata);
|
|
|
+ break;
|
|
|
+ case TO_CRYPT_ON:
|
|
|
+ TAPE_3590_CRYPT_INFO(device).status
|
|
|
+ |= TAPE390_CRYPT_ON_MASK;
|
|
|
+ *(device->modeset_byte) |= 0x03;
|
|
|
+ break;
|
|
|
+ case TO_CRYPT_OFF:
|
|
|
+ TAPE_3590_CRYPT_INFO(device).status
|
|
|
+ &= ~TAPE390_CRYPT_ON_MASK;
|
|
|
+ *(device->modeset_byte) &= ~0x03;
|
|
|
break;
|
|
|
case TO_RBI: /* RBI seems to succeed even without medium loaded. */
|
|
|
case TO_NOP: /* Same to NOP. */
|
|
@@ -409,8 +785,9 @@ tape_3590_done(struct tape_device *device, struct tape_request *request)
|
|
|
case TO_DIS:
|
|
|
case TO_ASSIGN:
|
|
|
case TO_UNASSIGN:
|
|
|
- break;
|
|
|
case TO_SIZE:
|
|
|
+ case TO_KEKL_SET:
|
|
|
+ case TO_KEKL_QUERY:
|
|
|
break;
|
|
|
}
|
|
|
return TAPE_IO_SUCCESS;
|
|
@@ -540,10 +917,8 @@ static int
|
|
|
tape_3590_erp_long_busy(struct tape_device *device,
|
|
|
struct tape_request *request, struct irb *irb)
|
|
|
{
|
|
|
- /* FIXME: how about WAITING for a minute ? */
|
|
|
- PRINT_WARN("(%s): Device is busy! Please wait a minute!\n",
|
|
|
- device->cdev->dev.bus_id);
|
|
|
- return tape_3590_erp_basic(device, request, irb, -EBUSY);
|
|
|
+ DBF_EVENT(6, "Device is busy\n");
|
|
|
+ return TAPE_IO_LONG_BUSY;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -951,6 +1326,34 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
|
|
|
device->cdev->dev.bus_id, sense->mc);
|
|
|
}
|
|
|
|
|
|
+static int tape_3590_crypt_error(struct tape_device *device,
|
|
|
+ struct tape_request *request, struct irb *irb)
|
|
|
+{
|
|
|
+ u8 cu_rc, ekm_rc1;
|
|
|
+ u16 ekm_rc2;
|
|
|
+ u32 drv_rc;
|
|
|
+ char *bus_id, *sense;
|
|
|
+
|
|
|
+ sense = ((struct tape_3590_sense *) irb->ecw)->fmt.data;
|
|
|
+ bus_id = device->cdev->dev.bus_id;
|
|
|
+ cu_rc = sense[0];
|
|
|
+ drv_rc = *((u32*) &sense[5]) & 0xffffff;
|
|
|
+ ekm_rc1 = sense[9];
|
|
|
+ ekm_rc2 = *((u16*) &sense[10]);
|
|
|
+ if ((cu_rc == 0) && (ekm_rc2 == 0xee31))
|
|
|
+ /* key not defined on EKM */
|
|
|
+ return tape_3590_erp_basic(device, request, irb, -EKEYREJECTED);
|
|
|
+ if ((cu_rc == 1) || (cu_rc == 2))
|
|
|
+ /* No connection to EKM */
|
|
|
+ return tape_3590_erp_basic(device, request, irb, -ENOTCONN);
|
|
|
+
|
|
|
+ PRINT_ERR("(%s): Unable to get encryption key from EKM\n", bus_id);
|
|
|
+ PRINT_ERR("(%s): CU=%02X DRIVE=%06X EKM=%02X:%04X\n", bus_id, cu_rc,
|
|
|
+ drv_rc, ekm_rc1, ekm_rc2);
|
|
|
+
|
|
|
+ return tape_3590_erp_basic(device, request, irb, -ENOKEY);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* 3590 error Recovery routine:
|
|
|
* If possible, it tries to recover from the error. If this is not possible,
|
|
@@ -979,6 +1382,8 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
|
|
|
|
|
|
sense = (struct tape_3590_sense *) irb->ecw;
|
|
|
|
|
|
+ DBF_EVENT(6, "Unit Check: RQC = %x\n", sense->rc_rqc);
|
|
|
+
|
|
|
/*
|
|
|
* First check all RC-QRCs where we want to do something special
|
|
|
* - "break": basic error recovery is done
|
|
@@ -999,6 +1404,8 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
|
|
|
case 0x2231:
|
|
|
tape_3590_print_era_msg(device, irb);
|
|
|
return tape_3590_erp_special_interrupt(device, request, irb);
|
|
|
+ case 0x2240:
|
|
|
+ return tape_3590_crypt_error(device, request, irb);
|
|
|
|
|
|
case 0x3010:
|
|
|
DBF_EVENT(2, "(%08x): Backward at Beginning of Partition\n",
|
|
@@ -1020,6 +1427,7 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
|
|
|
DBF_EVENT(2, "(%08x): Rewind Unload complete\n",
|
|
|
device->cdev_id);
|
|
|
tape_med_state_set(device, MS_UNLOADED);
|
|
|
+ tape_3590_schedule_work(device, TO_CRYPT_OFF);
|
|
|
return tape_3590_erp_basic(device, request, irb, 0);
|
|
|
|
|
|
case 0x4010:
|
|
@@ -1030,9 +1438,15 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
|
|
|
PRINT_WARN("(%s): Tape operation when medium not loaded\n",
|
|
|
device->cdev->dev.bus_id);
|
|
|
tape_med_state_set(device, MS_UNLOADED);
|
|
|
+ tape_3590_schedule_work(device, TO_CRYPT_OFF);
|
|
|
return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM);
|
|
|
case 0x4012: /* Device Long Busy */
|
|
|
+ /* XXX: Also use long busy handling here? */
|
|
|
+ DBF_EVENT(6, "(%08x): LONG BUSY\n", device->cdev_id);
|
|
|
tape_3590_print_era_msg(device, irb);
|
|
|
+ return tape_3590_erp_basic(device, request, irb, -EBUSY);
|
|
|
+ case 0x4014:
|
|
|
+ DBF_EVENT(6, "(%08x): Crypto LONG BUSY\n", device->cdev_id);
|
|
|
return tape_3590_erp_long_busy(device, request, irb);
|
|
|
|
|
|
case 0x5010:
|
|
@@ -1064,6 +1478,7 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
|
|
|
case 0x5120:
|
|
|
case 0x1120:
|
|
|
tape_med_state_set(device, MS_UNLOADED);
|
|
|
+ tape_3590_schedule_work(device, TO_CRYPT_OFF);
|
|
|
return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM);
|
|
|
|
|
|
case 0x6020:
|
|
@@ -1142,21 +1557,47 @@ tape_3590_setup_device(struct tape_device *device)
|
|
|
{
|
|
|
int rc;
|
|
|
struct tape_3590_disc_data *data;
|
|
|
+ char *rdc_data;
|
|
|
|
|
|
DBF_EVENT(6, "3590 device setup\n");
|
|
|
- data = kmalloc(sizeof(struct tape_3590_disc_data),
|
|
|
- GFP_KERNEL | GFP_DMA);
|
|
|
+ data = kzalloc(sizeof(struct tape_3590_disc_data), GFP_KERNEL | GFP_DMA);
|
|
|
if (data == NULL)
|
|
|
return -ENOMEM;
|
|
|
data->read_back_op = READ_PREVIOUS;
|
|
|
device->discdata = data;
|
|
|
|
|
|
- if ((rc = tape_std_assign(device)) == 0) {
|
|
|
- /* Try to find out if medium is loaded */
|
|
|
- if ((rc = tape_3590_sense_medium(device)) != 0)
|
|
|
- DBF_LH(3, "3590 medium sense returned %d\n", rc);
|
|
|
+ rdc_data = kmalloc(64, GFP_KERNEL | GFP_DMA);
|
|
|
+ if (!rdc_data) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto fail_kmalloc;
|
|
|
+ }
|
|
|
+ rc = read_dev_chars(device->cdev, (void**)&rdc_data, 64);
|
|
|
+ if (rc) {
|
|
|
+ DBF_LH(3, "Read device characteristics failed!\n");
|
|
|
+ goto fail_kmalloc;
|
|
|
+ }
|
|
|
+ rc = tape_std_assign(device);
|
|
|
+ if (rc)
|
|
|
+ goto fail_rdc_data;
|
|
|
+ if (rdc_data[31] == 0x13) {
|
|
|
+ PRINT_INFO("Device has crypto support\n");
|
|
|
+ data->crypt_info.capability |= TAPE390_CRYPT_SUPPORTED_MASK;
|
|
|
+ tape_3592_disable_crypt(device);
|
|
|
+ } else {
|
|
|
+ DBF_EVENT(6, "Device has NO crypto support\n");
|
|
|
}
|
|
|
+ /* Try to find out if medium is loaded */
|
|
|
+ rc = tape_3590_sense_medium(device);
|
|
|
+ if (rc) {
|
|
|
+ DBF_LH(3, "3590 medium sense returned %d\n", rc);
|
|
|
+ goto fail_rdc_data;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
|
|
|
+fail_rdc_data:
|
|
|
+ kfree(rdc_data);
|
|
|
+fail_kmalloc:
|
|
|
+ kfree(data);
|
|
|
return rc;
|
|
|
}
|
|
|
|