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