xref: /freebsd/contrib/smart/freebsd_dev.c (revision 346be36e8861e26bfed44cbf960903d0055f6660)
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