53f5ac1310
Since SES specs do not define mechanism to map enclosure slots to SATA disks, AHCI EM code I written many years ago appeared quite useless, that always bugged me. I was thinking whether it was a good idea, but if LSI HBAs do that, why I shouldn't? This change introduces simple non-standard mechanism for the mapping into both AHCI EM and SES code, that makes AHCI EM on capable controllers (most of Intel's) a first-class SES citizen, allowing it to report disk physical path to GEOM, show devices inserted into each enclosure slot in `sesutil map` and `getencstat`, control locate and fault LEDs for specific devices with `sesutil locate adaX on` and `sesutil fault adaX on`, etc. I've successfully tested this on Supermicro X10DRH-i motherboard connected with sideband cable of its S-SATA Mini-SAS connector to SAS815TQ backplane. It can indicate with LEDs Locate, Fault and Rebuild/Remap SES statuses for each disk identical to real SES of Supermicro SAS2 backplanes. MFC after: 2 weeks
664 lines
18 KiB
C
664 lines
18 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* Copyright (c) 2012 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/module.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/endian.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mutex.h>
|
|
#include <machine/stdarg.h>
|
|
#include <machine/resource.h>
|
|
#include <machine/bus.h>
|
|
#include <sys/rman.h>
|
|
#include <dev/led/led.h>
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcireg.h>
|
|
#include "ahci.h"
|
|
|
|
#include <cam/cam.h>
|
|
#include <cam/cam_ccb.h>
|
|
#include <cam/cam_sim.h>
|
|
#include <cam/cam_xpt_sim.h>
|
|
#include <cam/cam_debug.h>
|
|
#include <cam/scsi/scsi_ses.h>
|
|
|
|
/* local prototypes */
|
|
static void ahciemaction(struct cam_sim *sim, union ccb *ccb);
|
|
static void ahciempoll(struct cam_sim *sim);
|
|
static int ahci_em_reset(device_t dev);
|
|
static void ahci_em_led(void *priv, int onoff);
|
|
static void ahci_em_setleds(device_t dev, int c);
|
|
|
|
static int
|
|
ahci_em_probe(device_t dev)
|
|
{
|
|
|
|
device_set_desc_copy(dev, "AHCI enclosure management bridge");
|
|
return (BUS_PROBE_DEFAULT);
|
|
}
|
|
|
|
static int
|
|
ahci_em_attach(device_t dev)
|
|
{
|
|
device_t parent = device_get_parent(dev);
|
|
struct ahci_controller *ctlr = device_get_softc(parent);
|
|
struct ahci_enclosure *enc = device_get_softc(dev);
|
|
struct cam_devq *devq;
|
|
int i, c, rid, error;
|
|
char buf[32];
|
|
|
|
enc->dev = dev;
|
|
enc->quirks = ctlr->quirks;
|
|
enc->channels = ctlr->channels;
|
|
enc->ichannels = ctlr->ichannels;
|
|
mtx_init(&enc->mtx, "AHCI enclosure lock", NULL, MTX_DEF);
|
|
rid = 0;
|
|
if (!(enc->r_memc = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
|
&rid, RF_ACTIVE))) {
|
|
mtx_destroy(&enc->mtx);
|
|
return (ENXIO);
|
|
}
|
|
enc->capsem = ATA_INL(enc->r_memc, 0);
|
|
rid = 1;
|
|
if (!(enc->r_memt = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
|
&rid, RF_ACTIVE))) {
|
|
error = ENXIO;
|
|
goto err0;
|
|
}
|
|
if ((enc->capsem & (AHCI_EM_XMT | AHCI_EM_SMB)) == 0) {
|
|
rid = 2;
|
|
if (!(enc->r_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
|
&rid, RF_ACTIVE))) {
|
|
error = ENXIO;
|
|
goto err0;
|
|
}
|
|
} else
|
|
enc->r_memr = NULL;
|
|
mtx_lock(&enc->mtx);
|
|
if (ahci_em_reset(dev) != 0) {
|
|
error = ENXIO;
|
|
goto err1;
|
|
}
|
|
rid = ATA_IRQ_RID;
|
|
/* Create the device queue for our SIM. */
|
|
devq = cam_simq_alloc(1);
|
|
if (devq == NULL) {
|
|
device_printf(dev, "Unable to allocate SIM queue\n");
|
|
error = ENOMEM;
|
|
goto err1;
|
|
}
|
|
/* Construct SIM entry */
|
|
enc->sim = cam_sim_alloc(ahciemaction, ahciempoll, "ahciem", enc,
|
|
device_get_unit(dev), &enc->mtx,
|
|
1, 0, devq);
|
|
if (enc->sim == NULL) {
|
|
cam_simq_free(devq);
|
|
device_printf(dev, "Unable to allocate SIM\n");
|
|
error = ENOMEM;
|
|
goto err1;
|
|
}
|
|
if (xpt_bus_register(enc->sim, dev, 0) != CAM_SUCCESS) {
|
|
device_printf(dev, "unable to register xpt bus\n");
|
|
error = ENXIO;
|
|
goto err2;
|
|
}
|
|
if (xpt_create_path(&enc->path, /*periph*/NULL, cam_sim_path(enc->sim),
|
|
CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
|
|
device_printf(dev, "Unable to create path\n");
|
|
error = ENXIO;
|
|
goto err3;
|
|
}
|
|
mtx_unlock(&enc->mtx);
|
|
if (bootverbose) {
|
|
device_printf(dev, "Caps:%s%s%s%s%s%s%s%s\n",
|
|
(enc->capsem & AHCI_EM_PM) ? " PM":"",
|
|
(enc->capsem & AHCI_EM_ALHD) ? " ALHD":"",
|
|
(enc->capsem & AHCI_EM_XMT) ? " XMT":"",
|
|
(enc->capsem & AHCI_EM_SMB) ? " SMB":"",
|
|
(enc->capsem & AHCI_EM_SGPIO) ? " SGPIO":"",
|
|
(enc->capsem & AHCI_EM_SES2) ? " SES-2":"",
|
|
(enc->capsem & AHCI_EM_SAFTE) ? " SAF-TE":"",
|
|
(enc->capsem & AHCI_EM_LED) ? " LED":"");
|
|
}
|
|
if ((enc->capsem & AHCI_EM_LED)) {
|
|
for (c = 0; c < enc->channels; c++) {
|
|
if ((enc->ichannels & (1 << c)) == 0)
|
|
continue;
|
|
for (i = 0; i < AHCI_NUM_LEDS; i++) {
|
|
enc->leds[c * AHCI_NUM_LEDS + i].dev = dev;
|
|
enc->leds[c * AHCI_NUM_LEDS + i].num =
|
|
c * AHCI_NUM_LEDS + i;
|
|
}
|
|
if ((enc->capsem & AHCI_EM_ALHD) == 0) {
|
|
snprintf(buf, sizeof(buf), "%s.%d.act",
|
|
device_get_nameunit(parent), c);
|
|
enc->leds[c * AHCI_NUM_LEDS + 0].led =
|
|
led_create(ahci_em_led,
|
|
&enc->leds[c * AHCI_NUM_LEDS + 0], buf);
|
|
}
|
|
snprintf(buf, sizeof(buf), "%s.%d.locate",
|
|
device_get_nameunit(parent), c);
|
|
enc->leds[c * AHCI_NUM_LEDS + 1].led =
|
|
led_create(ahci_em_led,
|
|
&enc->leds[c * AHCI_NUM_LEDS + 1], buf);
|
|
snprintf(buf, sizeof(buf), "%s.%d.fault",
|
|
device_get_nameunit(parent), c);
|
|
enc->leds[c * AHCI_NUM_LEDS + 2].led =
|
|
led_create(ahci_em_led,
|
|
&enc->leds[c * AHCI_NUM_LEDS + 2], buf);
|
|
}
|
|
}
|
|
return (0);
|
|
|
|
err3:
|
|
xpt_bus_deregister(cam_sim_path(enc->sim));
|
|
err2:
|
|
cam_sim_free(enc->sim, /*free_devq*/TRUE);
|
|
err1:
|
|
mtx_unlock(&enc->mtx);
|
|
if (enc->r_memr)
|
|
bus_release_resource(dev, SYS_RES_MEMORY, 2, enc->r_memr);
|
|
err0:
|
|
if (enc->r_memt)
|
|
bus_release_resource(dev, SYS_RES_MEMORY, 1, enc->r_memt);
|
|
bus_release_resource(dev, SYS_RES_MEMORY, 0, enc->r_memc);
|
|
mtx_destroy(&enc->mtx);
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
ahci_em_detach(device_t dev)
|
|
{
|
|
struct ahci_enclosure *enc = device_get_softc(dev);
|
|
int i;
|
|
|
|
for (i = 0; i < enc->channels * AHCI_NUM_LEDS; i++) {
|
|
if (enc->leds[i].led)
|
|
led_destroy(enc->leds[i].led);
|
|
}
|
|
mtx_lock(&enc->mtx);
|
|
xpt_async(AC_LOST_DEVICE, enc->path, NULL);
|
|
xpt_free_path(enc->path);
|
|
xpt_bus_deregister(cam_sim_path(enc->sim));
|
|
cam_sim_free(enc->sim, /*free_devq*/TRUE);
|
|
mtx_unlock(&enc->mtx);
|
|
|
|
bus_release_resource(dev, SYS_RES_MEMORY, 0, enc->r_memc);
|
|
bus_release_resource(dev, SYS_RES_MEMORY, 1, enc->r_memt);
|
|
if (enc->r_memr)
|
|
bus_release_resource(dev, SYS_RES_MEMORY, 2, enc->r_memr);
|
|
mtx_destroy(&enc->mtx);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
ahci_em_reset(device_t dev)
|
|
{
|
|
struct ahci_enclosure *enc;
|
|
int i, timeout;
|
|
|
|
enc = device_get_softc(dev);
|
|
ATA_OUTL(enc->r_memc, 0, AHCI_EM_RST);
|
|
timeout = 1000;
|
|
while ((ATA_INL(enc->r_memc, 0) & AHCI_EM_RST) &&
|
|
--timeout > 0)
|
|
DELAY(1000);
|
|
if (timeout == 0) {
|
|
device_printf(dev, "EM timeout\n");
|
|
return (1);
|
|
}
|
|
for (i = 0; i < enc->channels; i++)
|
|
ahci_em_setleds(dev, i);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
ahci_em_suspend(device_t dev)
|
|
{
|
|
struct ahci_enclosure *enc = device_get_softc(dev);
|
|
|
|
mtx_lock(&enc->mtx);
|
|
xpt_freeze_simq(enc->sim, 1);
|
|
mtx_unlock(&enc->mtx);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
ahci_em_resume(device_t dev)
|
|
{
|
|
struct ahci_enclosure *enc = device_get_softc(dev);
|
|
|
|
mtx_lock(&enc->mtx);
|
|
ahci_em_reset(dev);
|
|
xpt_release_simq(enc->sim, TRUE);
|
|
mtx_unlock(&enc->mtx);
|
|
return (0);
|
|
}
|
|
|
|
devclass_t ahciem_devclass;
|
|
static device_method_t ahciem_methods[] = {
|
|
DEVMETHOD(device_probe, ahci_em_probe),
|
|
DEVMETHOD(device_attach, ahci_em_attach),
|
|
DEVMETHOD(device_detach, ahci_em_detach),
|
|
DEVMETHOD(device_suspend, ahci_em_suspend),
|
|
DEVMETHOD(device_resume, ahci_em_resume),
|
|
DEVMETHOD_END
|
|
};
|
|
static driver_t ahciem_driver = {
|
|
"ahciem",
|
|
ahciem_methods,
|
|
sizeof(struct ahci_enclosure)
|
|
};
|
|
DRIVER_MODULE(ahciem, ahci, ahciem_driver, ahciem_devclass, NULL, NULL);
|
|
|
|
static void
|
|
ahci_em_setleds(device_t dev, int c)
|
|
{
|
|
struct ahci_enclosure *enc;
|
|
int timeout;
|
|
int16_t val;
|
|
|
|
enc = device_get_softc(dev);
|
|
|
|
val = 0;
|
|
if (enc->status[c][2] & SESCTL_RQSACT) /* Activity */
|
|
val |= (1 << 0);
|
|
if (enc->status[c][1] & SESCTL_RQSRR) /* Rebuild */
|
|
val |= (1 << 6) | (1 << 3);
|
|
else if (enc->status[c][2] & SESCTL_RQSID) /* Identification */
|
|
val |= (1 << 3);
|
|
else if (enc->status[c][3] & SESCTL_RQSFLT) /* Fault */
|
|
val |= (1 << 6);
|
|
|
|
timeout = 10000;
|
|
while (ATA_INL(enc->r_memc, 0) & (AHCI_EM_TM | AHCI_EM_RST) &&
|
|
--timeout > 0)
|
|
DELAY(100);
|
|
if (timeout == 0)
|
|
device_printf(dev, "Transmit timeout\n");
|
|
ATA_OUTL(enc->r_memt, 0, (1 << 8) | (0 << 16) | (0 << 24));
|
|
ATA_OUTL(enc->r_memt, 4, c | (0 << 8) | (val << 16));
|
|
ATA_OUTL(enc->r_memc, 0, AHCI_EM_TM);
|
|
}
|
|
|
|
static void
|
|
ahci_em_led(void *priv, int onoff)
|
|
{
|
|
struct ahci_led *led;
|
|
struct ahci_enclosure *enc;
|
|
int c, l;
|
|
|
|
led = (struct ahci_led *)priv;
|
|
enc = device_get_softc(led->dev);
|
|
c = led->num / AHCI_NUM_LEDS;
|
|
l = led->num % AHCI_NUM_LEDS;
|
|
|
|
if (l == 0) {
|
|
if (onoff)
|
|
enc->status[c][2] |= 0x80;
|
|
else
|
|
enc->status[c][2] &= ~0x80;
|
|
} else if (l == 1) {
|
|
if (onoff)
|
|
enc->status[c][2] |= SESCTL_RQSID;
|
|
else
|
|
enc->status[c][2] &= ~SESCTL_RQSID;
|
|
} else if (l == 2) {
|
|
if (onoff)
|
|
enc->status[c][3] |= SESCTL_RQSFLT;
|
|
else
|
|
enc->status[c][3] &= SESCTL_RQSFLT;
|
|
}
|
|
ahci_em_setleds(led->dev, c);
|
|
}
|
|
|
|
static int
|
|
ahci_check_ids(union ccb *ccb)
|
|
{
|
|
|
|
if (ccb->ccb_h.target_id != 0) {
|
|
ccb->ccb_h.status = CAM_TID_INVALID;
|
|
xpt_done(ccb);
|
|
return (-1);
|
|
}
|
|
if (ccb->ccb_h.target_lun != 0) {
|
|
ccb->ccb_h.status = CAM_LUN_INVALID;
|
|
xpt_done(ccb);
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
ahci_em_emulate_ses_on_led(device_t dev, union ccb *ccb)
|
|
{
|
|
struct ahci_enclosure *enc;
|
|
struct ahci_channel *ch;
|
|
struct ses_status_page *page;
|
|
struct ses_status_array_dev_slot *ads, *ads0;
|
|
struct ses_elm_desc_hdr *elmd;
|
|
struct ses_elm_addlstatus_eip_hdr *elma;
|
|
struct ses_elm_ata_hdr *elmb;
|
|
uint8_t *buf;
|
|
int i;
|
|
|
|
enc = device_get_softc(dev);
|
|
buf = ccb->ataio.data_ptr;
|
|
|
|
/* General request validation. */
|
|
if (ccb->ataio.cmd.command != ATA_SEP_ATTN ||
|
|
ccb->ataio.dxfer_len < ccb->ataio.cmd.sector_count * 4) {
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
goto out;
|
|
}
|
|
|
|
/* SEMB IDENTIFY */
|
|
if (ccb->ataio.cmd.features == 0xEC &&
|
|
ccb->ataio.cmd.sector_count >= 16) {
|
|
bzero(buf, ccb->ataio.dxfer_len);
|
|
buf[0] = 64; /* Valid bytes. */
|
|
buf[2] = 0x30; /* NAA Locally Assigned. */
|
|
strncpy(&buf[3], device_get_nameunit(dev), 7);
|
|
strncpy(&buf[10], "AHCI ", SID_VENDOR_SIZE);
|
|
strncpy(&buf[18], "SGPIO Enclosure ", SID_PRODUCT_SIZE);
|
|
strncpy(&buf[34], "2.00", SID_REVISION_SIZE);
|
|
strncpy(&buf[39], "0001", 4);
|
|
strncpy(&buf[43], "S-E-S ", 6);
|
|
strncpy(&buf[49], "2.00", 4);
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
goto out;
|
|
}
|
|
|
|
/* SEMB RECEIVE DIAGNOSTIC RESULT (0) */
|
|
page = (struct ses_status_page *)buf;
|
|
if (ccb->ataio.cmd.lba_low == 0x02 &&
|
|
ccb->ataio.cmd.features == 0x00 &&
|
|
ccb->ataio.cmd.sector_count >= 3) {
|
|
bzero(buf, ccb->ataio.dxfer_len);
|
|
page->hdr.page_code = 0;
|
|
scsi_ulto2b(5, page->hdr.length);
|
|
buf[4] = 0x00;
|
|
buf[5] = 0x01;
|
|
buf[6] = 0x02;
|
|
buf[7] = 0x07;
|
|
buf[8] = 0x0a;
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
goto out;
|
|
}
|
|
|
|
/* SEMB RECEIVE DIAGNOSTIC RESULT (1) */
|
|
if (ccb->ataio.cmd.lba_low == 0x02 &&
|
|
ccb->ataio.cmd.features == 0x01 &&
|
|
ccb->ataio.cmd.sector_count >= 16) {
|
|
struct ses_enc_desc *ed;
|
|
struct ses_elm_type_desc *td;
|
|
|
|
bzero(buf, ccb->ataio.dxfer_len);
|
|
page->hdr.page_code = 0x01;
|
|
scsi_ulto2b(4 + sizeof(*ed) + sizeof(*td) + 11,
|
|
page->hdr.length);
|
|
ed = (struct ses_enc_desc *)&buf[8];
|
|
ed->byte0 = 0x11;
|
|
ed->subenc_id = 0;
|
|
ed->num_types = 1;
|
|
ed->length = 36;
|
|
ed->logical_id[0] = 0x30; /* NAA Locally Assigned. */
|
|
strncpy(&ed->logical_id[1], device_get_nameunit(dev), 7);
|
|
strncpy(ed->vendor_id, "AHCI ", SID_VENDOR_SIZE);
|
|
strncpy(ed->product_id, "SGPIO Enclosure ", SID_PRODUCT_SIZE);
|
|
strncpy(ed->product_rev, "2.00", SID_REVISION_SIZE);
|
|
td = (struct ses_elm_type_desc *)ses_enc_desc_next(ed);
|
|
td->etype_elm_type = 0x17;
|
|
td->etype_maxelt = enc->channels;
|
|
td->etype_subenc = 0;
|
|
td->etype_txt_len = 11;
|
|
snprintf((char *)(td + 1), 12, "Drive Slots");
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
goto out;
|
|
}
|
|
|
|
/* SEMB RECEIVE DIAGNOSTIC RESULT (2) */
|
|
if (ccb->ataio.cmd.lba_low == 0x02 &&
|
|
ccb->ataio.cmd.features == 0x02 &&
|
|
ccb->ataio.cmd.sector_count >= (3 + enc->channels)) {
|
|
bzero(buf, ccb->ataio.dxfer_len);
|
|
page->hdr.page_code = 0x02;
|
|
scsi_ulto2b(4 + 4 * (1 + enc->channels),
|
|
page->hdr.length);
|
|
for (i = 0; i < enc->channels; i++) {
|
|
ads = &page->elements[i + 1].array_dev_slot;
|
|
memcpy(ads, enc->status[i], 4);
|
|
ch = ahci_getch(device_get_parent(dev), i);
|
|
if (ch == NULL) {
|
|
ads->common.bytes[0] |= SES_OBJSTAT_UNKNOWN;
|
|
continue;
|
|
}
|
|
if (ch->pm_present)
|
|
ads->common.bytes[0] |= SES_OBJSTAT_UNKNOWN;
|
|
else if (ch->devices)
|
|
ads->common.bytes[0] |= SES_OBJSTAT_OK;
|
|
else if (ch->disablephy)
|
|
ads->common.bytes[0] |= SES_OBJSTAT_NOTAVAIL;
|
|
else
|
|
ads->common.bytes[0] |= SES_OBJSTAT_NOTINSTALLED;
|
|
if (ch->disablephy)
|
|
ads->common.bytes[3] |= SESCTL_DEVOFF;
|
|
ahci_putch(ch);
|
|
}
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
goto out;
|
|
}
|
|
|
|
/* SEMB SEND DIAGNOSTIC (2) */
|
|
if (ccb->ataio.cmd.lba_low == 0x82 &&
|
|
ccb->ataio.cmd.features == 0x02 &&
|
|
ccb->ataio.cmd.sector_count >= (3 + enc->channels)) {
|
|
ads0 = &page->elements[0].array_dev_slot;
|
|
for (i = 0; i < enc->channels; i++) {
|
|
ads = &page->elements[i + 1].array_dev_slot;
|
|
if (ads->common.bytes[0] & SESCTL_CSEL) {
|
|
enc->status[i][0] = 0;
|
|
enc->status[i][1] = ads->bytes[0] &
|
|
SESCTL_RQSRR;
|
|
enc->status[i][2] = ads->bytes[1] &
|
|
(SESCTL_RQSACT | SESCTL_RQSID);
|
|
enc->status[i][3] = ads->bytes[2] &
|
|
SESCTL_RQSFLT;
|
|
ahci_em_setleds(dev, i);
|
|
} else if (ads0->common.bytes[0] & SESCTL_CSEL) {
|
|
enc->status[i][0] = 0;
|
|
enc->status[i][1] = ads0->bytes[0] &
|
|
SESCTL_RQSRR;
|
|
enc->status[i][2] = ads0->bytes[1] &
|
|
(SESCTL_RQSACT | SESCTL_RQSID);
|
|
enc->status[i][3] = ads0->bytes[2] &
|
|
SESCTL_RQSFLT;
|
|
ahci_em_setleds(dev, i);
|
|
}
|
|
}
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
goto out;
|
|
}
|
|
|
|
/* SEMB RECEIVE DIAGNOSTIC RESULT (7) */
|
|
if (ccb->ataio.cmd.lba_low == 0x02 &&
|
|
ccb->ataio.cmd.features == 0x07 &&
|
|
ccb->ataio.cmd.sector_count >= (6 + 3 * enc->channels)) {
|
|
bzero(buf, ccb->ataio.dxfer_len);
|
|
page->hdr.page_code = 0x07;
|
|
scsi_ulto2b(4 + 15 + 11 * enc->channels, page->hdr.length);
|
|
elmd = (struct ses_elm_desc_hdr *)&buf[8];
|
|
scsi_ulto2b(11, elmd->length);
|
|
snprintf((char *)(elmd + 1), 12, "Drive Slots");
|
|
for (i = 0; i < enc->channels; i++) {
|
|
elmd = (struct ses_elm_desc_hdr *)&buf[8 + 15 + 11 * i];
|
|
scsi_ulto2b(7, elmd->length);
|
|
snprintf((char *)(elmd + 1), 8, "Slot %02d", i);
|
|
}
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
goto out;
|
|
}
|
|
|
|
/* SEMB RECEIVE DIAGNOSTIC RESULT (a) */
|
|
if (ccb->ataio.cmd.lba_low == 0x02 &&
|
|
ccb->ataio.cmd.features == 0x0a &&
|
|
ccb->ataio.cmd.sector_count >= (2 + 3 * enc->channels)) {
|
|
bzero(buf, ccb->ataio.dxfer_len);
|
|
page->hdr.page_code = 0x0a;
|
|
scsi_ulto2b(4 + (sizeof(*elma) + sizeof(*elmb)) * enc->channels,
|
|
page->hdr.length);
|
|
for (i = 0; i < enc->channels; i++) {
|
|
elma = (struct ses_elm_addlstatus_eip_hdr *)&buf[
|
|
8 + (sizeof(*elma) + sizeof(*elmb)) * i];
|
|
elma->base.byte0 = 0x10 | SPSP_PROTO_ATA;
|
|
elma->base.length = 2 + sizeof(*elmb);
|
|
elma->byte2 = 0x01;
|
|
elma->element_index = 1 + i;
|
|
ch = ahci_getch(device_get_parent(dev), i);
|
|
if (ch == NULL) {
|
|
elma->base.byte0 |= 0x80;
|
|
continue;
|
|
}
|
|
if (ch->devices == 0 || ch->pm_present)
|
|
elma->base.byte0 |= 0x80;
|
|
elmb = (struct ses_elm_ata_hdr *)(elma + 1);
|
|
scsi_ulto4b(cam_sim_path(ch->sim), elmb->bus);
|
|
scsi_ulto4b(0, elmb->target);
|
|
ahci_putch(ch);
|
|
}
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
goto out;
|
|
}
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
out:
|
|
xpt_done(ccb);
|
|
}
|
|
|
|
static void
|
|
ahci_em_begin_transaction(device_t dev, union ccb *ccb)
|
|
{
|
|
struct ahci_enclosure *enc;
|
|
struct ata_res *res;
|
|
|
|
enc = device_get_softc(dev);
|
|
res = &ccb->ataio.res;
|
|
bzero(res, sizeof(*res));
|
|
if ((ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) &&
|
|
(ccb->ataio.cmd.control & ATA_A_RESET)) {
|
|
res->lba_high = 0xc3;
|
|
res->lba_mid = 0x3c;
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
xpt_done(ccb);
|
|
return;
|
|
}
|
|
|
|
if (enc->capsem & AHCI_EM_LED) {
|
|
ahci_em_emulate_ses_on_led(dev, ccb);
|
|
return;
|
|
} else
|
|
device_printf(dev, "Unsupported enclosure interface\n");
|
|
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
xpt_done(ccb);
|
|
}
|
|
|
|
static void
|
|
ahciemaction(struct cam_sim *sim, union ccb *ccb)
|
|
{
|
|
device_t dev, parent;
|
|
struct ahci_enclosure *enc;
|
|
|
|
CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
|
|
("ahciemaction func_code=%x\n", ccb->ccb_h.func_code));
|
|
|
|
enc = cam_sim_softc(sim);
|
|
dev = enc->dev;
|
|
switch (ccb->ccb_h.func_code) {
|
|
case XPT_ATA_IO: /* Execute the requested I/O operation */
|
|
if (ahci_check_ids(ccb))
|
|
return;
|
|
ahci_em_begin_transaction(dev, ccb);
|
|
return;
|
|
case XPT_RESET_BUS: /* Reset the specified bus */
|
|
case XPT_RESET_DEV: /* Bus Device Reset the specified device */
|
|
ahci_em_reset(dev);
|
|
ccb->ccb_h.status = CAM_REQ_CMP;
|
|
break;
|
|
case XPT_PATH_INQ: /* Path routing inquiry */
|
|
{
|
|
struct ccb_pathinq *cpi = &ccb->cpi;
|
|
|
|
parent = device_get_parent(dev);
|
|
cpi->version_num = 1; /* XXX??? */
|
|
cpi->hba_inquiry = PI_SDTR_ABLE;
|
|
cpi->target_sprt = 0;
|
|
cpi->hba_misc = PIM_SEQSCAN;
|
|
cpi->hba_eng_cnt = 0;
|
|
cpi->max_target = 0;
|
|
cpi->max_lun = 0;
|
|
cpi->initiator_id = 0;
|
|
cpi->bus_id = cam_sim_bus(sim);
|
|
cpi->base_transfer_speed = 150000;
|
|
strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
|
|
strlcpy(cpi->hba_vid, "AHCI", HBA_IDLEN);
|
|
strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
|
|
cpi->unit_number = cam_sim_unit(sim);
|
|
cpi->transport = XPORT_SATA;
|
|
cpi->transport_version = XPORT_VERSION_UNSPECIFIED;
|
|
cpi->protocol = PROTO_ATA;
|
|
cpi->protocol_version = PROTO_VERSION_UNSPECIFIED;
|
|
cpi->maxio = MAXPHYS;
|
|
cpi->hba_vendor = pci_get_vendor(parent);
|
|
cpi->hba_device = pci_get_device(parent);
|
|
cpi->hba_subvendor = pci_get_subvendor(parent);
|
|
cpi->hba_subdevice = pci_get_subdevice(parent);
|
|
cpi->ccb_h.status = CAM_REQ_CMP;
|
|
break;
|
|
}
|
|
default:
|
|
ccb->ccb_h.status = CAM_REQ_INVALID;
|
|
break;
|
|
}
|
|
xpt_done(ccb);
|
|
}
|
|
|
|
static void
|
|
ahciempoll(struct cam_sim *sim)
|
|
{
|
|
|
|
}
|