18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tape device discipline for 3480/3490 tapes. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2001, 2009 68c2ecf20Sopenharmony_ci * Author(s): Carsten Otte <cotte@de.ibm.com> 78c2ecf20Sopenharmony_ci * Tuan Ngo-Anh <ngoanh@de.ibm.com> 88c2ecf20Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "tape_34xx" 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/bio.h> 178c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define TAPE_DBF_AREA tape_34xx_dbf 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "tape.h" 238c2ecf20Sopenharmony_ci#include "tape_std.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Pointer to debug area. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_cidebug_info_t *TAPE_DBF_AREA = NULL; 298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(TAPE_DBF_AREA); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define TAPE34XX_FMT_3480 0 328c2ecf20Sopenharmony_ci#define TAPE34XX_FMT_3480_2_XF 1 338c2ecf20Sopenharmony_ci#define TAPE34XX_FMT_3480_XF 2 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct tape_34xx_block_id { 368c2ecf20Sopenharmony_ci unsigned int wrap : 1; 378c2ecf20Sopenharmony_ci unsigned int segment : 7; 388c2ecf20Sopenharmony_ci unsigned int format : 2; 398c2ecf20Sopenharmony_ci unsigned int block : 22; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * A list of block ID's is used to faster seek blocks. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistruct tape_34xx_sbid { 468c2ecf20Sopenharmony_ci struct list_head list; 478c2ecf20Sopenharmony_ci struct tape_34xx_block_id bid; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void tape_34xx_delete_sbid_from(struct tape_device *, int); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * Medium sense for 34xx tapes. There is no 'real' medium sense call. 548c2ecf20Sopenharmony_ci * So we just do a normal sense. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_cistatic void __tape_34xx_medium_sense(struct tape_request *request) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct tape_device *device = request->device; 598c2ecf20Sopenharmony_ci unsigned char *sense; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (request->rc == 0) { 628c2ecf20Sopenharmony_ci sense = request->cpdata; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* 658c2ecf20Sopenharmony_ci * This isn't quite correct. But since INTERVENTION_REQUIRED 668c2ecf20Sopenharmony_ci * means that the drive is 'neither ready nor on-line' it is 678c2ecf20Sopenharmony_ci * only slightly inaccurate to say there is no tape loaded if 688c2ecf20Sopenharmony_ci * the drive isn't online... 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci if (sense[0] & SENSE_INTERVENTION_REQUIRED) 718c2ecf20Sopenharmony_ci tape_med_state_set(device, MS_UNLOADED); 728c2ecf20Sopenharmony_ci else 738c2ecf20Sopenharmony_ci tape_med_state_set(device, MS_LOADED); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (sense[1] & SENSE_WRITE_PROTECT) 768c2ecf20Sopenharmony_ci device->tape_generic_status |= GMT_WR_PROT(~0); 778c2ecf20Sopenharmony_ci else 788c2ecf20Sopenharmony_ci device->tape_generic_status &= ~GMT_WR_PROT(~0); 798c2ecf20Sopenharmony_ci } else 808c2ecf20Sopenharmony_ci DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n", 818c2ecf20Sopenharmony_ci request->rc); 828c2ecf20Sopenharmony_ci tape_free_request(request); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int tape_34xx_medium_sense(struct tape_device *device) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct tape_request *request; 888c2ecf20Sopenharmony_ci int rc; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci request = tape_alloc_request(1, 32); 918c2ecf20Sopenharmony_ci if (IS_ERR(request)) { 928c2ecf20Sopenharmony_ci DBF_EXCEPTION(6, "MSEN fail\n"); 938c2ecf20Sopenharmony_ci return PTR_ERR(request); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci request->op = TO_MSEN; 978c2ecf20Sopenharmony_ci tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); 988c2ecf20Sopenharmony_ci rc = tape_do_io_interruptible(device, request); 998c2ecf20Sopenharmony_ci __tape_34xx_medium_sense(request); 1008c2ecf20Sopenharmony_ci return rc; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void tape_34xx_medium_sense_async(struct tape_device *device) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct tape_request *request; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci request = tape_alloc_request(1, 32); 1088c2ecf20Sopenharmony_ci if (IS_ERR(request)) { 1098c2ecf20Sopenharmony_ci DBF_EXCEPTION(6, "MSEN fail\n"); 1108c2ecf20Sopenharmony_ci return; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci request->op = TO_MSEN; 1148c2ecf20Sopenharmony_ci tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); 1158c2ecf20Sopenharmony_ci request->callback = (void *) __tape_34xx_medium_sense; 1168c2ecf20Sopenharmony_ci request->callback_data = NULL; 1178c2ecf20Sopenharmony_ci tape_do_io_async(device, request); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistruct tape_34xx_work { 1218c2ecf20Sopenharmony_ci struct tape_device *device; 1228c2ecf20Sopenharmony_ci enum tape_op op; 1238c2ecf20Sopenharmony_ci struct work_struct work; 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* 1278c2ecf20Sopenharmony_ci * These functions are currently used only to schedule a medium_sense for 1288c2ecf20Sopenharmony_ci * later execution. This is because we get an interrupt whenever a medium 1298c2ecf20Sopenharmony_ci * is inserted but cannot call tape_do_io* from an interrupt context. 1308c2ecf20Sopenharmony_ci * Maybe that's useful for other actions we want to start from the 1318c2ecf20Sopenharmony_ci * interrupt handler. 1328c2ecf20Sopenharmony_ci * Note: the work handler is called by the system work queue. The tape 1338c2ecf20Sopenharmony_ci * commands started by the handler need to be asynchrounous, otherwise 1348c2ecf20Sopenharmony_ci * a deadlock can occur e.g. in case of a deferred cc=1 (see __tape_do_irq). 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_cistatic void 1378c2ecf20Sopenharmony_citape_34xx_work_handler(struct work_struct *work) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct tape_34xx_work *p = 1408c2ecf20Sopenharmony_ci container_of(work, struct tape_34xx_work, work); 1418c2ecf20Sopenharmony_ci struct tape_device *device = p->device; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci switch(p->op) { 1448c2ecf20Sopenharmony_ci case TO_MSEN: 1458c2ecf20Sopenharmony_ci tape_34xx_medium_sense_async(device); 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci default: 1488c2ecf20Sopenharmony_ci DBF_EVENT(3, "T34XX: internal error: unknown work\n"); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci tape_put_device(device); 1518c2ecf20Sopenharmony_ci kfree(p); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int 1558c2ecf20Sopenharmony_citape_34xx_schedule_work(struct tape_device *device, enum tape_op op) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct tape_34xx_work *p; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL) 1608c2ecf20Sopenharmony_ci return -ENOMEM; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci INIT_WORK(&p->work, tape_34xx_work_handler); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci p->device = tape_get_device(device); 1658c2ecf20Sopenharmony_ci p->op = op; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci schedule_work(&p->work); 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* 1728c2ecf20Sopenharmony_ci * Done Handler is called when dev stat = DEVICE-END (successful operation) 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_cistatic inline int 1758c2ecf20Sopenharmony_citape_34xx_done(struct tape_request *request) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci switch (request->op) { 1808c2ecf20Sopenharmony_ci case TO_DSE: 1818c2ecf20Sopenharmony_ci case TO_RUN: 1828c2ecf20Sopenharmony_ci case TO_WRI: 1838c2ecf20Sopenharmony_ci case TO_WTM: 1848c2ecf20Sopenharmony_ci case TO_ASSIGN: 1858c2ecf20Sopenharmony_ci case TO_UNASSIGN: 1868c2ecf20Sopenharmony_ci tape_34xx_delete_sbid_from(request->device, 0); 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci default: 1898c2ecf20Sopenharmony_ci ; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci return TAPE_IO_SUCCESS; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic inline int 1958c2ecf20Sopenharmony_citape_34xx_erp_failed(struct tape_request *request, int rc) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci DBF_EVENT(3, "Error recovery failed for %s (RC=%d)\n", 1988c2ecf20Sopenharmony_ci tape_op_verbose[request->op], rc); 1998c2ecf20Sopenharmony_ci return rc; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic inline int 2038c2ecf20Sopenharmony_citape_34xx_erp_succeeded(struct tape_request *request) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci DBF_EVENT(3, "Error Recovery successful for %s\n", 2068c2ecf20Sopenharmony_ci tape_op_verbose[request->op]); 2078c2ecf20Sopenharmony_ci return tape_34xx_done(request); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic inline int 2118c2ecf20Sopenharmony_citape_34xx_erp_retry(struct tape_request *request) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci DBF_EVENT(3, "xerp retr %s\n", tape_op_verbose[request->op]); 2148c2ecf20Sopenharmony_ci return TAPE_IO_RETRY; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* 2188c2ecf20Sopenharmony_ci * This function is called, when no request is outstanding and we get an 2198c2ecf20Sopenharmony_ci * interrupt 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_cistatic int 2228c2ecf20Sopenharmony_citape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci if (irb->scsw.cmd.dstat == 0x85) { /* READY */ 2258c2ecf20Sopenharmony_ci /* A medium was inserted in the drive. */ 2268c2ecf20Sopenharmony_ci DBF_EVENT(6, "xuud med\n"); 2278c2ecf20Sopenharmony_ci tape_34xx_delete_sbid_from(device, 0); 2288c2ecf20Sopenharmony_ci tape_34xx_schedule_work(device, TO_MSEN); 2298c2ecf20Sopenharmony_ci } else { 2308c2ecf20Sopenharmony_ci DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id); 2318c2ecf20Sopenharmony_ci tape_dump_sense_dbf(device, NULL, irb); 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci return TAPE_IO_SUCCESS; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* 2378c2ecf20Sopenharmony_ci * Read Opposite Error Recovery Function: 2388c2ecf20Sopenharmony_ci * Used, when Read Forward does not work 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_cistatic int 2418c2ecf20Sopenharmony_citape_34xx_erp_read_opposite(struct tape_device *device, 2428c2ecf20Sopenharmony_ci struct tape_request *request) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci if (request->op == TO_RFO) { 2458c2ecf20Sopenharmony_ci /* 2468c2ecf20Sopenharmony_ci * We did read forward, but the data could not be read 2478c2ecf20Sopenharmony_ci * *correctly*. We transform the request to a read backward 2488c2ecf20Sopenharmony_ci * and try again. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_ci tape_std_read_backward(device, request); 2518c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* 2558c2ecf20Sopenharmony_ci * We tried to read forward and backward, but hat no 2568c2ecf20Sopenharmony_ci * success -> failed. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int 2628c2ecf20Sopenharmony_citape_34xx_erp_bug(struct tape_device *device, struct tape_request *request, 2638c2ecf20Sopenharmony_ci struct irb *irb, int no) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci if (request->op != TO_ASSIGN) { 2668c2ecf20Sopenharmony_ci dev_err(&device->cdev->dev, "An unexpected condition %d " 2678c2ecf20Sopenharmony_ci "occurred in tape error recovery\n", no); 2688c2ecf20Sopenharmony_ci tape_dump_sense_dbf(device, request, irb); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* 2748c2ecf20Sopenharmony_ci * Handle data overrun between cu and drive. The channel speed might 2758c2ecf20Sopenharmony_ci * be too slow. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_cistatic int 2788c2ecf20Sopenharmony_citape_34xx_erp_overrun(struct tape_device *device, struct tape_request *request, 2798c2ecf20Sopenharmony_ci struct irb *irb) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci if (irb->ecw[3] == 0x40) { 2828c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "A data overrun occurred between" 2838c2ecf20Sopenharmony_ci " the control unit and tape unit\n"); 2848c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, irb, -1); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* 2908c2ecf20Sopenharmony_ci * Handle record sequence error. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_cistatic int 2938c2ecf20Sopenharmony_citape_34xx_erp_sequence(struct tape_device *device, 2948c2ecf20Sopenharmony_ci struct tape_request *request, struct irb *irb) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci if (irb->ecw[3] == 0x41) { 2978c2ecf20Sopenharmony_ci /* 2988c2ecf20Sopenharmony_ci * cu detected incorrect block-id sequence on tape. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The block ID sequence on the " 3018c2ecf20Sopenharmony_ci "tape is incorrect\n"); 3028c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci /* 3058c2ecf20Sopenharmony_ci * Record sequence error bit is set, but erpa does not 3068c2ecf20Sopenharmony_ci * show record sequence error. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, irb, -2); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/* 3128c2ecf20Sopenharmony_ci * This function analyses the tape's sense-data in case of a unit-check. 3138c2ecf20Sopenharmony_ci * If possible, it tries to recover from the error. Else the user is 3148c2ecf20Sopenharmony_ci * informed about the problem. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_cistatic int 3178c2ecf20Sopenharmony_citape_34xx_unit_check(struct tape_device *device, struct tape_request *request, 3188c2ecf20Sopenharmony_ci struct irb *irb) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int inhibit_cu_recovery; 3218c2ecf20Sopenharmony_ci __u8* sense; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci inhibit_cu_recovery = (*device->modeset_byte & 0x80) ? 1 : 0; 3248c2ecf20Sopenharmony_ci sense = irb->ecw; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if ( 3278c2ecf20Sopenharmony_ci sense[0] & SENSE_COMMAND_REJECT && 3288c2ecf20Sopenharmony_ci sense[1] & SENSE_WRITE_PROTECT 3298c2ecf20Sopenharmony_ci ) { 3308c2ecf20Sopenharmony_ci if ( 3318c2ecf20Sopenharmony_ci request->op == TO_DSE || 3328c2ecf20Sopenharmony_ci request->op == TO_WRI || 3338c2ecf20Sopenharmony_ci request->op == TO_WTM 3348c2ecf20Sopenharmony_ci ) { 3358c2ecf20Sopenharmony_ci /* medium is write protected */ 3368c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EACCES); 3378c2ecf20Sopenharmony_ci } else { 3388c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, irb, -3); 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* 3438c2ecf20Sopenharmony_ci * Special cases for various tape-states when reaching 3448c2ecf20Sopenharmony_ci * end of recorded area 3458c2ecf20Sopenharmony_ci * 3468c2ecf20Sopenharmony_ci * FIXME: Maybe a special case of the special case: 3478c2ecf20Sopenharmony_ci * sense[0] == SENSE_EQUIPMENT_CHECK && 3488c2ecf20Sopenharmony_ci * sense[1] == SENSE_DRIVE_ONLINE && 3498c2ecf20Sopenharmony_ci * sense[3] == 0x47 (Volume Fenced) 3508c2ecf20Sopenharmony_ci * 3518c2ecf20Sopenharmony_ci * This was caused by continued FSF or FSR after an 3528c2ecf20Sopenharmony_ci * 'End Of Data'. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci if (( 3558c2ecf20Sopenharmony_ci sense[0] == SENSE_DATA_CHECK || 3568c2ecf20Sopenharmony_ci sense[0] == SENSE_EQUIPMENT_CHECK || 3578c2ecf20Sopenharmony_ci sense[0] == SENSE_EQUIPMENT_CHECK + SENSE_DEFERRED_UNIT_CHECK 3588c2ecf20Sopenharmony_ci ) && ( 3598c2ecf20Sopenharmony_ci sense[1] == SENSE_DRIVE_ONLINE || 3608c2ecf20Sopenharmony_ci sense[1] == SENSE_BEGINNING_OF_TAPE + SENSE_WRITE_MODE 3618c2ecf20Sopenharmony_ci )) { 3628c2ecf20Sopenharmony_ci switch (request->op) { 3638c2ecf20Sopenharmony_ci /* 3648c2ecf20Sopenharmony_ci * sense[0] == SENSE_DATA_CHECK && 3658c2ecf20Sopenharmony_ci * sense[1] == SENSE_DRIVE_ONLINE 3668c2ecf20Sopenharmony_ci * sense[3] == 0x36 (End Of Data) 3678c2ecf20Sopenharmony_ci * 3688c2ecf20Sopenharmony_ci * Further seeks might return a 'Volume Fenced'. 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci case TO_FSF: 3718c2ecf20Sopenharmony_ci case TO_FSB: 3728c2ecf20Sopenharmony_ci /* Trying to seek beyond end of recorded area */ 3738c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -ENOSPC); 3748c2ecf20Sopenharmony_ci case TO_BSB: 3758c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* 3788c2ecf20Sopenharmony_ci * sense[0] == SENSE_DATA_CHECK && 3798c2ecf20Sopenharmony_ci * sense[1] == SENSE_DRIVE_ONLINE && 3808c2ecf20Sopenharmony_ci * sense[3] == 0x36 (End Of Data) 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_ci case TO_LBL: 3838c2ecf20Sopenharmony_ci /* Block could not be located. */ 3848c2ecf20Sopenharmony_ci tape_34xx_delete_sbid_from(device, 0); 3858c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci case TO_RFO: 3888c2ecf20Sopenharmony_ci /* Read beyond end of recorded area -> 0 bytes read */ 3898c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, 0); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci * sense[0] == SENSE_EQUIPMENT_CHECK && 3938c2ecf20Sopenharmony_ci * sense[1] == SENSE_DRIVE_ONLINE && 3948c2ecf20Sopenharmony_ci * sense[3] == 0x38 (Physical End Of Volume) 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci case TO_WRI: 3978c2ecf20Sopenharmony_ci /* Writing at physical end of volume */ 3988c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -ENOSPC); 3998c2ecf20Sopenharmony_ci default: 4008c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, 0); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* Sensing special bits */ 4058c2ecf20Sopenharmony_ci if (sense[0] & SENSE_BUS_OUT_CHECK) 4068c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (sense[0] & SENSE_DATA_CHECK) { 4098c2ecf20Sopenharmony_ci /* 4108c2ecf20Sopenharmony_ci * hardware failure, damaged tape or improper 4118c2ecf20Sopenharmony_ci * operating conditions 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_ci switch (sense[3]) { 4148c2ecf20Sopenharmony_ci case 0x23: 4158c2ecf20Sopenharmony_ci /* a read data check occurred */ 4168c2ecf20Sopenharmony_ci if ((sense[2] & SENSE_TAPE_SYNC_MODE) || 4178c2ecf20Sopenharmony_ci inhibit_cu_recovery) 4188c2ecf20Sopenharmony_ci // data check is not permanent, may be 4198c2ecf20Sopenharmony_ci // recovered. We always use async-mode with 4208c2ecf20Sopenharmony_ci // cu-recovery, so this should *never* happen. 4218c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, 4228c2ecf20Sopenharmony_ci irb, -4); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* data check is permanent, CU recovery has failed */ 4258c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "A read error occurred " 4268c2ecf20Sopenharmony_ci "that cannot be recovered\n"); 4278c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 4288c2ecf20Sopenharmony_ci case 0x25: 4298c2ecf20Sopenharmony_ci // a write data check occurred 4308c2ecf20Sopenharmony_ci if ((sense[2] & SENSE_TAPE_SYNC_MODE) || 4318c2ecf20Sopenharmony_ci inhibit_cu_recovery) 4328c2ecf20Sopenharmony_ci // data check is not permanent, may be 4338c2ecf20Sopenharmony_ci // recovered. We always use async-mode with 4348c2ecf20Sopenharmony_ci // cu-recovery, so this should *never* happen. 4358c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, 4368c2ecf20Sopenharmony_ci irb, -5); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci // data check is permanent, cu-recovery has failed 4398c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "A write error on the " 4408c2ecf20Sopenharmony_ci "tape cannot be recovered\n"); 4418c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 4428c2ecf20Sopenharmony_ci case 0x26: 4438c2ecf20Sopenharmony_ci /* Data Check (read opposite) occurred. */ 4448c2ecf20Sopenharmony_ci return tape_34xx_erp_read_opposite(device, request); 4458c2ecf20Sopenharmony_ci case 0x28: 4468c2ecf20Sopenharmony_ci /* ID-Mark at tape start couldn't be written */ 4478c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "Writing the ID-mark " 4488c2ecf20Sopenharmony_ci "failed\n"); 4498c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 4508c2ecf20Sopenharmony_ci case 0x31: 4518c2ecf20Sopenharmony_ci /* Tape void. Tried to read beyond end of device. */ 4528c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "Reading the tape beyond" 4538c2ecf20Sopenharmony_ci " the end of the recorded area failed\n"); 4548c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -ENOSPC); 4558c2ecf20Sopenharmony_ci case 0x41: 4568c2ecf20Sopenharmony_ci /* Record sequence error. */ 4578c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape contains an " 4588c2ecf20Sopenharmony_ci "incorrect block ID sequence\n"); 4598c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 4608c2ecf20Sopenharmony_ci default: 4618c2ecf20Sopenharmony_ci /* all data checks for 3480 should result in one of 4628c2ecf20Sopenharmony_ci * the above erpa-codes. For 3490, other data-check 4638c2ecf20Sopenharmony_ci * conditions do exist. */ 4648c2ecf20Sopenharmony_ci if (device->cdev->id.driver_info == tape_3480) 4658c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, 4668c2ecf20Sopenharmony_ci irb, -6); 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (sense[0] & SENSE_OVERRUN) 4718c2ecf20Sopenharmony_ci return tape_34xx_erp_overrun(device, request, irb); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (sense[1] & SENSE_RECORD_SEQUENCE_ERR) 4748c2ecf20Sopenharmony_ci return tape_34xx_erp_sequence(device, request, irb); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* Sensing erpa codes */ 4778c2ecf20Sopenharmony_ci switch (sense[3]) { 4788c2ecf20Sopenharmony_ci case 0x00: 4798c2ecf20Sopenharmony_ci /* Unit check with erpa code 0. Report and ignore. */ 4808c2ecf20Sopenharmony_ci return TAPE_IO_SUCCESS; 4818c2ecf20Sopenharmony_ci case 0x21: 4828c2ecf20Sopenharmony_ci /* 4838c2ecf20Sopenharmony_ci * Data streaming not operational. CU will switch to 4848c2ecf20Sopenharmony_ci * interlock mode. Reissue the command. 4858c2ecf20Sopenharmony_ci */ 4868c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 4878c2ecf20Sopenharmony_ci case 0x22: 4888c2ecf20Sopenharmony_ci /* 4898c2ecf20Sopenharmony_ci * Path equipment check. Might be drive adapter error, buffer 4908c2ecf20Sopenharmony_ci * error on the lower interface, internal path not usable, 4918c2ecf20Sopenharmony_ci * or error during cartridge load. 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "A path equipment check occurred" 4948c2ecf20Sopenharmony_ci " for the tape device\n"); 4958c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 4968c2ecf20Sopenharmony_ci case 0x24: 4978c2ecf20Sopenharmony_ci /* 4988c2ecf20Sopenharmony_ci * Load display check. Load display was command was issued, 4998c2ecf20Sopenharmony_ci * but the drive is displaying a drive check message. Can 5008c2ecf20Sopenharmony_ci * be threated as "device end". 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ci return tape_34xx_erp_succeeded(request); 5038c2ecf20Sopenharmony_ci case 0x27: 5048c2ecf20Sopenharmony_ci /* 5058c2ecf20Sopenharmony_ci * Command reject. May indicate illegal channel program or 5068c2ecf20Sopenharmony_ci * buffer over/underrun. Since all channel programs are 5078c2ecf20Sopenharmony_ci * issued by this driver and ought be correct, we assume a 5088c2ecf20Sopenharmony_ci * over/underrun situation and retry the channel program. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 5118c2ecf20Sopenharmony_ci case 0x29: 5128c2ecf20Sopenharmony_ci /* 5138c2ecf20Sopenharmony_ci * Function incompatible. Either the tape is idrc compressed 5148c2ecf20Sopenharmony_ci * but the hardware isn't capable to do idrc, or a perform 5158c2ecf20Sopenharmony_ci * subsystem func is issued and the CU is not on-line. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 5188c2ecf20Sopenharmony_ci case 0x2a: 5198c2ecf20Sopenharmony_ci /* 5208c2ecf20Sopenharmony_ci * Unsolicited environmental data. An internal counter 5218c2ecf20Sopenharmony_ci * overflows, we can ignore this and reissue the cmd. 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 5248c2ecf20Sopenharmony_ci case 0x2b: 5258c2ecf20Sopenharmony_ci /* 5268c2ecf20Sopenharmony_ci * Environmental data present. Indicates either unload 5278c2ecf20Sopenharmony_ci * completed ok or read buffered log command completed ok. 5288c2ecf20Sopenharmony_ci */ 5298c2ecf20Sopenharmony_ci if (request->op == TO_RUN) { 5308c2ecf20Sopenharmony_ci /* Rewind unload completed ok. */ 5318c2ecf20Sopenharmony_ci tape_med_state_set(device, MS_UNLOADED); 5328c2ecf20Sopenharmony_ci return tape_34xx_erp_succeeded(request); 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci /* tape_34xx doesn't use read buffered log commands. */ 5358c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, irb, sense[3]); 5368c2ecf20Sopenharmony_ci case 0x2c: 5378c2ecf20Sopenharmony_ci /* 5388c2ecf20Sopenharmony_ci * Permanent equipment check. CU has tried recovery, but 5398c2ecf20Sopenharmony_ci * did not succeed. 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 5428c2ecf20Sopenharmony_ci case 0x2d: 5438c2ecf20Sopenharmony_ci /* Data security erase failure. */ 5448c2ecf20Sopenharmony_ci if (request->op == TO_DSE) 5458c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 5468c2ecf20Sopenharmony_ci /* Data security erase failure, but no such command issued. */ 5478c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, irb, sense[3]); 5488c2ecf20Sopenharmony_ci case 0x2e: 5498c2ecf20Sopenharmony_ci /* 5508c2ecf20Sopenharmony_ci * Not capable. This indicates either that the drive fails 5518c2ecf20Sopenharmony_ci * reading the format id mark or that that format specified 5528c2ecf20Sopenharmony_ci * is not supported by the drive. 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape unit cannot process " 5558c2ecf20Sopenharmony_ci "the tape format\n"); 5568c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EMEDIUMTYPE); 5578c2ecf20Sopenharmony_ci case 0x30: 5588c2ecf20Sopenharmony_ci /* The medium is write protected. */ 5598c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape medium is write-" 5608c2ecf20Sopenharmony_ci "protected\n"); 5618c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EACCES); 5628c2ecf20Sopenharmony_ci case 0x32: 5638c2ecf20Sopenharmony_ci // Tension loss. We cannot recover this, it's an I/O error. 5648c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape does not have the " 5658c2ecf20Sopenharmony_ci "required tape tension\n"); 5668c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 5678c2ecf20Sopenharmony_ci case 0x33: 5688c2ecf20Sopenharmony_ci /* 5698c2ecf20Sopenharmony_ci * Load Failure. The cartridge was not inserted correctly or 5708c2ecf20Sopenharmony_ci * the tape is not threaded correctly. 5718c2ecf20Sopenharmony_ci */ 5728c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape unit failed to load" 5738c2ecf20Sopenharmony_ci " the cartridge\n"); 5748c2ecf20Sopenharmony_ci tape_34xx_delete_sbid_from(device, 0); 5758c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 5768c2ecf20Sopenharmony_ci case 0x34: 5778c2ecf20Sopenharmony_ci /* 5788c2ecf20Sopenharmony_ci * Unload failure. The drive cannot maintain tape tension 5798c2ecf20Sopenharmony_ci * and control tape movement during an unload operation. 5808c2ecf20Sopenharmony_ci */ 5818c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "Automatic unloading of the tape" 5828c2ecf20Sopenharmony_ci " cartridge failed\n"); 5838c2ecf20Sopenharmony_ci if (request->op == TO_RUN) 5848c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 5858c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, irb, sense[3]); 5868c2ecf20Sopenharmony_ci case 0x35: 5878c2ecf20Sopenharmony_ci /* 5888c2ecf20Sopenharmony_ci * Drive equipment check. One of the following: 5898c2ecf20Sopenharmony_ci * - cu cannot recover from a drive detected error 5908c2ecf20Sopenharmony_ci * - a check code message is shown on drive display 5918c2ecf20Sopenharmony_ci * - the cartridge loader does not respond correctly 5928c2ecf20Sopenharmony_ci * - a failure occurs during an index, load, or unload cycle 5938c2ecf20Sopenharmony_ci */ 5948c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "An equipment check has occurred" 5958c2ecf20Sopenharmony_ci " on the tape unit\n"); 5968c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 5978c2ecf20Sopenharmony_ci case 0x36: 5988c2ecf20Sopenharmony_ci if (device->cdev->id.driver_info == tape_3490) 5998c2ecf20Sopenharmony_ci /* End of data. */ 6008c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 6018c2ecf20Sopenharmony_ci /* This erpa is reserved for 3480 */ 6028c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, irb, sense[3]); 6038c2ecf20Sopenharmony_ci case 0x37: 6048c2ecf20Sopenharmony_ci /* 6058c2ecf20Sopenharmony_ci * Tape length error. The tape is shorter than reported in 6068c2ecf20Sopenharmony_ci * the beginning-of-tape data. 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape information states an" 6098c2ecf20Sopenharmony_ci " incorrect length\n"); 6108c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 6118c2ecf20Sopenharmony_ci case 0x38: 6128c2ecf20Sopenharmony_ci /* 6138c2ecf20Sopenharmony_ci * Physical end of tape. A read/write operation reached 6148c2ecf20Sopenharmony_ci * the physical end of tape. 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ci if (request->op==TO_WRI || 6178c2ecf20Sopenharmony_ci request->op==TO_DSE || 6188c2ecf20Sopenharmony_ci request->op==TO_WTM) 6198c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -ENOSPC); 6208c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 6218c2ecf20Sopenharmony_ci case 0x39: 6228c2ecf20Sopenharmony_ci /* Backward at Beginning of tape. */ 6238c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 6248c2ecf20Sopenharmony_ci case 0x3a: 6258c2ecf20Sopenharmony_ci /* Drive switched to not ready. */ 6268c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape unit is not ready\n"); 6278c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 6288c2ecf20Sopenharmony_ci case 0x3b: 6298c2ecf20Sopenharmony_ci /* Manual rewind or unload. This causes an I/O error. */ 6308c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape medium has been " 6318c2ecf20Sopenharmony_ci "rewound or unloaded manually\n"); 6328c2ecf20Sopenharmony_ci tape_34xx_delete_sbid_from(device, 0); 6338c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 6348c2ecf20Sopenharmony_ci case 0x42: 6358c2ecf20Sopenharmony_ci /* 6368c2ecf20Sopenharmony_ci * Degraded mode. A condition that can cause degraded 6378c2ecf20Sopenharmony_ci * performance is detected. 6388c2ecf20Sopenharmony_ci */ 6398c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape subsystem is running " 6408c2ecf20Sopenharmony_ci "in degraded mode\n"); 6418c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 6428c2ecf20Sopenharmony_ci case 0x43: 6438c2ecf20Sopenharmony_ci /* Drive not ready. */ 6448c2ecf20Sopenharmony_ci tape_34xx_delete_sbid_from(device, 0); 6458c2ecf20Sopenharmony_ci tape_med_state_set(device, MS_UNLOADED); 6468c2ecf20Sopenharmony_ci /* Some commands commands are successful even in this case */ 6478c2ecf20Sopenharmony_ci if (sense[1] & SENSE_DRIVE_ONLINE) { 6488c2ecf20Sopenharmony_ci switch(request->op) { 6498c2ecf20Sopenharmony_ci case TO_ASSIGN: 6508c2ecf20Sopenharmony_ci case TO_UNASSIGN: 6518c2ecf20Sopenharmony_ci case TO_DIS: 6528c2ecf20Sopenharmony_ci case TO_NOP: 6538c2ecf20Sopenharmony_ci return tape_34xx_done(request); 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci default: 6568c2ecf20Sopenharmony_ci break; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -ENOMEDIUM); 6608c2ecf20Sopenharmony_ci case 0x44: 6618c2ecf20Sopenharmony_ci /* Locate Block unsuccessful. */ 6628c2ecf20Sopenharmony_ci if (request->op != TO_BLOCK && request->op != TO_LBL) 6638c2ecf20Sopenharmony_ci /* No locate block was issued. */ 6648c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, 6658c2ecf20Sopenharmony_ci irb, sense[3]); 6668c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 6678c2ecf20Sopenharmony_ci case 0x45: 6688c2ecf20Sopenharmony_ci /* The drive is assigned to a different channel path. */ 6698c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape unit is already " 6708c2ecf20Sopenharmony_ci "assigned\n"); 6718c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 6728c2ecf20Sopenharmony_ci case 0x46: 6738c2ecf20Sopenharmony_ci /* 6748c2ecf20Sopenharmony_ci * Drive not on-line. Drive may be switched offline, 6758c2ecf20Sopenharmony_ci * the power supply may be switched off or 6768c2ecf20Sopenharmony_ci * the drive address may not be set correctly. 6778c2ecf20Sopenharmony_ci */ 6788c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape unit is not online\n"); 6798c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 6808c2ecf20Sopenharmony_ci case 0x47: 6818c2ecf20Sopenharmony_ci /* Volume fenced. CU reports volume integrity is lost. */ 6828c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The control unit has fenced " 6838c2ecf20Sopenharmony_ci "access to the tape volume\n"); 6848c2ecf20Sopenharmony_ci tape_34xx_delete_sbid_from(device, 0); 6858c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 6868c2ecf20Sopenharmony_ci case 0x48: 6878c2ecf20Sopenharmony_ci /* Log sense data and retry request. */ 6888c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 6898c2ecf20Sopenharmony_ci case 0x49: 6908c2ecf20Sopenharmony_ci /* Bus out check. A parity check error on the bus was found. */ 6918c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "A parity error occurred on the " 6928c2ecf20Sopenharmony_ci "tape bus\n"); 6938c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 6948c2ecf20Sopenharmony_ci case 0x4a: 6958c2ecf20Sopenharmony_ci /* Control unit erp failed. */ 6968c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "I/O error recovery failed on " 6978c2ecf20Sopenharmony_ci "the tape control unit\n"); 6988c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 6998c2ecf20Sopenharmony_ci case 0x4b: 7008c2ecf20Sopenharmony_ci /* 7018c2ecf20Sopenharmony_ci * CU and drive incompatible. The drive requests micro-program 7028c2ecf20Sopenharmony_ci * patches, which are not available on the CU. 7038c2ecf20Sopenharmony_ci */ 7048c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape unit requires a " 7058c2ecf20Sopenharmony_ci "firmware update\n"); 7068c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 7078c2ecf20Sopenharmony_ci case 0x4c: 7088c2ecf20Sopenharmony_ci /* 7098c2ecf20Sopenharmony_ci * Recovered Check-One failure. Cu develops a hardware error, 7108c2ecf20Sopenharmony_ci * but is able to recover. 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 7138c2ecf20Sopenharmony_ci case 0x4d: 7148c2ecf20Sopenharmony_ci if (device->cdev->id.driver_info == tape_3490) 7158c2ecf20Sopenharmony_ci /* 7168c2ecf20Sopenharmony_ci * Resetting event received. Since the driver does 7178c2ecf20Sopenharmony_ci * not support resetting event recovery (which has to 7188c2ecf20Sopenharmony_ci * be handled by the I/O Layer), retry our command. 7198c2ecf20Sopenharmony_ci */ 7208c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 7218c2ecf20Sopenharmony_ci /* This erpa is reserved for 3480. */ 7228c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, irb, sense[3]); 7238c2ecf20Sopenharmony_ci case 0x4e: 7248c2ecf20Sopenharmony_ci if (device->cdev->id.driver_info == tape_3490) { 7258c2ecf20Sopenharmony_ci /* 7268c2ecf20Sopenharmony_ci * Maximum block size exceeded. This indicates, that 7278c2ecf20Sopenharmony_ci * the block to be written is larger than allowed for 7288c2ecf20Sopenharmony_ci * buffered mode. 7298c2ecf20Sopenharmony_ci */ 7308c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The maximum block size" 7318c2ecf20Sopenharmony_ci " for buffered mode is exceeded\n"); 7328c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -ENOBUFS); 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci /* This erpa is reserved for 3480. */ 7358c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, irb, sense[3]); 7368c2ecf20Sopenharmony_ci case 0x50: 7378c2ecf20Sopenharmony_ci /* 7388c2ecf20Sopenharmony_ci * Read buffered log (Overflow). CU is running in extended 7398c2ecf20Sopenharmony_ci * buffered log mode, and a counter overflows. This should 7408c2ecf20Sopenharmony_ci * never happen, since we're never running in extended 7418c2ecf20Sopenharmony_ci * buffered log mode. 7428c2ecf20Sopenharmony_ci */ 7438c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 7448c2ecf20Sopenharmony_ci case 0x51: 7458c2ecf20Sopenharmony_ci /* 7468c2ecf20Sopenharmony_ci * Read buffered log (EOV). EOF processing occurs while the 7478c2ecf20Sopenharmony_ci * CU is in extended buffered log mode. This should never 7488c2ecf20Sopenharmony_ci * happen, since we're never running in extended buffered 7498c2ecf20Sopenharmony_ci * log mode. 7508c2ecf20Sopenharmony_ci */ 7518c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 7528c2ecf20Sopenharmony_ci case 0x52: 7538c2ecf20Sopenharmony_ci /* End of Volume complete. Rewind unload completed ok. */ 7548c2ecf20Sopenharmony_ci if (request->op == TO_RUN) { 7558c2ecf20Sopenharmony_ci tape_med_state_set(device, MS_UNLOADED); 7568c2ecf20Sopenharmony_ci tape_34xx_delete_sbid_from(device, 0); 7578c2ecf20Sopenharmony_ci return tape_34xx_erp_succeeded(request); 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, irb, sense[3]); 7608c2ecf20Sopenharmony_ci case 0x53: 7618c2ecf20Sopenharmony_ci /* Global command intercept. */ 7628c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 7638c2ecf20Sopenharmony_ci case 0x54: 7648c2ecf20Sopenharmony_ci /* Channel interface recovery (temporary). */ 7658c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 7668c2ecf20Sopenharmony_ci case 0x55: 7678c2ecf20Sopenharmony_ci /* Channel interface recovery (permanent). */ 7688c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "A channel interface error cannot be" 7698c2ecf20Sopenharmony_ci " recovered\n"); 7708c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 7718c2ecf20Sopenharmony_ci case 0x56: 7728c2ecf20Sopenharmony_ci /* Channel protocol error. */ 7738c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "A channel protocol error " 7748c2ecf20Sopenharmony_ci "occurred\n"); 7758c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 7768c2ecf20Sopenharmony_ci case 0x57: 7778c2ecf20Sopenharmony_ci /* 7788c2ecf20Sopenharmony_ci * 3480: Attention intercept. 7798c2ecf20Sopenharmony_ci * 3490: Global status intercept. 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 7828c2ecf20Sopenharmony_ci case 0x5a: 7838c2ecf20Sopenharmony_ci /* 7848c2ecf20Sopenharmony_ci * Tape length incompatible. The tape inserted is too long, 7858c2ecf20Sopenharmony_ci * which could cause damage to the tape or the drive. 7868c2ecf20Sopenharmony_ci */ 7878c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape unit does not support " 7888c2ecf20Sopenharmony_ci "the tape length\n"); 7898c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 7908c2ecf20Sopenharmony_ci case 0x5b: 7918c2ecf20Sopenharmony_ci /* Format 3480 XF incompatible */ 7928c2ecf20Sopenharmony_ci if (sense[1] & SENSE_BEGINNING_OF_TAPE) 7938c2ecf20Sopenharmony_ci /* The tape will get overwritten. */ 7948c2ecf20Sopenharmony_ci return tape_34xx_erp_retry(request); 7958c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape unit does not support" 7968c2ecf20Sopenharmony_ci " format 3480 XF\n"); 7978c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 7988c2ecf20Sopenharmony_ci case 0x5c: 7998c2ecf20Sopenharmony_ci /* Format 3480-2 XF incompatible */ 8008c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape unit does not support tape " 8018c2ecf20Sopenharmony_ci "format 3480-2 XF\n"); 8028c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EIO); 8038c2ecf20Sopenharmony_ci case 0x5d: 8048c2ecf20Sopenharmony_ci /* Tape length violation. */ 8058c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape unit does not support" 8068c2ecf20Sopenharmony_ci " the current tape length\n"); 8078c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EMEDIUMTYPE); 8088c2ecf20Sopenharmony_ci case 0x5e: 8098c2ecf20Sopenharmony_ci /* Compaction algorithm incompatible. */ 8108c2ecf20Sopenharmony_ci dev_warn (&device->cdev->dev, "The tape unit does not support" 8118c2ecf20Sopenharmony_ci " the compaction algorithm\n"); 8128c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -EMEDIUMTYPE); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* The following erpas should have been covered earlier. */ 8158c2ecf20Sopenharmony_ci case 0x23: /* Read data check. */ 8168c2ecf20Sopenharmony_ci case 0x25: /* Write data check. */ 8178c2ecf20Sopenharmony_ci case 0x26: /* Data check (read opposite). */ 8188c2ecf20Sopenharmony_ci case 0x28: /* Write id mark check. */ 8198c2ecf20Sopenharmony_ci case 0x31: /* Tape void. */ 8208c2ecf20Sopenharmony_ci case 0x40: /* Overrun error. */ 8218c2ecf20Sopenharmony_ci case 0x41: /* Record sequence error. */ 8228c2ecf20Sopenharmony_ci /* All other erpas are reserved for future use. */ 8238c2ecf20Sopenharmony_ci default: 8248c2ecf20Sopenharmony_ci return tape_34xx_erp_bug(device, request, irb, sense[3]); 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci/* 8298c2ecf20Sopenharmony_ci * 3480/3490 interrupt handler 8308c2ecf20Sopenharmony_ci */ 8318c2ecf20Sopenharmony_cistatic int 8328c2ecf20Sopenharmony_citape_34xx_irq(struct tape_device *device, struct tape_request *request, 8338c2ecf20Sopenharmony_ci struct irb *irb) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci if (request == NULL) 8368c2ecf20Sopenharmony_ci return tape_34xx_unsolicited_irq(device, irb); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) && 8398c2ecf20Sopenharmony_ci (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) && 8408c2ecf20Sopenharmony_ci (request->op == TO_WRI)) { 8418c2ecf20Sopenharmony_ci /* Write at end of volume */ 8428c2ecf20Sopenharmony_ci return tape_34xx_erp_failed(request, -ENOSPC); 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) 8468c2ecf20Sopenharmony_ci return tape_34xx_unit_check(device, request, irb); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { 8498c2ecf20Sopenharmony_ci /* 8508c2ecf20Sopenharmony_ci * A unit exception occurs on skipping over a tapemark block. 8518c2ecf20Sopenharmony_ci */ 8528c2ecf20Sopenharmony_ci if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) { 8538c2ecf20Sopenharmony_ci if (request->op == TO_BSB || request->op == TO_FSB) 8548c2ecf20Sopenharmony_ci request->rescnt++; 8558c2ecf20Sopenharmony_ci else 8568c2ecf20Sopenharmony_ci DBF_EVENT(5, "Unit Exception!\n"); 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci return tape_34xx_done(request); 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci DBF_EVENT(6, "xunknownirq\n"); 8628c2ecf20Sopenharmony_ci tape_dump_sense_dbf(device, request, irb); 8638c2ecf20Sopenharmony_ci return TAPE_IO_STOP; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci/* 8678c2ecf20Sopenharmony_ci * ioctl_overload 8688c2ecf20Sopenharmony_ci */ 8698c2ecf20Sopenharmony_cistatic int 8708c2ecf20Sopenharmony_citape_34xx_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci if (cmd == TAPE390_DISPLAY) { 8738c2ecf20Sopenharmony_ci struct display_struct disp; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (copy_from_user(&disp, (char __user *) arg, sizeof(disp)) != 0) 8768c2ecf20Sopenharmony_ci return -EFAULT; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci return tape_std_display(device, &disp); 8798c2ecf20Sopenharmony_ci } else 8808c2ecf20Sopenharmony_ci return -EINVAL; 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic inline void 8848c2ecf20Sopenharmony_citape_34xx_append_new_sbid(struct tape_34xx_block_id bid, struct list_head *l) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct tape_34xx_sbid * new_sbid; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci new_sbid = kmalloc(sizeof(*new_sbid), GFP_ATOMIC); 8898c2ecf20Sopenharmony_ci if (!new_sbid) 8908c2ecf20Sopenharmony_ci return; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci new_sbid->bid = bid; 8938c2ecf20Sopenharmony_ci list_add(&new_sbid->list, l); 8948c2ecf20Sopenharmony_ci} 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci/* 8978c2ecf20Sopenharmony_ci * Build up the search block ID list. The block ID consists of a logical 8988c2ecf20Sopenharmony_ci * block number and a hardware specific part. The hardware specific part 8998c2ecf20Sopenharmony_ci * helps the tape drive to speed up searching for a specific block. 9008c2ecf20Sopenharmony_ci */ 9018c2ecf20Sopenharmony_cistatic void 9028c2ecf20Sopenharmony_citape_34xx_add_sbid(struct tape_device *device, struct tape_34xx_block_id bid) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci struct list_head * sbid_list; 9058c2ecf20Sopenharmony_ci struct tape_34xx_sbid * sbid; 9068c2ecf20Sopenharmony_ci struct list_head * l; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci /* 9098c2ecf20Sopenharmony_ci * immediately return if there is no list at all or the block to add 9108c2ecf20Sopenharmony_ci * is located in segment 1 of wrap 0 because this position is used 9118c2ecf20Sopenharmony_ci * if no hardware position data is supplied. 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_ci sbid_list = (struct list_head *) device->discdata; 9148c2ecf20Sopenharmony_ci if (!sbid_list || (bid.segment < 2 && bid.wrap == 0)) 9158c2ecf20Sopenharmony_ci return; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci /* 9188c2ecf20Sopenharmony_ci * Search the position where to insert the new entry. Hardware 9198c2ecf20Sopenharmony_ci * acceleration uses only the segment and wrap number. So we 9208c2ecf20Sopenharmony_ci * need only one entry for a specific wrap/segment combination. 9218c2ecf20Sopenharmony_ci * If there is a block with a lower number but the same hard- 9228c2ecf20Sopenharmony_ci * ware position data we just update the block number in the 9238c2ecf20Sopenharmony_ci * existing entry. 9248c2ecf20Sopenharmony_ci */ 9258c2ecf20Sopenharmony_ci list_for_each(l, sbid_list) { 9268c2ecf20Sopenharmony_ci sbid = list_entry(l, struct tape_34xx_sbid, list); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if ( 9298c2ecf20Sopenharmony_ci (sbid->bid.segment == bid.segment) && 9308c2ecf20Sopenharmony_ci (sbid->bid.wrap == bid.wrap) 9318c2ecf20Sopenharmony_ci ) { 9328c2ecf20Sopenharmony_ci if (bid.block < sbid->bid.block) 9338c2ecf20Sopenharmony_ci sbid->bid = bid; 9348c2ecf20Sopenharmony_ci else return; 9358c2ecf20Sopenharmony_ci break; 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci /* Sort in according to logical block number. */ 9398c2ecf20Sopenharmony_ci if (bid.block < sbid->bid.block) { 9408c2ecf20Sopenharmony_ci tape_34xx_append_new_sbid(bid, l->prev); 9418c2ecf20Sopenharmony_ci break; 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci /* List empty or new block bigger than last entry. */ 9458c2ecf20Sopenharmony_ci if (l == sbid_list) 9468c2ecf20Sopenharmony_ci tape_34xx_append_new_sbid(bid, l->prev); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci DBF_LH(4, "Current list is:\n"); 9498c2ecf20Sopenharmony_ci list_for_each(l, sbid_list) { 9508c2ecf20Sopenharmony_ci sbid = list_entry(l, struct tape_34xx_sbid, list); 9518c2ecf20Sopenharmony_ci DBF_LH(4, "%d:%03d@%05d\n", 9528c2ecf20Sopenharmony_ci sbid->bid.wrap, 9538c2ecf20Sopenharmony_ci sbid->bid.segment, 9548c2ecf20Sopenharmony_ci sbid->bid.block 9558c2ecf20Sopenharmony_ci ); 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci/* 9608c2ecf20Sopenharmony_ci * Delete all entries from the search block ID list that belong to tape blocks 9618c2ecf20Sopenharmony_ci * equal or higher than the given number. 9628c2ecf20Sopenharmony_ci */ 9638c2ecf20Sopenharmony_cistatic void 9648c2ecf20Sopenharmony_citape_34xx_delete_sbid_from(struct tape_device *device, int from) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci struct list_head * sbid_list; 9678c2ecf20Sopenharmony_ci struct tape_34xx_sbid * sbid; 9688c2ecf20Sopenharmony_ci struct list_head * l; 9698c2ecf20Sopenharmony_ci struct list_head * n; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci sbid_list = (struct list_head *) device->discdata; 9728c2ecf20Sopenharmony_ci if (!sbid_list) 9738c2ecf20Sopenharmony_ci return; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci list_for_each_safe(l, n, sbid_list) { 9768c2ecf20Sopenharmony_ci sbid = list_entry(l, struct tape_34xx_sbid, list); 9778c2ecf20Sopenharmony_ci if (sbid->bid.block >= from) { 9788c2ecf20Sopenharmony_ci DBF_LH(4, "Delete sbid %d:%03d@%05d\n", 9798c2ecf20Sopenharmony_ci sbid->bid.wrap, 9808c2ecf20Sopenharmony_ci sbid->bid.segment, 9818c2ecf20Sopenharmony_ci sbid->bid.block 9828c2ecf20Sopenharmony_ci ); 9838c2ecf20Sopenharmony_ci list_del(l); 9848c2ecf20Sopenharmony_ci kfree(sbid); 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci/* 9908c2ecf20Sopenharmony_ci * Merge hardware position data into a block id. 9918c2ecf20Sopenharmony_ci */ 9928c2ecf20Sopenharmony_cistatic void 9938c2ecf20Sopenharmony_citape_34xx_merge_sbid( 9948c2ecf20Sopenharmony_ci struct tape_device * device, 9958c2ecf20Sopenharmony_ci struct tape_34xx_block_id * bid 9968c2ecf20Sopenharmony_ci) { 9978c2ecf20Sopenharmony_ci struct tape_34xx_sbid * sbid; 9988c2ecf20Sopenharmony_ci struct tape_34xx_sbid * sbid_to_use; 9998c2ecf20Sopenharmony_ci struct list_head * sbid_list; 10008c2ecf20Sopenharmony_ci struct list_head * l; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci sbid_list = (struct list_head *) device->discdata; 10038c2ecf20Sopenharmony_ci bid->wrap = 0; 10048c2ecf20Sopenharmony_ci bid->segment = 1; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci if (!sbid_list || list_empty(sbid_list)) 10078c2ecf20Sopenharmony_ci return; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci sbid_to_use = NULL; 10108c2ecf20Sopenharmony_ci list_for_each(l, sbid_list) { 10118c2ecf20Sopenharmony_ci sbid = list_entry(l, struct tape_34xx_sbid, list); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci if (sbid->bid.block >= bid->block) 10148c2ecf20Sopenharmony_ci break; 10158c2ecf20Sopenharmony_ci sbid_to_use = sbid; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci if (sbid_to_use) { 10188c2ecf20Sopenharmony_ci bid->wrap = sbid_to_use->bid.wrap; 10198c2ecf20Sopenharmony_ci bid->segment = sbid_to_use->bid.segment; 10208c2ecf20Sopenharmony_ci DBF_LH(4, "Use %d:%03d@%05d for %05d\n", 10218c2ecf20Sopenharmony_ci sbid_to_use->bid.wrap, 10228c2ecf20Sopenharmony_ci sbid_to_use->bid.segment, 10238c2ecf20Sopenharmony_ci sbid_to_use->bid.block, 10248c2ecf20Sopenharmony_ci bid->block 10258c2ecf20Sopenharmony_ci ); 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cistatic int 10308c2ecf20Sopenharmony_citape_34xx_setup_device(struct tape_device * device) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci int rc; 10338c2ecf20Sopenharmony_ci struct list_head * discdata; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci DBF_EVENT(6, "34xx device setup\n"); 10368c2ecf20Sopenharmony_ci if ((rc = tape_std_assign(device)) == 0) { 10378c2ecf20Sopenharmony_ci if ((rc = tape_34xx_medium_sense(device)) != 0) { 10388c2ecf20Sopenharmony_ci DBF_LH(3, "34xx medium sense returned %d\n", rc); 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci discdata = kmalloc(sizeof(struct list_head), GFP_KERNEL); 10428c2ecf20Sopenharmony_ci if (discdata) { 10438c2ecf20Sopenharmony_ci INIT_LIST_HEAD(discdata); 10448c2ecf20Sopenharmony_ci device->discdata = discdata; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci return rc; 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic void 10518c2ecf20Sopenharmony_citape_34xx_cleanup_device(struct tape_device *device) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci tape_std_unassign(device); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if (device->discdata) { 10568c2ecf20Sopenharmony_ci tape_34xx_delete_sbid_from(device, 0); 10578c2ecf20Sopenharmony_ci kfree(device->discdata); 10588c2ecf20Sopenharmony_ci device->discdata = NULL; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci/* 10648c2ecf20Sopenharmony_ci * MTTELL: Tell block. Return the number of block relative to current file. 10658c2ecf20Sopenharmony_ci */ 10668c2ecf20Sopenharmony_cistatic int 10678c2ecf20Sopenharmony_citape_34xx_mttell(struct tape_device *device, int mt_count) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci struct { 10708c2ecf20Sopenharmony_ci struct tape_34xx_block_id cbid; 10718c2ecf20Sopenharmony_ci struct tape_34xx_block_id dbid; 10728c2ecf20Sopenharmony_ci } __attribute__ ((packed)) block_id; 10738c2ecf20Sopenharmony_ci int rc; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci rc = tape_std_read_block_id(device, (__u64 *) &block_id); 10768c2ecf20Sopenharmony_ci if (rc) 10778c2ecf20Sopenharmony_ci return rc; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci tape_34xx_add_sbid(device, block_id.cbid); 10808c2ecf20Sopenharmony_ci return block_id.cbid.block; 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci/* 10848c2ecf20Sopenharmony_ci * MTSEEK: seek to the specified block. 10858c2ecf20Sopenharmony_ci */ 10868c2ecf20Sopenharmony_cistatic int 10878c2ecf20Sopenharmony_citape_34xx_mtseek(struct tape_device *device, int mt_count) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci struct tape_request *request; 10908c2ecf20Sopenharmony_ci struct tape_34xx_block_id * bid; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (mt_count > 0x3fffff) { 10938c2ecf20Sopenharmony_ci DBF_EXCEPTION(6, "xsee parm\n"); 10948c2ecf20Sopenharmony_ci return -EINVAL; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci request = tape_alloc_request(3, 4); 10978c2ecf20Sopenharmony_ci if (IS_ERR(request)) 10988c2ecf20Sopenharmony_ci return PTR_ERR(request); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci /* setup ccws */ 11018c2ecf20Sopenharmony_ci request->op = TO_LBL; 11028c2ecf20Sopenharmony_ci bid = (struct tape_34xx_block_id *) request->cpdata; 11038c2ecf20Sopenharmony_ci bid->format = (*device->modeset_byte & 0x08) ? 11048c2ecf20Sopenharmony_ci TAPE34XX_FMT_3480_XF : TAPE34XX_FMT_3480; 11058c2ecf20Sopenharmony_ci bid->block = mt_count; 11068c2ecf20Sopenharmony_ci tape_34xx_merge_sbid(device, bid); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); 11098c2ecf20Sopenharmony_ci tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata); 11108c2ecf20Sopenharmony_ci tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci /* execute it */ 11138c2ecf20Sopenharmony_ci return tape_do_io_free(device, request); 11148c2ecf20Sopenharmony_ci} 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci/* 11178c2ecf20Sopenharmony_ci * List of 3480/3490 magnetic tape commands. 11188c2ecf20Sopenharmony_ci */ 11198c2ecf20Sopenharmony_cistatic tape_mtop_fn tape_34xx_mtop[TAPE_NR_MTOPS] = { 11208c2ecf20Sopenharmony_ci [MTRESET] = tape_std_mtreset, 11218c2ecf20Sopenharmony_ci [MTFSF] = tape_std_mtfsf, 11228c2ecf20Sopenharmony_ci [MTBSF] = tape_std_mtbsf, 11238c2ecf20Sopenharmony_ci [MTFSR] = tape_std_mtfsr, 11248c2ecf20Sopenharmony_ci [MTBSR] = tape_std_mtbsr, 11258c2ecf20Sopenharmony_ci [MTWEOF] = tape_std_mtweof, 11268c2ecf20Sopenharmony_ci [MTREW] = tape_std_mtrew, 11278c2ecf20Sopenharmony_ci [MTOFFL] = tape_std_mtoffl, 11288c2ecf20Sopenharmony_ci [MTNOP] = tape_std_mtnop, 11298c2ecf20Sopenharmony_ci [MTRETEN] = tape_std_mtreten, 11308c2ecf20Sopenharmony_ci [MTBSFM] = tape_std_mtbsfm, 11318c2ecf20Sopenharmony_ci [MTFSFM] = tape_std_mtfsfm, 11328c2ecf20Sopenharmony_ci [MTEOM] = tape_std_mteom, 11338c2ecf20Sopenharmony_ci [MTERASE] = tape_std_mterase, 11348c2ecf20Sopenharmony_ci [MTRAS1] = NULL, 11358c2ecf20Sopenharmony_ci [MTRAS2] = NULL, 11368c2ecf20Sopenharmony_ci [MTRAS3] = NULL, 11378c2ecf20Sopenharmony_ci [MTSETBLK] = tape_std_mtsetblk, 11388c2ecf20Sopenharmony_ci [MTSETDENSITY] = NULL, 11398c2ecf20Sopenharmony_ci [MTSEEK] = tape_34xx_mtseek, 11408c2ecf20Sopenharmony_ci [MTTELL] = tape_34xx_mttell, 11418c2ecf20Sopenharmony_ci [MTSETDRVBUFFER] = NULL, 11428c2ecf20Sopenharmony_ci [MTFSS] = NULL, 11438c2ecf20Sopenharmony_ci [MTBSS] = NULL, 11448c2ecf20Sopenharmony_ci [MTWSM] = NULL, 11458c2ecf20Sopenharmony_ci [MTLOCK] = NULL, 11468c2ecf20Sopenharmony_ci [MTUNLOCK] = NULL, 11478c2ecf20Sopenharmony_ci [MTLOAD] = tape_std_mtload, 11488c2ecf20Sopenharmony_ci [MTUNLOAD] = tape_std_mtunload, 11498c2ecf20Sopenharmony_ci [MTCOMPRESSION] = tape_std_mtcompression, 11508c2ecf20Sopenharmony_ci [MTSETPART] = NULL, 11518c2ecf20Sopenharmony_ci [MTMKPART] = NULL 11528c2ecf20Sopenharmony_ci}; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci/* 11558c2ecf20Sopenharmony_ci * Tape discipline structure for 3480 and 3490. 11568c2ecf20Sopenharmony_ci */ 11578c2ecf20Sopenharmony_cistatic struct tape_discipline tape_discipline_34xx = { 11588c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 11598c2ecf20Sopenharmony_ci .setup_device = tape_34xx_setup_device, 11608c2ecf20Sopenharmony_ci .cleanup_device = tape_34xx_cleanup_device, 11618c2ecf20Sopenharmony_ci .process_eov = tape_std_process_eov, 11628c2ecf20Sopenharmony_ci .irq = tape_34xx_irq, 11638c2ecf20Sopenharmony_ci .read_block = tape_std_read_block, 11648c2ecf20Sopenharmony_ci .write_block = tape_std_write_block, 11658c2ecf20Sopenharmony_ci .ioctl_fn = tape_34xx_ioctl, 11668c2ecf20Sopenharmony_ci .mtop_array = tape_34xx_mtop 11678c2ecf20Sopenharmony_ci}; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_cistatic struct ccw_device_id tape_34xx_ids[] = { 11708c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE(0x3480, 0, 0x3480, 0), .driver_info = tape_3480}, 11718c2ecf20Sopenharmony_ci { CCW_DEVICE_DEVTYPE(0x3490, 0, 0x3490, 0), .driver_info = tape_3490}, 11728c2ecf20Sopenharmony_ci { /* end of list */ }, 11738c2ecf20Sopenharmony_ci}; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_cistatic int 11768c2ecf20Sopenharmony_citape_34xx_online(struct ccw_device *cdev) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci return tape_generic_online( 11798c2ecf20Sopenharmony_ci dev_get_drvdata(&cdev->dev), 11808c2ecf20Sopenharmony_ci &tape_discipline_34xx 11818c2ecf20Sopenharmony_ci ); 11828c2ecf20Sopenharmony_ci} 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cistatic struct ccw_driver tape_34xx_driver = { 11858c2ecf20Sopenharmony_ci .driver = { 11868c2ecf20Sopenharmony_ci .name = "tape_34xx", 11878c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 11888c2ecf20Sopenharmony_ci }, 11898c2ecf20Sopenharmony_ci .ids = tape_34xx_ids, 11908c2ecf20Sopenharmony_ci .probe = tape_generic_probe, 11918c2ecf20Sopenharmony_ci .remove = tape_generic_remove, 11928c2ecf20Sopenharmony_ci .set_online = tape_34xx_online, 11938c2ecf20Sopenharmony_ci .set_offline = tape_generic_offline, 11948c2ecf20Sopenharmony_ci .freeze = tape_generic_pm_suspend, 11958c2ecf20Sopenharmony_ci .int_class = IRQIO_TAP, 11968c2ecf20Sopenharmony_ci}; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic int 11998c2ecf20Sopenharmony_citape_34xx_init (void) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci int rc; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci TAPE_DBF_AREA = debug_register ( "tape_34xx", 2, 2, 4*sizeof(long)); 12048c2ecf20Sopenharmony_ci debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view); 12058c2ecf20Sopenharmony_ci#ifdef DBF_LIKE_HELL 12068c2ecf20Sopenharmony_ci debug_set_level(TAPE_DBF_AREA, 6); 12078c2ecf20Sopenharmony_ci#endif 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci DBF_EVENT(3, "34xx init\n"); 12108c2ecf20Sopenharmony_ci /* Register driver for 3480/3490 tapes. */ 12118c2ecf20Sopenharmony_ci rc = ccw_driver_register(&tape_34xx_driver); 12128c2ecf20Sopenharmony_ci if (rc) 12138c2ecf20Sopenharmony_ci DBF_EVENT(3, "34xx init failed\n"); 12148c2ecf20Sopenharmony_ci else 12158c2ecf20Sopenharmony_ci DBF_EVENT(3, "34xx registered\n"); 12168c2ecf20Sopenharmony_ci return rc; 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_cistatic void 12208c2ecf20Sopenharmony_citape_34xx_exit(void) 12218c2ecf20Sopenharmony_ci{ 12228c2ecf20Sopenharmony_ci ccw_driver_unregister(&tape_34xx_driver); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci debug_unregister(TAPE_DBF_AREA); 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, tape_34xx_ids); 12288c2ecf20Sopenharmony_ciMODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH"); 12298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Linux on zSeries channel attached 3480 tape device driver"); 12308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_cimodule_init(tape_34xx_init); 12338c2ecf20Sopenharmony_cimodule_exit(tape_34xx_exit); 1234