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