xref: /freebsd/sbin/camcontrol/epc.c (revision 7028e630d6110789502cfc0f17b36b6f513ec297)
19a6844d5SKenneth D. Merry /*-
29a6844d5SKenneth D. Merry  * Copyright (c) 2016 Spectra Logic Corporation
39a6844d5SKenneth D. Merry  * All rights reserved.
49a6844d5SKenneth D. Merry  *
59a6844d5SKenneth D. Merry  * Redistribution and use in source and binary forms, with or without
69a6844d5SKenneth D. Merry  * modification, are permitted provided that the following conditions
79a6844d5SKenneth D. Merry  * are met:
89a6844d5SKenneth D. Merry  * 1. Redistributions of source code must retain the above copyright
99a6844d5SKenneth D. Merry  *    notice, this list of conditions, and the following disclaimer,
109a6844d5SKenneth D. Merry  *    without modification.
119a6844d5SKenneth D. Merry  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
129a6844d5SKenneth D. Merry  *    substantially similar to the "NO WARRANTY" disclaimer below
139a6844d5SKenneth D. Merry  *    ("Disclaimer") and any redistribution must be conditioned upon
149a6844d5SKenneth D. Merry  *    including a substantially similar Disclaimer requirement for further
159a6844d5SKenneth D. Merry  *    binary redistribution.
169a6844d5SKenneth D. Merry  *
179a6844d5SKenneth D. Merry  * NO WARRANTY
189a6844d5SKenneth D. Merry  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
199a6844d5SKenneth D. Merry  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
209a6844d5SKenneth D. Merry  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
219a6844d5SKenneth D. Merry  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
229a6844d5SKenneth D. Merry  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239a6844d5SKenneth D. Merry  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249a6844d5SKenneth D. Merry  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259a6844d5SKenneth D. Merry  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
269a6844d5SKenneth D. Merry  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
279a6844d5SKenneth D. Merry  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
289a6844d5SKenneth D. Merry  * POSSIBILITY OF SUCH DAMAGES.
299a6844d5SKenneth D. Merry  *
309a6844d5SKenneth D. Merry  * Authors: Ken Merry           (Spectra Logic Corporation)
319a6844d5SKenneth D. Merry  */
329a6844d5SKenneth D. Merry /*
339a6844d5SKenneth D. Merry  * ATA Extended Power Conditions (EPC) support
349a6844d5SKenneth D. Merry  */
359a6844d5SKenneth D. Merry 
36*7028e630SElyes Haouas #include <sys/param.h>
379a6844d5SKenneth D. Merry #include <sys/ioctl.h>
389a6844d5SKenneth D. Merry #include <sys/stdint.h>
399a6844d5SKenneth D. Merry #include <sys/endian.h>
409a6844d5SKenneth D. Merry #include <sys/sbuf.h>
419a6844d5SKenneth D. Merry #include <sys/queue.h>
429a6844d5SKenneth D. Merry #include <sys/ata.h>
439a6844d5SKenneth D. Merry 
449a6844d5SKenneth D. Merry #include <stdio.h>
459a6844d5SKenneth D. Merry #include <stdlib.h>
469a6844d5SKenneth D. Merry #include <inttypes.h>
479a6844d5SKenneth D. Merry #include <unistd.h>
489a6844d5SKenneth D. Merry #include <string.h>
499a6844d5SKenneth D. Merry #include <strings.h>
509a6844d5SKenneth D. Merry #include <fcntl.h>
519a6844d5SKenneth D. Merry #include <ctype.h>
529a6844d5SKenneth D. Merry #include <limits.h>
539a6844d5SKenneth D. Merry #include <err.h>
549a6844d5SKenneth D. Merry #include <locale.h>
559a6844d5SKenneth D. Merry 
569a6844d5SKenneth D. Merry #include <cam/cam.h>
579a6844d5SKenneth D. Merry #include <cam/cam_debug.h>
589a6844d5SKenneth D. Merry #include <cam/cam_ccb.h>
599a6844d5SKenneth D. Merry #include <cam/scsi/scsi_all.h>
609a6844d5SKenneth D. Merry #include <cam/scsi/scsi_da.h>
619a6844d5SKenneth D. Merry #include <cam/scsi/scsi_pass.h>
629a6844d5SKenneth D. Merry #include <cam/scsi/scsi_message.h>
639a6844d5SKenneth D. Merry #include <camlib.h>
649a6844d5SKenneth D. Merry #include "camcontrol.h"
659a6844d5SKenneth D. Merry 
669a6844d5SKenneth D. Merry typedef enum {
679a6844d5SKenneth D. Merry 	EPC_ACTION_NONE		= 0x00,
689a6844d5SKenneth D. Merry 	EPC_ACTION_LIST		= 0x01,
699a6844d5SKenneth D. Merry 	EPC_ACTION_TIMER_SET	= 0x02,
709a6844d5SKenneth D. Merry 	EPC_ACTION_IMMEDIATE	= 0x03,
719a6844d5SKenneth D. Merry 	EPC_ACTION_GETMODE	= 0x04
729a6844d5SKenneth D. Merry } epc_action;
739a6844d5SKenneth D. Merry 
749a6844d5SKenneth D. Merry static struct scsi_nv epc_flags[] = {
759a6844d5SKenneth D. Merry 	{ "Supported", ATA_PCL_COND_SUPPORTED },
769a6844d5SKenneth D. Merry 	{ "Saveable", ATA_PCL_COND_SUPPORTED },
779a6844d5SKenneth D. Merry 	{ "Changeable", ATA_PCL_COND_CHANGEABLE },
789a6844d5SKenneth D. Merry 	{ "Default Timer Enabled", ATA_PCL_DEFAULT_TIMER_EN },
799a6844d5SKenneth D. Merry 	{ "Saved Timer Enabled", ATA_PCL_SAVED_TIMER_EN },
809a6844d5SKenneth D. Merry 	{ "Current Timer Enabled", ATA_PCL_CURRENT_TIMER_EN },
819a6844d5SKenneth D. Merry 	{ "Hold Power Condition Not Supported", ATA_PCL_HOLD_PC_NOT_SUP }
829a6844d5SKenneth D. Merry };
839a6844d5SKenneth D. Merry 
849a6844d5SKenneth D. Merry static struct scsi_nv epc_power_cond_map[] = {
859a6844d5SKenneth D. Merry 	{ "Standby_z", ATA_EPC_STANDBY_Z },
869a6844d5SKenneth D. Merry 	{ "z", ATA_EPC_STANDBY_Z },
879a6844d5SKenneth D. Merry 	{ "Standby_y", ATA_EPC_STANDBY_Y },
889a6844d5SKenneth D. Merry 	{ "y", ATA_EPC_STANDBY_Y },
899a6844d5SKenneth D. Merry 	{ "Idle_a", ATA_EPC_IDLE_A },
909a6844d5SKenneth D. Merry 	{ "a", ATA_EPC_IDLE_A },
919a6844d5SKenneth D. Merry 	{ "Idle_b", ATA_EPC_IDLE_B },
929a6844d5SKenneth D. Merry 	{ "b", ATA_EPC_IDLE_B },
939a6844d5SKenneth D. Merry 	{ "Idle_c", ATA_EPC_IDLE_C },
949a6844d5SKenneth D. Merry 	{ "c", ATA_EPC_IDLE_C }
959a6844d5SKenneth D. Merry };
969a6844d5SKenneth D. Merry 
979a6844d5SKenneth D. Merry static struct scsi_nv epc_rst_val[] = {
989a6844d5SKenneth D. Merry 	{ "default", ATA_SF_EPC_RST_DFLT },
999a6844d5SKenneth D. Merry 	{ "saved", 0}
1009a6844d5SKenneth D. Merry };
1019a6844d5SKenneth D. Merry 
1029a6844d5SKenneth D. Merry static struct scsi_nv epc_ps_map[] = {
1039a6844d5SKenneth D. Merry 	{ "unknown", ATA_SF_EPC_SRC_UNKNOWN },
1049a6844d5SKenneth D. Merry 	{ "battery", ATA_SF_EPC_SRC_BAT },
1059a6844d5SKenneth D. Merry 	{ "notbattery", ATA_SF_EPC_SRC_NOT_BAT }
1069a6844d5SKenneth D. Merry };
1079a6844d5SKenneth D. Merry 
1089a6844d5SKenneth D. Merry /*
1099a6844d5SKenneth D. Merry  * These aren't subcommands of the EPC SET FEATURES subcommand, but rather
1109a6844d5SKenneth D. Merry  * commands that determine the current capabilities and status of the drive.
1119a6844d5SKenneth D. Merry  * The EPC subcommands are limited to 4 bits, so we won't collide with any
1129a6844d5SKenneth D. Merry  * future values.
1139a6844d5SKenneth D. Merry  */
1149a6844d5SKenneth D. Merry #define	CCTL_EPC_GET_STATUS	0x8001
1159a6844d5SKenneth D. Merry #define	CCTL_EPC_LIST		0x8002
1169a6844d5SKenneth D. Merry 
1179a6844d5SKenneth D. Merry static struct scsi_nv epc_cmd_map[] = {
1189a6844d5SKenneth D. Merry 	{ "restore", ATA_SF_EPC_RESTORE },
1199a6844d5SKenneth D. Merry 	{ "goto", ATA_SF_EPC_GOTO },
1209a6844d5SKenneth D. Merry 	{ "timer", ATA_SF_EPC_SET_TIMER },
1219a6844d5SKenneth D. Merry 	{ "state", ATA_SF_EPC_SET_STATE },
1229a6844d5SKenneth D. Merry 	{ "enable", ATA_SF_EPC_ENABLE },
1239a6844d5SKenneth D. Merry 	{ "disable", ATA_SF_EPC_DISABLE },
1249a6844d5SKenneth D. Merry 	{ "source", ATA_SF_EPC_SET_SOURCE },
1259a6844d5SKenneth D. Merry 	{ "status", CCTL_EPC_GET_STATUS },
1269a6844d5SKenneth D. Merry 	{ "list", CCTL_EPC_LIST }
1279a6844d5SKenneth D. Merry };
1289a6844d5SKenneth D. Merry 
1299a6844d5SKenneth D. Merry static int epc_list(struct cam_device *device, camcontrol_devtype devtype,
1309a6844d5SKenneth D. Merry 		    union ccb *ccb, int retry_count, int timeout);
1319a6844d5SKenneth D. Merry static void epc_print_pcl_desc(struct ata_power_cond_log_desc *desc,
1329a6844d5SKenneth D. Merry 			       const char *prefix);
1339a6844d5SKenneth D. Merry static int epc_getmode(struct cam_device *device, camcontrol_devtype devtype,
1349a6844d5SKenneth D. Merry 		       union ccb *ccb, int retry_count, int timeout,
1359a6844d5SKenneth D. Merry 		       int power_only);
1369a6844d5SKenneth D. Merry static int epc_set_features(struct cam_device *device,
1379a6844d5SKenneth D. Merry 			    camcontrol_devtype devtype, union ccb *ccb,
1389a6844d5SKenneth D. Merry 			    int retry_count, int timeout, int action,
1399a6844d5SKenneth D. Merry 			    int power_cond, int timer, int enable, int save,
1409a6844d5SKenneth D. Merry 			    int delayed_entry, int hold, int power_src,
1419a6844d5SKenneth D. Merry 			    int restore_src);
1429a6844d5SKenneth D. Merry 
1439a6844d5SKenneth D. Merry static void
1449a6844d5SKenneth D. Merry epc_print_pcl_desc(struct ata_power_cond_log_desc *desc, const char *prefix)
1459a6844d5SKenneth D. Merry {
1469a6844d5SKenneth D. Merry 	int first;
1479a6844d5SKenneth D. Merry 	unsigned int i,	num_printed, max_chars;
1489a6844d5SKenneth D. Merry 
1499a6844d5SKenneth D. Merry 	first = 1;
1509a6844d5SKenneth D. Merry 	max_chars = 75;
1519a6844d5SKenneth D. Merry 
1529a6844d5SKenneth D. Merry 	num_printed = printf("%sFlags: ", prefix);
153*7028e630SElyes Haouas 	for (i = 0; i < nitems(epc_flags); i++) {
1549a6844d5SKenneth D. Merry 		if ((desc->flags & epc_flags[i].value) == 0)
1559a6844d5SKenneth D. Merry 			continue;
1569a6844d5SKenneth D. Merry 		if (first == 0) {
1579a6844d5SKenneth D. Merry 			num_printed += printf(", ");
1589a6844d5SKenneth D. Merry 		}
1599a6844d5SKenneth D. Merry 		if ((num_printed + strlen(epc_flags[i].name)) > max_chars) {
1609a6844d5SKenneth D. Merry 			printf("\n");
1619a6844d5SKenneth D. Merry 			num_printed = printf("%s       ", prefix);
1629a6844d5SKenneth D. Merry 		}
1639a6844d5SKenneth D. Merry 		num_printed += printf("%s", epc_flags[i].name);
1649a6844d5SKenneth D. Merry 		first = 0;
1659a6844d5SKenneth D. Merry 	}
1669a6844d5SKenneth D. Merry 	if (first != 0)
1679a6844d5SKenneth D. Merry 		printf("None");
1689a6844d5SKenneth D. Merry 	printf("\n");
1699a6844d5SKenneth D. Merry 
1709a6844d5SKenneth D. Merry 	printf("%sDefault timer setting: %.1f sec\n", prefix,
1719a6844d5SKenneth D. Merry 	    (double)(le32dec(desc->default_timer) / 10));
1729a6844d5SKenneth D. Merry 	printf("%sSaved timer setting: %.1f sec\n", prefix,
1739a6844d5SKenneth D. Merry 	    (double)(le32dec(desc->saved_timer) / 10));
1749a6844d5SKenneth D. Merry 	printf("%sCurrent timer setting: %.1f sec\n", prefix,
1759a6844d5SKenneth D. Merry 	    (double)(le32dec(desc->current_timer) / 10));
1769a6844d5SKenneth D. Merry 	printf("%sNominal time to active: %.1f sec\n", prefix,
1779a6844d5SKenneth D. Merry 	    (double)(le32dec(desc->nom_time_to_active) / 10));
1789a6844d5SKenneth D. Merry 	printf("%sMinimum timer: %.1f sec\n", prefix,
1799a6844d5SKenneth D. Merry 	    (double)(le32dec(desc->min_timer) / 10));
1809a6844d5SKenneth D. Merry 	printf("%sMaximum timer: %.1f sec\n", prefix,
1819a6844d5SKenneth D. Merry 	    (double)(le32dec(desc->max_timer) / 10));
1829a6844d5SKenneth D. Merry 	printf("%sNumber of transitions to power condition: %u\n", prefix,
1839a6844d5SKenneth D. Merry 	    le32dec(desc->num_transitions_to_pc));
1849a6844d5SKenneth D. Merry 	printf("%sHours in power condition: %u\n", prefix,
1859a6844d5SKenneth D. Merry 	    le32dec(desc->hours_in_pc));
1869a6844d5SKenneth D. Merry }
1879a6844d5SKenneth D. Merry 
1889a6844d5SKenneth D. Merry static int
1899a6844d5SKenneth D. Merry epc_list(struct cam_device *device, camcontrol_devtype devtype, union ccb *ccb,
1909a6844d5SKenneth D. Merry 	 int retry_count, int timeout)
1919a6844d5SKenneth D. Merry {
1929a6844d5SKenneth D. Merry 	struct ata_power_cond_log_idle *idle_log;
1939a6844d5SKenneth D. Merry 	struct ata_power_cond_log_standby *standby_log;
1949a6844d5SKenneth D. Merry 	uint8_t log_buf[sizeof(*idle_log) + sizeof(*standby_log)];
1959a6844d5SKenneth D. Merry 	uint16_t log_addr = ATA_POWER_COND_LOG;
1969a6844d5SKenneth D. Merry 	uint16_t page_number = ATA_PCL_IDLE;
1979a6844d5SKenneth D. Merry 	uint64_t lba;
1989a6844d5SKenneth D. Merry 	int error = 0;
1999a6844d5SKenneth D. Merry 
2009a6844d5SKenneth D. Merry 	lba = (((uint64_t)page_number & 0xff00) << 32) |
2019a6844d5SKenneth D. Merry 	      ((page_number & 0x00ff) << 8) |
2029a6844d5SKenneth D. Merry 	      (log_addr & 0xff);
2039a6844d5SKenneth D. Merry 
2049a6844d5SKenneth D. Merry 	error = build_ata_cmd(ccb,
2059a6844d5SKenneth D. Merry 	    /*retry_count*/ retry_count,
2069a6844d5SKenneth D. Merry 	    /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS,
2079a6844d5SKenneth D. Merry 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
2089a6844d5SKenneth D. Merry 	    /*protocol*/ AP_PROTO_DMA | AP_EXTEND,
2099a6844d5SKenneth D. Merry 	    /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS |
2109a6844d5SKenneth D. Merry 			  AP_FLAG_TLEN_SECT_CNT |
2119a6844d5SKenneth D. Merry 			  AP_FLAG_TDIR_FROM_DEV,
2129a6844d5SKenneth D. Merry 	    /*features*/ 0,
2139a6844d5SKenneth D. Merry 	    /*sector_count*/ 2,
2149a6844d5SKenneth D. Merry 	    /*lba*/ lba,
2159a6844d5SKenneth D. Merry 	    /*command*/ ATA_READ_LOG_DMA_EXT,
2169a6844d5SKenneth D. Merry 	    /*auxiliary*/ 0,
2179a6844d5SKenneth D. Merry 	    /*data_ptr*/ log_buf,
2189a6844d5SKenneth D. Merry 	    /*dxfer_len*/ sizeof(log_buf),
2199a6844d5SKenneth D. Merry 	    /*cdb_storage*/ NULL,
2209a6844d5SKenneth D. Merry 	    /*cdb_storage_len*/ 0,
2219a6844d5SKenneth D. Merry 	    /*sense_len*/ SSD_FULL_SIZE,
2229a6844d5SKenneth D. Merry 	    /*timeout*/ timeout ? timeout : 60000,
2239a6844d5SKenneth D. Merry 	    /*is48bit*/ 1,
2249a6844d5SKenneth D. Merry 	    /*devtype*/ devtype);
2259a6844d5SKenneth D. Merry 
2269a6844d5SKenneth D. Merry 	if (error != 0) {
2279a6844d5SKenneth D. Merry 		warnx("%s: build_ata_cmd() failed, likely programmer error",
2289a6844d5SKenneth D. Merry 		    __func__);
2299a6844d5SKenneth D. Merry 		goto bailout;
2309a6844d5SKenneth D. Merry 	}
2319a6844d5SKenneth D. Merry 
2329a6844d5SKenneth D. Merry 	if (retry_count > 0)
2339a6844d5SKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
2349a6844d5SKenneth D. Merry 
2359a6844d5SKenneth D. Merry 	error = cam_send_ccb(device, ccb);
2369a6844d5SKenneth D. Merry 	if (error != 0) {
2379a6844d5SKenneth D. Merry 		warn("error sending ATA READ LOG EXT CCB");
2389a6844d5SKenneth D. Merry 		error = 1;
2399a6844d5SKenneth D. Merry 		goto bailout;
2409a6844d5SKenneth D. Merry 	}
2419a6844d5SKenneth D. Merry 
2429a6844d5SKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
2439a6844d5SKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr);
2449a6844d5SKenneth D. Merry 		error = 1;
2459a6844d5SKenneth D. Merry 		goto bailout;
2469a6844d5SKenneth D. Merry 	}
2479a6844d5SKenneth D. Merry 
2489a6844d5SKenneth D. Merry 	idle_log = (struct ata_power_cond_log_idle *)log_buf;
2499a6844d5SKenneth D. Merry 	standby_log =
2509a6844d5SKenneth D. Merry 	    (struct ata_power_cond_log_standby *)&log_buf[sizeof(*idle_log)];
2519a6844d5SKenneth D. Merry 
2529a6844d5SKenneth D. Merry 	printf("ATA Power Conditions Log:\n");
2539a6844d5SKenneth D. Merry 	printf("  Idle power conditions page:\n");
2549a6844d5SKenneth D. Merry 	printf("    Idle A condition:\n");
2559a6844d5SKenneth D. Merry 	epc_print_pcl_desc(&idle_log->idle_a_desc, "      ");
2569a6844d5SKenneth D. Merry 	printf("    Idle B condition:\n");
2579a6844d5SKenneth D. Merry 	epc_print_pcl_desc(&idle_log->idle_b_desc, "      ");
2589a6844d5SKenneth D. Merry 	printf("    Idle C condition:\n");
2599a6844d5SKenneth D. Merry 	epc_print_pcl_desc(&idle_log->idle_c_desc, "      ");
2609a6844d5SKenneth D. Merry 	printf("  Standby power conditions page:\n");
2619a6844d5SKenneth D. Merry 	printf("    Standby Y condition:\n");
2629a6844d5SKenneth D. Merry 	epc_print_pcl_desc(&standby_log->standby_y_desc, "      ");
2639a6844d5SKenneth D. Merry 	printf("    Standby Z condition:\n");
2649a6844d5SKenneth D. Merry 	epc_print_pcl_desc(&standby_log->standby_z_desc, "      ");
2659a6844d5SKenneth D. Merry bailout:
2669a6844d5SKenneth D. Merry 	return (error);
2679a6844d5SKenneth D. Merry }
2689a6844d5SKenneth D. Merry 
2699a6844d5SKenneth D. Merry static int
2709a6844d5SKenneth D. Merry epc_getmode(struct cam_device *device, camcontrol_devtype devtype,
2719a6844d5SKenneth D. Merry 	    union ccb *ccb, int retry_count, int timeout, int power_only)
2729a6844d5SKenneth D. Merry {
2739a6844d5SKenneth D. Merry 	struct ata_params *ident = NULL;
2749a6844d5SKenneth D. Merry 	struct ata_identify_log_sup_cap sup_cap;
2759a6844d5SKenneth D. Merry 	const char *mode_name = NULL;
2769a6844d5SKenneth D. Merry 	uint8_t error = 0, ata_device = 0, status = 0;
2779a6844d5SKenneth D. Merry 	uint16_t count = 0;
2789a6844d5SKenneth D. Merry 	uint64_t lba = 0;
2799a6844d5SKenneth D. Merry 	uint32_t page_number, log_address;
2809a6844d5SKenneth D. Merry 	uint64_t caps = 0;
2819a6844d5SKenneth D. Merry 	int avail_bytes = 0;
2829a6844d5SKenneth D. Merry 	int res_available = 0;
2839a6844d5SKenneth D. Merry 	int retval;
2849a6844d5SKenneth D. Merry 
2859a6844d5SKenneth D. Merry 	retval = 0;
2869a6844d5SKenneth D. Merry 
2879a6844d5SKenneth D. Merry 	if (power_only != 0)
2889a6844d5SKenneth D. Merry 		goto check_power_mode;
2899a6844d5SKenneth D. Merry 
2909a6844d5SKenneth D. Merry 	/*
2919a6844d5SKenneth D. Merry 	 * Get standard ATA Identify data.
2929a6844d5SKenneth D. Merry 	 */
2939a6844d5SKenneth D. Merry 	retval = ata_do_identify(device, retry_count, timeout, ccb, &ident);
2949a6844d5SKenneth D. Merry 	if (retval != 0) {
2959a6844d5SKenneth D. Merry 		warnx("Couldn't get identify data");
2969a6844d5SKenneth D. Merry 		goto bailout;
2979a6844d5SKenneth D. Merry 	}
2989a6844d5SKenneth D. Merry 
2999a6844d5SKenneth D. Merry 	/*
3009a6844d5SKenneth D. Merry 	 * Get the ATA Identify Data Log (0x30),
3019a6844d5SKenneth D. Merry 	 * Supported Capabilities Page (0x03).
3029a6844d5SKenneth D. Merry 	 */
3039a6844d5SKenneth D. Merry 	log_address = ATA_IDENTIFY_DATA_LOG;
3049a6844d5SKenneth D. Merry 	page_number = ATA_IDL_SUP_CAP;
3059a6844d5SKenneth D. Merry 	lba = (((uint64_t)page_number & 0xff00) << 32) |
3069a6844d5SKenneth D. Merry 	       ((page_number & 0x00ff) << 8) |
3079a6844d5SKenneth D. Merry 	       (log_address & 0xff);
3089a6844d5SKenneth D. Merry 
3099a6844d5SKenneth D. Merry 	bzero(&sup_cap, sizeof(sup_cap));
3109a6844d5SKenneth D. Merry 	/*
3119a6844d5SKenneth D. Merry 	 * XXX KDM check the supported protocol.
3129a6844d5SKenneth D. Merry 	 */
3139a6844d5SKenneth D. Merry 	retval = build_ata_cmd(ccb,
3149a6844d5SKenneth D. Merry 	    /*retry_count*/ retry_count,
3159a6844d5SKenneth D. Merry 	    /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS,
3169a6844d5SKenneth D. Merry 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
3179a6844d5SKenneth D. Merry 	    /*protocol*/ AP_PROTO_DMA |
3189a6844d5SKenneth D. Merry 			 AP_EXTEND,
3199a6844d5SKenneth D. Merry 	    /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS |
3209a6844d5SKenneth D. Merry 			  AP_FLAG_TLEN_SECT_CNT |
3219a6844d5SKenneth D. Merry 			  AP_FLAG_TDIR_FROM_DEV,
3229a6844d5SKenneth D. Merry 	    /*features*/ 0,
3239a6844d5SKenneth D. Merry 	    /*sector_count*/ 1,
3249a6844d5SKenneth D. Merry 	    /*lba*/ lba,
3259a6844d5SKenneth D. Merry 	    /*command*/ ATA_READ_LOG_DMA_EXT,
3269a6844d5SKenneth D. Merry 	    /*auxiliary*/ 0,
3279a6844d5SKenneth D. Merry 	    /*data_ptr*/ (uint8_t *)&sup_cap,
3289a6844d5SKenneth D. Merry 	    /*dxfer_len*/ sizeof(sup_cap),
3299a6844d5SKenneth D. Merry 	    /*cdb_storage*/ NULL,
3309a6844d5SKenneth D. Merry 	    /*cdb_storage_len*/ 0,
3319a6844d5SKenneth D. Merry 	    /*sense_len*/ SSD_FULL_SIZE,
3329a6844d5SKenneth D. Merry 	    /*timeout*/ timeout ? timeout : 60000,
3339a6844d5SKenneth D. Merry 	    /*is48bit*/ 1,
3349a6844d5SKenneth D. Merry 	    /*devtype*/ devtype);
3359a6844d5SKenneth D. Merry 
3369a6844d5SKenneth D. Merry 	if (retval != 0) {
3379a6844d5SKenneth D. Merry 		warnx("%s: build_ata_cmd() failed, likely a programmer error",
3389a6844d5SKenneth D. Merry 		    __func__);
3399a6844d5SKenneth D. Merry 		goto bailout;
3409a6844d5SKenneth D. Merry 	}
3419a6844d5SKenneth D. Merry 
3429a6844d5SKenneth D. Merry 	if (retry_count > 0)
3439a6844d5SKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
3449a6844d5SKenneth D. Merry 
3459a6844d5SKenneth D. Merry 	retval = cam_send_ccb(device, ccb);
3469a6844d5SKenneth D. Merry 	if (retval != 0) {
3479a6844d5SKenneth D. Merry 		warn("error sending ATA READ LOG CCB");
3489a6844d5SKenneth D. Merry 		retval = 1;
3499a6844d5SKenneth D. Merry 		goto bailout;
3509a6844d5SKenneth D. Merry 	}
3519a6844d5SKenneth D. Merry 
3529a6844d5SKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
3539a6844d5SKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr);
3549a6844d5SKenneth D. Merry 		retval = 1;
3559a6844d5SKenneth D. Merry 		goto bailout;
3569a6844d5SKenneth D. Merry 	}
3579a6844d5SKenneth D. Merry 
3589a6844d5SKenneth D. Merry 	if (ccb->ccb_h.func_code == XPT_SCSI_IO) {
3599a6844d5SKenneth D. Merry 		avail_bytes = ccb->csio.dxfer_len - ccb->csio.resid;
3609a6844d5SKenneth D. Merry 	} else {
3619a6844d5SKenneth D. Merry 		avail_bytes = ccb->ataio.dxfer_len - ccb->ataio.resid;
3629a6844d5SKenneth D. Merry 	}
3639a6844d5SKenneth D. Merry 	if (avail_bytes < (int)sizeof(sup_cap)) {
3649a6844d5SKenneth D. Merry 		warnx("Couldn't get enough of the ATA Supported "
3659a6844d5SKenneth D. Merry 		    "Capabilities log, %d bytes returned", avail_bytes);
3669a6844d5SKenneth D. Merry 		retval = 1;
3679a6844d5SKenneth D. Merry 		goto bailout;
3689a6844d5SKenneth D. Merry 	}
3699a6844d5SKenneth D. Merry 	caps = le64dec(sup_cap.sup_cap);
3709a6844d5SKenneth D. Merry 	if ((caps & ATA_SUP_CAP_VALID) == 0) {
3719a6844d5SKenneth D. Merry 		warnx("Supported capabilities bits are not valid");
3729a6844d5SKenneth D. Merry 		retval = 1;
3739a6844d5SKenneth D. Merry 		goto bailout;
3749a6844d5SKenneth D. Merry 	}
3759a6844d5SKenneth D. Merry 
3769a6844d5SKenneth D. Merry 	printf("APM: %sSupported, %sEnabled\n",
3779a6844d5SKenneth D. Merry 	    (ident->support.command2 & ATA_SUPPORT_APM) ? "" : "NOT ",
3789a6844d5SKenneth D. Merry 	    (ident->enabled.command2 & ATA_SUPPORT_APM) ? "" : "NOT ");
3799a6844d5SKenneth D. Merry 	printf("EPC: %sSupported, %sEnabled\n",
3809a6844d5SKenneth D. Merry 	    (ident->support2 & ATA_SUPPORT_EPC) ? "" : "NOT ",
3819a6844d5SKenneth D. Merry 	    (ident->enabled2 & ATA_ENABLED_EPC) ? "" : "NOT ");
3829a6844d5SKenneth D. Merry 	printf("Low Power Standby %sSupported\n",
3839a6844d5SKenneth D. Merry 	    (caps & ATA_SC_LP_STANDBY_SUP) ? "" : "NOT ");
3849a6844d5SKenneth D. Merry 	printf("Set EPC Power Source %sSupported\n",
3859a6844d5SKenneth D. Merry 	    (caps & ATA_SC_SET_EPC_PS_SUP) ? "" : "NOT ");
3869a6844d5SKenneth D. Merry 
3879a6844d5SKenneth D. Merry 
3889a6844d5SKenneth D. Merry check_power_mode:
3899a6844d5SKenneth D. Merry 
3909a6844d5SKenneth D. Merry 	retval = build_ata_cmd(ccb,
3919a6844d5SKenneth D. Merry 	    /*retry_count*/ retry_count,
3929a6844d5SKenneth D. Merry 	    /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS,
3939a6844d5SKenneth D. Merry 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
3949a6844d5SKenneth D. Merry 	    /*protocol*/ AP_PROTO_NON_DATA |
3959a6844d5SKenneth D. Merry 			 AP_EXTEND,
3969a6844d5SKenneth D. Merry 	    /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS |
3979a6844d5SKenneth D. Merry 			  AP_FLAG_TLEN_NO_DATA |
3989a6844d5SKenneth D. Merry 			  AP_FLAG_CHK_COND,
3999a6844d5SKenneth D. Merry 	    /*features*/ ATA_SF_EPC,
4009a6844d5SKenneth D. Merry 	    /*sector_count*/ 0,
4019a6844d5SKenneth D. Merry 	    /*lba*/ 0,
4029a6844d5SKenneth D. Merry 	    /*command*/ ATA_CHECK_POWER_MODE,
4039a6844d5SKenneth D. Merry 	    /*auxiliary*/ 0,
4049a6844d5SKenneth D. Merry 	    /*data_ptr*/ NULL,
4059a6844d5SKenneth D. Merry 	    /*dxfer_len*/ 0,
4069a6844d5SKenneth D. Merry 	    /*cdb_storage*/ NULL,
4079a6844d5SKenneth D. Merry 	    /*cdb_storage_len*/ 0,
4089a6844d5SKenneth D. Merry 	    /*sense_len*/ SSD_FULL_SIZE,
4099a6844d5SKenneth D. Merry 	    /*timeout*/ timeout ? timeout : 60000,
4109a6844d5SKenneth D. Merry 	    /*is48bit*/ 0,
4119a6844d5SKenneth D. Merry 	    /*devtype*/ devtype);
4129a6844d5SKenneth D. Merry 
4139a6844d5SKenneth D. Merry 	if (retval != 0) {
4149a6844d5SKenneth D. Merry 		warnx("%s: build_ata_cmd() failed, likely a programmer error",
4159a6844d5SKenneth D. Merry 		    __func__);
4169a6844d5SKenneth D. Merry 		goto bailout;
4179a6844d5SKenneth D. Merry 	}
4189a6844d5SKenneth D. Merry 
4199a6844d5SKenneth D. Merry 	if (retry_count > 0)
4209a6844d5SKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
4219a6844d5SKenneth D. Merry 
4229a6844d5SKenneth D. Merry 	retval = cam_send_ccb(device, ccb);
4239a6844d5SKenneth D. Merry 	if (retval != 0) {
4249a6844d5SKenneth D. Merry 		warn("error sending ATA CHECK POWER MODE CCB");
4259a6844d5SKenneth D. Merry 		retval = 1;
4269a6844d5SKenneth D. Merry 		goto bailout;
4279a6844d5SKenneth D. Merry 	}
4289a6844d5SKenneth D. Merry 
4299a6844d5SKenneth D. Merry 	/*
4309a6844d5SKenneth D. Merry 	 * Check to see whether we got the requested ATA result if this
4319a6844d5SKenneth D. Merry 	 * is an SCSI ATA PASS-THROUGH command.
4329a6844d5SKenneth D. Merry 	 */
4339a6844d5SKenneth D. Merry 	if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR)
4349a6844d5SKenneth D. Merry 	 && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND)) {
4359a6844d5SKenneth D. Merry 		int error_code, sense_key, asc, ascq;
4369a6844d5SKenneth D. Merry 
4379a6844d5SKenneth D. Merry 		retval = scsi_extract_sense_ccb(ccb, &error_code,
4389a6844d5SKenneth D. Merry 		    &sense_key, &asc, &ascq);
4399a6844d5SKenneth D. Merry 		if (retval == 0) {
4409a6844d5SKenneth D. Merry 			cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,
4419a6844d5SKenneth D. Merry 			    stderr);
4429a6844d5SKenneth D. Merry 			retval = 1;
4439a6844d5SKenneth D. Merry 			goto bailout;
4449a6844d5SKenneth D. Merry 		}
4459a6844d5SKenneth D. Merry 		if ((sense_key == SSD_KEY_RECOVERED_ERROR)
4469a6844d5SKenneth D. Merry 		 && (asc == 0x00)
4479a6844d5SKenneth D. Merry 		 && (ascq == 0x1d)) {
4489a6844d5SKenneth D. Merry 			res_available = 1;
4499a6844d5SKenneth D. Merry 		}
4509a6844d5SKenneth D. Merry 
4519a6844d5SKenneth D. Merry 	}
4529a6844d5SKenneth D. Merry 	if (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
4539a6844d5SKenneth D. Merry 	 && (res_available == 0)) {
4549a6844d5SKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr);
4559a6844d5SKenneth D. Merry 		retval = 1;
4569a6844d5SKenneth D. Merry 		goto bailout;
4579a6844d5SKenneth D. Merry 	}
4589a6844d5SKenneth D. Merry 
4599a6844d5SKenneth D. Merry 	retval = get_ata_status(device, ccb, &error, &count, &lba, &ata_device,
4609a6844d5SKenneth D. Merry 	    &status);
4619a6844d5SKenneth D. Merry 	if (retval != 0) {
4629a6844d5SKenneth D. Merry 		warnx("Unable to get ATA CHECK POWER MODE result");
4639a6844d5SKenneth D. Merry 		retval = 1;
4649a6844d5SKenneth D. Merry 		goto bailout;
4659a6844d5SKenneth D. Merry 	}
4669a6844d5SKenneth D. Merry 
4679a6844d5SKenneth D. Merry 	mode_name = scsi_nv_to_str(epc_power_cond_map,
468*7028e630SElyes Haouas 	    nitems(epc_power_cond_map), count);
4699a6844d5SKenneth D. Merry 	printf("Current power state: ");
4709a6844d5SKenneth D. Merry 	/* Note: ident can be null in power_only mode */
4719a6844d5SKenneth D. Merry 	if ((ident == NULL)
4729a6844d5SKenneth D. Merry 	 || (ident->enabled2 & ATA_ENABLED_EPC)) {
4739a6844d5SKenneth D. Merry 		if (mode_name != NULL)
4749a6844d5SKenneth D. Merry 			printf("%s", mode_name);
47575bc7150SAndriy Gapon 		else if (count == ATA_PM_ACTIVE_IDLE) {
4769a6844d5SKenneth D. Merry 			printf("PM0:Active or PM1:Idle");
4779a6844d5SKenneth D. Merry 		}
4789a6844d5SKenneth D. Merry 	} else {
4799a6844d5SKenneth D. Merry 		switch (count) {
48075bc7150SAndriy Gapon 		case ATA_PM_STANDBY:
4819a6844d5SKenneth D. Merry 			printf("PM2:Standby");
4829a6844d5SKenneth D. Merry 			break;
48375bc7150SAndriy Gapon 		case ATA_PM_IDLE:
4849a6844d5SKenneth D. Merry 			printf("PM1:Idle");
4859a6844d5SKenneth D. Merry 			break;
48675bc7150SAndriy Gapon 		case ATA_PM_ACTIVE_IDLE:
4879a6844d5SKenneth D. Merry 			printf("PM0:Active or PM1:Idle");
4889a6844d5SKenneth D. Merry 			break;
4899a6844d5SKenneth D. Merry 		}
4909a6844d5SKenneth D. Merry 	}
4919a6844d5SKenneth D. Merry 	printf("(0x%02x)\n", count);
4929a6844d5SKenneth D. Merry 
4939a6844d5SKenneth D. Merry 	if (power_only != 0)
4949a6844d5SKenneth D. Merry 		goto bailout;
4959a6844d5SKenneth D. Merry 
4969a6844d5SKenneth D. Merry 	if (caps & ATA_SC_LP_STANDBY_SUP) {
4979a6844d5SKenneth D. Merry 		uint32_t wait_mode;
4989a6844d5SKenneth D. Merry 
4999a6844d5SKenneth D. Merry 		wait_mode = (lba >> 20) & 0xff;
5009a6844d5SKenneth D. Merry 		if (wait_mode == 0xff) {
5019a6844d5SKenneth D. Merry 			printf("Device not waiting to enter lower power "
5029a6844d5SKenneth D. Merry 			    "condition");
5039a6844d5SKenneth D. Merry 		} else {
5049a6844d5SKenneth D. Merry 			mode_name = scsi_nv_to_str(epc_power_cond_map,
5059a6844d5SKenneth D. Merry 			    sizeof(epc_power_cond_map) /
5069a6844d5SKenneth D. Merry 			    sizeof(epc_power_cond_map[0]), wait_mode);
5079a6844d5SKenneth D. Merry 			printf("Device waiting to enter mode %s (0x%02x)\n",
5089a6844d5SKenneth D. Merry 			    (mode_name != NULL) ? mode_name : "Unknown",
5099a6844d5SKenneth D. Merry 			    wait_mode);
5109a6844d5SKenneth D. Merry 		}
5119a6844d5SKenneth D. Merry 		printf("Device is %sheld in the current power condition\n",
5129a6844d5SKenneth D. Merry 		    (lba & 0x80000) ? "" : "NOT ");
5139a6844d5SKenneth D. Merry 	}
5149a6844d5SKenneth D. Merry bailout:
5159a6844d5SKenneth D. Merry 	return (retval);
5169a6844d5SKenneth D. Merry 
5179a6844d5SKenneth D. Merry }
5189a6844d5SKenneth D. Merry 
5199a6844d5SKenneth D. Merry static int
5209a6844d5SKenneth D. Merry epc_set_features(struct cam_device *device, camcontrol_devtype devtype,
5219a6844d5SKenneth D. Merry 		 union ccb *ccb, int retry_count, int timeout, int action,
5229a6844d5SKenneth D. Merry 		 int power_cond, int timer, int enable, int save,
5239a6844d5SKenneth D. Merry 		 int delayed_entry, int hold, int power_src, int restore_src)
5249a6844d5SKenneth D. Merry {
5259a6844d5SKenneth D. Merry 	uint64_t lba;
5269a6844d5SKenneth D. Merry 	uint16_t count = 0;
5279a6844d5SKenneth D. Merry 	int error;
5289a6844d5SKenneth D. Merry 
5299a6844d5SKenneth D. Merry 	error = 0;
5309a6844d5SKenneth D. Merry 
5319a6844d5SKenneth D. Merry 	lba = action;
5329a6844d5SKenneth D. Merry 
5339a6844d5SKenneth D. Merry 	switch (action) {
5349a6844d5SKenneth D. Merry 	case ATA_SF_EPC_SET_TIMER:
5359a6844d5SKenneth D. Merry 		lba |= ((timer << ATA_SF_EPC_TIMER_SHIFT) &
5369a6844d5SKenneth D. Merry 			 ATA_SF_EPC_TIMER_MASK);
5379a6844d5SKenneth D. Merry 		/* FALLTHROUGH */
5389a6844d5SKenneth D. Merry 	case ATA_SF_EPC_SET_STATE:
5399a6844d5SKenneth D. Merry 		lba |= (enable ? ATA_SF_EPC_TIMER_EN : 0) |
5409a6844d5SKenneth D. Merry 		       (save ? ATA_SF_EPC_TIMER_SAVE : 0);
5419a6844d5SKenneth D. Merry 		count = power_cond;
5429a6844d5SKenneth D. Merry 		break;
5439a6844d5SKenneth D. Merry 	case ATA_SF_EPC_GOTO:
5449a6844d5SKenneth D. Merry 		count = power_cond;
5459a6844d5SKenneth D. Merry 		lba |= (delayed_entry ? ATA_SF_EPC_GOTO_DELAY : 0) |
5469a6844d5SKenneth D. Merry 		       (hold ? ATA_SF_EPC_GOTO_HOLD : 0);
5479a6844d5SKenneth D. Merry 		break;
5489a6844d5SKenneth D. Merry 	case ATA_SF_EPC_RESTORE:
5499a6844d5SKenneth D. Merry 		lba |= restore_src |
5509a6844d5SKenneth D. Merry 		       (save ? ATA_SF_EPC_RST_SAVE : 0);
5519a6844d5SKenneth D. Merry 		break;
5529a6844d5SKenneth D. Merry 	case ATA_SF_EPC_ENABLE:
5539a6844d5SKenneth D. Merry 	case ATA_SF_EPC_DISABLE:
5549a6844d5SKenneth D. Merry 		break;
5559a6844d5SKenneth D. Merry 	case ATA_SF_EPC_SET_SOURCE:
5569a6844d5SKenneth D. Merry 		count = power_src;
5579a6844d5SKenneth D. Merry 		break;
5589a6844d5SKenneth D. Merry 	}
5599a6844d5SKenneth D. Merry 
5609a6844d5SKenneth D. Merry 	error = build_ata_cmd(ccb,
5619a6844d5SKenneth D. Merry 	    /*retry_count*/ retry_count,
5629a6844d5SKenneth D. Merry 	    /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS,
5639a6844d5SKenneth D. Merry 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
5649a6844d5SKenneth D. Merry 	    /*protocol*/ AP_PROTO_NON_DATA | AP_EXTEND,
5659a6844d5SKenneth D. Merry 	    /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS |
5669a6844d5SKenneth D. Merry 			  AP_FLAG_TLEN_NO_DATA |
5679a6844d5SKenneth D. Merry 			  AP_FLAG_TDIR_FROM_DEV,
5689a6844d5SKenneth D. Merry 	    /*features*/ ATA_SF_EPC,
5699a6844d5SKenneth D. Merry 	    /*sector_count*/ count,
5709a6844d5SKenneth D. Merry 	    /*lba*/ lba,
5719a6844d5SKenneth D. Merry 	    /*command*/ ATA_SETFEATURES,
5729a6844d5SKenneth D. Merry 	    /*auxiliary*/ 0,
5739a6844d5SKenneth D. Merry 	    /*data_ptr*/ NULL,
5749a6844d5SKenneth D. Merry 	    /*dxfer_len*/ 0,
5759a6844d5SKenneth D. Merry 	    /*cdb_storage*/ NULL,
5769a6844d5SKenneth D. Merry 	    /*cdb_storage_len*/ 0,
5779a6844d5SKenneth D. Merry 	    /*sense_len*/ SSD_FULL_SIZE,
5789a6844d5SKenneth D. Merry 	    /*timeout*/ timeout ? timeout : 60000,
5799a6844d5SKenneth D. Merry 	    /*is48bit*/ 1,
5809a6844d5SKenneth D. Merry 	    /*devtype*/ devtype);
5819a6844d5SKenneth D. Merry 
5829a6844d5SKenneth D. Merry 	if (error != 0) {
5839a6844d5SKenneth D. Merry 		warnx("%s: build_ata_cmd() failed, likely a programmer error",
5849a6844d5SKenneth D. Merry 		    __func__);
5859a6844d5SKenneth D. Merry 		goto bailout;
5869a6844d5SKenneth D. Merry 	}
5879a6844d5SKenneth D. Merry 
5889a6844d5SKenneth D. Merry 	if (retry_count > 0)
5899a6844d5SKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
5909a6844d5SKenneth D. Merry 
5919a6844d5SKenneth D. Merry 	error = cam_send_ccb(device, ccb);
5929a6844d5SKenneth D. Merry 	if (error != 0) {
5939a6844d5SKenneth D. Merry 		warn("error sending ATA SET FEATURES CCB");
5949a6844d5SKenneth D. Merry 		error = 1;
5959a6844d5SKenneth D. Merry 		goto bailout;
5969a6844d5SKenneth D. Merry 	}
5979a6844d5SKenneth D. Merry 
5989a6844d5SKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
5999a6844d5SKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr);
6009a6844d5SKenneth D. Merry 		error = 1;
6019a6844d5SKenneth D. Merry 		goto bailout;
6029a6844d5SKenneth D. Merry 	}
6039a6844d5SKenneth D. Merry 
6049a6844d5SKenneth D. Merry bailout:
6059a6844d5SKenneth D. Merry 	return (error);
6069a6844d5SKenneth D. Merry }
6079a6844d5SKenneth D. Merry 
6089a6844d5SKenneth D. Merry int
6099a6844d5SKenneth D. Merry epc(struct cam_device *device, int argc, char **argv, char *combinedopt,
6109a6844d5SKenneth D. Merry     int retry_count, int timeout, int verbosemode __unused)
6119a6844d5SKenneth D. Merry {
6129a6844d5SKenneth D. Merry 	union ccb *ccb = NULL;
6139a6844d5SKenneth D. Merry 	int error = 0;
6149a6844d5SKenneth D. Merry 	int c;
6159a6844d5SKenneth D. Merry 	int action = -1;
6169a6844d5SKenneth D. Merry 	camcontrol_devtype devtype;
6179a6844d5SKenneth D. Merry 	double timer_val = -1;
6189a6844d5SKenneth D. Merry 	int timer_tenths = 0, power_cond = -1;
6199a6844d5SKenneth D. Merry 	int delayed_entry = 0, hold = 0;
6209a6844d5SKenneth D. Merry 	int enable = -1, save = 0;
6219a6844d5SKenneth D. Merry 	int restore_src = -1;
6229a6844d5SKenneth D. Merry 	int power_src = -1;
6239a6844d5SKenneth D. Merry 	int power_only = 0;
6249a6844d5SKenneth D. Merry 
6259a6844d5SKenneth D. Merry 
6269a6844d5SKenneth D. Merry 	ccb = cam_getccb(device);
6279a6844d5SKenneth D. Merry 	if (ccb == NULL) {
6289a6844d5SKenneth D. Merry 		warnx("%s: error allocating CCB", __func__);
6299a6844d5SKenneth D. Merry 		error = 1;
6309a6844d5SKenneth D. Merry 		goto bailout;
6319a6844d5SKenneth D. Merry 	}
6329a6844d5SKenneth D. Merry 
6339a6844d5SKenneth D. Merry 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
6349a6844d5SKenneth D. Merry 		switch (c) {
6359a6844d5SKenneth D. Merry 		case 'c': {
6369a6844d5SKenneth D. Merry 			scsi_nv_status status;
6379a6844d5SKenneth D. Merry 			int entry_num;
6389a6844d5SKenneth D. Merry 
6399a6844d5SKenneth D. Merry 			status = scsi_get_nv(epc_cmd_map,
640*7028e630SElyes Haouas 			    nitems(epc_cmd_map),
6419a6844d5SKenneth D. Merry 			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
6429a6844d5SKenneth D. Merry 			if (status == SCSI_NV_FOUND)
6439a6844d5SKenneth D. Merry 				action = epc_cmd_map[entry_num].value;
6449a6844d5SKenneth D. Merry 			else {
6459a6844d5SKenneth D. Merry 				warnx("%s: %s: %s option %s", __func__,
6469a6844d5SKenneth D. Merry 				    (status == SCSI_NV_AMBIGUOUS) ?
6479a6844d5SKenneth D. Merry 				    "ambiguous" : "invalid", "epc command",
6489a6844d5SKenneth D. Merry 				    optarg);
6499a6844d5SKenneth D. Merry 				error = 1;
6509a6844d5SKenneth D. Merry 				goto bailout;
6519a6844d5SKenneth D. Merry 			}
6529a6844d5SKenneth D. Merry 			break;
6539a6844d5SKenneth D. Merry 		}
6549a6844d5SKenneth D. Merry 		case 'd':
6559a6844d5SKenneth D. Merry 			enable = 0;
6569a6844d5SKenneth D. Merry 			break;
6579a6844d5SKenneth D. Merry 		case 'D':
6589a6844d5SKenneth D. Merry 			delayed_entry = 1;
6599a6844d5SKenneth D. Merry 			break;
6609a6844d5SKenneth D. Merry 		case 'e':
6619a6844d5SKenneth D. Merry 			enable = 1;
6629a6844d5SKenneth D. Merry 			break;
6639a6844d5SKenneth D. Merry 		case 'H':
6649a6844d5SKenneth D. Merry 			hold = 1;
6659a6844d5SKenneth D. Merry 			break;
6669a6844d5SKenneth D. Merry 		case 'p': {
6679a6844d5SKenneth D. Merry 			scsi_nv_status status;
6689a6844d5SKenneth D. Merry 			int entry_num;
6699a6844d5SKenneth D. Merry 
6709a6844d5SKenneth D. Merry 			status = scsi_get_nv(epc_power_cond_map,
6719a6844d5SKenneth D. Merry 			    (sizeof(epc_power_cond_map) /
6729a6844d5SKenneth D. Merry 			     sizeof(epc_power_cond_map[0])), optarg,
6739a6844d5SKenneth D. Merry 			     &entry_num, SCSI_NV_FLAG_IG_CASE);
6749a6844d5SKenneth D. Merry 			if (status == SCSI_NV_FOUND)
6759a6844d5SKenneth D. Merry 				power_cond =epc_power_cond_map[entry_num].value;
6769a6844d5SKenneth D. Merry 			else {
6779a6844d5SKenneth D. Merry 				warnx("%s: %s: %s option %s", __func__,
6789a6844d5SKenneth D. Merry 				    (status == SCSI_NV_AMBIGUOUS) ?
6799a6844d5SKenneth D. Merry 				    "ambiguous" : "invalid", "power condition",
6809a6844d5SKenneth D. Merry 				    optarg);
6819a6844d5SKenneth D. Merry 				error = 1;
6829a6844d5SKenneth D. Merry 				goto bailout;
6839a6844d5SKenneth D. Merry 			}
6849a6844d5SKenneth D. Merry 			break;
6859a6844d5SKenneth D. Merry 		}
6869a6844d5SKenneth D. Merry 		case 'P':
6879a6844d5SKenneth D. Merry 			power_only = 1;
6889a6844d5SKenneth D. Merry 			break;
6899a6844d5SKenneth D. Merry 		case 'r': {
6909a6844d5SKenneth D. Merry 			scsi_nv_status status;
6919a6844d5SKenneth D. Merry 			int entry_num;
6929a6844d5SKenneth D. Merry 
6939a6844d5SKenneth D. Merry 			status = scsi_get_nv(epc_rst_val,
6949a6844d5SKenneth D. Merry 			    (sizeof(epc_rst_val) /
6959a6844d5SKenneth D. Merry 			     sizeof(epc_rst_val[0])), optarg,
6969a6844d5SKenneth D. Merry 			     &entry_num, SCSI_NV_FLAG_IG_CASE);
6979a6844d5SKenneth D. Merry 			if (status == SCSI_NV_FOUND)
6989a6844d5SKenneth D. Merry 				restore_src = epc_rst_val[entry_num].value;
6999a6844d5SKenneth D. Merry 			else {
7009a6844d5SKenneth D. Merry 				warnx("%s: %s: %s option %s", __func__,
7019a6844d5SKenneth D. Merry 				    (status == SCSI_NV_AMBIGUOUS) ?
7029a6844d5SKenneth D. Merry 				    "ambiguous" : "invalid",
7039a6844d5SKenneth D. Merry 				    "restore value source", optarg);
7049a6844d5SKenneth D. Merry 				error = 1;
7059a6844d5SKenneth D. Merry 				goto bailout;
7069a6844d5SKenneth D. Merry 			}
7079a6844d5SKenneth D. Merry 			break;
7089a6844d5SKenneth D. Merry 		}
7099a6844d5SKenneth D. Merry 		case 's':
7109a6844d5SKenneth D. Merry 			save = 1;
7119a6844d5SKenneth D. Merry 			break;
7129a6844d5SKenneth D. Merry 		case 'S': {
7139a6844d5SKenneth D. Merry 			scsi_nv_status status;
7149a6844d5SKenneth D. Merry 			int entry_num;
7159a6844d5SKenneth D. Merry 
7169a6844d5SKenneth D. Merry 			status = scsi_get_nv(epc_ps_map,
717*7028e630SElyes Haouas 			    nitems(epc_ps_map),
7189a6844d5SKenneth D. Merry 			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
7199a6844d5SKenneth D. Merry 			if (status == SCSI_NV_FOUND)
7209a6844d5SKenneth D. Merry 				power_src = epc_ps_map[entry_num].value;
7219a6844d5SKenneth D. Merry 			else {
7229a6844d5SKenneth D. Merry 				warnx("%s: %s: %s option %s", __func__,
7239a6844d5SKenneth D. Merry 				    (status == SCSI_NV_AMBIGUOUS) ?
7249a6844d5SKenneth D. Merry 				    "ambiguous" : "invalid", "power source",
7259a6844d5SKenneth D. Merry 				    optarg);
7269a6844d5SKenneth D. Merry 				error = 1;
7279a6844d5SKenneth D. Merry 				goto bailout;
7289a6844d5SKenneth D. Merry 			}
7299a6844d5SKenneth D. Merry 			break;
7309a6844d5SKenneth D. Merry 		}
7319a6844d5SKenneth D. Merry 		case 'T': {
7329a6844d5SKenneth D. Merry 			char *endptr;
7339a6844d5SKenneth D. Merry 
7349a6844d5SKenneth D. Merry 			timer_val = strtod(optarg, &endptr);
7359a6844d5SKenneth D. Merry 			if (timer_val < 0) {
7369a6844d5SKenneth D. Merry 				warnx("Invalid timer value %f", timer_val);
7379a6844d5SKenneth D. Merry 				error = 1;
7389a6844d5SKenneth D. Merry 				goto bailout;
7399a6844d5SKenneth D. Merry 			} else if (*endptr != '\0') {
7409a6844d5SKenneth D. Merry 				warnx("Invalid timer value %s", optarg);
7419a6844d5SKenneth D. Merry 				error = 1;
7429a6844d5SKenneth D. Merry 				goto bailout;
7439a6844d5SKenneth D. Merry 			}
7449a6844d5SKenneth D. Merry 			timer_tenths = timer_val * 10;
7459a6844d5SKenneth D. Merry 			break;
7469a6844d5SKenneth D. Merry 		}
7479a6844d5SKenneth D. Merry 		default:
7489a6844d5SKenneth D. Merry 			break;
7499a6844d5SKenneth D. Merry 		}
7509a6844d5SKenneth D. Merry 	}
7519a6844d5SKenneth D. Merry 
7529a6844d5SKenneth D. Merry 	if (action == -1) {
7539a6844d5SKenneth D. Merry 		warnx("Must specify an action");
7549a6844d5SKenneth D. Merry 		error = 1;
7559a6844d5SKenneth D. Merry 		goto bailout;
7569a6844d5SKenneth D. Merry 	}
7579a6844d5SKenneth D. Merry 
7589a6844d5SKenneth D. Merry 	error = get_device_type(device, retry_count, timeout,
7599a6844d5SKenneth D. Merry 	    /*printerrors*/ 1, &devtype);
7609a6844d5SKenneth D. Merry 	if (error != 0)
7619a6844d5SKenneth D. Merry 		errx(1, "Unable to determine device type");
7629a6844d5SKenneth D. Merry 
7639a6844d5SKenneth D. Merry 	switch (devtype) {
7649a6844d5SKenneth D. Merry 	case CC_DT_ATA:
76540152db5SWarner Losh 	case CC_DT_SATL:
7669a6844d5SKenneth D. Merry 		break;
7679a6844d5SKenneth D. Merry 	default:
7689a6844d5SKenneth D. Merry 		warnx("The epc subcommand only works with ATA protocol "
7699a6844d5SKenneth D. Merry 		    "devices");
7709a6844d5SKenneth D. Merry 		error = 1;
7719a6844d5SKenneth D. Merry 		goto bailout;
7729a6844d5SKenneth D. Merry 		break; /*NOTREACHED*/
7739a6844d5SKenneth D. Merry 	}
7749a6844d5SKenneth D. Merry 
7759a6844d5SKenneth D. Merry 	switch (action) {
7769a6844d5SKenneth D. Merry 	case ATA_SF_EPC_SET_TIMER:
7779a6844d5SKenneth D. Merry 		if (timer_val == -1) {
7789a6844d5SKenneth D. Merry 			warnx("Must specify a timer value (-T time)");
7799a6844d5SKenneth D. Merry 			error = 1;
7809a6844d5SKenneth D. Merry 		}
78177d9b0efSAlan Somers 		/* FALLTHROUGH */
7829a6844d5SKenneth D. Merry 	case ATA_SF_EPC_SET_STATE:
7839a6844d5SKenneth D. Merry 		if (enable == -1) {
7849a6844d5SKenneth D. Merry 			warnx("Must specify enable (-e) or disable (-d)");
7859a6844d5SKenneth D. Merry 			error = 1;
7869a6844d5SKenneth D. Merry 		}
7879a6844d5SKenneth D. Merry 		/* FALLTHROUGH */
7889a6844d5SKenneth D. Merry 	case ATA_SF_EPC_GOTO:
7899a6844d5SKenneth D. Merry 		if (power_cond == -1) {
7909a6844d5SKenneth D. Merry 			warnx("Must specify a power condition with -p");
7919a6844d5SKenneth D. Merry 			error = 1;
7929a6844d5SKenneth D. Merry 		}
7939a6844d5SKenneth D. Merry 		if (error != 0)
7949a6844d5SKenneth D. Merry 			goto bailout;
7959a6844d5SKenneth D. Merry 		break;
7969a6844d5SKenneth D. Merry 	case ATA_SF_EPC_SET_SOURCE:
7979a6844d5SKenneth D. Merry 		if (power_src == -1) {
7989a6844d5SKenneth D. Merry 			warnx("Must specify a power source (-S battery or "
7999a6844d5SKenneth D. Merry 			    "-S notbattery) value");
8009a6844d5SKenneth D. Merry 			error = 1;
8019a6844d5SKenneth D. Merry 			goto bailout;
8029a6844d5SKenneth D. Merry 		}
8039a6844d5SKenneth D. Merry 		break;
8049a6844d5SKenneth D. Merry 	case ATA_SF_EPC_RESTORE:
8059a6844d5SKenneth D. Merry 		if (restore_src == -1) {
8069a6844d5SKenneth D. Merry 			warnx("Must specify a source for restored value, "
8079a6844d5SKenneth D. Merry 			    "-r default or -r saved");
8089a6844d5SKenneth D. Merry 			error = 1;
8099a6844d5SKenneth D. Merry 			goto bailout;
8109a6844d5SKenneth D. Merry 		}
8119a6844d5SKenneth D. Merry 		break;
8129a6844d5SKenneth D. Merry 	case ATA_SF_EPC_ENABLE:
8139a6844d5SKenneth D. Merry 	case ATA_SF_EPC_DISABLE:
8149a6844d5SKenneth D. Merry 	case CCTL_EPC_GET_STATUS:
8159a6844d5SKenneth D. Merry 	case CCTL_EPC_LIST:
8169a6844d5SKenneth D. Merry 	default:
8179a6844d5SKenneth D. Merry 		break;
8189a6844d5SKenneth D. Merry 	}
8199a6844d5SKenneth D. Merry 
8209a6844d5SKenneth D. Merry 	switch (action) {
8219a6844d5SKenneth D. Merry 	case CCTL_EPC_GET_STATUS:
8229a6844d5SKenneth D. Merry 		error = epc_getmode(device, devtype, ccb, retry_count, timeout,
8239a6844d5SKenneth D. Merry 		    power_only);
8249a6844d5SKenneth D. Merry 		break;
8259a6844d5SKenneth D. Merry 	case CCTL_EPC_LIST:
8269a6844d5SKenneth D. Merry 		error = epc_list(device, devtype, ccb, retry_count, timeout);
8279a6844d5SKenneth D. Merry 		break;
8289a6844d5SKenneth D. Merry 	case ATA_SF_EPC_RESTORE:
8299a6844d5SKenneth D. Merry 	case ATA_SF_EPC_GOTO:
8309a6844d5SKenneth D. Merry 	case ATA_SF_EPC_SET_TIMER:
8319a6844d5SKenneth D. Merry 	case ATA_SF_EPC_SET_STATE:
8329a6844d5SKenneth D. Merry 	case ATA_SF_EPC_ENABLE:
8339a6844d5SKenneth D. Merry 	case ATA_SF_EPC_DISABLE:
8349a6844d5SKenneth D. Merry 	case ATA_SF_EPC_SET_SOURCE:
8359a6844d5SKenneth D. Merry 		error = epc_set_features(device, devtype, ccb, retry_count,
8369a6844d5SKenneth D. Merry 		    timeout, action, power_cond, timer_tenths, enable, save,
8379a6844d5SKenneth D. Merry 		    delayed_entry, hold, power_src, restore_src);
8389a6844d5SKenneth D. Merry 		break;
8399a6844d5SKenneth D. Merry 	default:
8409a6844d5SKenneth D. Merry 		warnx("Not implemented yet");
8419a6844d5SKenneth D. Merry 		error = 1;
8429a6844d5SKenneth D. Merry 		goto bailout;
8439a6844d5SKenneth D. Merry 		break;
8449a6844d5SKenneth D. Merry 	}
8459a6844d5SKenneth D. Merry 
8469a6844d5SKenneth D. Merry 
8479a6844d5SKenneth D. Merry bailout:
8489a6844d5SKenneth D. Merry 	if (ccb != NULL)
8499a6844d5SKenneth D. Merry 		cam_freeccb(ccb);
8509a6844d5SKenneth D. Merry 
8519a6844d5SKenneth D. Merry 	return (error);
8529a6844d5SKenneth D. Merry }
853