Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 00ac37f5 authored by Douglas Gilbert's avatar Douglas Gilbert Committed by Jeff Garzik
Browse files

[libata scsi] MODE SELECT, strengthen mode sense



  - move default mode pages to the front of libata-scsi.c
    so various functions can access them
  - partial annotation of these pages, point out divergence
    from sat-r06
  - replace various mode page magic numbers with defines
  - strengthen MODE SENSE command decoding: handle DBD
    bit in cdb, yield block descriptor (per sat-r06) and
    handle mode sub pages

Signed-off-by: default avatarDouglas Gilbert <dougg@torque.net>
Signed-off-by: default avatarJeff Garzik <jgarzik@pobox.com>
parent a21a84a3
Loading
Loading
Loading
Loading
+103 −41
Original line number Diff line number Diff line
@@ -51,6 +51,45 @@ typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, const u8 *scs
static struct ata_device *
ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev);

#define RW_RECOVERY_MPAGE 0x1
#define RW_RECOVERY_MPAGE_LEN 12
#define CACHE_MPAGE 0x8
#define CACHE_MPAGE_LEN 20
#define CONTROL_MPAGE 0xa
#define CONTROL_MPAGE_LEN 12
#define ALL_MPAGES 0x3f
#define ALL_SUB_MPAGES 0xff


static const u8 def_rw_recovery_mpage[] = {
	RW_RECOVERY_MPAGE,
	RW_RECOVERY_MPAGE_LEN - 2,
	(1 << 7) |	/* AWRE, sat-r06 say it shall be 0 */
	    (1 << 6),	/* ARRE (auto read reallocation) */
	0,		/* read retry count */
	0, 0, 0, 0,
	0,		/* write retry count */
	0, 0, 0
};

static const u8 def_cache_mpage[CACHE_MPAGE_LEN] = {
	CACHE_MPAGE,
	CACHE_MPAGE_LEN - 2,
	0,		/* contains WCE, needs to be 0 for logic */
	0, 0, 0, 0, 0, 0, 0, 0, 0,
	0,		/* contains DRA, needs to be 0 for logic */
	0, 0, 0, 0, 0, 0, 0
};

static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = {
	CONTROL_MPAGE,
	CONTROL_MPAGE_LEN - 2,
	2,	/* DSENSE=0, GLTSD=1 */
	0,	/* [QAM+QERR may be 1, see 05-359r1] */
	0, 0, 0, 0, 0xff, 0xff,
	0, 30	/* extended self test time, see 05-359r1 */
};


static void ata_scsi_invalid_field(struct scsi_cmnd *cmd,
				   void (*done)(struct scsi_cmnd *))
@@ -1583,13 +1622,9 @@ static void ata_msense_push(u8 **ptr_io, const u8 *last,
static unsigned int ata_msense_caching(u16 *id, u8 **ptr_io,
				       const u8 *last)
{
	u8 page[] = {
		0x8,				/* page code */
		0x12,				/* page length */
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 10 zeroes */
		0, 0, 0, 0, 0, 0, 0, 0		/* 8 zeroes */
	};
	u8 page[CACHE_MPAGE_LEN];

	memcpy(page, def_cache_mpage, sizeof(page));
	if (ata_id_wcache_enabled(id))
		page[2] |= (1 << 2);	/* write cache enable */
	if (!ata_id_rahead_enabled(id))
@@ -1613,15 +1648,9 @@ static unsigned int ata_msense_caching(u16 *id, u8 **ptr_io,

static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last)
{
	const u8 page[] = {0xa, 0xa, 6, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 30};

	/* byte 2: set the descriptor format sense data bit (bit 2)
	 * since we need to support returning this format for SAT
	 * commands and any SCSI commands against a 48b LBA device.
	 */

	ata_msense_push(ptr_io, last, page, sizeof(page));
	return sizeof(page);
	ata_msense_push(ptr_io, last, def_control_mpage,
			sizeof(def_control_mpage));
	return sizeof(def_control_mpage);
}

/**
@@ -1638,15 +1667,10 @@ static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last)

static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last)
{
	const u8 page[] = {
		0x1,			  /* page code */
		0xa,			  /* page length */
		(1 << 7) | (1 << 6),	  /* note auto r/w reallocation */
		0, 0, 0, 0, 0, 0, 0, 0, 0 /* 9 zeroes */
	};

	ata_msense_push(ptr_io, last, page, sizeof(page));
	return sizeof(page);
	ata_msense_push(ptr_io, last, def_rw_recovery_mpage,
			sizeof(def_rw_recovery_mpage));
	return sizeof(def_rw_recovery_mpage);
}

