17419d6e4SChuck Tuffli /*
27419d6e4SChuck Tuffli * Copyright (c) 2016-2021 Chuck Tuffli <chuck@tuffli.net>
37419d6e4SChuck Tuffli *
47419d6e4SChuck Tuffli * Permission to use, copy, modify, and distribute this software for any
57419d6e4SChuck Tuffli * purpose with or without fee is hereby granted, provided that the above
67419d6e4SChuck Tuffli * copyright notice and this permission notice appear in all copies.
77419d6e4SChuck Tuffli *
87419d6e4SChuck Tuffli * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
97419d6e4SChuck Tuffli * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
107419d6e4SChuck Tuffli * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
117419d6e4SChuck Tuffli * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
127419d6e4SChuck Tuffli * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
137419d6e4SChuck Tuffli * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
147419d6e4SChuck Tuffli * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
157419d6e4SChuck Tuffli */
167419d6e4SChuck Tuffli #include <stdio.h>
177419d6e4SChuck Tuffli #include <stdlib.h>
187419d6e4SChuck Tuffli #include <fcntl.h>
197419d6e4SChuck Tuffli #include <string.h>
207419d6e4SChuck Tuffli #include <err.h>
217419d6e4SChuck Tuffli #include <errno.h>
227419d6e4SChuck Tuffli #include <camlib.h>
237419d6e4SChuck Tuffli #include <cam/scsi/scsi_message.h>
247419d6e4SChuck Tuffli
257419d6e4SChuck Tuffli #include "libsmart.h"
267419d6e4SChuck Tuffli #include "libsmart_priv.h"
277419d6e4SChuck Tuffli #include "libsmart_dev.h"
287419d6e4SChuck Tuffli
297419d6e4SChuck Tuffli /* Provide compatibility for FreeBSD 11.0 */
307419d6e4SChuck Tuffli #if (__FreeBSD_version < 1101000)
317419d6e4SChuck Tuffli
327419d6e4SChuck Tuffli struct scsi_log_informational_exceptions {
337419d6e4SChuck Tuffli struct scsi_log_param_header hdr;
347419d6e4SChuck Tuffli #define SLP_IE_GEN 0x0000
357419d6e4SChuck Tuffli uint8_t ie_asc;
367419d6e4SChuck Tuffli uint8_t ie_ascq;
377419d6e4SChuck Tuffli uint8_t temperature;
387419d6e4SChuck Tuffli };
397419d6e4SChuck Tuffli
407419d6e4SChuck Tuffli #endif
417419d6e4SChuck Tuffli
427419d6e4SChuck Tuffli struct fbsd_smart {
437419d6e4SChuck Tuffli smart_t common;
447419d6e4SChuck Tuffli struct cam_device *camdev;
457419d6e4SChuck Tuffli };
467419d6e4SChuck Tuffli
477419d6e4SChuck Tuffli static smart_protocol_e __device_get_proto(struct fbsd_smart *);
487419d6e4SChuck Tuffli static bool __device_proto_tunneled(struct fbsd_smart *);
497419d6e4SChuck Tuffli static int32_t __device_get_info(struct fbsd_smart *);
507419d6e4SChuck Tuffli
517419d6e4SChuck Tuffli smart_h
device_open(smart_protocol_e protocol,char * devname)527419d6e4SChuck Tuffli device_open(smart_protocol_e protocol, char *devname)
537419d6e4SChuck Tuffli {
547419d6e4SChuck Tuffli struct fbsd_smart *h = NULL;
557419d6e4SChuck Tuffli
567419d6e4SChuck Tuffli h = malloc(sizeof(struct fbsd_smart));
577419d6e4SChuck Tuffli if (h == NULL)
587419d6e4SChuck Tuffli return NULL;
597419d6e4SChuck Tuffli
607419d6e4SChuck Tuffli memset(h, 0, sizeof(struct fbsd_smart));
617419d6e4SChuck Tuffli
627419d6e4SChuck Tuffli h->common.protocol = SMART_PROTO_MAX;
637419d6e4SChuck Tuffli h->camdev = cam_open_device(devname, O_RDWR);
647419d6e4SChuck Tuffli if (h->camdev == NULL) {
657419d6e4SChuck Tuffli printf("%s: error opening %s - %s\n",
667419d6e4SChuck Tuffli __func__, devname,
677419d6e4SChuck Tuffli cam_errbuf);
687419d6e4SChuck Tuffli free(h);
697419d6e4SChuck Tuffli h = NULL;
707419d6e4SChuck Tuffli } else {
717419d6e4SChuck Tuffli smart_protocol_e proto = __device_get_proto(h);
727419d6e4SChuck Tuffli
737419d6e4SChuck Tuffli if ((protocol == SMART_PROTO_AUTO) ||
747419d6e4SChuck Tuffli (protocol == proto)) {
757419d6e4SChuck Tuffli h->common.protocol = proto;
767419d6e4SChuck Tuffli } else {
777419d6e4SChuck Tuffli printf("%s: protocol mismatch %d vs %d\n",
787419d6e4SChuck Tuffli __func__, protocol, proto);
797419d6e4SChuck Tuffli }
807419d6e4SChuck Tuffli
817419d6e4SChuck Tuffli if (proto == SMART_PROTO_SCSI) {
827419d6e4SChuck Tuffli if (__device_proto_tunneled(h)) {
837419d6e4SChuck Tuffli h->common.protocol = SMART_PROTO_ATA;
847419d6e4SChuck Tuffli h->common.info.tunneled = 1;
857419d6e4SChuck Tuffli }
867419d6e4SChuck Tuffli }
877419d6e4SChuck Tuffli
887419d6e4SChuck Tuffli __device_get_info(h);
897419d6e4SChuck Tuffli }
907419d6e4SChuck Tuffli
917419d6e4SChuck Tuffli return h;
927419d6e4SChuck Tuffli }
937419d6e4SChuck Tuffli
947419d6e4SChuck Tuffli void
device_close(smart_h h)957419d6e4SChuck Tuffli device_close(smart_h h)
967419d6e4SChuck Tuffli {
977419d6e4SChuck Tuffli struct fbsd_smart *fsmart = h;
987419d6e4SChuck Tuffli
997419d6e4SChuck Tuffli if (fsmart != NULL) {
1007419d6e4SChuck Tuffli if (fsmart->camdev != NULL) {
1017419d6e4SChuck Tuffli cam_close_device(fsmart->camdev);
1027419d6e4SChuck Tuffli }
1037419d6e4SChuck Tuffli
1047419d6e4SChuck Tuffli free(fsmart);
1057419d6e4SChuck Tuffli }
1067419d6e4SChuck Tuffli }
1077419d6e4SChuck Tuffli
1087419d6e4SChuck Tuffli static const uint8_t smart_read_data[] = {
1097419d6e4SChuck Tuffli 0xb0, 0xd0, 0x00, 0x4f, 0xc2, 0x00,
1107419d6e4SChuck Tuffli 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1117419d6e4SChuck Tuffli };
1127419d6e4SChuck Tuffli
1137419d6e4SChuck Tuffli static const uint8_t smart_return_status[] = {
1147419d6e4SChuck Tuffli 0xb0, 0xda, 0x00, 0x4f, 0xc2, 0x00,
1157419d6e4SChuck Tuffli 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1167419d6e4SChuck Tuffli };
1177419d6e4SChuck Tuffli
1187419d6e4SChuck Tuffli static int32_t
__device_read_ata(smart_h h,uint32_t page,void * buf,size_t bsize,union ccb * ccb)1197419d6e4SChuck Tuffli __device_read_ata(smart_h h, uint32_t page, void *buf, size_t bsize, union ccb *ccb)
1207419d6e4SChuck Tuffli {
1217419d6e4SChuck Tuffli struct fbsd_smart *fsmart = h;
1227419d6e4SChuck Tuffli const uint8_t *smart_fis;
1237419d6e4SChuck Tuffli uint32_t smart_fis_size = 0;
124*346be36eSChuck Tuffli uint32_t flags = 0;
1257419d6e4SChuck Tuffli uint16_t sector_count = 0;
1267419d6e4SChuck Tuffli uint8_t protocol = 0;
1277419d6e4SChuck Tuffli
1287419d6e4SChuck Tuffli switch (page) {
1297419d6e4SChuck Tuffli case PAGE_ID_ATA_SMART_READ_DATA: /* Support SMART READ DATA */
1307419d6e4SChuck Tuffli smart_fis = smart_read_data;
1317419d6e4SChuck Tuffli smart_fis_size = sizeof(smart_read_data);
132*346be36eSChuck Tuffli flags = CAM_DIR_IN;
1337419d6e4SChuck Tuffli sector_count = 1;
1347419d6e4SChuck Tuffli protocol = AP_PROTO_PIO_IN;
1357419d6e4SChuck Tuffli break;
1367419d6e4SChuck Tuffli case PAGE_ID_ATA_SMART_RET_STATUS: /* Support SMART RETURN STATUS */
1377419d6e4SChuck Tuffli smart_fis = smart_return_status;
1387419d6e4SChuck Tuffli smart_fis_size = sizeof(smart_return_status);
1397419d6e4SChuck Tuffli /* Command has no data but uses the return status */
140*346be36eSChuck Tuffli flags = CAM_DIR_NONE;
1417419d6e4SChuck Tuffli protocol = AP_PROTO_NON_DATA;
1427419d6e4SChuck Tuffli bsize = 0;
1437419d6e4SChuck Tuffli break;
1447419d6e4SChuck Tuffli default:
1457419d6e4SChuck Tuffli return EINVAL;
1467419d6e4SChuck Tuffli }
1477419d6e4SChuck Tuffli
1487419d6e4SChuck Tuffli if (fsmart->common.info.tunneled) {
1497419d6e4SChuck Tuffli struct ata_pass_16 *cdb;
1507419d6e4SChuck Tuffli uint8_t cdb_flags;
1517419d6e4SChuck Tuffli
1527419d6e4SChuck Tuffli if (bsize > 0) {
1537419d6e4SChuck Tuffli cdb_flags = AP_FLAG_TDIR_FROM_DEV |
1547419d6e4SChuck Tuffli AP_FLAG_BYT_BLOK_BLOCKS |
1557419d6e4SChuck Tuffli AP_FLAG_TLEN_SECT_CNT;
1567419d6e4SChuck Tuffli } else {
1577419d6e4SChuck Tuffli cdb_flags = AP_FLAG_CHK_COND |
1587419d6e4SChuck Tuffli AP_FLAG_TDIR_FROM_DEV |
1597419d6e4SChuck Tuffli AP_FLAG_BYT_BLOK_BLOCKS;
1607419d6e4SChuck Tuffli }
1617419d6e4SChuck Tuffli
1627419d6e4SChuck Tuffli cdb = (struct ata_pass_16 *)ccb->csio.cdb_io.cdb_bytes;
1637419d6e4SChuck Tuffli memset(cdb, 0, sizeof(*cdb));
1647419d6e4SChuck Tuffli
1657419d6e4SChuck Tuffli scsi_ata_pass_16(&ccb->csio,
1667419d6e4SChuck Tuffli /*retries*/ 1,
1677419d6e4SChuck Tuffli /*cbfcnp*/ NULL,
168*346be36eSChuck Tuffli /*flags*/ flags,
1697419d6e4SChuck Tuffli /*tag_action*/ MSG_SIMPLE_Q_TAG,
1707419d6e4SChuck Tuffli /*protocol*/ protocol,
1717419d6e4SChuck Tuffli /*ata_flags*/ cdb_flags,
1727419d6e4SChuck Tuffli /*features*/ page,
1737419d6e4SChuck Tuffli /*sector_count*/sector_count,
1747419d6e4SChuck Tuffli /*lba*/ 0,
1757419d6e4SChuck Tuffli /*command*/ ATA_SMART_CMD,
1767419d6e4SChuck Tuffli /*control*/ 0,
1777419d6e4SChuck Tuffli /*data_ptr*/ buf,
1787419d6e4SChuck Tuffli /*dxfer_len*/ bsize,
1797419d6e4SChuck Tuffli /*sense_len*/ SSD_FULL_SIZE,
1807419d6e4SChuck Tuffli /*timeout*/ 5000
1817419d6e4SChuck Tuffli );
1827419d6e4SChuck Tuffli cdb->lba_mid = 0x4f;
1837419d6e4SChuck Tuffli cdb->lba_high = 0xc2;
1847419d6e4SChuck Tuffli cdb->device = 0; /* scsi_ata_pass_16() sets this */
1857419d6e4SChuck Tuffli } else {
1867419d6e4SChuck Tuffli memcpy(&ccb->ataio.cmd.command, smart_fis, smart_fis_size);
1877419d6e4SChuck Tuffli
1887419d6e4SChuck Tuffli cam_fill_ataio(&ccb->ataio,
1897419d6e4SChuck Tuffli /* retries */1,
1907419d6e4SChuck Tuffli /* cbfcnp */NULL,
191*346be36eSChuck Tuffli /* flags */flags,
1927419d6e4SChuck Tuffli /* tag_action */0,
1937419d6e4SChuck Tuffli /* data_ptr */buf,
1947419d6e4SChuck Tuffli /* dxfer_len */bsize,
1957419d6e4SChuck Tuffli /* timeout */5000);
1967419d6e4SChuck Tuffli ccb->ataio.cmd.flags |= CAM_ATAIO_NEEDRESULT;
1977419d6e4SChuck Tuffli ccb->ataio.cmd.control = 0;
1987419d6e4SChuck Tuffli }
1997419d6e4SChuck Tuffli
2007419d6e4SChuck Tuffli return 0;
2017419d6e4SChuck Tuffli }
2027419d6e4SChuck Tuffli
2037419d6e4SChuck Tuffli static int32_t
__device_read_scsi(smart_h h,uint32_t page,void * buf,size_t bsize,union ccb * ccb)2047419d6e4SChuck Tuffli __device_read_scsi(__attribute__((unused)) smart_h h, uint32_t page, void *buf, size_t bsize, union ccb *ccb)
2057419d6e4SChuck Tuffli {
2067419d6e4SChuck Tuffli
2077419d6e4SChuck Tuffli scsi_log_sense(&ccb->csio,
2087419d6e4SChuck Tuffli /* retries */1,
2097419d6e4SChuck Tuffli /* cbfcnp */NULL,
2107419d6e4SChuck Tuffli /* tag_action */0,
2117419d6e4SChuck Tuffli /* page_code */SLS_PAGE_CTRL_CUMULATIVE,
2127419d6e4SChuck Tuffli /* page */page,
2137419d6e4SChuck Tuffli /* save_pages */0,
2147419d6e4SChuck Tuffli /* ppc */0,
2157419d6e4SChuck Tuffli /* paramptr */0,
2167419d6e4SChuck Tuffli /* param_buf */buf,
2177419d6e4SChuck Tuffli /* param_len */bsize,
2187419d6e4SChuck Tuffli /* sense_len */0,
2197419d6e4SChuck Tuffli /* timeout */5000);
2207419d6e4SChuck Tuffli
2217419d6e4SChuck Tuffli return 0;
2227419d6e4SChuck Tuffli }
2237419d6e4SChuck Tuffli
2247419d6e4SChuck Tuffli static int32_t
__device_read_nvme(smart_h h,uint32_t page,void * buf,size_t bsize,union ccb * ccb)2257419d6e4SChuck Tuffli __device_read_nvme(__attribute__((unused)) smart_h h, uint32_t page, void *buf, size_t bsize, union ccb *ccb)
2267419d6e4SChuck Tuffli {
2277419d6e4SChuck Tuffli struct ccb_nvmeio *nvmeio = &ccb->nvmeio;
2287419d6e4SChuck Tuffli uint32_t numd = 0; /* number of dwords */
2297419d6e4SChuck Tuffli
2307419d6e4SChuck Tuffli /*
2317419d6e4SChuck Tuffli * NVME CAM passthru
2327419d6e4SChuck Tuffli * 1200000 > version > 1101510 uses nvmeio->cmd.opc
2337419d6e4SChuck Tuffli * 1200059 > version > 1200038 uses nvmeio->cmd.opc
2347419d6e4SChuck Tuffli * 1200081 > version > 1200058 uses nvmeio->cmd.opc_fuse
2357419d6e4SChuck Tuffli * > 1200080 uses nvmeio->cmd.opc
2367419d6e4SChuck Tuffli * This code doesn't support the brief 'opc_fuse' period.
2377419d6e4SChuck Tuffli */
2387419d6e4SChuck Tuffli #if ((__FreeBSD_version > 1200038) || ((__FreeBSD_version > 1101510) && (__FreeBSD_version < 1200000)))
2397419d6e4SChuck Tuffli switch (page) {
2407419d6e4SChuck Tuffli case NVME_LOG_HEALTH_INFORMATION:
2417419d6e4SChuck Tuffli numd = (sizeof(struct nvme_health_information_page) / sizeof(uint32_t));
2427419d6e4SChuck Tuffli break;
2437419d6e4SChuck Tuffli default:
2447419d6e4SChuck Tuffli /* Unsupported log page */
2457419d6e4SChuck Tuffli return EINVAL;
2467419d6e4SChuck Tuffli }
2477419d6e4SChuck Tuffli
2487419d6e4SChuck Tuffli /* Subtract 1 because NUMD is a zero based value */
2497419d6e4SChuck Tuffli numd--;
2507419d6e4SChuck Tuffli
2517419d6e4SChuck Tuffli nvmeio->cmd.opc = NVME_OPC_GET_LOG_PAGE;
2527419d6e4SChuck Tuffli nvmeio->cmd.nsid = NVME_GLOBAL_NAMESPACE_TAG;
2537419d6e4SChuck Tuffli nvmeio->cmd.cdw10 = page | (numd << 16);
2547419d6e4SChuck Tuffli
2557419d6e4SChuck Tuffli cam_fill_nvmeadmin(&ccb->nvmeio,
2567419d6e4SChuck Tuffli /* retries */1,
2577419d6e4SChuck Tuffli /* cbfcnp */NULL,
2587419d6e4SChuck Tuffli /* flags */CAM_DIR_IN,
2597419d6e4SChuck Tuffli /* data_ptr */buf,
2607419d6e4SChuck Tuffli /* dxfer_len */bsize,
2617419d6e4SChuck Tuffli /* timeout */5000);
2627419d6e4SChuck Tuffli #endif
2637419d6e4SChuck Tuffli return 0;
2647419d6e4SChuck Tuffli }
2657419d6e4SChuck Tuffli
2667419d6e4SChuck Tuffli /*
2677419d6e4SChuck Tuffli * Retrieve the SMART RETURN STATUS
2687419d6e4SChuck Tuffli *
2697419d6e4SChuck Tuffli * SMART RETURN STATUS provides the reliability status of the
2707419d6e4SChuck Tuffli * device and can be used as a high-level indication of health.
2717419d6e4SChuck Tuffli */
2727419d6e4SChuck Tuffli static int32_t
__device_status_ata(smart_h h,union ccb * ccb)2737419d6e4SChuck Tuffli __device_status_ata(smart_h h, union ccb *ccb)
2747419d6e4SChuck Tuffli {
2757419d6e4SChuck Tuffli struct fbsd_smart *fsmart = h;
2767419d6e4SChuck Tuffli uint8_t *buf = NULL;
2777419d6e4SChuck Tuffli uint32_t page = 0;
2787419d6e4SChuck Tuffli uint8_t lba_high = 0, lba_mid = 0, device = 0, status = 0;
2797419d6e4SChuck Tuffli
2807419d6e4SChuck Tuffli if (fsmart->common.info.tunneled) {
2817419d6e4SChuck Tuffli struct ata_res_pass16 {
2827419d6e4SChuck Tuffli u_int16_t reserved[5];
2837419d6e4SChuck Tuffli u_int8_t flags;
2847419d6e4SChuck Tuffli u_int8_t error;
2857419d6e4SChuck Tuffli u_int8_t sector_count_exp;
2867419d6e4SChuck Tuffli u_int8_t sector_count;
2877419d6e4SChuck Tuffli u_int8_t lba_low_exp;
2887419d6e4SChuck Tuffli u_int8_t lba_low;
2897419d6e4SChuck Tuffli u_int8_t lba_mid_exp;
2907419d6e4SChuck Tuffli u_int8_t lba_mid;
2917419d6e4SChuck Tuffli u_int8_t lba_high_exp;
2927419d6e4SChuck Tuffli u_int8_t lba_high;
2937419d6e4SChuck Tuffli u_int8_t device;
2947419d6e4SChuck Tuffli u_int8_t status;
2957419d6e4SChuck Tuffli } *res_pass16 = (struct ata_res_pass16 *)(uintptr_t)
2967419d6e4SChuck Tuffli &ccb->csio.sense_data;
2977419d6e4SChuck Tuffli
2987419d6e4SChuck Tuffli buf = ccb->csio.data_ptr;
2997419d6e4SChuck Tuffli page = ((struct ata_pass_16 *)ccb->csio.cdb_io.cdb_bytes)->features;
3007419d6e4SChuck Tuffli lba_high = res_pass16->lba_high;
3017419d6e4SChuck Tuffli lba_mid = res_pass16->lba_mid;
3027419d6e4SChuck Tuffli device = res_pass16->device;
3037419d6e4SChuck Tuffli status = res_pass16->status;
3047419d6e4SChuck Tuffli
3057419d6e4SChuck Tuffli /*
3067419d6e4SChuck Tuffli * Note that this generates an expected CHECK CONDITION.
3077419d6e4SChuck Tuffli * Mask it so the outer function doesn't print an error
3087419d6e4SChuck Tuffli * message.
3097419d6e4SChuck Tuffli */
3107419d6e4SChuck Tuffli ccb->ccb_h.status &= ~CAM_STATUS_MASK;
3117419d6e4SChuck Tuffli ccb->ccb_h.status |= CAM_REQ_CMP;
3127419d6e4SChuck Tuffli } else {
3137419d6e4SChuck Tuffli struct ccb_ataio *ataio = (struct ccb_ataio *)&ccb->ataio;
3147419d6e4SChuck Tuffli
3157419d6e4SChuck Tuffli buf = ataio->data_ptr;
3167419d6e4SChuck Tuffli page = ataio->cmd.features;
3177419d6e4SChuck Tuffli lba_high = ataio->res.lba_high;
3187419d6e4SChuck Tuffli lba_mid = ataio->res.lba_mid;
3197419d6e4SChuck Tuffli device = ataio->res.device;
3207419d6e4SChuck Tuffli status = ataio->res.status;
3217419d6e4SChuck Tuffli }
3227419d6e4SChuck Tuffli
3237419d6e4SChuck Tuffli switch (page) {
3247419d6e4SChuck Tuffli case PAGE_ID_ATA_SMART_RET_STATUS:
3257419d6e4SChuck Tuffli /*
3267419d6e4SChuck Tuffli * Typically, SMART related log pages return data, but this
3277419d6e4SChuck Tuffli * command is different in that the data is encoded in the
3287419d6e4SChuck Tuffli * result registers.
3297419d6e4SChuck Tuffli *
3307419d6e4SChuck Tuffli * Handle this in a UNIX-like way by writing a 0 (no errors)
3317419d6e4SChuck Tuffli * or 1 (threshold exceeded condition) to the output buffer.
3327419d6e4SChuck Tuffli */
3337419d6e4SChuck Tuffli dprintf("SMART_RET_STATUS: lba mid=%#x high=%#x device=%#x status=%#x\n",
3347419d6e4SChuck Tuffli lba_mid,
3357419d6e4SChuck Tuffli lba_high,
3367419d6e4SChuck Tuffli device,
3377419d6e4SChuck Tuffli status);
3387419d6e4SChuck Tuffli if ((lba_high == 0x2c) && (lba_mid == 0xf4)) {
3397419d6e4SChuck Tuffli buf[0] = 1;
3407419d6e4SChuck Tuffli } else if ((lba_high == 0xc2) && (lba_mid == 0x4f)) {
3417419d6e4SChuck Tuffli buf[0] = 0;
3427419d6e4SChuck Tuffli } else {
3437419d6e4SChuck Tuffli /* Ruh-roh ... */
3447419d6e4SChuck Tuffli buf[0] = 255;
3457419d6e4SChuck Tuffli }
3467419d6e4SChuck Tuffli break;
3477419d6e4SChuck Tuffli default:
3487419d6e4SChuck Tuffli ;
3497419d6e4SChuck Tuffli }
3507419d6e4SChuck Tuffli
3517419d6e4SChuck Tuffli return 0;
3527419d6e4SChuck Tuffli }
3537419d6e4SChuck Tuffli
3547419d6e4SChuck Tuffli int32_t
device_read_log(smart_h h,uint32_t page,void * buf,size_t bsize)3557419d6e4SChuck Tuffli device_read_log(smart_h h, uint32_t page, void *buf, size_t bsize)
3567419d6e4SChuck Tuffli {
3577419d6e4SChuck Tuffli struct fbsd_smart *fsmart = h;
3587419d6e4SChuck Tuffli union ccb *ccb = NULL;
3597419d6e4SChuck Tuffli int rc = 0;
3607419d6e4SChuck Tuffli
3617419d6e4SChuck Tuffli if (fsmart == NULL)
3627419d6e4SChuck Tuffli return EINVAL;
3637419d6e4SChuck Tuffli
3647419d6e4SChuck Tuffli dprintf("read log page %#x\n", page);
3657419d6e4SChuck Tuffli
3667419d6e4SChuck Tuffli ccb = cam_getccb(fsmart->camdev);
3677419d6e4SChuck Tuffli if (ccb == NULL)
3687419d6e4SChuck Tuffli return ENOMEM;
3697419d6e4SChuck Tuffli
3707419d6e4SChuck Tuffli CCB_CLEAR_ALL_EXCEPT_HDR(ccb);
3717419d6e4SChuck Tuffli
3727419d6e4SChuck Tuffli switch (fsmart->common.protocol) {
3737419d6e4SChuck Tuffli case SMART_PROTO_ATA:
3747419d6e4SChuck Tuffli rc = __device_read_ata(h, page, buf, bsize, ccb);
3757419d6e4SChuck Tuffli break;
3767419d6e4SChuck Tuffli case SMART_PROTO_SCSI:
3777419d6e4SChuck Tuffli rc = __device_read_scsi(h, page, buf, bsize, ccb);
3787419d6e4SChuck Tuffli break;
3797419d6e4SChuck Tuffli case SMART_PROTO_NVME:
3807419d6e4SChuck Tuffli rc = __device_read_nvme(h, page, buf, bsize, ccb);
3817419d6e4SChuck Tuffli break;
3827419d6e4SChuck Tuffli default:
3837419d6e4SChuck Tuffli warnx("unsupported protocol %d", fsmart->common.protocol);
3847419d6e4SChuck Tuffli cam_freeccb(ccb);
3857419d6e4SChuck Tuffli return ENODEV;
3867419d6e4SChuck Tuffli }
3877419d6e4SChuck Tuffli
3887419d6e4SChuck Tuffli if (rc) {
3897419d6e4SChuck Tuffli if (rc == EINVAL)
3907419d6e4SChuck Tuffli warnx("unsupported page %#x", page);
3917419d6e4SChuck Tuffli
3927419d6e4SChuck Tuffli return rc;
3937419d6e4SChuck Tuffli }
3947419d6e4SChuck Tuffli
3957419d6e4SChuck Tuffli if (((rc = cam_send_ccb(fsmart->camdev, ccb)) < 0)
3967419d6e4SChuck Tuffli || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
3977419d6e4SChuck Tuffli if (rc < 0)
3987419d6e4SChuck Tuffli warn("error sending command");
3997419d6e4SChuck Tuffli }
4007419d6e4SChuck Tuffli
4017419d6e4SChuck Tuffli /*
4027419d6e4SChuck Tuffli * Most commands don't need any post-processing. But then there's
4037419d6e4SChuck Tuffli * ATA. It's why we can't have nice things :(
4047419d6e4SChuck Tuffli */
4057419d6e4SChuck Tuffli switch (fsmart->common.protocol) {
4067419d6e4SChuck Tuffli case SMART_PROTO_ATA:
4077419d6e4SChuck Tuffli __device_status_ata(h, ccb);
4087419d6e4SChuck Tuffli break;
4097419d6e4SChuck Tuffli default:
4107419d6e4SChuck Tuffli ;
4117419d6e4SChuck Tuffli }
4127419d6e4SChuck Tuffli
4137419d6e4SChuck Tuffli if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
4147419d6e4SChuck Tuffli cam_error_print(fsmart->camdev, ccb, CAM_ESF_ALL,
4157419d6e4SChuck Tuffli CAM_EPF_ALL, stderr);
4167419d6e4SChuck Tuffli }
4177419d6e4SChuck Tuffli
4187419d6e4SChuck Tuffli cam_freeccb(ccb);
4197419d6e4SChuck Tuffli
4207419d6e4SChuck Tuffli return 0;
4217419d6e4SChuck Tuffli }
4227419d6e4SChuck Tuffli
4237419d6e4SChuck Tuffli /*
4247419d6e4SChuck Tuffli * The SCSI / ATA Translation (SAT) requires devices to support the ATA
4257419d6e4SChuck Tuffli * Information VPD Page (T10/2126-D Revision 04). Use the existence of
4267419d6e4SChuck Tuffli * this page to identify tunneled devices.
4277419d6e4SChuck Tuffli */
4287419d6e4SChuck Tuffli static bool
__device_proto_tunneled(struct fbsd_smart * fsmart)4297419d6e4SChuck Tuffli __device_proto_tunneled(struct fbsd_smart *fsmart)
4307419d6e4SChuck Tuffli {
4317419d6e4SChuck Tuffli union ccb *ccb = NULL;
4327419d6e4SChuck Tuffli struct scsi_vpd_supported_page_list supportedp;
4337419d6e4SChuck Tuffli uint32_t i;
4347419d6e4SChuck Tuffli bool is_tunneled = false;
4357419d6e4SChuck Tuffli
4367419d6e4SChuck Tuffli if (fsmart->common.protocol != SMART_PROTO_SCSI) {
4377419d6e4SChuck Tuffli return false;
4387419d6e4SChuck Tuffli }
4397419d6e4SChuck Tuffli
4407419d6e4SChuck Tuffli ccb = cam_getccb(fsmart->camdev);
4417419d6e4SChuck Tuffli if (!ccb) {
4427419d6e4SChuck Tuffli warn("Allocation failure ccb=%p", ccb);
4437419d6e4SChuck Tuffli goto __device_proto_tunneled_out;
4447419d6e4SChuck Tuffli }
4457419d6e4SChuck Tuffli
4467419d6e4SChuck Tuffli scsi_inquiry(&ccb->csio,
4477419d6e4SChuck Tuffli 3, // retries
4487419d6e4SChuck Tuffli NULL, // callback function
4497419d6e4SChuck Tuffli MSG_SIMPLE_Q_TAG, // tag action
4507419d6e4SChuck Tuffli (uint8_t *)&supportedp,
4517419d6e4SChuck Tuffli sizeof(struct scsi_vpd_supported_page_list),
4527419d6e4SChuck Tuffli 1, // EVPD
4537419d6e4SChuck Tuffli SVPD_SUPPORTED_PAGE_LIST, // page code
4547419d6e4SChuck Tuffli SSD_FULL_SIZE, // sense length
4557419d6e4SChuck Tuffli 5000); // timeout
4567419d6e4SChuck Tuffli
4577419d6e4SChuck Tuffli ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
4587419d6e4SChuck Tuffli
4597419d6e4SChuck Tuffli if ((cam_send_ccb(fsmart->camdev, ccb) >= 0) &&
4607419d6e4SChuck Tuffli ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) {
4617419d6e4SChuck Tuffli dprintf("Looking for page %#x (total = %u):\n", SVPD_ATA_INFORMATION,
4627419d6e4SChuck Tuffli supportedp.length);
4637419d6e4SChuck Tuffli for (i = 0; i < supportedp.length; i++) {
4647419d6e4SChuck Tuffli dprintf("\t[%u] = %#x\n", i, supportedp.list[i]);
4657419d6e4SChuck Tuffli if (supportedp.list[i] == SVPD_ATA_INFORMATION) {
4667419d6e4SChuck Tuffli is_tunneled = true;
4677419d6e4SChuck Tuffli break;
4687419d6e4SChuck Tuffli }
4697419d6e4SChuck Tuffli }
4707419d6e4SChuck Tuffli }
4717419d6e4SChuck Tuffli
4727419d6e4SChuck Tuffli cam_freeccb(ccb);
4737419d6e4SChuck Tuffli
4747419d6e4SChuck Tuffli __device_proto_tunneled_out:
4757419d6e4SChuck Tuffli return is_tunneled;
4767419d6e4SChuck Tuffli }
4777419d6e4SChuck Tuffli
4787419d6e4SChuck Tuffli /**
4797419d6e4SChuck Tuffli * Retrieve the device protocol type via the transport settings
4807419d6e4SChuck Tuffli *
4817419d6e4SChuck Tuffli * @return protocol type or SMART_PROTO_MAX on error
4827419d6e4SChuck Tuffli */
4837419d6e4SChuck Tuffli static smart_protocol_e
__device_get_proto(struct fbsd_smart * fsmart)4847419d6e4SChuck Tuffli __device_get_proto(struct fbsd_smart *fsmart)
4857419d6e4SChuck Tuffli {
4867419d6e4SChuck Tuffli smart_protocol_e proto = SMART_PROTO_MAX;
4877419d6e4SChuck Tuffli union ccb *ccb;
4887419d6e4SChuck Tuffli
4897419d6e4SChuck Tuffli if (!fsmart || !fsmart->camdev) {
4907419d6e4SChuck Tuffli warn("Bad handle %p", fsmart);
4917419d6e4SChuck Tuffli return proto;
4927419d6e4SChuck Tuffli }
4937419d6e4SChuck Tuffli
4947419d6e4SChuck Tuffli ccb = cam_getccb(fsmart->camdev);
4957419d6e4SChuck Tuffli if (ccb != NULL) {
4967419d6e4SChuck Tuffli CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cts);
4977419d6e4SChuck Tuffli
4987419d6e4SChuck Tuffli ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
4997419d6e4SChuck Tuffli ccb->cts.type = CTS_TYPE_CURRENT_SETTINGS;
5007419d6e4SChuck Tuffli
5017419d6e4SChuck Tuffli if (cam_send_ccb(fsmart->camdev, ccb) >= 0) {
5027419d6e4SChuck Tuffli if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
5037419d6e4SChuck Tuffli struct ccb_trans_settings *cts = &ccb->cts;
5047419d6e4SChuck Tuffli
5057419d6e4SChuck Tuffli switch (cts->protocol) {
5067419d6e4SChuck Tuffli case PROTO_ATA:
5077419d6e4SChuck Tuffli proto = SMART_PROTO_ATA;
5087419d6e4SChuck Tuffli break;
5097419d6e4SChuck Tuffli case PROTO_SCSI:
5107419d6e4SChuck Tuffli proto = SMART_PROTO_SCSI;
5117419d6e4SChuck Tuffli break;
5127419d6e4SChuck Tuffli case PROTO_NVME:
5137419d6e4SChuck Tuffli proto = SMART_PROTO_NVME;
5147419d6e4SChuck Tuffli break;
5157419d6e4SChuck Tuffli default:
5167419d6e4SChuck Tuffli printf("%s: unknown protocol %d\n",
5177419d6e4SChuck Tuffli __func__,
5187419d6e4SChuck Tuffli cts->protocol);
5197419d6e4SChuck Tuffli }
5207419d6e4SChuck Tuffli }
5217419d6e4SChuck Tuffli }
5227419d6e4SChuck Tuffli
5237419d6e4SChuck Tuffli cam_freeccb(ccb);
5247419d6e4SChuck Tuffli }
5257419d6e4SChuck Tuffli
5267419d6e4SChuck Tuffli return proto;
5277419d6e4SChuck Tuffli }
5287419d6e4SChuck Tuffli
5297419d6e4SChuck Tuffli static int32_t
__device_info_ata(struct fbsd_smart * fsmart,struct ccb_getdev * cgd)5307419d6e4SChuck Tuffli __device_info_ata(struct fbsd_smart *fsmart, struct ccb_getdev *cgd)
5317419d6e4SChuck Tuffli {
5327419d6e4SChuck Tuffli smart_info_t *sinfo = NULL;
5337419d6e4SChuck Tuffli
5347419d6e4SChuck Tuffli if (!fsmart || !cgd) {
5357419d6e4SChuck Tuffli return -1;
5367419d6e4SChuck Tuffli }
5377419d6e4SChuck Tuffli
5387419d6e4SChuck Tuffli sinfo = &fsmart->common.info;
5397419d6e4SChuck Tuffli
5407419d6e4SChuck Tuffli sinfo->supported = cgd->ident_data.support.command1 &
5417419d6e4SChuck Tuffli ATA_SUPPORT_SMART;
5427419d6e4SChuck Tuffli
5437419d6e4SChuck Tuffli dprintf("ATA command1 = %#x\n", cgd->ident_data.support.command1);
5447419d6e4SChuck Tuffli
5457419d6e4SChuck Tuffli cam_strvis((uint8_t *)sinfo->device, cgd->ident_data.model,
5467419d6e4SChuck Tuffli sizeof(cgd->ident_data.model),
5477419d6e4SChuck Tuffli sizeof(sinfo->device));
5487419d6e4SChuck Tuffli cam_strvis((uint8_t *)sinfo->rev, cgd->ident_data.revision,
5497419d6e4SChuck Tuffli sizeof(cgd->ident_data.revision),
5507419d6e4SChuck Tuffli sizeof(sinfo->rev));
5517419d6e4SChuck Tuffli cam_strvis((uint8_t *)sinfo->serial, cgd->ident_data.serial,
5527419d6e4SChuck Tuffli sizeof(cgd->ident_data.serial),
5537419d6e4SChuck Tuffli sizeof(sinfo->serial));
5547419d6e4SChuck Tuffli
5557419d6e4SChuck Tuffli return 0;
5567419d6e4SChuck Tuffli }
5577419d6e4SChuck Tuffli
5587419d6e4SChuck Tuffli static int32_t
__device_info_scsi(struct fbsd_smart * fsmart,struct ccb_getdev * cgd)5597419d6e4SChuck Tuffli __device_info_scsi(struct fbsd_smart *fsmart, struct ccb_getdev *cgd)
5607419d6e4SChuck Tuffli {
5617419d6e4SChuck Tuffli smart_info_t *sinfo = NULL;
5627419d6e4SChuck Tuffli union ccb *ccb = NULL;
5637419d6e4SChuck Tuffli struct scsi_vpd_unit_serial_number *snum = NULL;
5647419d6e4SChuck Tuffli struct scsi_log_informational_exceptions ie = {0};
5657419d6e4SChuck Tuffli
5667419d6e4SChuck Tuffli if (!fsmart || !cgd) {
5677419d6e4SChuck Tuffli return -1;
5687419d6e4SChuck Tuffli }
5697419d6e4SChuck Tuffli
5707419d6e4SChuck Tuffli sinfo = &fsmart->common.info;
5717419d6e4SChuck Tuffli
5727419d6e4SChuck Tuffli cam_strvis((uint8_t *)sinfo->vendor, (uint8_t *)cgd->inq_data.vendor,
5737419d6e4SChuck Tuffli sizeof(cgd->inq_data.vendor),
5747419d6e4SChuck Tuffli sizeof(sinfo->vendor));
5757419d6e4SChuck Tuffli cam_strvis((uint8_t *)sinfo->device, (uint8_t *)cgd->inq_data.product,
5767419d6e4SChuck Tuffli sizeof(cgd->inq_data.product),
5777419d6e4SChuck Tuffli sizeof(sinfo->device));
5787419d6e4SChuck Tuffli cam_strvis((uint8_t *)sinfo->rev, (uint8_t *)cgd->inq_data.revision,
5797419d6e4SChuck Tuffli sizeof(cgd->inq_data.revision),
5807419d6e4SChuck Tuffli sizeof(sinfo->rev));
5817419d6e4SChuck Tuffli
5827419d6e4SChuck Tuffli ccb = cam_getccb(fsmart->camdev);
5837419d6e4SChuck Tuffli snum = malloc(sizeof(struct scsi_vpd_unit_serial_number));
5847419d6e4SChuck Tuffli if (!ccb || !snum) {
5857419d6e4SChuck Tuffli warn("Allocation failure ccb=%p snum=%p", ccb, snum);
5867419d6e4SChuck Tuffli goto __device_info_scsi_out;
5877419d6e4SChuck Tuffli }
5887419d6e4SChuck Tuffli
5897419d6e4SChuck Tuffli /* Get the serial number */
5907419d6e4SChuck Tuffli CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
5917419d6e4SChuck Tuffli
5927419d6e4SChuck Tuffli scsi_inquiry(&ccb->csio,
5937419d6e4SChuck Tuffli 3, // retries
5947419d6e4SChuck Tuffli NULL, // callback function
5957419d6e4SChuck Tuffli MSG_SIMPLE_Q_TAG, // tag action
5967419d6e4SChuck Tuffli (uint8_t *)snum,
5977419d6e4SChuck Tuffli sizeof(struct scsi_vpd_unit_serial_number),
5987419d6e4SChuck Tuffli 1, // EVPD
5997419d6e4SChuck Tuffli SVPD_UNIT_SERIAL_NUMBER, // page code
6007419d6e4SChuck Tuffli SSD_FULL_SIZE, // sense length
6017419d6e4SChuck Tuffli 5000); // timeout
6027419d6e4SChuck Tuffli
6037419d6e4SChuck Tuffli ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
6047419d6e4SChuck Tuffli
6057419d6e4SChuck Tuffli if ((cam_send_ccb(fsmart->camdev, ccb) >= 0) &&
6067419d6e4SChuck Tuffli ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) {
6077419d6e4SChuck Tuffli cam_strvis((uint8_t *)sinfo->serial, snum->serial_num,
6087419d6e4SChuck Tuffli snum->length,
6097419d6e4SChuck Tuffli sizeof(sinfo->serial));
6107419d6e4SChuck Tuffli sinfo->serial[sizeof(sinfo->serial) - 1] = '\0';
6117419d6e4SChuck Tuffli }
6127419d6e4SChuck Tuffli
6137419d6e4SChuck Tuffli memset(ccb, 0, sizeof(*ccb));
6147419d6e4SChuck Tuffli
6157419d6e4SChuck Tuffli scsi_log_sense(&ccb->csio,
6167419d6e4SChuck Tuffli /* retries */1,
6177419d6e4SChuck Tuffli /* cbfcnp */NULL,
6187419d6e4SChuck Tuffli /* tag_action */0,
6197419d6e4SChuck Tuffli /* page_code */SLS_PAGE_CTRL_CUMULATIVE,
6207419d6e4SChuck Tuffli /* page */SLS_IE_PAGE,
6217419d6e4SChuck Tuffli /* save_pages */0,
6227419d6e4SChuck Tuffli /* ppc */0,
6237419d6e4SChuck Tuffli /* paramptr */0,
6247419d6e4SChuck Tuffli /* param_buf */(uint8_t *)&ie,
6257419d6e4SChuck Tuffli /* param_len */sizeof(ie),
6267419d6e4SChuck Tuffli /* sense_len */0,
6277419d6e4SChuck Tuffli /* timeout */5000);
6287419d6e4SChuck Tuffli
6297419d6e4SChuck Tuffli /*
6307419d6e4SChuck Tuffli * Note: The existance of the Informational Exceptions (IE) log page
6317419d6e4SChuck Tuffli * appears to be the litmus test for SMART support in SCSI
6327419d6e4SChuck Tuffli * devices. Confusingly, smartctl will report SMART health
6337419d6e4SChuck Tuffli * status as 'OK' if the device doesn't support the IE page.
6347419d6e4SChuck Tuffli * For now, just report the facts.
6357419d6e4SChuck Tuffli */
6367419d6e4SChuck Tuffli if ((cam_send_ccb(fsmart->camdev, ccb) >= 0) &&
6377419d6e4SChuck Tuffli ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) {
6387419d6e4SChuck Tuffli if ((ie.hdr.param_len < 4) || ie.ie_asc || ie.ie_ascq) {
6397419d6e4SChuck Tuffli printf("Log Sense, Informational Exceptions failed "
6407419d6e4SChuck Tuffli "(length=%u asc=%#x ascq=%#x)\n",
6417419d6e4SChuck Tuffli ie.hdr.param_len, ie.ie_asc, ie.ie_ascq);
6427419d6e4SChuck Tuffli } else {
6437419d6e4SChuck Tuffli sinfo->supported = true;
6447419d6e4SChuck Tuffli }
6457419d6e4SChuck Tuffli }
6467419d6e4SChuck Tuffli
6477419d6e4SChuck Tuffli __device_info_scsi_out:
6487419d6e4SChuck Tuffli free(snum);
6497419d6e4SChuck Tuffli if (ccb)
6507419d6e4SChuck Tuffli cam_freeccb(ccb);
6517419d6e4SChuck Tuffli
6527419d6e4SChuck Tuffli return 0;
6537419d6e4SChuck Tuffli }
6547419d6e4SChuck Tuffli
6557419d6e4SChuck Tuffli static int32_t
__device_info_nvme(struct fbsd_smart * fsmart,struct ccb_getdev * cgd)6567419d6e4SChuck Tuffli __device_info_nvme(struct fbsd_smart *fsmart, struct ccb_getdev *cgd)
6577419d6e4SChuck Tuffli {
6587419d6e4SChuck Tuffli union ccb *ccb;
6597419d6e4SChuck Tuffli smart_info_t *sinfo = NULL;
6607419d6e4SChuck Tuffli struct nvme_controller_data cd;
6617419d6e4SChuck Tuffli
6627419d6e4SChuck Tuffli if (!fsmart || !cgd) {
6637419d6e4SChuck Tuffli return -1;
6647419d6e4SChuck Tuffli }
6657419d6e4SChuck Tuffli
6667419d6e4SChuck Tuffli sinfo = &fsmart->common.info;
6677419d6e4SChuck Tuffli
6687419d6e4SChuck Tuffli sinfo->supported = true;
6697419d6e4SChuck Tuffli
6707419d6e4SChuck Tuffli ccb = cam_getccb(fsmart->camdev);
6717419d6e4SChuck Tuffli if (ccb != NULL) {
6727419d6e4SChuck Tuffli struct ccb_dev_advinfo *cdai = &ccb->cdai;
6737419d6e4SChuck Tuffli
6747419d6e4SChuck Tuffli CCB_CLEAR_ALL_EXCEPT_HDR(cdai);
6757419d6e4SChuck Tuffli
6767419d6e4SChuck Tuffli cdai->ccb_h.func_code = XPT_DEV_ADVINFO;
6777419d6e4SChuck Tuffli cdai->ccb_h.flags = CAM_DIR_IN;
6787419d6e4SChuck Tuffli cdai->flags = CDAI_FLAG_NONE;
6797419d6e4SChuck Tuffli #ifdef CDAI_TYPE_NVME_CNTRL
6807419d6e4SChuck Tuffli cdai->buftype = CDAI_TYPE_NVME_CNTRL;
6817419d6e4SChuck Tuffli #else
6827419d6e4SChuck Tuffli cdai->buftype = 6;
6837419d6e4SChuck Tuffli #endif
6847419d6e4SChuck Tuffli cdai->bufsiz = sizeof(struct nvme_controller_data);
6857419d6e4SChuck Tuffli cdai->buf = (uint8_t *)&cd;
6867419d6e4SChuck Tuffli
6877419d6e4SChuck Tuffli if (cam_send_ccb(fsmart->camdev, ccb) >= 0) {
6887419d6e4SChuck Tuffli if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
6897419d6e4SChuck Tuffli cam_strvis((uint8_t *)sinfo->device, cd.mn,
6907419d6e4SChuck Tuffli sizeof(cd.mn),
6917419d6e4SChuck Tuffli sizeof(sinfo->device));
6927419d6e4SChuck Tuffli cam_strvis((uint8_t *)sinfo->rev, cd.fr,
6937419d6e4SChuck Tuffli sizeof(cd.fr),
6947419d6e4SChuck Tuffli sizeof(sinfo->rev));
6957419d6e4SChuck Tuffli cam_strvis((uint8_t *)sinfo->serial, cd.sn,
6967419d6e4SChuck Tuffli sizeof(cd.sn),
6977419d6e4SChuck Tuffli sizeof(sinfo->serial));
6987419d6e4SChuck Tuffli }
6997419d6e4SChuck Tuffli }
7007419d6e4SChuck Tuffli
7017419d6e4SChuck Tuffli cam_freeccb(ccb);
7027419d6e4SChuck Tuffli }
7037419d6e4SChuck Tuffli
7047419d6e4SChuck Tuffli return 0;
7057419d6e4SChuck Tuffli }
7067419d6e4SChuck Tuffli
7077419d6e4SChuck Tuffli static int32_t
__device_info_tunneled_ata(struct fbsd_smart * fsmart)7087419d6e4SChuck Tuffli __device_info_tunneled_ata(struct fbsd_smart *fsmart)
7097419d6e4SChuck Tuffli {
7107419d6e4SChuck Tuffli struct ata_params ident_data;
7117419d6e4SChuck Tuffli union ccb *ccb = NULL;
7127419d6e4SChuck Tuffli int32_t rc = -1;
7137419d6e4SChuck Tuffli
7147419d6e4SChuck Tuffli ccb = cam_getccb(fsmart->camdev);
7157419d6e4SChuck Tuffli if (ccb == NULL) {
7167419d6e4SChuck Tuffli goto __device_info_tunneled_ata_out;
7177419d6e4SChuck Tuffli }
7187419d6e4SChuck Tuffli
7197419d6e4SChuck Tuffli memset(&ident_data, 0, sizeof(struct ata_params));
7207419d6e4SChuck Tuffli
7217419d6e4SChuck Tuffli CCB_CLEAR_ALL_EXCEPT_HDR(ccb);
7227419d6e4SChuck Tuffli
7237419d6e4SChuck Tuffli scsi_ata_pass_16(&ccb->csio,
7247419d6e4SChuck Tuffli /*retries*/ 1,
7257419d6e4SChuck Tuffli /*cbfcnp*/ NULL,
7267419d6e4SChuck Tuffli /*flags*/ CAM_DIR_IN,
7277419d6e4SChuck Tuffli /*tag_action*/ MSG_SIMPLE_Q_TAG,
7287419d6e4SChuck Tuffli /*protocol*/ AP_PROTO_PIO_IN,
7297419d6e4SChuck Tuffli /*ata_flags*/ AP_FLAG_TLEN_SECT_CNT |
7307419d6e4SChuck Tuffli AP_FLAG_BYT_BLOK_BLOCKS |
7317419d6e4SChuck Tuffli AP_FLAG_TDIR_FROM_DEV,
7327419d6e4SChuck Tuffli /*features*/ 0,
7337419d6e4SChuck Tuffli /*sector_count*/sizeof(struct ata_params),
7347419d6e4SChuck Tuffli /*lba*/ 0,
7357419d6e4SChuck Tuffli /*command*/ ATA_ATA_IDENTIFY,
7367419d6e4SChuck Tuffli /*control*/ 0,
7377419d6e4SChuck Tuffli /*data_ptr*/ (uint8_t *)&ident_data,
7387419d6e4SChuck Tuffli /*dxfer_len*/ sizeof(struct ata_params),
7397419d6e4SChuck Tuffli /*sense_len*/ SSD_FULL_SIZE,
7407419d6e4SChuck Tuffli /*timeout*/ 5000
7417419d6e4SChuck Tuffli );
7427419d6e4SChuck Tuffli
7437419d6e4SChuck Tuffli rc = cam_send_ccb(fsmart->camdev, ccb);
7447419d6e4SChuck Tuffli if (rc != 0) {
7457419d6e4SChuck Tuffli warnx("%s: scsi_ata_pass_16() failed (programmer error?)",
7467419d6e4SChuck Tuffli __func__);
7477419d6e4SChuck Tuffli goto __device_info_tunneled_ata_out;
7487419d6e4SChuck Tuffli }
7497419d6e4SChuck Tuffli
7507419d6e4SChuck Tuffli fsmart->common.info.supported = ident_data.support.command1 &
7517419d6e4SChuck Tuffli ATA_SUPPORT_SMART;
7527419d6e4SChuck Tuffli
7537419d6e4SChuck Tuffli dprintf("ATA command1 = %#x\n", ident_data.support.command1);
7547419d6e4SChuck Tuffli
7557419d6e4SChuck Tuffli __device_info_tunneled_ata_out:
7567419d6e4SChuck Tuffli if (ccb) {
7577419d6e4SChuck Tuffli cam_freeccb(ccb);
7587419d6e4SChuck Tuffli }
7597419d6e4SChuck Tuffli
7607419d6e4SChuck Tuffli return rc;
7617419d6e4SChuck Tuffli }
7627419d6e4SChuck Tuffli
7637419d6e4SChuck Tuffli /**
7647419d6e4SChuck Tuffli * Retrieve the device information and use to populate the info structure
7657419d6e4SChuck Tuffli */
7667419d6e4SChuck Tuffli static int32_t
__device_get_info(struct fbsd_smart * fsmart)7677419d6e4SChuck Tuffli __device_get_info(struct fbsd_smart *fsmart)
7687419d6e4SChuck Tuffli {
7697419d6e4SChuck Tuffli union ccb *ccb;
7707419d6e4SChuck Tuffli int32_t rc = -1;
7717419d6e4SChuck Tuffli
7727419d6e4SChuck Tuffli if (!fsmart || !fsmart->camdev) {
7737419d6e4SChuck Tuffli warn("Bad handle %p", fsmart);
7747419d6e4SChuck Tuffli return -1;
7757419d6e4SChuck Tuffli }
7767419d6e4SChuck Tuffli
7777419d6e4SChuck Tuffli ccb = cam_getccb(fsmart->camdev);
7787419d6e4SChuck Tuffli if (ccb != NULL) {
7797419d6e4SChuck Tuffli struct ccb_getdev *cgd = &ccb->cgd;
7807419d6e4SChuck Tuffli
7817419d6e4SChuck Tuffli CCB_CLEAR_ALL_EXCEPT_HDR(cgd);
7827419d6e4SChuck Tuffli
7837419d6e4SChuck Tuffli /*
7847419d6e4SChuck Tuffli * GDEV_TYPE doesn't support NVMe. What we do get is:
7857419d6e4SChuck Tuffli * - device (ata/model, scsi/product)
7867419d6e4SChuck Tuffli * - revision (ata, scsi)
7877419d6e4SChuck Tuffli * - serial (ata)
7887419d6e4SChuck Tuffli * - vendor (scsi)
7897419d6e4SChuck Tuffli * - supported (ata)
7907419d6e4SChuck Tuffli *
7917419d6e4SChuck Tuffli * Serial # for all proto via ccb_dev_advinfo (buftype CDAI_TYPE_SERIAL_NUM)
7927419d6e4SChuck Tuffli */
7937419d6e4SChuck Tuffli ccb->ccb_h.func_code = XPT_GDEV_TYPE;
7947419d6e4SChuck Tuffli
7957419d6e4SChuck Tuffli if (cam_send_ccb(fsmart->camdev, ccb) >= 0) {
7967419d6e4SChuck Tuffli if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
7977419d6e4SChuck Tuffli switch (cgd->protocol) {
7987419d6e4SChuck Tuffli case PROTO_ATA:
7997419d6e4SChuck Tuffli rc = __device_info_ata(fsmart, cgd);
8007419d6e4SChuck Tuffli break;
8017419d6e4SChuck Tuffli case PROTO_SCSI:
8027419d6e4SChuck Tuffli rc = __device_info_scsi(fsmart, cgd);
8037419d6e4SChuck Tuffli if (!rc && fsmart->common.protocol == SMART_PROTO_ATA) {
8047419d6e4SChuck Tuffli rc = __device_info_tunneled_ata(fsmart);
8057419d6e4SChuck Tuffli }
8067419d6e4SChuck Tuffli break;
8077419d6e4SChuck Tuffli case PROTO_NVME:
8087419d6e4SChuck Tuffli rc = __device_info_nvme(fsmart, cgd);
8097419d6e4SChuck Tuffli break;
8107419d6e4SChuck Tuffli default:
8117419d6e4SChuck Tuffli printf("%s: unsupported protocol %d\n",
8127419d6e4SChuck Tuffli __func__, cgd->protocol);
8137419d6e4SChuck Tuffli }
8147419d6e4SChuck Tuffli }
8157419d6e4SChuck Tuffli }
8167419d6e4SChuck Tuffli
8177419d6e4SChuck Tuffli cam_freeccb(ccb);
8187419d6e4SChuck Tuffli }
8197419d6e4SChuck Tuffli
8207419d6e4SChuck Tuffli return rc;
8217419d6e4SChuck Tuffli }
822