freebsd-skq/sys/cam/ata/ata_xpt.c
mav 24017b5387 Implement media change notification for DA and CD removable media devices.
It includes three parts:
 1) Modifications to CAM to detect media media changes and report them to
disk(9) layer. For modern SATA (and potentially UAS) devices it utilizes
Asynchronous Notification mechanism to receive events from hardware.
Active polling with TEST UNIT READY commands with 3 seconds period is used
for incapable hardware. After that both CD and DA drivers work the same way,
detecting two conditions: "NOT READY: Medium not present" after medium was
detected previously, and "UNIT ATTENTION: Not ready to ready change, medium
may have changed". First one reported to disk(9) as media removal, second
as media insert/change. To reliably receive second event new
AC_UNIT_ATTENTION async added to make UAs broadcasted to all periphs by
generic error handling code in cam_periph_error().
 2) Modifications to GEOM core to handle media remove and change events.
Media removal handled by spoiling all consumers attached to the provider.
Media change event also schedules provider retaste after spoiling to probe
new media. New flag G_CF_ORPHAN was added to consumers to reflect that
consumer is in process of destruction. It allows retaste to create new
geom instance of the same class, while previous one is still dying.
 3) Modifications to some GEOM classes: DEV -- to report media change
events to devd; VFS -- to handle spoiling same as orphan to prevent
accessing replaced media. PART class already handles spoiling alike to
orphan.

Reviewed by:	silence on geom@ and scsi@
Tested by:	avg
Sponsored by:	iXsystems, Inc. / PC-BSD
MFC after:	2 months
2012-07-29 11:51:48 +00:00

2098 lines
62 KiB
C

