/* * 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); }
void qdio_allocate_dbf(struct qdio_initialize *init_data, struct qdio_irq *irq_ptr) { char text[20]; DBF_EVENT("qfmt:%1d", init_data->q_format); DBF_HEX(init_data->adapter_name, 8); DBF_EVENT("qpff%4x", init_data->qib_param_field_format); DBF_HEX(&init_data->qib_param_field, sizeof(void *)); DBF_HEX(&init_data->input_slib_elements, sizeof(void *)); DBF_HEX(&init_data->output_slib_elements, sizeof(void *)); DBF_EVENT("niq:%1d noq:%1d", init_data->no_input_qs, init_data->no_output_qs); DBF_HEX(&init_data->input_handler, sizeof(void *)); DBF_HEX(&init_data->output_handler, sizeof(void *)); DBF_HEX(&init_data->int_parm, sizeof(long)); DBF_HEX(&init_data->input_sbal_addr_array, sizeof(void *)); DBF_HEX(&init_data->output_sbal_addr_array, sizeof(void *)); DBF_EVENT("irq:%8lx", (unsigned long)irq_ptr); /* allocate trace view for the interface */ snprintf(text, 20, "qdio_%s", dev_name(&init_data->cdev->dev)); irq_ptr->debug_area = debug_register(text, 2, 1, 16); debug_register_view(irq_ptr->debug_area, &debug_hex_ascii_view); debug_set_level(irq_ptr->debug_area, DBF_WARN); DBF_DEV_EVENT(DBF_ERR, irq_ptr, "dbf created"); }
/* * This function is called, when no request is outstanding and we get an * interrupt */ static int tape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb) { if (irb->scsw.dstat == 0x85 /* READY */) { /* A medium was inserted in the drive. */ DBF_EVENT(6, "xuud med\n"); tape_34xx_delete_sbid_from(device, 0); tape_34xx_schedule_work(device, TO_MSEN); } else { DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id); PRINT_WARN("Unsolicited IRQ (Device End) caught.\n"); tape_dump_sense(device, NULL, irb); } return TAPE_IO_SUCCESS; }
/* * 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); }
static inline int tape_34xx_erp_succeeded(struct tape_request *request) { DBF_EVENT(3, "Error Recovery successful for %s\n", tape_op_verbose[request->op]); return tape_34xx_done(request); }
static inline int tape_34xx_erp_failed(struct tape_request *request, int rc) { DBF_EVENT(3, "Error recovery failed for %s (RC=%d)\n", tape_op_verbose[request->op], rc); return rc; }
static void setup_queues(struct qdio_irq *irq_ptr, struct qdio_initialize *qdio_init) { struct qdio_q *q; void **input_sbal_array = qdio_init->input_sbal_addr_array; void **output_sbal_array = qdio_init->output_sbal_addr_array; struct qdio_outbuf_state *output_sbal_state_array = qdio_init->output_sbal_state_array; int i; for_each_input_queue(irq_ptr, q, i) { DBF_EVENT("inq:%1d", i); setup_queues_misc(q, irq_ptr, qdio_init->input_handler, i); q->is_input_q = 1; q->u.in.queue_start_poll = qdio_init->queue_start_poll_array ? qdio_init->queue_start_poll_array[i] : NULL; setup_storage_lists(q, irq_ptr, input_sbal_array, i); input_sbal_array += QDIO_MAX_BUFFERS_PER_Q; if (is_thinint_irq(irq_ptr)) { tasklet_init(&q->tasklet, tiqdio_inbound_processing, (unsigned long) q); } else { tasklet_init(&q->tasklet, qdio_inbound_processing, (unsigned long) q); } }
/* * 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 void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr, void **sbals_array, int i) { struct qdio_q *prev; int j; DBF_HEX(&q, sizeof(void *)); q->sl = (struct sl *)((char *)q->slib + PAGE_SIZE / 2); /* fill in sbal */ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) { q->sbal[j] = *sbals_array++; WARN_ON((unsigned long)q->sbal[j] & 0xff); } /* fill in slib */ if (i > 0) { prev = (q->is_input_q) ? irq_ptr->input_qs[i - 1] : irq_ptr->output_qs[i - 1]; prev->slib->nsliba = (unsigned long)q->slib; } q->slib->sla = (unsigned long)q->sl; q->slib->slsba = (unsigned long)&q->slsb.val[0]; /* fill in sl */ for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++) q->sl->element[j].sbal = (unsigned long)q->sbal[j]; DBF_EVENT("sl-slsb-sbal"); DBF_HEX(q->sl, sizeof(void *)); DBF_HEX(&q->slsb, sizeof(void *)); DBF_HEX(q->sbal, sizeof(void *)); }
/* * Medium sense for 34xx tapes. There is no 'real' medium sense call. * So we just do a normal sense. */ static void __tape_34xx_medium_sense(struct tape_request *request) { struct tape_device *device = request->device; unsigned char *sense; if (request->rc == 0) { sense = request->cpdata; /* * This isn't quite correct. But since INTERVENTION_REQUIRED * means that the drive is 'neither ready nor on-line' it is * only slightly inaccurate to say there is no tape loaded if * the drive isn't online... */ if (sense[0] & SENSE_INTERVENTION_REQUIRED) tape_med_state_set(device, MS_UNLOADED); else tape_med_state_set(device, MS_LOADED); if (sense[1] & SENSE_WRITE_PROTECT) device->tape_generic_status |= GMT_WR_PROT(~0); else device->tape_generic_status &= ~GMT_WR_PROT(~0); } else DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n", request->rc); tape_free_request(request); }
static struct tape_request *__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 ERR_PTR(-ENOSYS); request = tape_alloc_request(2, 72); if (IS_ERR(request)) return 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 request; }
/* * Tape state functions */ void tape_state_set(struct tape_device *device, enum tape_state newstate) { const char *str; if (device->tape_state == TS_NOT_OPER) { DBF_EVENT(3, "ts_set err: not oper\n"); return; } DBF_EVENT(4, "ts. dev: %x\n", device->first_minor); DBF_EVENT(4, "old ts:\t\n"); if (device->tape_state < TS_SIZE && device->tape_state >=0 ) str = tape_state_verbose[device->tape_state]; else str = "UNKNOWN TS"; DBF_EVENT(4, "%s\n", str); DBF_EVENT(4, "new ts:\t\n"); if (newstate < TS_SIZE && newstate >= 0) str = tape_state_verbose[newstate]; else str = "UNKNOWN TS"; DBF_EVENT(4, "%s\n", str); device->tape_state = newstate; wake_up(&device->state_change_wq); }
static int dasd_statistics_write(struct file *file, const char __user *user_buf, unsigned long user_len, void *data) { #ifdef CONFIG_DASD_PROFILE char *buffer, *str; if (user_len > 65536) user_len = 65536; buffer = dasd_get_user_string(user_buf, user_len); if (IS_ERR(buffer)) return PTR_ERR(buffer); DBF_EVENT(DBF_DEBUG, "/proc/dasd/statictics: '%s'\n", buffer); /* check for valid verbs */ for (str = buffer; isspace(*str); str++); if (strncmp(str, "set", 3) == 0 && isspace(str[3])) { /* 'set xxx' was given */ for (str = str + 4; isspace(*str); str++); if (strcmp(str, "on") == 0) { /* switch on statistics profiling */ dasd_profile_level = DASD_PROFILE_ON; pr_info("The statistics feature has been switched " "on\n"); } else if (strcmp(str, "off") == 0) { /* switch off and reset statistics profiling */ memset(&dasd_global_profile, 0, sizeof (struct dasd_profile_info_t)); dasd_profile_level = DASD_PROFILE_OFF; pr_info("The statistics feature has been switched " "off\n"); } else goto out_error; } else if (strncmp(str, "reset", 5) == 0) { /* reset the statistics */ memset(&dasd_global_profile, 0, sizeof (struct dasd_profile_info_t)); pr_info("The statistics have been reset\n"); } else goto out_error; kfree(buffer); return user_len; out_error: pr_warning("%s is not a supported value for /proc/dasd/statistics\n", str); kfree(buffer); return -EINVAL; #else pr_warning("/proc/dasd/statistics: is not activated in this kernel\n"); return user_len; #endif /* CONFIG_DASD_PROFILE */ }
for_each_output_queue(irq_ptr, q, i) { DBF_EVENT("outq:%1d", i); setup_queues_misc(q, irq_ptr, qdio_init->output_handler, i); q->is_input_q = 0; setup_storage_lists(q, irq_ptr, output_sbal_array, i); output_sbal_array += QDIO_MAX_BUFFERS_PER_Q; tasklet_init(&q->tasklet, qdio_outbound_processing, (unsigned long) q); setup_timer(&q->u.out.timer, (void(*)(unsigned long)) &qdio_outbound_timer, (unsigned long)q); }
/* * 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; }
static void setup_queues(struct qdio_irq *irq_ptr, struct qdio_initialize *qdio_init) { struct qdio_q *q; void **input_sbal_array = qdio_init->input_sbal_addr_array; void **output_sbal_array = qdio_init->output_sbal_addr_array; int i; for_each_input_queue(irq_ptr, q, i) { DBF_EVENT("in-q:%1d", i); setup_queues_misc(q, irq_ptr, qdio_init->input_handler, i); q->is_input_q = 1; <<<<<<< HEAD
/* * 3480/3490 interrupt handler */ static int tape_34xx_irq(struct tape_device *device, struct tape_request *request, struct irb *irb) { if (request == NULL) return tape_34xx_unsolicited_irq(device, irb); if ((irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) && (irb->scsw.dstat & DEV_STAT_DEV_END) && (request->op == TO_WRI)) { /* Write at end of volume */ PRINT_INFO("End of volume\n"); /* XXX */ return tape_34xx_erp_failed(request, -ENOSPC); } if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) return tape_34xx_unit_check(device, request, irb); if (irb->scsw.dstat & DEV_STAT_DEV_END) { /* * A unit exception occurs on skipping over a tapemark block. */ if (irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) { if (request->op == TO_BSB || request->op == TO_FSB) request->rescnt++; else DBF_EVENT(5, "Unit Exception!\n"); } return tape_34xx_done(request); } DBF_EVENT(6, "xunknownirq\n"); PRINT_ERR("Unexpected interrupt.\n"); PRINT_ERR("Current op is: %s", tape_op_verbose[request->op]); tape_dump_sense(device, request, irb); return TAPE_IO_STOP; }
int __init qdio_debug_init(void) { debugfs_root = debugfs_create_dir("qdio", NULL); qdio_dbf_setup = debug_register("qdio_setup", 16, 1, 16); debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view); debug_set_level(qdio_dbf_setup, DBF_INFO); DBF_EVENT("dbf created\n"); qdio_dbf_error = debug_register("qdio_error", 4, 1, 16); debug_register_view(qdio_dbf_error, &debug_hex_ascii_view); debug_set_level(qdio_dbf_error, DBF_INFO); DBF_ERROR("dbf created\n"); return 0; }
static void tape_3590_read_opposite(struct tape_device *device, struct tape_request *request) { struct tape_3590_disc_data *data; request->op = TO_RBA; tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); data = device->discdata; tape_ccw_cc_idal(request->cpaddr + 1, data->read_back_op, device->char_data.idal_buf); tape_ccw_cc(request->cpaddr + 2, FORSPACEBLOCK, 0, NULL); tape_ccw_end(request->cpaddr + 3, NOP, 0, NULL); DBF_EVENT(6, "xrop ccwg\n"); }
/* * MTSEEK: seek to the specified block. */ static int tape_3590_mtseek(struct tape_device *device, int count) { struct tape_request *request; DBF_EVENT(6, "xsee id: %x\n", count); request = tape_alloc_request(3, 4); if (IS_ERR(request)) return PTR_ERR(request); request->op = TO_LBL; tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); *(__u32 *) request->cpdata = count; tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata); tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); 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); }
/* * Done Handler is called when dev stat = DEVICE-END (successful operation) */ static inline int tape_34xx_done(struct tape_request *request) { DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]); switch (request->op) { case TO_DSE: case TO_RUN: case TO_WRI: case TO_WTM: case TO_ASSIGN: case TO_UNASSIGN: tape_34xx_delete_sbid_from(request->device, 0); break; default: ; } return TAPE_IO_SUCCESS; }
/* * Medium sense for 34xx tapes. There is no 'real' medium sense call. * So we just do a normal sense. */ static int tape_34xx_medium_sense(struct tape_device *device) { struct tape_request *request; unsigned char *sense; int rc; request = tape_alloc_request(1, 32); if (IS_ERR(request)) { DBF_EXCEPTION(6, "MSEN fail\n"); return PTR_ERR(request); } request->op = TO_MSEN; tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); rc = tape_do_io_interruptible(device, request); if (request->rc == 0) { sense = request->cpdata; /* * This isn't quite correct. But since INTERVENTION_REQUIRED * means that the drive is 'neither ready nor on-line' it is * only slightly inaccurate to say there is no tape loaded if * the drive isn't online... */ if (sense[0] & SENSE_INTERVENTION_REQUIRED) tape_med_state_set(device, MS_UNLOADED); else tape_med_state_set(device, MS_LOADED); if (sense[1] & SENSE_WRITE_PROTECT) device->tape_generic_status |= GMT_WR_PROT(~0); else device->tape_generic_status &= ~GMT_WR_PROT(~0); } else { DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n", request->rc); } tape_free_request(request); return rc; }
/* * Read Opposite Error Recovery Function: * Used, when Read Forward does not work */ static void tape_3590_read_opposite(struct tape_device *device, struct tape_request *request) { struct tape_3590_disc_data *data; /* * We have allocated 4 ccws in tape_std_read, so we can now * transform the request to a read backward, followed by a * forward space block. */ request->op = TO_RBA; tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); data = device->discdata; tape_ccw_cc_idal(request->cpaddr + 1, data->read_back_op, device->char_data.idal_buf); tape_ccw_cc(request->cpaddr + 2, FORSPACEBLOCK, 0, NULL); tape_ccw_end(request->cpaddr + 3, NOP, 0, NULL); DBF_EVENT(6, "xrop ccwg\n"); }
/* * These functions are currently used only to schedule a medium_sense for * later execution. This is because we get an interrupt whenever a medium * is inserted but cannot call tape_do_io* from an interrupt context. * Maybe that's useful for other actions we want to start from the * interrupt handler. */ static void tape_34xx_work_handler(void *data) { struct { struct tape_device *device; enum tape_op op; struct work_struct work; } *p = data; switch(p->op) { case TO_MSEN: tape_34xx_medium_sense(p->device); break; default: DBF_EVENT(3, "T34XX: internal error: unknown work\n"); } p->device = tape_put_device(p->device); kfree(p); }
/* * 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; }
/* * 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; }
static inline int tape_34xx_erp_retry(struct tape_request *request) { DBF_EVENT(3, "xerp retr %s\n", tape_op_verbose[request->op]); return TAPE_IO_RETRY; }