/**
@@ -1655,7 +1679,9 @@ static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last)
 *	@rbuf: Response buffer, to which simulated SCSI cmd output is sent.
 *	@buflen: Response buffer length.
 *
 *	Simulate MODE SENSE commands.
 *	Simulate MODE SENSE commands. Assume this is invoked for direct
 *	access devices (e.g. disks) only. There should be no block
 *	descriptor for other device types.
 *
 *	LOCKING:
 *	spin_lock_irqsave(host_set lock)
@@ -1665,15 +1691,22 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
				  unsigned int buflen)
{
	u8 *scsicmd = args->cmd->cmnd, *p, *last;
	unsigned int page_control, six_byte, output_len;
	const u8 sat_blk_desc[] = {
		0, 0, 0, 0,	/* number of blocks: sat unspecified */
		0,
		0, 0x2, 0x0	/* block length: 512 bytes */
	};
	u8 pg, spg;
	unsigned int ebd, page_control, six_byte, output_len, alloc_len, minlen;

	VPRINTK("ENTER\n");

	six_byte = (scsicmd[0] == MODE_SENSE);

	/* we only support saved and current values (which we treat
	 * in the same manner)
	ebd = !(scsicmd[1] & 0x8);      /* dbd bit inverted == edb */
	/*
	 * LLBA bit in msense(10) ignored (compliant)
	 */

	page_control = scsicmd[2] >> 6;
	switch (page_control) {
	case 0: /* current */
@@ -1686,29 +1719,42 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
		goto invalid_fld;
	}

	if (six_byte)
		output_len = 4;
	else
		output_len = 8;
	if (six_byte) {
		output_len = 4 + (ebd ? 8 : 0);
		alloc_len = scsicmd[4];
	} else {
		output_len = 8 + (ebd ? 8 : 0);
		alloc_len = (scsicmd[7] << 8) + scsicmd[8];
	}
	minlen = (alloc_len < buflen) ? alloc_len : buflen;

	p = rbuf + output_len;
	last = rbuf + buflen - 1;
	last = rbuf + minlen - 1;

	switch(scsicmd[2] & 0x3f) {
	case 0x01:		/* r/w error recovery */
	pg = scsicmd[2] & 0x3f;
	spg = scsicmd[3];
	/*
	 * No mode subpages supported (yet) but asking for _all_
	 * subpages may be valid
	 */
	if (spg && (spg != ALL_SUB_MPAGES))
		goto invalid_fld;

	switch(pg) {
	case RW_RECOVERY_MPAGE:
		output_len += ata_msense_rw_recovery(&p, last);
		break;

	case 0x08:		/* caching */
	case CACHE_MPAGE:
		output_len += ata_msense_caching(args->id, &p, last);
		break;

	case 0x0a: {		/* control mode */
	case CONTROL_MPAGE: {
		output_len += ata_msense_ctl_mode(&p, last);
		break;
		}

	case 0x3f:		/* all pages */
	case ALL_MPAGES:
		output_len += ata_msense_rw_recovery(&p, last);
		output_len += ata_msense_caching(args->id, &p, last);
		output_len += ata_msense_ctl_mode(&p, last);
@@ -1718,15 +1764,31 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf,
		goto invalid_fld;
	}

	if (minlen < 1)
		return 0;
	if (six_byte) {
		output_len--;
		rbuf[0] = output_len;
		if (ebd) {
			if (minlen > 3)
				rbuf[3] = sizeof(sat_blk_desc);
			if (minlen > 11)
				memcpy(rbuf + 4, sat_blk_desc,
				       sizeof(sat_blk_desc));
		}
	} else {
		output_len -= 2;
		rbuf[0] = output_len >> 8;
		if (minlen > 1)
			rbuf[1] = output_len;
		if (ebd) {
			if (minlen > 7)
				rbuf[7] = sizeof(sat_blk_desc);
			if (minlen > 15)
				memcpy(rbuf + 8, sat_blk_desc,
				       sizeof(sat_blk_desc));
		}
	}

	return 0;

invalid_fld:
@@ -2156,7 +2218,7 @@ ata_scsi_map_proto(u8 byte1)
 *	Zero on success, non-zero on failure.
 */
static unsigned int
ata_scsi_pass_thru(struct ata_queued_cmd *qc, u8 *scsicmd)
ata_scsi_pass_thru(struct ata_queued_cmd *qc, const u8 *scsicmd)
{
	struct ata_taskfile *tf = &(qc->tf);
	struct scsi_cmnd *cmd = qc->scsicmd;