/*-
* Copyright (c) 2009 Alexander Motin <mav@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification, immediately at the beginning of the file.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/interrupt.h>
#include <sys/sbuf.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <cam/cam_queue.h>
#include <cam/cam_periph.h>
#include <cam/cam_sim.h>
#include <cam/cam_xpt.h>
#include <cam/cam_xpt_sim.h>
#include <cam/cam_xpt_periph.h>
#include <cam/cam_xpt_internal.h>
#include <cam/cam_debug.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_message.h>
#include <cam/ata/ata_all.h>
#include <machine/stdarg.h> /* for xpt_print below */
#include "opt_cam.h"
struct ata_quirk_entry {
struct scsi_inquiry_pattern inq_pat;
u_int8_t quirks;
#define CAM_QUIRK_MAXTAGS 0x01
u_int mintags;
u_int maxtags;
};
static periph_init_t probe_periph_init;
static struct periph_driver probe_driver =
{
probe_periph_init, "aprobe",
TAILQ_HEAD_INITIALIZER(probe_driver.units), /* generation */ 0,
CAM_PERIPH_DRV_EARLY
};
PERIPHDRIVER_DECLARE(aprobe, probe_driver);
typedef enum {
PROBE_RESET,
PROBE_IDENTIFY,
PROBE_SPINUP,
PROBE_SETMODE,
PROBE_SETPM,
PROBE_SETAPST,
PROBE_SETDMAAA,
PROBE_SETAN,
PROBE_SET_MULTI,
PROBE_INQUIRY,
PROBE_FULL_INQUIRY,
PROBE_PM_PID,
PROBE_PM_PRV,
PROBE_IDENTIFY_SES,
PROBE_IDENTIFY_SAFTE,
PROBE_DONE,
PROBE_INVALID
} probe_action;
static char *probe_action_text[] = {
"PROBE_RESET",
"PROBE_IDENTIFY",
"PROBE_SPINUP",
"PROBE_SETMODE",
"PROBE_SETPM",
"PROBE_SETAPST",
"PROBE_SETDMAAA",
"PROBE_SETAN",
"PROBE_SET_MULTI",
"PROBE_INQUIRY",
"PROBE_FULL_INQUIRY",
"PROBE_PM_PID",
"PROBE_PM_PRV",
"PROBE_IDENTIFY_SES",
"PROBE_IDENTIFY_SAFTE",
"PROBE_DONE",
"PROBE_INVALID"
};
#define PROBE_SET_ACTION(softc, newaction) \
do { \
char **text; \
text = probe_action_text; \
CAM_DEBUG((softc)->periph->path, CAM_DEBUG_PROBE, \
("Probe %s to %s\n", text[(softc)->action], \
text[(newaction)])); \
(softc)->action = (newaction); \
} while(0)
typedef enum {
PROBE_NO_ANNOUNCE = 0x04
} probe_flags;
typedef struct {
TAILQ_HEAD(, ccb_hdr) request_ccbs;
struct ata_params ident_data;
probe_action action;
probe_flags flags;
uint32_t pm_pid;
uint32_t pm_prv;
int restart;
int spinup;
int faults;
u_int caps;
struct cam_periph *periph;
} probe_softc;
static struct ata_quirk_entry ata_quirk_table[] =
{
{
/* Default tagged queuing parameters for all devices */
{
T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED,
/*vendor*/"*", /*product*/"*", /*revision*/"*"
},
/*quirks*/0, /*mintags*/0, /*maxtags*/0
},
};
static const int ata_quirk_table_size =
sizeof(ata_quirk_table) / sizeof(*ata_quirk_table);
static cam_status proberegister(struct cam_periph *periph,
void *arg);
static void probeschedule(struct cam_periph *probe_periph);
static void probestart(struct cam_periph *periph, union ccb *start_ccb);
static void proberequestdefaultnegotiation(struct cam_periph *periph);
static void probedone(struct cam_periph *periph, union ccb *done_ccb);
static void probecleanup(struct cam_periph *periph);
static void ata_find_quirk(struct cam_ed *device);
static void ata_scan_bus(struct cam_periph *periph, union ccb *ccb);
static void ata_scan_lun(struct cam_periph *periph,
struct cam_path *path, cam_flags flags,
union ccb *ccb);
static void xptscandone(struct cam_periph *periph, union ccb *done_ccb);
static struct cam_ed *
ata_alloc_device(struct cam_eb *bus, struct cam_et *target,
lun_id_t lun_id);
static void ata_device_transport(struct cam_path *path);
static void ata_get_transfer_settings(struct ccb_trans_settings *cts);
static void ata_set_transfer_settings(struct ccb_trans_settings *cts,
struct cam_ed *device,
int async_update);
static void ata_dev_async(u_int32_t async_code,
struct cam_eb *bus,
struct cam_et *target,
struct cam_ed *device,
void *async_arg);
static void ata_action(union ccb *start_ccb);
static void ata_announce_periph(struct cam_periph *periph);
static int ata_dma = 1;
static int atapi_dma = 1;
TUNABLE_INT("hw.ata.ata_dma", &ata_dma);
TUNABLE_INT("hw.ata.atapi_dma", &atapi_dma);
static struct xpt_xport ata_xport = {
.alloc_device = ata_alloc_device,
.action = ata_action,
.async = ata_dev_async,
.announce = ata_announce_periph,
};
struct xpt_xport *
ata_get_xport(void)
{
return (&ata_xport);
}
static void
probe_periph_init()
{
}
static cam_status
proberegister(struct cam_periph *periph, void *arg)
{
union ccb *request_ccb; /* CCB representing the probe request */
cam_status status;
probe_softc *softc;
request_ccb = (union ccb *)arg;
if (periph == NULL) {
printf("proberegister: periph was NULL!!\n");
return(CAM_REQ_CMP_ERR);
}
if (request_ccb == NULL) {
printf("proberegister: no probe CCB, "
"can't register device\n");
return(CAM_REQ_CMP_ERR);
}
softc = (probe_softc *)malloc(sizeof(*softc), M_CAMXPT, M_ZERO | M_NOWAIT);
if (softc == NULL) {
printf("proberegister: Unable to probe new device. "
"Unable to allocate softc\n");
return(CAM_REQ_CMP_ERR);
}
TAILQ_INIT(&softc->request_ccbs);
TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h,
periph_links.tqe);
softc->flags = 0;
periph->softc = softc;
softc->periph = periph;
softc->action = PROBE_INVALID;
status = cam_periph_acquire(periph);
if (status != CAM_REQ_CMP) {
return (status);
}
CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("Probe started\n"));
/*
* Ensure nobody slip in until probe finish.
*/
cam_freeze_devq_arg(periph->path,
RELSIM_RELEASE_RUNLEVEL, CAM_RL_XPT + 1);
probeschedule(periph);
return(CAM_REQ_CMP);
}
static void
probeschedule(struct cam_periph *periph)
{
union ccb *ccb;
probe_softc *softc;
softc = (probe_softc *)periph->softc;
ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs);
if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) ||
periph->path->device->protocol == PROTO_SATAPM ||
periph->path->device->protocol == PROTO_SEMB)
PROBE_SET_ACTION(softc, PROBE_RESET);
else
PROBE_SET_ACTION(softc, PROBE_IDENTIFY);
if (ccb->crcn.flags & CAM_EXPECT_INQ_CHANGE)
softc->flags |= PROBE_NO_ANNOUNCE;
else
softc->flags &= ~PROBE_NO_ANNOUNCE;
xpt_schedule(periph, CAM_PRIORITY_XPT);
}
static void
probestart(struct cam_periph *periph, union ccb *start_ccb)
{
struct ccb_trans_settings cts;
struct ccb_ataio *ataio;
struct ccb_scsiio *csio;
probe_softc *softc;
struct cam_path *path;
struct ata_params *ident_buf;
CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probestart\n"));
softc = (probe_softc *)periph->softc;
path = start_ccb->ccb_h.path;
ataio = &start_ccb->ataio;
csio = &start_ccb->csio;
ident_buf = &periph->path->device->ident_data;
if (softc->restart) {
softc->restart = 0;
if ((path->device->flags & CAM_DEV_UNCONFIGURED) ||
path->device->protocol == PROTO_SATAPM ||
path->device->protocol == PROTO_SEMB)
softc->action = PROBE_RESET;
else
softc->action = PROBE_IDENTIFY;
}
switch (softc->action) {
case PROBE_RESET:
cam_fill_ataio(ataio,
0,
probedone,
/*flags*/CAM_DIR_NONE,
0,
/*data_ptr*/NULL,
/*dxfer_len*/0,
15 * 1000);
ata_reset_cmd(ataio);
break;
case PROBE_IDENTIFY:
cam_fill_ataio(ataio,
1,
probedone,
/*flags*/CAM_DIR_IN,
0,
/*data_ptr*/(u_int8_t *)&softc->ident_data,
/*dxfer_len*/sizeof(softc->ident_data),
30 * 1000);
if (periph->path->device->protocol == PROTO_ATA)
ata_28bit_cmd(ataio, ATA_ATA_IDENTIFY, 0, 0, 0);
else
ata_28bit_cmd(ataio, ATA_ATAPI_IDENTIFY, 0, 0, 0);
break;
case PROBE_SPINUP:
if (bootverbose)
xpt_print(path, "Spinning up device\n");
cam_fill_ataio(ataio,
1,
probedone,
/*flags*/CAM_DIR_NONE | CAM_HIGH_POWER,
0,
/*data_ptr*/NULL,
/*dxfer_len*/0,
30 * 1000);
ata_28bit_cmd(ataio, ATA_SETFEATURES, ATA_SF_PUIS_SPINUP, 0, 0);
break;
case PROBE_SETMODE:
{
int mode, wantmode;
mode = 0;
/* Fetch user modes from SIM. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_USER_SETTINGS;
xpt_action((union ccb *)&cts);
if (path->device->transport == XPORT_ATA) {
if (cts.xport_specific.ata.valid & CTS_ATA_VALID_MODE)
mode = cts.xport_specific.ata.mode;
} else {
if (cts.xport_specific.sata.valid & CTS_SATA_VALID_MODE)
mode = cts.xport_specific.sata.mode;
}
if (periph->path->device->protocol == PROTO_ATA) {
if (ata_dma == 0 && (mode == 0 || mode > ATA_PIO_MAX))
mode = ATA_PIO_MAX;
} else {
if (atapi_dma == 0 && (mode == 0 || mode > ATA_PIO_MAX))
mode = ATA_PIO_MAX;
}
negotiate:
/* Honor device capabilities. */
wantmode = mode = ata_max_mode(ident_buf, mode);
/* Report modes to SIM. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
if (path->device->transport == XPORT_ATA) {
cts.xport_specific.ata.mode = mode;
cts.xport_specific.ata.valid = CTS_ATA_VALID_MODE;
} else {
cts.xport_specific.sata.mode = mode;
cts.xport_specific.sata.valid = CTS_SATA_VALID_MODE;
}
xpt_action((union ccb *)&cts);
/* Fetch current modes from SIM. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
xpt_action((union ccb *)&cts);
if (path->device->transport == XPORT_ATA) {
if (cts.xport_specific.ata.valid & CTS_ATA_VALID_MODE)
mode = cts.xport_specific.ata.mode;
} else {
if (cts.xport_specific.ata.valid & CTS_SATA_VALID_MODE)
mode = cts.xport_specific.sata.mode;
}
/* If SIM disagree - renegotiate. */
if (mode != wantmode)
goto negotiate;
/* Remember what transport thinks about DMA. */
if (mode < ATA_DMA)
path->device->inq_flags &= ~SID_DMA;
else
path->device->inq_flags |= SID_DMA;
xpt_async(AC_GETDEV_CHANGED, path, NULL);
cam_fill_ataio(ataio,
1,
probedone,
/*flags*/CAM_DIR_NONE,
0,
/*data_ptr*/NULL,
/*dxfer_len*/0,
30 * 1000);
ata_28bit_cmd(ataio, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
break;
}
case PROBE_SETPM:
cam_fill_ataio(ataio,
1,
probedone,
CAM_DIR_NONE,
0,
NULL,
0,
30*1000);
ata_28bit_cmd(ataio, ATA_SETFEATURES,
(softc->caps & CTS_SATA_CAPS_H_PMREQ) ? 0x10 : 0x90,
0, 0x03);
break;
case PROBE_SETAPST:
cam_fill_ataio(ataio,
1,
probedone,
CAM_DIR_NONE,
0,
NULL,
0,
30*1000);
ata_28bit_cmd(ataio, ATA_SETFEATURES,
(softc->caps & CTS_SATA_CAPS_H_APST) ? 0x10 : 0x90,
0, 0x07);
break;
case PROBE_SETDMAAA:
cam_fill_ataio(ataio,
1,
probedone,
CAM_DIR_NONE,
0,
NULL,
0,
30*1000);
ata_28bit_cmd(ataio, ATA_SETFEATURES,
(softc->caps & CTS_SATA_CAPS_H_DMAAA) ? 0x10 : 0x90,
0, 0x02);
break;
case PROBE_SETAN:
/* Remember what transport thinks about AEN. */
if (softc->caps & CTS_SATA_CAPS_H_AN)
path->device->inq_flags |= SID_AEN;
else
path->device->inq_flags &= ~SID_AEN;
xpt_async(AC_GETDEV_CHANGED, path, NULL);
cam_fill_ataio(ataio,
1,
probedone,
CAM_DIR_NONE,
0,
NULL,
0,
30*1000);
ata_28bit_cmd(ataio, ATA_SETFEATURES,
(softc->caps & CTS_SATA_CAPS_H_AN) ? 0x10 : 0x90,
0, 0x05);
break;
case PROBE_SET_MULTI:
{
u_int sectors, bytecount;
bytecount = 8192; /* SATA maximum */
/* Fetch user bytecount from SIM. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_USER_SETTINGS;
xpt_action((union ccb *)&cts);
if (path->device->transport == XPORT_ATA) {
if (cts.xport_specific.ata.valid & CTS_ATA_VALID_BYTECOUNT)
bytecount = cts.xport_specific.ata.bytecount;
} else {
if (cts.xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT)
bytecount = cts.xport_specific.sata.bytecount;
}
/* Honor device capabilities. */
sectors = max(1, min(ident_buf->sectors_intr & 0xff,
bytecount / ata_logical_sector_size(ident_buf)));
/* Report bytecount to SIM. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
if (path->device->transport == XPORT_ATA) {
cts.xport_specific.ata.bytecount = sectors *
ata_logical_sector_size(ident_buf);
cts.xport_specific.ata.valid = CTS_ATA_VALID_BYTECOUNT;
} else {
cts.xport_specific.sata.bytecount = sectors *
ata_logical_sector_size(ident_buf);
cts.xport_specific.sata.valid = CTS_SATA_VALID_BYTECOUNT;
}
xpt_action((union ccb *)&cts);
/* Fetch current bytecount from SIM. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
xpt_action((union ccb *)&cts);
if (path->device->transport == XPORT_ATA) {
if (cts.xport_specific.ata.valid & CTS_ATA_VALID_BYTECOUNT)
bytecount = cts.xport_specific.ata.bytecount;
} else {
if (cts.xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT)
bytecount = cts.xport_specific.sata.bytecount;
}
sectors = bytecount / ata_logical_sector_size(ident_buf);
cam_fill_ataio(ataio,
1,
probedone,
CAM_DIR_NONE,
0,
NULL,
0,
30*1000);
ata_28bit_cmd(ataio, ATA_SET_MULTI, 0, 0, sectors);
break;
}
case PROBE_INQUIRY:
{
u_int bytecount;
bytecount = 8192; /* SATA maximum */
/* Fetch user bytecount from SIM. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_USER_SETTINGS;
xpt_action((union ccb *)&cts);
if (path->device->transport == XPORT_ATA) {
if (cts.xport_specific.ata.valid & CTS_ATA_VALID_BYTECOUNT)
bytecount = cts.xport_specific.ata.bytecount;
} else {
if (cts.xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT)
bytecount = cts.xport_specific.sata.bytecount;
}
/* Honor device capabilities. */
bytecount &= ~1;
bytecount = max(2, min(65534, bytecount));
if (ident_buf->satacapabilities != 0x0000 &&
ident_buf->satacapabilities != 0xffff) {
bytecount = min(8192, bytecount);
}
/* Report bytecount to SIM. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
if (path->device->transport == XPORT_ATA) {
cts.xport_specific.ata.bytecount = bytecount;
cts.xport_specific.ata.valid = CTS_ATA_VALID_BYTECOUNT;
} else {
cts.xport_specific.sata.bytecount = bytecount;
cts.xport_specific.sata.valid = CTS_SATA_VALID_BYTECOUNT;
}
xpt_action((union ccb *)&cts);
/* FALLTHROUGH */
}
case PROBE_FULL_INQUIRY:
{
u_int inquiry_len;
struct scsi_inquiry_data *inq_buf =
&periph->path->device->inq_data;
if (softc->action == PROBE_INQUIRY)
inquiry_len = SHORT_INQUIRY_LENGTH;
else
inquiry_len = SID_ADDITIONAL_LENGTH(inq_buf);
/*
* Some parallel SCSI devices fail to send an
* ignore wide residue message when dealing with
* odd length inquiry requests. Round up to be
* safe.
*/
inquiry_len = roundup2(inquiry_len, 2);
scsi_inquiry(csio,
/*retries*/1,
probedone,
MSG_SIMPLE_Q_TAG,
(u_int8_t *)inq_buf,
inquiry_len,
/*evpd*/FALSE,
/*page_code*/0,
SSD_MIN_SIZE,
/*timeout*/60 * 1000);
break;
}
case PROBE_PM_PID:
cam_fill_ataio(ataio,
1,
probedone,
/*flags*/CAM_DIR_NONE,
0,
/*data_ptr*/NULL,
/*dxfer_len*/0,
10 * 1000);
ata_pm_read_cmd(ataio, 0, 15);
break;
case PROBE_PM_PRV:
cam_fill_ataio(ataio,
1,
probedone,
/*flags*/CAM_DIR_NONE,
0,
/*data_ptr*/NULL,
/*dxfer_len*/0,
10 * 1000);
ata_pm_read_cmd(ataio, 1, 15);
break;
case PROBE_IDENTIFY_SES:
cam_fill_ataio(ataio,
1,
probedone,
/*flags*/CAM_DIR_IN,
0,
/*data_ptr*/(u_int8_t *)&softc->ident_data,
/*dxfer_len*/sizeof(softc->ident_data),
30 * 1000);
ata_28bit_cmd(ataio, ATA_SEP_ATTN, 0xEC, 0x02,
sizeof(softc->ident_data) / 4);
break;
case PROBE_IDENTIFY_SAFTE:
cam_fill_ataio(ataio,
1,
probedone,
/*flags*/CAM_DIR_IN,
0,
/*data_ptr*/(u_int8_t *)&softc->ident_data,
/*dxfer_len*/sizeof(softc->ident_data),
30 * 1000);
ata_28bit_cmd(ataio, ATA_SEP_ATTN, 0xEC, 0x00,
sizeof(softc->ident_data) / 4);
break;
default:
panic("probestart: invalid action state 0x%x\n", softc->action);
}
xpt_action(start_ccb);
}
static void
proberequestdefaultnegotiation(struct cam_periph *periph)
{
struct ccb_trans_settings cts;
xpt_setup_ccb(&cts.ccb_h, periph->path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_USER_SETTINGS;
xpt_action((union ccb *)&cts);
if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
return;
cts.xport_specific.valid = 0;
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
xpt_action((union ccb *)&cts);
}
static void
probedone(struct cam_periph *periph, union ccb *done_ccb)
{
struct ccb_trans_settings cts;
struct ata_params *ident_buf;
struct scsi_inquiry_data *inq_buf;
probe_softc *softc;
struct cam_path *path;
cam_status status;
u_int32_t priority;
u_int caps;
int changed = 1, found = 1;
static const uint8_t fake_device_id_hdr[8] =
{0, SVPD_DEVICE_ID, 0, 12,
SVPD_ID_CODESET_BINARY, SVPD_ID_TYPE_NAA, 0, 8};
CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probedone\n"));
softc = (probe_softc *)periph->softc;
path = done_ccb->ccb_h.path;
priority = done_ccb->ccb_h.pinfo.priority;
ident_buf = &path->device->ident_data;
inq_buf = &path->device->inq_data;
if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
if (cam_periph_error(done_ccb,
0, softc->restart ? (SF_NO_RECOVERY | SF_NO_RETRY) : 0,
NULL) == ERESTART)
return;
if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
/* Don't wedge the queue */
xpt_release_devq(done_ccb->ccb_h.path, /*count*/1,
/*run_queue*/TRUE);
}
status = done_ccb->ccb_h.status & CAM_STATUS_MASK;
if (softc->restart) {
softc->faults++;
if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) ==
CAM_CMD_TIMEOUT)
softc->faults += 4;
if (softc->faults < 10)
goto done;
else
softc->restart = 0;
/* Old PIO2 devices may not support mode setting. */
} else if (softc->action == PROBE_SETMODE &&
status == CAM_ATA_STATUS_ERROR &&
ata_max_pmode(ident_buf) <= ATA_PIO2 &&
(ident_buf->capabilities1 & ATA_SUPPORT_IORDY) == 0) {
goto noerror;
/*
* Some old WD SATA disks report supported and enabled
* device-initiated interface power management, but return
* ABORT on attempt to disable it.
*/
} else if (softc->action == PROBE_SETPM &&
status == CAM_ATA_STATUS_ERROR) {
goto noerror;
/*
* Some HP SATA disks report supported DMA Auto-Activation,
* but return ABORT on attempt to enable it.
*/
} else if (softc->action == PROBE_SETDMAAA &&
status == CAM_ATA_STATUS_ERROR) {
goto noerror;
/*
* SES and SAF-TE SEPs have different IDENTIFY commands,
* but SATA specification doesn't tell how to identify them.
* Until better way found, just try another if first fail.
*/
} else if (softc->action == PROBE_IDENTIFY_SES &&
status == CAM_ATA_STATUS_ERROR) {
PROBE_SET_ACTION(softc, PROBE_IDENTIFY_SAFTE);
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
}
/*
* If we get to this point, we got an error status back
* from the inquiry and the error status doesn't require
* automatically retrying the command. Therefore, the
* inquiry failed. If we had inquiry information before
* for this device, but this latest inquiry command failed,
* the device has probably gone away. If this device isn't
* already marked unconfigured, notify the peripheral
* drivers that this device is no more.
*/
device_fail: if ((path->device->flags & CAM_DEV_UNCONFIGURED) == 0)
xpt_async(AC_LOST_DEVICE, path, NULL);
PROBE_SET_ACTION(softc, PROBE_INVALID);
found = 0;
goto done;
}
noerror:
if (softc->restart)
goto done;
switch (softc->action) {
case PROBE_RESET:
{
int sign = (done_ccb->ataio.res.lba_high << 8) +
done_ccb->ataio.res.lba_mid;
CAM_DEBUG(path, CAM_DEBUG_PROBE,
("SIGNATURE: %04x\n", sign));
if (sign == 0x0000 &&
done_ccb->ccb_h.target_id != 15) {
path->device->protocol = PROTO_ATA;
PROBE_SET_ACTION(softc, PROBE_IDENTIFY);
} else if (sign == 0x9669 &&
done_ccb->ccb_h.target_id == 15) {
/* Report SIM that PM is present. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
cts.xport_specific.sata.pm_present = 1;
cts.xport_specific.sata.valid = CTS_SATA_VALID_PM;
xpt_action((union ccb *)&cts);
path->device->protocol = PROTO_SATAPM;
PROBE_SET_ACTION(softc, PROBE_PM_PID);
} else if (sign == 0xc33c &&
done_ccb->ccb_h.target_id != 15) {
path->device->protocol = PROTO_SEMB;
PROBE_SET_ACTION(softc, PROBE_IDENTIFY_SES);
} else if (sign == 0xeb14 &&
done_ccb->ccb_h.target_id != 15) {
path->device->protocol = PROTO_SCSI;
PROBE_SET_ACTION(softc, PROBE_IDENTIFY);
} else {
if (done_ccb->ccb_h.target_id != 15) {
xpt_print(path,
"Unexpected signature 0x%04x\n", sign);
}
goto device_fail;
}
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
}
case PROBE_IDENTIFY:
{
struct ccb_pathinq cpi;
int16_t *ptr;
ident_buf = &softc->ident_data;
for (ptr = (int16_t *)ident_buf;
ptr < (int16_t *)ident_buf + sizeof(struct ata_params)/2; ptr++) {
*ptr = le16toh(*ptr);
}
if (strncmp(ident_buf->model, "FX", 2) &&
strncmp(ident_buf->model, "NEC", 3) &&
strncmp(ident_buf->model, "Pioneer", 7) &&
strncmp(ident_buf->model, "SHARP", 5)) {
ata_bswap(ident_buf->model, sizeof(ident_buf->model));
ata_bswap(ident_buf->revision, sizeof(ident_buf->revision));
ata_bswap(ident_buf->serial, sizeof(ident_buf->serial));
}
ata_btrim(ident_buf->model, sizeof(ident_buf->model));
ata_bpack(ident_buf->model, ident_buf->model, sizeof(ident_buf->model));
ata_btrim(ident_buf->revision, sizeof(ident_buf->revision));
ata_bpack(ident_buf->revision, ident_buf->revision, sizeof(ident_buf->revision));
ata_btrim(ident_buf->serial, sizeof(ident_buf->serial));
ata_bpack(ident_buf->serial, ident_buf->serial, sizeof(ident_buf->serial));
/* Device may need spin-up before IDENTIFY become valid. */
if ((ident_buf->specconf == 0x37c8 ||
ident_buf->specconf == 0x738c) &&
((ident_buf->config & ATA_RESP_INCOMPLETE) ||
softc->spinup == 0)) {
PROBE_SET_ACTION(softc, PROBE_SPINUP);
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
}
ident_buf = &path->device->ident_data;
if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) == 0) {
/* Check that it is the same device. */
if (bcmp(softc->ident_data.model, ident_buf->model,
sizeof(ident_buf->model)) ||
bcmp(softc->ident_data.revision, ident_buf->revision,
sizeof(ident_buf->revision)) ||
bcmp(softc->ident_data.serial, ident_buf->serial,
sizeof(ident_buf->serial))) {
/* Device changed. */
xpt_async(AC_LOST_DEVICE, path, NULL);
} else {
bcopy(&softc->ident_data, ident_buf, sizeof(struct ata_params));
changed = 0;
}
}
if (changed) {
bcopy(&softc->ident_data, ident_buf, sizeof(struct ata_params));
/* Clean up from previous instance of this device */
if (path->device->serial_num != NULL) {
free(path->device->serial_num, M_CAMXPT);
path->device->serial_num = NULL;
path->device->serial_num_len = 0;
}
if (path->device->device_id != NULL) {
free(path->device->device_id, M_CAMXPT);
path->device->device_id = NULL;
path->device->device_id_len = 0;
}
path->device->serial_num =
(u_int8_t *)malloc((sizeof(ident_buf->serial) + 1),
M_CAMXPT, M_NOWAIT);
if (path->device->serial_num != NULL) {
bcopy(ident_buf->serial,
path->device->serial_num,
sizeof(ident_buf->serial));
path->device->serial_num[sizeof(ident_buf->serial)]
= '\0';
path->device->serial_num_len =
strlen(path->device->serial_num);
}
if (ident_buf->enabled.extension &
ATA_SUPPORT_64BITWWN) {
path->device->device_id =
malloc(16, M_CAMXPT, M_NOWAIT);
if (path->device->device_id != NULL) {
path->device->device_id_len = 16;
bcopy(&fake_device_id_hdr,
path->device->device_id, 8);
bcopy(ident_buf->wwn,
path->device->device_id + 8, 8);
}
}
path->device->flags |= CAM_DEV_IDENTIFY_DATA_VALID;
xpt_async(AC_GETDEV_CHANGED, path, NULL);
}
if (ident_buf->satacapabilities & ATA_SUPPORT_NCQ) {
path->device->mintags = 2;
path->device->maxtags =
ATA_QUEUE_LEN(ident_buf->queue) + 1;
}
ata_find_quirk(path->device);
if (path->device->mintags != 0 &&
path->bus->sim->max_tagged_dev_openings != 0) {
/* Check if the SIM does not want queued commands. */
bzero(&cpi, sizeof(cpi));
xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE);
cpi.ccb_h.func_code = XPT_PATH_INQ;
xpt_action((union ccb *)&cpi);
if (cpi.ccb_h.status == CAM_REQ_CMP &&
(cpi.hba_inquiry & PI_TAG_ABLE)) {
/* Report SIM which tags are allowed. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
cts.xport_specific.sata.tags = path->device->maxtags;
cts.xport_specific.sata.valid = CTS_SATA_VALID_TAGS;
xpt_action((union ccb *)&cts);
}
}
ata_device_transport(path);
if (changed)
proberequestdefaultnegotiation(periph);
PROBE_SET_ACTION(softc, PROBE_SETMODE);
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
}
case PROBE_SPINUP:
if (bootverbose)
xpt_print(path, "Spin-up done\n");
softc->spinup = 1;
PROBE_SET_ACTION(softc, PROBE_IDENTIFY);
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
case PROBE_SETMODE:
if (path->device->transport != XPORT_SATA)
goto notsata;
/* Set supported bits. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
xpt_action((union ccb *)&cts);
if (cts.xport_specific.sata.valid & CTS_SATA_VALID_CAPS)
caps = cts.xport_specific.sata.caps & CTS_SATA_CAPS_H;
else
caps = 0;
if (ident_buf->satacapabilities != 0xffff) {
if (ident_buf->satacapabilities & ATA_SUPPORT_IFPWRMNGTRCV)
caps |= CTS_SATA_CAPS_D_PMREQ;
if (ident_buf->satacapabilities & ATA_SUPPORT_HAPST)
caps |= CTS_SATA_CAPS_D_APST;
}
/* Mask unwanted bits. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_USER_SETTINGS;
xpt_action((union ccb *)&cts);
if (cts.xport_specific.sata.valid & CTS_SATA_VALID_CAPS)
caps &= cts.xport_specific.sata.caps;
else
caps = 0;
/* Store result to SIM. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
cts.xport_specific.sata.caps = caps;
cts.xport_specific.sata.valid = CTS_SATA_VALID_CAPS;
xpt_action((union ccb *)&cts);
softc->caps = caps;
if ((ident_buf->satasupport & ATA_SUPPORT_IFPWRMNGT) &&
(!(softc->caps & CTS_SATA_CAPS_H_PMREQ)) !=
(!(ident_buf->sataenabled & ATA_SUPPORT_IFPWRMNGT))) {
PROBE_SET_ACTION(softc, PROBE_SETPM);
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
}
/* FALLTHROUGH */
case PROBE_SETPM:
if (ident_buf->satacapabilities != 0xffff &&
(ident_buf->satacapabilities & ATA_SUPPORT_DAPST) &&
(!(softc->caps & CTS_SATA_CAPS_H_APST)) !=
(!(ident_buf->sataenabled & ATA_ENABLED_DAPST))) {
PROBE_SET_ACTION(softc, PROBE_SETAPST);
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
}
/* FALLTHROUGH */
case PROBE_SETAPST:
if ((ident_buf->satasupport & ATA_SUPPORT_AUTOACTIVATE) &&
(!(softc->caps & CTS_SATA_CAPS_H_DMAAA)) !=
(!(ident_buf->sataenabled & ATA_SUPPORT_AUTOACTIVATE))) {
PROBE_SET_ACTION(softc, PROBE_SETDMAAA);
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
}
/* FALLTHROUGH */
case PROBE_SETDMAAA:
if ((ident_buf->satasupport & ATA_SUPPORT_ASYNCNOTIF) &&
(!(softc->caps & CTS_SATA_CAPS_H_AN)) !=
(!(ident_buf->sataenabled & ATA_SUPPORT_ASYNCNOTIF))) {
PROBE_SET_ACTION(softc, PROBE_SETAN);
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
}
/* FALLTHROUGH */
case PROBE_SETAN:
notsata:
if (path->device->protocol == PROTO_ATA) {
PROBE_SET_ACTION(softc, PROBE_SET_MULTI);
} else {
PROBE_SET_ACTION(softc, PROBE_INQUIRY);
}
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
case PROBE_SET_MULTI:
if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) {
path->device->flags &= ~CAM_DEV_UNCONFIGURED;
xpt_acquire_device(path->device);
done_ccb->ccb_h.func_code = XPT_GDEV_TYPE;
xpt_action(done_ccb);
xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path,
done_ccb);
}
PROBE_SET_ACTION(softc, PROBE_DONE);
break;
case PROBE_INQUIRY:
case PROBE_FULL_INQUIRY:
{
u_int8_t periph_qual, len;
path->device->flags |= CAM_DEV_INQUIRY_DATA_VALID;
periph_qual = SID_QUAL(inq_buf);
if (periph_qual != SID_QUAL_LU_CONNECTED)
break;
/*
* We conservatively request only
* SHORT_INQUIRY_LEN bytes of inquiry
* information during our first try
* at sending an INQUIRY. If the device
* has more information to give,
* perform a second request specifying
* the amount of information the device
* is willing to give.
*/
len = inq_buf->additional_length
+ offsetof(struct scsi_inquiry_data, additional_length) + 1;
if (softc->action == PROBE_INQUIRY
&& len > SHORT_INQUIRY_LENGTH) {
PROBE_SET_ACTION(softc, PROBE_FULL_INQUIRY);
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
}
ata_device_transport(path);
if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) {
path->device->flags &= ~CAM_DEV_UNCONFIGURED;
xpt_acquire_device(path->device);
done_ccb->ccb_h.func_code = XPT_GDEV_TYPE;
xpt_action(done_ccb);
xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, done_ccb);
}
PROBE_SET_ACTION(softc, PROBE_DONE);
break;
}
case PROBE_PM_PID:
if ((path->device->flags & CAM_DEV_IDENTIFY_DATA_VALID) == 0)
bzero(ident_buf, sizeof(*ident_buf));
softc->pm_pid = (done_ccb->ataio.res.lba_high << 24) +
(done_ccb->ataio.res.lba_mid << 16) +
(done_ccb->ataio.res.lba_low << 8) +
done_ccb->ataio.res.sector_count;
((uint32_t *)ident_buf)[0] = softc->pm_pid;
snprintf(ident_buf->model, sizeof(ident_buf->model),
"Port Multiplier %08x", softc->pm_pid);
PROBE_SET_ACTION(softc, PROBE_PM_PRV);
xpt_release_ccb(done_ccb);
xpt_schedule(periph, priority);
return;
case PROBE_PM_PRV:
softc->pm_prv = (done_ccb->ataio.res.lba_high << 24) +
(done_ccb->ataio.res.lba_mid << 16) +
(done_ccb->ataio.res.lba_low << 8) +
done_ccb->ataio.res.sector_count;
((uint32_t *)ident_buf)[1] = softc->pm_prv;
snprintf(ident_buf->revision, sizeof(ident_buf->revision),
"%04x", softc->pm_prv);
path->device->flags |= CAM_DEV_IDENTIFY_DATA_VALID;
ata_device_transport(path);
if (periph->path->device->flags & CAM_DEV_UNCONFIGURED)
proberequestdefaultnegotiation(periph);
/* Set supported bits. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
xpt_action((union ccb *)&cts);
if (cts.xport_specific.sata.valid & CTS_SATA_VALID_CAPS)
caps = cts.xport_specific.sata.caps & CTS_SATA_CAPS_H;
else
caps = 0;
/* All PMPs must support PM requests. */
caps |= CTS_SATA_CAPS_D_PMREQ;
/* Mask unwanted bits. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_USER_SETTINGS;
xpt_action((union ccb *)&cts);
if (cts.xport_specific.sata.valid & CTS_SATA_VALID_CAPS)
caps &= cts.xport_specific.sata.caps;
else
caps = 0;
/* Store result to SIM. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
cts.xport_specific.sata.caps = caps;
cts.xport_specific.sata.valid = CTS_SATA_VALID_CAPS;
xpt_action((union ccb *)&cts);
softc->caps = caps;
/* Remember what transport thinks about AEN. */
if (softc->caps & CTS_SATA_CAPS_H_AN)
path->device->inq_flags |= SID_AEN;
else
path->device->inq_flags &= ~SID_AEN;
xpt_async(AC_GETDEV_CHANGED, path, NULL);
if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) {
path->device->flags &= ~CAM_DEV_UNCONFIGURED;
xpt_acquire_device(path->device);
done_ccb->ccb_h.func_code = XPT_GDEV_TYPE;
xpt_action(done_ccb);
xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path,
done_ccb);
} else {
done_ccb->ccb_h.func_code = XPT_GDEV_TYPE;
xpt_action(done_ccb);
xpt_async(AC_SCSI_AEN, done_ccb->ccb_h.path, done_ccb);
}
PROBE_SET_ACTION(softc, PROBE_DONE);
break;
case PROBE_IDENTIFY_SES:
case PROBE_IDENTIFY_SAFTE:
if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) == 0) {
/* Check that it is the same device. */
if (bcmp(&softc->ident_data, ident_buf, 53)) {
/* Device changed. */
xpt_async(AC_LOST_DEVICE, path, NULL);
} else {
bcopy(&softc->ident_data, ident_buf, sizeof(struct ata_params));
changed = 0;
}
}
if (changed) {
bcopy(&softc->ident_data, ident_buf, sizeof(struct ata_params));
/* Clean up from previous instance of this device */
if (path->device->device_id != NULL) {
free(path->device->device_id, M_CAMXPT);
path->device->device_id = NULL;
path->device->device_id_len = 0;
}
path->device->device_id =
malloc(16, M_CAMXPT, M_NOWAIT);
if (path->device->device_id != NULL) {
path->device->device_id_len = 16;
bcopy(&fake_device_id_hdr,
path->device->device_id, 8);
bcopy(((uint8_t*)ident_buf) + 2,
path->device->device_id + 8, 8);
}
path->device->flags |= CAM_DEV_IDENTIFY_DATA_VALID;
}
ata_device_transport(path);
if (changed)
proberequestdefaultnegotiation(periph);
if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) {
path->device->flags &= ~CAM_DEV_UNCONFIGURED;
xpt_acquire_device(path->device);
done_ccb->ccb_h.func_code = XPT_GDEV_TYPE;
xpt_action(done_ccb);
xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path,
done_ccb);
}
PROBE_SET_ACTION(softc, PROBE_DONE);
break;
default:
panic("probedone: invalid action state 0x%x\n", softc->action);
}
done:
if (softc->restart) {
softc->restart = 0;
xpt_release_ccb(done_ccb);
probeschedule(periph);
return;
}
xpt_release_ccb(done_ccb);
CAM_DEBUG(periph->path, CAM_DEBUG_PROBE, ("Probe completed\n"));
while ((done_ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs))) {
TAILQ_REMOVE(&softc->request_ccbs,
&done_ccb->ccb_h, periph_links.tqe);
done_ccb->ccb_h.status = found ? CAM_REQ_CMP : CAM_REQ_CMP_ERR;
xpt_done(done_ccb);
}
cam_periph_invalidate(periph);
cam_release_devq(periph->path,
RELSIM_RELEASE_RUNLEVEL, 0, CAM_RL_XPT + 1, FALSE);
cam_periph_release_locked(periph);
}
static void
probecleanup(struct cam_periph *periph)
{
free(periph->softc, M_CAMXPT);
}
static void
ata_find_quirk(struct cam_ed *device)
{
struct ata_quirk_entry *quirk;
caddr_t match;
match = cam_quirkmatch((caddr_t)&device->ident_data,
(caddr_t)ata_quirk_table,
ata_quirk_table_size,
sizeof(*ata_quirk_table), ata_identify_match);
if (match == NULL)
panic("xpt_find_quirk: device didn't match wildcard entry!!");
quirk = (struct ata_quirk_entry *)match;
device->quirk = quirk;
if (quirk->quirks & CAM_QUIRK_MAXTAGS) {
device->mintags = quirk->mintags;
device->maxtags = quirk->maxtags;
}
}
typedef struct {
union ccb *request_ccb;
struct ccb_pathinq *cpi;
int counter;
} ata_scan_bus_info;
/*
* To start a scan, request_ccb is an XPT_SCAN_BUS ccb.
* As the scan progresses, xpt_scan_bus is used as the
* callback on completion function.
*/
static void
ata_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
{
struct cam_path *path;
ata_scan_bus_info *scan_info;
union ccb *work_ccb, *reset_ccb;
cam_status status;
CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_TRACE,
("xpt_scan_bus\n"));
switch (request_ccb->ccb_h.func_code) {
case XPT_SCAN_BUS:
case XPT_SCAN_TGT:
/* Find out the characteristics of the bus */
work_ccb = xpt_alloc_ccb_nowait();
if (work_ccb == NULL) {
request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
xpt_done(request_ccb);
return;
}
xpt_setup_ccb(&work_ccb->ccb_h, request_ccb->ccb_h.path,
request_ccb->ccb_h.pinfo.priority);
work_ccb->ccb_h.func_code = XPT_PATH_INQ;
xpt_action(work_ccb);
if (work_ccb->ccb_h.status != CAM_REQ_CMP) {
request_ccb->ccb_h.status = work_ccb->ccb_h.status;
xpt_free_ccb(work_ccb);
xpt_done(request_ccb);
return;
}
/* We may need to reset bus first, if we haven't done it yet. */
if ((work_ccb->cpi.hba_inquiry &
(PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE)) &&
!(work_ccb->cpi.hba_misc & PIM_NOBUSRESET) &&
!timevalisset(&request_ccb->ccb_h.path->bus->last_reset)) {
reset_ccb = xpt_alloc_ccb_nowait();
if (reset_ccb == NULL) {
request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
xpt_free_ccb(work_ccb);
xpt_done(request_ccb);
return;
}
xpt_setup_ccb(&reset_ccb->ccb_h, request_ccb->ccb_h.path,
CAM_PRIORITY_NONE);
reset_ccb->ccb_h.func_code = XPT_RESET_BUS;
xpt_action(reset_ccb);
if (reset_ccb->ccb_h.status != CAM_REQ_CMP) {
request_ccb->ccb_h.status = reset_ccb->ccb_h.status;
xpt_free_ccb(reset_ccb);
xpt_free_ccb(work_ccb);
xpt_done(request_ccb);
return;
}
xpt_free_ccb(reset_ccb);
}
/* Save some state for use while we probe for devices */
scan_info = (ata_scan_bus_info *)
malloc(sizeof(ata_scan_bus_info), M_CAMXPT, M_NOWAIT);
if (scan_info == NULL) {
request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
xpt_free_ccb(work_ccb);
xpt_done(request_ccb);
return;
}
scan_info->request_ccb = request_ccb;
scan_info->cpi = &work_ccb->cpi;
/* If PM supported, probe it first. */
if (scan_info->cpi->hba_inquiry & PI_SATAPM)
scan_info->counter = scan_info->cpi->max_target;
else
scan_info->counter = 0;
work_ccb = xpt_alloc_ccb_nowait();
if (work_ccb == NULL) {
free(scan_info, M_CAMXPT);
request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
xpt_done(request_ccb);
break;
}
goto scan_next;
case XPT_SCAN_LUN:
work_ccb = request_ccb;
/* Reuse the same CCB to query if a device was really found */
scan_info = (ata_scan_bus_info *)work_ccb->ccb_h.ppriv_ptr0;
/* If there is PMP... */
if ((scan_info->cpi->hba_inquiry & PI_SATAPM) &&
(scan_info->counter == scan_info->cpi->max_target)) {
if (work_ccb->ccb_h.status == CAM_REQ_CMP) {
/* everything else will be probed by it */
/* Free the current request path- we're done with it. */
xpt_free_path(work_ccb->ccb_h.path);
goto done;
} else {
struct ccb_trans_settings cts;
/* Report SIM that PM is absent. */
bzero(&cts, sizeof(cts));
xpt_setup_ccb(&cts.ccb_h,
work_ccb->ccb_h.path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
cts.xport_specific.sata.pm_present = 0;
cts.xport_specific.sata.valid = CTS_SATA_VALID_PM;
xpt_action((union ccb *)&cts);
}
}
/* Free the current request path- we're done with it. */
xpt_free_path(work_ccb->ccb_h.path);
if (scan_info->counter ==
((scan_info->cpi->hba_inquiry & PI_SATAPM) ?
0 : scan_info->cpi->max_target)) {
done:
xpt_free_ccb(work_ccb);
xpt_free_ccb((union ccb *)scan_info->cpi);
request_ccb = scan_info->request_ccb;
free(scan_info, M_CAMXPT);
request_ccb->ccb_h.status = CAM_REQ_CMP;
xpt_done(request_ccb);
break;
}
/* Take next device. Wrap from max (PMP) to 0. */
scan_info->counter = (scan_info->counter + 1 ) %
(scan_info->cpi->max_target + 1);
scan_next:
status = xpt_create_path(&path, xpt_periph,
scan_info->request_ccb->ccb_h.path_id,
scan_info->counter, 0);
if (status != CAM_REQ_CMP) {
printf("xpt_scan_bus: xpt_create_path failed"
" with status %#x, bus scan halted\n",
status);
xpt_free_ccb(work_ccb);
xpt_free_ccb((union ccb *)scan_info->cpi);
request_ccb = scan_info->request_ccb;
free(scan_info, M_CAMXPT);
request_ccb->ccb_h.status = status;
xpt_done(request_ccb);
break;
}
xpt_setup_ccb(&work_ccb->ccb_h, path,
scan_info->request_ccb->ccb_h.pinfo.priority);
work_ccb->ccb_h.func_code = XPT_SCAN_LUN;
work_ccb->ccb_h.cbfcnp = ata_scan_bus;
work_ccb->ccb_h.ppriv_ptr0 = scan_info;
work_ccb->crcn.flags = scan_info->request_ccb->crcn.flags;
xpt_action(work_ccb);
break;
default:
break;
}
}
static void
ata_scan_lun(struct cam_periph *periph, struct cam_path *path,
cam_flags flags, union ccb *request_ccb)
{
struct ccb_pathinq cpi;
cam_status status;
struct cam_path *new_path;
struct cam_periph *old_periph;
CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_scan_lun\n"));
xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE);
cpi.ccb_h.func_code = XPT_PATH_INQ;
xpt_action((union ccb *)&cpi);
if (cpi.ccb_h.status != CAM_REQ_CMP) {
if (request_ccb != NULL) {
request_ccb->ccb_h.status = cpi.ccb_h.status;
xpt_done(request_ccb);
}
return;
}
if (request_ccb == NULL) {
request_ccb = malloc(sizeof(union ccb), M_CAMXPT, M_NOWAIT);
if (request_ccb == NULL) {
xpt_print(path, "xpt_scan_lun: can't allocate CCB, "
"can't continue\n");
return;
}
new_path = malloc(sizeof(*new_path), M_CAMXPT, M_NOWAIT);
if (new_path == NULL) {
xpt_print(path, "xpt_scan_lun: can't allocate path, "
"can't continue\n");
free(request_ccb, M_CAMXPT);
return;
}
status = xpt_compile_path(new_path, xpt_periph,
path->bus->path_id,
path->target->target_id,
path->device->lun_id);
if (status != CAM_REQ_CMP) {
xpt_print(path, "xpt_scan_lun: can't compile path, "
"can't continue\n");
free(request_ccb, M_CAMXPT);
free(new_path, M_CAMXPT);
return;
}
xpt_setup_ccb(&request_ccb->ccb_h, new_path, CAM_PRIORITY_XPT);
request_ccb->ccb_h.cbfcnp = xptscandone;
request_ccb->ccb_h.func_code = XPT_SCAN_LUN;
request_ccb->crcn.flags = flags;
}
if ((old_periph = cam_periph_find(path, "aprobe")) != NULL) {
if ((old_periph->flags & CAM_PERIPH_INVALID) == 0) {
probe_softc *softc;
softc = (probe_softc *)old_periph->softc;
TAILQ_INSERT_TAIL(&softc->request_ccbs,
&request_ccb->ccb_h, periph_links.tqe);
softc->restart = 1;
} else {
request_ccb->ccb_h.status = CAM_REQ_CMP_ERR;
xpt_done(request_ccb);
}
} else {
status = cam_periph_alloc(proberegister, NULL, probecleanup,
probestart, "aprobe",
CAM_PERIPH_BIO,
request_ccb->ccb_h.path, NULL, 0,
request_ccb);
if (status != CAM_REQ_CMP) {
xpt_print(path, "xpt_scan_lun: cam_alloc_periph "
"returned an error, can't continue probe\n");
request_ccb->ccb_h.status = status;
xpt_done(request_ccb);
}
}
}
static void
xptscandone(struct cam_periph *periph, union ccb *done_ccb)
{
xpt_release_path(done_ccb->ccb_h.path);
free(done_ccb->ccb_h.path, M_CAMXPT);
free(done_ccb, M_CAMXPT);
}
static struct cam_ed *
ata_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
{
struct cam_path path;
struct ata_quirk_entry *quirk;
struct cam_ed *device;
struct cam_ed *cur_device;
device = xpt_alloc_device(bus, target, lun_id);
if (device == NULL)
return (NULL);
/*
* Take the default quirk entry until we have inquiry
* data and can determine a better quirk to use.
*/
quirk = &ata_quirk_table[ata_quirk_table_size - 1];
device->quirk = (void *)quirk;
device->mintags = 0;
device->maxtags = 0;
bzero(&device->inq_data, sizeof(device->inq_data));
device->inq_flags = 0;
device->queue_flags = 0;
device->serial_num = NULL;
device->serial_num_len = 0;
/*
* XXX should be limited by number of CCBs this bus can
* do.
*/
bus->sim->max_ccbs += device->ccbq.devq_openings;
/* Insertion sort into our target's device list */
cur_device = TAILQ_FIRST(&target->ed_entries);
while (cur_device != NULL && cur_device->lun_id < lun_id)
cur_device = TAILQ_NEXT(cur_device, links);
if (cur_device != NULL) {
TAILQ_INSERT_BEFORE(cur_device, device, links);
} else {
TAILQ_INSERT_TAIL(&target->ed_entries, device, links);
}
target->generation++;
if (lun_id != CAM_LUN_WILDCARD) {
xpt_compile_path(&path,
NULL,
bus->path_id,
target->target_id,
lun_id);
ata_device_transport(&path);
xpt_release_path(&path);
}
return (device);
}
static void
ata_device_transport(struct cam_path *path)
{
struct ccb_pathinq cpi;
struct ccb_trans_settings cts;
struct scsi_inquiry_data *inq_buf = NULL;
struct ata_params *ident_buf = NULL;
/* Get transport information from the SIM */
xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE);
cpi.ccb_h.func_code = XPT_PATH_INQ;
xpt_action((union ccb *)&cpi);
path->device->transport = cpi.transport;
if ((path->device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0)
inq_buf = &path->device->inq_data;
if ((path->device->flags & CAM_DEV_IDENTIFY_DATA_VALID) != 0)
ident_buf = &path->device->ident_data;
if (path->device->protocol == PROTO_ATA) {
path->device->protocol_version = ident_buf ?
ata_version(ident_buf->version_major) : cpi.protocol_version;
} else if (path->device->protocol == PROTO_SCSI) {
path->device->protocol_version = inq_buf ?
SID_ANSI_REV(inq_buf) : cpi.protocol_version;
}
path->device->transport_version = ident_buf ?
ata_version(ident_buf->version_major) : cpi.transport_version;
/* Tell the controller what we think */
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NONE);
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
cts.transport = path->device->transport;
cts.transport_version = path->device->transport_version;
cts.protocol = path->device->protocol;
cts.protocol_version = path->device->protocol_version;
cts.proto_specific.valid = 0;
if (ident_buf) {
if (path->device->transport == XPORT_ATA) {
cts.xport_specific.ata.atapi =
(ident_buf->config == ATA_PROTO_CFA) ? 0 :
((ident_buf->config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_16) ? 16 :
((ident_buf->config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12) ? 12 : 0;
cts.xport_specific.ata.valid = CTS_ATA_VALID_ATAPI;
} else {
cts.xport_specific.sata.atapi =
(ident_buf->config == ATA_PROTO_CFA) ? 0 :
((ident_buf->config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_16) ? 16 :
((ident_buf->config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12) ? 12 : 0;
cts.xport_specific.sata.valid = CTS_SATA_VALID_ATAPI;
}
} else
cts.xport_specific.valid = 0;
xpt_action((union ccb *)&cts);
}
static void
ata_dev_advinfo(union ccb *start_ccb)
{
struct cam_ed *device;
struct ccb_dev_advinfo *cdai;
off_t amt;
start_ccb->ccb_h.status = CAM_REQ_INVALID;
device = start_ccb->ccb_h.path->device;
cdai = &start_ccb->cdai;
switch(cdai->buftype) {
case CDAI_TYPE_SCSI_DEVID:
if (cdai->flags & CDAI_FLAG_STORE)
return;
cdai->provsiz = device->device_id_len;
if (device->device_id_len == 0)
break;
amt = device->device_id_len;
if (cdai->provsiz > cdai->bufsiz)
amt = cdai->bufsiz;
memcpy(cdai->buf, device->device_id, amt);
break;
case CDAI_TYPE_SERIAL_NUM:
if (cdai->flags & CDAI_FLAG_STORE)
return;
cdai->provsiz = device->serial_num_len;
if (device->serial_num_len == 0)
break;
amt = device->serial_num_len;
if (cdai->provsiz > cdai->bufsiz)
amt = cdai->bufsiz;
memcpy(cdai->buf, device->serial_num, amt);
break;
case CDAI_TYPE_PHYS_PATH:
if (cdai->flags & CDAI_FLAG_STORE) {
if (device->physpath != NULL)
free(device->physpath, M_CAMXPT);
device->physpath_len = cdai->bufsiz;
/* Clear existing buffer if zero length */
if (cdai->bufsiz == 0)
break;
device->physpath = malloc(cdai->bufsiz, M_CAMXPT, M_NOWAIT);
if (device->physpath == NULL) {
start_ccb->ccb_h.status = CAM_REQ_ABORTED;
return;
}
memcpy(device->physpath, cdai->buf, cdai->bufsiz);
} else {
cdai->provsiz = device->physpath_len;
if (device->physpath_len == 0)
break;
amt = device->physpath_len;
if (cdai->provsiz > cdai->bufsiz)
amt = cdai->bufsiz;
memcpy(cdai->buf, device->physpath, amt);
}
break;
default:
return;
}
start_ccb->ccb_h.status = CAM_REQ_CMP;
if (cdai->flags & CDAI_FLAG_STORE) {
int owned;
owned = mtx_owned(start_ccb->ccb_h.path->bus->sim->mtx);
if (owned == 0)
mtx_lock(start_ccb->ccb_h.path->bus->sim->mtx);
xpt_async(AC_ADVINFO_CHANGED, start_ccb->ccb_h.path,
(void *)(uintptr_t)cdai->buftype);
if (owned == 0)
mtx_unlock(start_ccb->ccb_h.path->bus->sim->mtx);
}
}
static void
ata_action(union ccb *start_ccb)
{
switch (start_ccb->ccb_h.func_code) {
case XPT_SET_TRAN_SETTINGS:
{
ata_set_transfer_settings(&start_ccb->cts,
start_ccb->ccb_h.path->device,
/*async_update*/FALSE);
break;
}
case XPT_SCAN_BUS:
case XPT_SCAN_TGT:
ata_scan_bus(start_ccb->ccb_h.path->periph, start_ccb);
break;
case XPT_SCAN_LUN:
ata_scan_lun(start_ccb->ccb_h.path->periph,
start_ccb->ccb_h.path, start_ccb->crcn.flags,
start_ccb);
break;
case XPT_GET_TRAN_SETTINGS:
{
ata_get_transfer_settings(&start_ccb->cts);
break;
}
case XPT_SCSI_IO:
{
struct cam_ed *device;
u_int maxlen = 0;
device = start_ccb->ccb_h.path->device;
if (device->protocol == PROTO_SCSI &&
(device->flags & CAM_DEV_IDENTIFY_DATA_VALID)) {
uint16_t p =
device->ident_data.config & ATA_PROTO_MASK;
maxlen =
(device->ident_data.config == ATA_PROTO_CFA) ? 0 :
(p == ATA_PROTO_ATAPI_16) ? 16 :
(p == ATA_PROTO_ATAPI_12) ? 12 : 0;
}
if (start_ccb->csio.cdb_len > maxlen) {
start_ccb->ccb_h.status = CAM_REQ_INVALID;
xpt_done(start_ccb);
break;
}
xpt_action_default(start_ccb);
break;
}
case XPT_DEV_ADVINFO:
{
ata_dev_advinfo(start_ccb);
break;
}
default:
xpt_action_default(start_ccb);
break;
}
}
static void
ata_get_transfer_settings(struct ccb_trans_settings *cts)
{
struct ccb_trans_settings_ata *ata;
struct ccb_trans_settings_scsi *scsi;
struct cam_ed *device;
struct cam_sim *sim;
device = cts->ccb_h.path->device;
sim = cts->ccb_h.path->bus->sim;
(*(sim->sim_action))(sim, (union ccb *)cts);
if (cts->protocol == PROTO_UNKNOWN ||
cts->protocol == PROTO_UNSPECIFIED) {
cts->protocol = device->protocol;
cts->protocol_version = device->protocol_version;
}
if (cts->protocol == PROTO_ATA) {
ata = &cts->proto_specific.ata;
if ((ata->valid & CTS_ATA_VALID_TQ) == 0) {
ata->valid |= CTS_ATA_VALID_TQ;
if (cts->type == CTS_TYPE_USER_SETTINGS ||
(device->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 ||
(device->inq_flags & SID_CmdQue) != 0)
ata->flags |= CTS_ATA_FLAGS_TAG_ENB;
}
}
if (cts->protocol == PROTO_SCSI) {
scsi = &cts->proto_specific.scsi;
if ((scsi->valid & CTS_SCSI_VALID_TQ) == 0) {
scsi->valid |= CTS_SCSI_VALID_TQ;
if (cts->type == CTS_TYPE_USER_SETTINGS ||
(device->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 ||
(device->inq_flags & SID_CmdQue) != 0)
scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB;
}
}
if (cts->transport == XPORT_UNKNOWN ||
cts->transport == XPORT_UNSPECIFIED) {
cts->transport = device->transport;
cts->transport_version = device->transport_version;
}
}
static void
ata_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device,
int async_update)
{
struct ccb_pathinq cpi;
struct ccb_trans_settings_ata *ata;
struct ccb_trans_settings_scsi *scsi;
struct cam_sim *sim;
struct ata_params *ident_data;
struct scsi_inquiry_data *inq_data;
if (device == NULL) {
cts->ccb_h.status = CAM_PATH_INVALID;
xpt_done((union ccb *)cts);
return;
}
if (cts->protocol == PROTO_UNKNOWN
|| cts->protocol == PROTO_UNSPECIFIED) {
cts->protocol = device->protocol;
cts->protocol_version = device->protocol_version;
}
if (cts->protocol_version == PROTO_VERSION_UNKNOWN
|| cts->protocol_version == PROTO_VERSION_UNSPECIFIED)
cts->protocol_version = device->protocol_version;
if (cts->protocol != device->protocol) {
xpt_print(cts->ccb_h.path, "Uninitialized Protocol %x:%x?\n",
cts->protocol, device->protocol);
cts->protocol = device->protocol;
}
if (cts->protocol_version > device->protocol_version) {
if (bootverbose) {
xpt_print(cts->ccb_h.path, "Down reving Protocol "
"Version from %d to %d?\n", cts->protocol_version,
device->protocol_version);
}
cts->protocol_version = device->protocol_version;
}
if (cts->transport == XPORT_UNKNOWN
|| cts->transport == XPORT_UNSPECIFIED) {
cts->transport = device->transport;
cts->transport_version = device->transport_version;
}
if (cts->transport_version == XPORT_VERSION_UNKNOWN
|| cts->transport_version == XPORT_VERSION_UNSPECIFIED)
cts->transport_version = device->transport_version;
if (cts->transport != device->transport) {
xpt_print(cts->ccb_h.path, "Uninitialized Transport %x:%x?\n",
cts->transport, device->transport);
cts->transport = device->transport;
}
if (cts->transport_version > device->transport_version) {
if (bootverbose) {
xpt_print(cts->ccb_h.path, "Down reving Transport "
"Version from %d to %d?\n", cts->transport_version,
device->transport_version);
}
cts->transport_version = device->transport_version;
}
sim = cts->ccb_h.path->bus->sim;
ident_data = &device->ident_data;
inq_data = &device->inq_data;
if (cts->protocol == PROTO_ATA)
ata = &cts->proto_specific.ata;
else
ata = NULL;
if (cts->protocol == PROTO_SCSI)
scsi = &cts->proto_specific.scsi;
else
scsi = NULL;
xpt_setup_ccb(&cpi.ccb_h, cts->ccb_h.path, CAM_PRIORITY_NONE);
cpi.ccb_h.func_code = XPT_PATH_INQ;
xpt_action((union ccb *)&cpi);
/* Sanity checking */
if ((cpi.hba_inquiry & PI_TAG_ABLE) == 0
|| (ata && (ident_data->satacapabilities & ATA_SUPPORT_NCQ) == 0)
|| (scsi && (INQ_DATA_TQ_ENABLED(inq_data)) == 0)
|| (device->queue_flags & SCP_QUEUE_DQUE) != 0
|| (device->mintags == 0)) {
/*
* Can't tag on hardware that doesn't support tags,
* doesn't have it enabled, or has broken tag support.
*/
if (ata)
ata->flags &= ~CTS_ATA_FLAGS_TAG_ENB;
if (scsi)
scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB;
}
/* Start/stop tags use. */
if (cts->type == CTS_TYPE_CURRENT_SETTINGS &&
((ata && (ata->valid & CTS_ATA_VALID_TQ) != 0) ||
(scsi && (scsi->valid & CTS_SCSI_VALID_TQ) != 0))) {
int nowt, newt = 0;
nowt = ((device->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 ||
(device->inq_flags & SID_CmdQue) != 0);
if (ata)
newt = (ata->flags & CTS_ATA_FLAGS_TAG_ENB) != 0;
if (scsi)
newt = (scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0;
if (newt && !nowt) {
/*
* Delay change to use tags until after a
* few commands have gone to this device so
* the controller has time to perform transfer
* negotiations without tagged messages getting
* in the way.
*/
device->tag_delay_count = CAM_TAG_DELAY_COUNT;
device->flags |= CAM_DEV_TAG_AFTER_COUNT;
} else if (nowt && !newt)
xpt_stop_tags(cts->ccb_h.path);
}
if (async_update == FALSE)
(*(sim->sim_action))(sim, (union ccb *)cts);
}
/*
* Handle any per-device event notifications that require action by the XPT.
*/
static void
ata_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target,
struct cam_ed *device, void *async_arg)
{
cam_status status;
struct cam_path newpath;
/*
* We only need to handle events for real devices.
*/
if (target->target_id == CAM_TARGET_WILDCARD
|| device->lun_id == CAM_LUN_WILDCARD)
return;
/*
* We need our own path with wildcards expanded to
* handle certain types of events.
*/
if ((async_code == AC_SENT_BDR)
|| (async_code == AC_BUS_RESET)
|| (async_code == AC_INQ_CHANGED))
status = xpt_compile_path(&newpath, NULL,
bus->path_id,
target->target_id,
device->lun_id);
else
status = CAM_REQ_CMP_ERR;
if (status == CAM_REQ_CMP) {
if (async_code == AC_INQ_CHANGED) {
/*
* We've sent a start unit command, or
* something similar to a device that
* may have caused its inquiry data to
* change. So we re-scan the device to
* refresh the inquiry data for it.
*/
ata_scan_lun(newpath.periph, &newpath,
CAM_EXPECT_INQ_CHANGE, NULL);
} else {
/* We need to reinitialize device after reset. */
ata_scan_lun(newpath.periph, &newpath,
0, NULL);
}
xpt_release_path(&newpath);
} else if (async_code == AC_LOST_DEVICE &&
(device->flags & CAM_DEV_UNCONFIGURED) == 0) {
device->flags |= CAM_DEV_UNCONFIGURED;
xpt_release_device(device);
} else if (async_code == AC_TRANSFER_NEG) {
struct ccb_trans_settings *settings;
settings = (struct ccb_trans_settings *)async_arg;
ata_set_transfer_settings(settings, device,
/*async_update*/TRUE);
}
}
static void
ata_announce_periph(struct cam_periph *periph)
{
struct ccb_pathinq cpi;
struct ccb_trans_settings cts;
struct cam_path *path = periph->path;
u_int speed;
u_int mb;
mtx_assert(periph->sim->mtx, MA_OWNED);
xpt_setup_ccb(&cts.ccb_h, path, CAM_PRIORITY_NORMAL);
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
xpt_action((union ccb*)&cts);
if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
return;
/* Ask the SIM for its base transfer speed */
xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NORMAL);
cpi.ccb_h.func_code = XPT_PATH_INQ;
xpt_action((union ccb *)&cpi);
/* Report connection speed */
speed = cpi.base_transfer_speed;
if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_ATA) {
struct ccb_trans_settings_pata *pata =
&cts.xport_specific.ata;
if (pata->valid & CTS_ATA_VALID_MODE)
speed = ata_mode2speed(pata->mode);
}
if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SATA) {
struct ccb_trans_settings_sata *sata =
&cts.xport_specific.sata;
if (sata->valid & CTS_SATA_VALID_REVISION)
speed = ata_revision2speed(sata->revision);
}
mb = speed / 1000;
if (mb > 0)
printf("%s%d: %d.%03dMB/s transfers",
periph->periph_name, periph->unit_number,
mb, speed % 1000);
else
printf("%s%d: %dKB/s transfers", periph->periph_name,
periph->unit_number, speed);
/* Report additional information about connection */
if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_ATA) {
struct ccb_trans_settings_pata *pata =
&cts.xport_specific.ata;
printf(" (");
if (pata->valid & CTS_ATA_VALID_MODE)
printf("%s, ", ata_mode2string(pata->mode));
if ((pata->valid & CTS_ATA_VALID_ATAPI) && pata->atapi != 0)
printf("ATAPI %dbytes, ", pata->atapi);
if (pata->valid & CTS_ATA_VALID_BYTECOUNT)
printf("PIO %dbytes", pata->bytecount);
printf(")");
}
if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SATA) {
struct ccb_trans_settings_sata *sata =
&cts.xport_specific.sata;
printf(" (");
if (sata->valid & CTS_SATA_VALID_REVISION)
printf("SATA %d.x, ", sata->revision);
else
printf("SATA, ");
if (sata->valid & CTS_SATA_VALID_MODE)
printf("%s, ", ata_mode2string(sata->mode));
if ((sata->valid & CTS_ATA_VALID_ATAPI) && sata->atapi != 0)
printf("ATAPI %dbytes, ", sata->atapi);
if (sata->valid & CTS_SATA_VALID_BYTECOUNT)
printf("PIO %dbytes", sata->bytecount);
printf(")");
}
printf("\n");
}