tape_3590.c
来自「linux 内核源代码」· C语言 代码 · 共 1,767 行 · 第 1/4 页
C
1,767 行
/* * drivers/s390/char/tape_3590.c * tape device discipline for 3590 tapes. * * 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> */#include <linux/module.h>#include <linux/init.h>#include <linux/bio.h>#include <asm/ebcdic.h>#define TAPE_DBF_AREA tape_3590_dbf#include "tape.h"#include "tape_std.h"#include "tape_3590.h"/* * Pointer to debug area. */debug_info_t *TAPE_DBF_AREA = NULL;EXPORT_SYMBOL(TAPE_DBF_AREA);/******************************************************************* * Error Recovery fuctions: * - Read Opposite: implemented * - Read Device (buffered) log: BRA * - Read Library log: BRA * - Swap Devices: BRA * - Long Busy: implemented * - Special Intercept: BRA * - Read Alternate: implemented *******************************************************************/#define PRINTK_HEADER "TAPE_3590: "static const char *tape_3590_msg[TAPE_3590_MAX_MSG] = { [0x00] = "", [0x10] = "Lost Sense", [0x11] = "Assigned Elsewhere", [0x12] = "Allegiance Reset", [0x13] = "Shared Access Violation", [0x20] = "Command Reject", [0x21] = "Configuration Error", [0x22] = "Protection Exception", [0x23] = "Write Protect", [0x24] = "Write Length", [0x25] = "Read-Only Format", [0x31] = "Beginning of Partition", [0x33] = "End of Partition", [0x34] = "End of Data", [0x35] = "Block not found", [0x40] = "Device Intervention", [0x41] = "Loader Intervention", [0x42] = "Library Intervention", [0x50] = "Write Error", [0x51] = "Erase Error", [0x52] = "Formatting Error", [0x53] = "Read Error", [0x54] = "Unsupported Format", [0x55] = "No Formatting", [0x56] = "Positioning lost", [0x57] = "Read Length", [0x60] = "Unsupported Medium", [0x61] = "Medium Length Error", [0x62] = "Medium removed", [0x64] = "Load Check", [0x65] = "Unload Check", [0x70] = "Equipment Check", [0x71] = "Bus out Check", [0x72] = "Protocol Error", [0x73] = "Interface Error", [0x74] = "Overrun", [0x75] = "Halt Signal", [0x90] = "Device fenced", [0x91] = "Device Path fenced", [0xa0] = "Volume misplaced", [0xa1] = "Volume inaccessible", [0xa2] = "Volume in input", [0xa3] = "Volume ejected", [0xa4] = "All categories reserved", [0xa5] = "Duplicate Volume", [0xa6] = "Library Manager Offline", [0xa7] = "Library Output Station full", [0xa8] = "Vision System non-operational", [0xa9] = "Library Manager Equipment Check", [0xaa] = "Library Equipment Check", [0xab] = "All Library Cells full", [0xac] = "No Cleaner Volumes in Library", [0xad] = "I/O Station door open", [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 */static inttape_3590_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg){ switch (cmd) { case TAPE390_DISPLAY: { struct display_struct disp; if (copy_from_user(&disp, (char __user *) arg, sizeof(disp))) return -EFAULT; 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);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?