- if (rc == EINVAL) - warnx("unsupported page %#x", page); - - return rc; - } - - if (((rc = cam_send_ccb(fsmart->camdev, ccb)) < 0) - || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { - if (rc < 0) - warn("error sending command"); - } - - /* - * Most commands don't need any post-processing. But then there's - * ATA. It's why we can't have nice things :( - */ - switch (fsmart->common.protocol) { - case SMART_PROTO_ATA: - __device_status_ata(h, ccb); - break; - default: - ; - } - - if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { - cam_error_print(fsmart->camdev, ccb, CAM_ESF_ALL, - CAM_EPF_ALL, stderr); - } - - cam_freeccb(ccb); - - return 0; -} - -/* - * The SCSI / ATA Translation (SAT) requires devices to support the ATA - * Information VPD Page (T10/2126-D Revision 04). Use the existence of - * this page to identify tunneled devices. - */ -static bool -__device_proto_tunneled(struct fbsd_smart *fsmart) -{ - union ccb *ccb = NULL; - struct scsi_vpd_supported_page_list supportedp; - uint32_t i; - bool is_tunneled = false; - - if (fsmart->common.protocol != SMART_PROTO_SCSI) { - return false; - } - - ccb = cam_getccb(fsmart->camdev); - if (!ccb) { - warn("Allocation failure ccb=%p", ccb); - goto __device_proto_tunneled_out; - } - - scsi_inquiry(&ccb->csio, - 3, // retries - NULL, // callback function - MSG_SIMPLE_Q_TAG, // tag action - (uint8_t *)&supportedp, - sizeof(struct scsi_vpd_supported_page_list), - 1, // EVPD - SVPD_SUPPORTED_PAGE_LIST, // page code - SSD_FULL_SIZE, // sense length - 5000); // timeout - - ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; - - if ((cam_send_ccb(fsmart->camdev, ccb) >= 0) && - ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) { - dprintf("Looking for page %#x (total = %u):\n", SVPD_ATA_INFORMATION, - supportedp.length); - for (i = 0; i < supportedp.length; i++) { - dprintf("\t[%u] = %#x\n", i, supportedp.list[i]); - if (supportedp.list[i] == SVPD_ATA_INFORMATION) { - is_tunneled = true; - break; - } - } - } - - cam_freeccb(ccb); - -__device_proto_tunneled_out: - return is_tunneled; -} - -/** - * Retrieve the device protocol type via the transport settings - * - * @return protocol type or SMART_PROTO_MAX on error - */ -static smart_protocol_e -__device_get_proto(struct fbsd_smart *fsmart) -{ - smart_protocol_e proto = SMART_PROTO_MAX; - union ccb *ccb; - - if (!fsmart || !fsmart->camdev) { - warn("Bad handle %p", fsmart); - return proto; - } - - ccb = cam_getccb(fsmart->camdev); - if (ccb != NULL) { - CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cts); - - ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS; - ccb->cts.type = CTS_TYPE_CURRENT_SETTINGS; - - if (cam_send_ccb(fsmart->camdev, ccb) >= 0) { - if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { - struct ccb_trans_settings *cts = &ccb->cts; - - switch (cts->protocol) { - case PROTO_ATA: - proto = SMART_PROTO_ATA; - break; - case PROTO_SCSI: - proto = SMART_PROTO_SCSI; - break; - case PROTO_NVME: - proto = SMART_PROTO_NVME; - break; - default: - printf("%s: unknown protocol %d\n", - __func__, - cts->protocol); - } - } - } - - cam_freeccb(ccb); - } - - return proto; -} - -static int32_t -__device_info_ata(struct fbsd_smart *fsmart, struct ccb_getdev *cgd) -{ - smart_info_t *sinfo = NULL; - - if (!fsmart || !cgd) { - return -1; - } - - sinfo = &fsmart->common.info; - - sinfo->supported = cgd->ident_data.support.command1 & - ATA_SUPPORT_SMART; - - dprintf("ATA command1 = %#x\n", cgd->ident_data.support.command1); - - cam_strvis((uint8_t *)sinfo->device, cgd->ident_data.model, - sizeof(cgd->ident_data.model), - sizeof(sinfo->device)); - cam_strvis((uint8_t *)sinfo->rev, cgd->ident_data.revision, - sizeof(cgd->ident_data.revision), - sizeof(sinfo->rev)); - cam_strvis((uint8_t *)sinfo->serial, cgd->ident_data.serial, - sizeof(cgd->ident_data.serial), - sizeof(sinfo->serial)); - - return 0; -} - -static int32_t -__device_info_scsi(struct fbsd_smart *fsmart, struct ccb_getdev *cgd) -{ - smart_info_t *sinfo = NULL; - union ccb *ccb = NULL; - struct scsi_vpd_unit_serial_number *snum = NULL; - struct scsi_log_informational_exceptions ie = {0}; - - if (!fsmart || !cgd) { - return -1; - } - - sinfo = &fsmart->common.info; - - cam_strvis((uint8_t *)sinfo->vendor, (uint8_t *)cgd->inq_data.vendor, - sizeof(cgd->inq_data.vendor), - sizeof(sinfo->vendor)); - cam_strvis((uint8_t *)sinfo->device, (uint8_t *)cgd->inq_data.product, - sizeof(cgd->inq_data.product), - sizeof(sinfo->device)); - cam_strvis((uint8_t *)sinfo->rev, (uint8_t *)cgd->inq_data.revision, - sizeof(cgd->inq_data.revision), - sizeof(sinfo->rev)); - - ccb = cam_getccb(fsmart->camdev); - snum = malloc(sizeof(struct scsi_vpd_unit_serial_number)); - if (!ccb || !snum) { - warn("Allocation failure ccb=%p snum=%p", ccb, snum); - goto __device_info_scsi_out; - } - - /* Get the serial number */ - CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio); - - scsi_inquiry(&ccb->csio, - 3, // retries - NULL, // callback function - MSG_SIMPLE_Q_TAG, // tag action - (uint8_t *)snum, - sizeof(struct scsi_vpd_unit_serial_number), - 1, // EVPD - SVPD_UNIT_SERIAL_NUMBER, // page code - SSD_FULL_SIZE, // sense length - 5000); // timeout - - ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; - - if ((cam_send_ccb(fsmart->camdev, ccb) >= 0) && - ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) { - cam_strvis((uint8_t *)sinfo->serial, snum->serial_num, - snum->length, - sizeof(sinfo->serial)); - sinfo->serial[sizeof(sinfo->serial) - 1] = '\0'; - } - - memset(ccb, 0, sizeof(*ccb)); - - scsi_log_sense(&ccb->csio, - /* retries */1, - /* cbfcnp */NULL, - /* tag_action */0, - /* page_code */SLS_PAGE_CTRL_CUMULATIVE, - /* page */SLS_IE_PAGE, - /* save_pages */0, - /* ppc */0, - /* paramptr */0, - /* param_buf */(uint8_t *)&ie, - /* param_len */sizeof(ie), - /* sense_len */0, - /* timeout */5000); - - /* - * Note: The existance of the Informational Exceptions (IE) log page - * appears to be the litmus test for SMART support in SCSI - * devices. Confusingly, smartctl will report SMART health - * status as 'OK' if the device doesn't support the IE page. - * For now, just report the facts. - */ - if ((cam_send_ccb(fsmart->camdev, ccb) >= 0) && - ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) { - if ((ie.hdr.param_len < 4) || ie.ie_asc || ie.ie_ascq) { - printf("Log Sense, Informational Exceptions failed " - "(length=%u asc=%#x ascq=%#x)\n", - ie.hdr.param_len, ie.ie_asc, ie.ie_ascq); - } else { - sinfo->supported = true; - } - } - -__device_info_scsi_out: - free(snum); - if (ccb) - cam_freeccb(ccb); - - return 0; -} - -static int32_t -__device_info_nvme(struct fbsd_smart *fsmart, struct ccb_getdev *cgd) -{ - union ccb *ccb; - smart_info_t *sinfo = NULL; - struct nvme_controller_data cd; - - if (!fsmart || !cgd) { - return -1; - } - - sinfo = &fsmart->common.info; - - sinfo->supported = true; - - ccb = cam_getccb(fsmart->camdev); - if (ccb != NULL) { - struct ccb_dev_advinfo *cdai = &ccb->cdai; - - CCB_CLEAR_ALL_EXCEPT_HDR(cdai); - - cdai->ccb_h.func_code = XPT_DEV_ADVINFO; - cdai->ccb_h.flags = CAM_DIR_IN; - cdai->flags = CDAI_FLAG_NONE; -#ifdef CDAI_TYPE_NVME_CNTRL - cdai->buftype = CDAI_TYPE_NVME_CNTRL; -#else - cdai->buftype = 6; -#endif - cdai->bufsiz = sizeof(struct nvme_controller_data); - cdai->buf = (uint8_t *)&cd; - - if (cam_send_ccb(fsmart->camdev, ccb) >= 0) { - if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { - cam_strvis((uint8_t *)sinfo->device, cd.mn, - sizeof(cd.mn), - sizeof(sinfo->device)); - cam_strvis((uint8_t *)sinfo->rev, cd.fr, - sizeof(cd.fr), - sizeof(sinfo->rev)); - cam_strvis((uint8_t *)sinfo->serial, cd.sn, - sizeof(cd.sn), - sizeof(sinfo->serial)); - } - } - - cam_freeccb(ccb); - } - - return 0; -} - -static int32_t -__device_info_tunneled_ata(struct fbsd_smart *fsmart) -{ - struct ata_params ident_data; - union ccb *ccb = NULL; - struct ata_pass_16 *ata_pass_16; - struct ata_cmd ata_cmd; - int32_t rc = -1; - - ccb = cam_getccb(fsmart->camdev); - if (ccb == NULL) { - goto __device_info_tunneled_ata_out; - } - - memset(&ident_data, 0, sizeof(struct ata_params)); - - CCB_CLEAR_ALL_EXCEPT_HDR(ccb); - - scsi_ata_pass_16(&ccb->csio, - /*retries*/ 1, - /*cbfcnp*/ NULL, - /*flags*/ CAM_DIR_IN, - /*tag_action*/ MSG_SIMPLE_Q_TAG, - /*protocol*/ AP_PROTO_PIO_IN, - /*ata_flags*/ AP_FLAG_TLEN_SECT_CNT | - AP_FLAG_BYT_BLOK_BLOCKS | - AP_FLAG_TDIR_FROM_DEV, - /*features*/ 0, - /*sector_count*/sizeof(struct ata_params), - /*lba*/ 0, - /*command*/ ATA_ATA_IDENTIFY, - /*control*/ 0, - /*data_ptr*/ (uint8_t *)&ident_data, - /*dxfer_len*/ sizeof(struct ata_params), - /*sense_len*/ SSD_FULL_SIZE, - /*timeout*/ 5000 - ); - - ata_pass_16 = (struct ata_pass_16 *)ccb->csio.cdb_io.cdb_bytes; - ata_cmd.command = ata_pass_16->command; - ata_cmd.control = ata_pass_16->control; - ata_cmd.features = ata_pass_16->features; - - rc = cam_send_ccb(fsmart->camdev, ccb); - if (rc != 0) { - warnx("%s: scsi_ata_pass_16() failed (programmer error?)", - __func__); - goto __device_info_tunneled_ata_out; - } - - fsmart->common.info.supported = ident_data.support.command1 & - ATA_SUPPORT_SMART; - - dprintf("ATA command1 = %#x\n", ident_data.support.command1); - -__device_info_tunneled_ata_out: - if (ccb) { - cam_freeccb(ccb); - } - - return rc; -} - -/** - * Retrieve the device information and use to populate the info structure - */ -static int32_t -__device_get_info(struct fbsd_smart *fsmart) -{ - union ccb *ccb; - int32_t rc = -1; - - if (!fsmart || !fsmart->camdev) { - warn("Bad handle %p", fsmart); - return -1; - } - - ccb = cam_getccb(fsmart->camdev); - if (ccb != NULL) { - struct ccb_getdev *cgd = &ccb->cgd; - - CCB_CLEAR_ALL_EXCEPT_HDR(cgd); - - /* - * GDEV_TYPE doesn't support NVMe. What we do get is: - * - device (ata/model, scsi/product) - * - revision (ata, scsi) - * - serial (ata) - * - vendor (scsi) - * - supported (ata) - * - * Serial # for all proto via ccb_dev_advinfo (buftype CDAI_TYPE_SERIAL_NUM) - */ - ccb->ccb_h.func_code = XPT_GDEV_TYPE; - - if (cam_send_ccb(fsmart->camdev, ccb) >= 0) { - if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { - switch (cgd->protocol) { - case PROTO_ATA: - rc = __device_info_ata(fsmart, cgd); - break; - case PROTO_SCSI: - rc = __device_info_scsi(fsmart, cgd); - if (!rc && fsmart->common.protocol == SMART_PROTO_ATA) { - rc = __device_info_tunneled_ata(fsmart); - } - break; - case PROTO_NVME: - rc = __device_info_nvme(fsmart, cgd); - break; - default: - printf("%s: unsupported protocol %d\n", - __func__, cgd->protocol); - } - } - } - - cam_freeccb(ccb); - } - - return rc; -} diff --git a/contrib/smart/libsmart.c b/contrib/smart/libsmart.c deleted file mode 100644 index a1732de09ed9..000000000000 --- a/contrib/smart/libsmart.c +++ /dev/null @@ -1,1359 +0,0 @@ -/* - * Copyright (c) 2016-2026 Chuck Tuffli - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include -#include -#include -#include -#include -#include *** 2512 LINES SKIPPED ***