/*
* tape device discipline for 3480/3490 tapes.
*
* Copyright IBM Corp. 2001, 2009
* Author(s): Carsten Otte <cotte@de.ibm.com>
* Tuan Ngo-Anh <ngoanh@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#define KMSG_COMPONENT "tape_34xx"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/bio.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#define TAPE_DBF_AREA tape_34xx_dbf
#include "tape.h"
#include "tape_std.h"
/*
* Pointer to debug area.
*/
debug_info_t *TAPE_DBF_AREA = NULL;
EXPORT_SYMBOL(TAPE_DBF_AREA);
#define TAPE34XX_FMT_3480 0
#define TAPE34XX_FMT_3480_2_XF 1
#define TAPE34XX_FMT_3480_XF 2
struct tape_34xx_block_id {
unsigned int wrap : 1;
unsigned int segment : 7;
unsigned int format : 2;
unsigned int block : 22;
};
/*
* A list of block ID's is used to faster seek blocks.
*/
struct tape_34xx_sbid {
struct list_head list;
struct tape_34xx_block_id bid;
};
static void tape_34xx_delete_sbid_from(struct tape_device *, int);
/*
* 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 int tape_34xx_medium_sense(struct tape_device *device)
{
struct tape_request *request;
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);
__tape_34xx_medium_sense(request);
return rc;
}
static void tape_34xx_medium_sense_async(struct tape_device *device)
{
struct tape_request *request;
request = tape_alloc_request(1, 32);
if (IS_ERR(request)) {
DBF_EXCEPTION(6, "MSEN fail\n");
return;
}
request->op = TO_MSEN;
tape_ccw_end(request->cpaddr,