1300207Sken/*- 2300207Sken * Copyright (c) 2016 Spectra Logic Corporation 3300207Sken * All rights reserved. 4300207Sken * 5300207Sken * Redistribution and use in source and binary forms, with or without 6300207Sken * modification, are permitted provided that the following conditions 7300207Sken * are met: 8300207Sken * 1. Redistributions of source code must retain the above copyright 9300207Sken * notice, this list of conditions, and the following disclaimer, 10300207Sken * without modification. 11300207Sken * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12300207Sken * substantially similar to the "NO WARRANTY" disclaimer below 13300207Sken * ("Disclaimer") and any redistribution must be conditioned upon 14300207Sken * including a substantially similar Disclaimer requirement for further 15300207Sken * binary redistribution. 16300207Sken * 17300207Sken * NO WARRANTY 18300207Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19300207Sken * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20300207Sken * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 21300207Sken * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22300207Sken * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23300207Sken * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24300207Sken * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25300207Sken * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26300207Sken * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 27300207Sken * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28300207Sken * POSSIBILITY OF SUCH DAMAGES. 29300207Sken * 30300207Sken * Authors: Ken Merry (Spectra Logic Corporation) 31300207Sken */ 32300207Sken/* 33300207Sken * ATA Extended Power Conditions (EPC) support 34300207Sken */ 35300207Sken 36300207Sken#include <sys/cdefs.h> 37300207Sken__FBSDID("$FreeBSD: stable/11/sbin/camcontrol/epc.c 350796 2019-08-08 21:55:49Z mav $"); 38300207Sken 39300207Sken#include <sys/ioctl.h> 40300207Sken#include <sys/stdint.h> 41300207Sken#include <sys/types.h> 42300207Sken#include <sys/endian.h> 43300207Sken#include <sys/sbuf.h> 44300207Sken#include <sys/queue.h> 45300207Sken#include <sys/ata.h> 46300207Sken 47300207Sken#include <stdio.h> 48300207Sken#include <stdlib.h> 49300207Sken#include <inttypes.h> 50300207Sken#include <unistd.h> 51300207Sken#include <string.h> 52300207Sken#include <strings.h> 53300207Sken#include <fcntl.h> 54300207Sken#include <ctype.h> 55300207Sken#include <limits.h> 56300207Sken#include <err.h> 57300207Sken#include <locale.h> 58300207Sken 59300207Sken#include <cam/cam.h> 60300207Sken#include <cam/cam_debug.h> 61300207Sken#include <cam/cam_ccb.h> 62300207Sken#include <cam/scsi/scsi_all.h> 63300207Sken#include <cam/scsi/scsi_da.h> 64300207Sken#include <cam/scsi/scsi_pass.h> 65300207Sken#include <cam/scsi/scsi_message.h> 66300207Sken#include <camlib.h> 67300207Sken#include "camcontrol.h" 68300207Sken 69300207Skentypedef enum { 70300207Sken EPC_ACTION_NONE = 0x00, 71300207Sken EPC_ACTION_LIST = 0x01, 72300207Sken EPC_ACTION_TIMER_SET = 0x02, 73300207Sken EPC_ACTION_IMMEDIATE = 0x03, 74300207Sken EPC_ACTION_GETMODE = 0x04 75300207Sken} epc_action; 76300207Sken 77300207Skenstatic struct scsi_nv epc_flags[] = { 78300207Sken { "Supported", ATA_PCL_COND_SUPPORTED }, 79300207Sken { "Saveable", ATA_PCL_COND_SUPPORTED }, 80300207Sken { "Changeable", ATA_PCL_COND_CHANGEABLE }, 81300207Sken { "Default Timer Enabled", ATA_PCL_DEFAULT_TIMER_EN }, 82300207Sken { "Saved Timer Enabled", ATA_PCL_SAVED_TIMER_EN }, 83300207Sken { "Current Timer Enabled", ATA_PCL_CURRENT_TIMER_EN }, 84300207Sken { "Hold Power Condition Not Supported", ATA_PCL_HOLD_PC_NOT_SUP } 85300207Sken}; 86300207Sken 87300207Skenstatic struct scsi_nv epc_power_cond_map[] = { 88300207Sken { "Standby_z", ATA_EPC_STANDBY_Z }, 89300207Sken { "z", ATA_EPC_STANDBY_Z }, 90300207Sken { "Standby_y", ATA_EPC_STANDBY_Y }, 91300207Sken { "y", ATA_EPC_STANDBY_Y }, 92300207Sken { "Idle_a", ATA_EPC_IDLE_A }, 93300207Sken { "a", ATA_EPC_IDLE_A }, 94300207Sken { "Idle_b", ATA_EPC_IDLE_B }, 95300207Sken { "b", ATA_EPC_IDLE_B }, 96300207Sken { "Idle_c", ATA_EPC_IDLE_C }, 97300207Sken { "c", ATA_EPC_IDLE_C } 98300207Sken}; 99300207Sken 100300207Skenstatic struct scsi_nv epc_rst_val[] = { 101300207Sken { "default", ATA_SF_EPC_RST_DFLT }, 102300207Sken { "saved", 0} 103300207Sken}; 104300207Sken 105300207Skenstatic struct scsi_nv epc_ps_map[] = { 106300207Sken { "unknown", ATA_SF_EPC_SRC_UNKNOWN }, 107300207Sken { "battery", ATA_SF_EPC_SRC_BAT }, 108300207Sken { "notbattery", ATA_SF_EPC_SRC_NOT_BAT } 109300207Sken}; 110300207Sken 111300207Sken/* 112300207Sken * These aren't subcommands of the EPC SET FEATURES subcommand, but rather 113300207Sken * commands that determine the current capabilities and status of the drive. 114300207Sken * The EPC subcommands are limited to 4 bits, so we won't collide with any 115300207Sken * future values. 116300207Sken */ 117300207Sken#define CCTL_EPC_GET_STATUS 0x8001 118300207Sken#define CCTL_EPC_LIST 0x8002 119300207Sken 120300207Skenstatic struct scsi_nv epc_cmd_map[] = { 121300207Sken { "restore", ATA_SF_EPC_RESTORE }, 122300207Sken { "goto", ATA_SF_EPC_GOTO }, 123300207Sken { "timer", ATA_SF_EPC_SET_TIMER }, 124300207Sken { "state", ATA_SF_EPC_SET_STATE }, 125300207Sken { "enable", ATA_SF_EPC_ENABLE }, 126300207Sken { "disable", ATA_SF_EPC_DISABLE }, 127300207Sken { "source", ATA_SF_EPC_SET_SOURCE }, 128300207Sken { "status", CCTL_EPC_GET_STATUS }, 129300207Sken { "list", CCTL_EPC_LIST } 130300207Sken}; 131300207Sken 132300207Skenstatic int epc_list(struct cam_device *device, camcontrol_devtype devtype, 133300207Sken union ccb *ccb, int retry_count, int timeout); 134300207Skenstatic void epc_print_pcl_desc(struct ata_power_cond_log_desc *desc, 135300207Sken const char *prefix); 136300207Skenstatic int epc_getmode(struct cam_device *device, camcontrol_devtype devtype, 137300207Sken union ccb *ccb, int retry_count, int timeout, 138300207Sken int power_only); 139300207Skenstatic int epc_set_features(struct cam_device *device, 140300207Sken camcontrol_devtype devtype, union ccb *ccb, 141300207Sken int retry_count, int timeout, int action, 142300207Sken int power_cond, int timer, int enable, int save, 143300207Sken int delayed_entry, int hold, int power_src, 144300207Sken int restore_src); 145300207Sken 146300207Skenstatic void 147300207Skenepc_print_pcl_desc(struct ata_power_cond_log_desc *desc, const char *prefix) 148300207Sken{ 149300207Sken int first; 150300207Sken unsigned int i, num_printed, max_chars; 151300207Sken 152300207Sken first = 1; 153300207Sken max_chars = 75; 154300207Sken 155300207Sken num_printed = printf("%sFlags: ", prefix); 156300207Sken for (i = 0; i < (sizeof(epc_flags) / sizeof(epc_flags[0])); i++) { 157300207Sken if ((desc->flags & epc_flags[i].value) == 0) 158300207Sken continue; 159300207Sken if (first == 0) { 160300207Sken num_printed += printf(", "); 161300207Sken } 162300207Sken if ((num_printed + strlen(epc_flags[i].name)) > max_chars) { 163300207Sken printf("\n"); 164300207Sken num_printed = printf("%s ", prefix); 165300207Sken } 166300207Sken num_printed += printf("%s", epc_flags[i].name); 167300207Sken first = 0; 168300207Sken } 169300207Sken if (first != 0) 170300207Sken printf("None"); 171300207Sken printf("\n"); 172300207Sken 173300207Sken printf("%sDefault timer setting: %.1f sec\n", prefix, 174300207Sken (double)(le32dec(desc->default_timer) / 10)); 175300207Sken printf("%sSaved timer setting: %.1f sec\n", prefix, 176300207Sken (double)(le32dec(desc->saved_timer) / 10)); 177300207Sken printf("%sCurrent timer setting: %.1f sec\n", prefix, 178300207Sken (double)(le32dec(desc->current_timer) / 10)); 179300207Sken printf("%sNominal time to active: %.1f sec\n", prefix, 180300207Sken (double)(le32dec(desc->nom_time_to_active) / 10)); 181300207Sken printf("%sMinimum timer: %.1f sec\n", prefix, 182300207Sken (double)(le32dec(desc->min_timer) / 10)); 183300207Sken printf("%sMaximum timer: %.1f sec\n", prefix, 184300207Sken (double)(le32dec(desc->max_timer) / 10)); 185300207Sken printf("%sNumber of transitions to power condition: %u\n", prefix, 186300207Sken le32dec(desc->num_transitions_to_pc)); 187300207Sken printf("%sHours in power condition: %u\n", prefix, 188300207Sken le32dec(desc->hours_in_pc)); 189300207Sken} 190300207Sken 191300207Skenstatic int 192300207Skenepc_list(struct cam_device *device, camcontrol_devtype devtype, union ccb *ccb, 193300207Sken int retry_count, int timeout) 194300207Sken{ 195300207Sken struct ata_power_cond_log_idle *idle_log; 196300207Sken struct ata_power_cond_log_standby *standby_log; 197300207Sken uint8_t log_buf[sizeof(*idle_log) + sizeof(*standby_log)]; 198300207Sken uint16_t log_addr = ATA_POWER_COND_LOG; 199300207Sken uint16_t page_number = ATA_PCL_IDLE; 200300207Sken uint64_t lba; 201300207Sken int error = 0; 202300207Sken 203300207Sken lba = (((uint64_t)page_number & 0xff00) << 32) | 204300207Sken ((page_number & 0x00ff) << 8) | 205300207Sken (log_addr & 0xff); 206300207Sken 207300207Sken error = build_ata_cmd(ccb, 208300207Sken /*retry_count*/ retry_count, 209300207Sken /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS, 210300207Sken /*tag_action*/ MSG_SIMPLE_Q_TAG, 211300207Sken /*protocol*/ AP_PROTO_DMA | AP_EXTEND, 212300207Sken /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS | 213300207Sken AP_FLAG_TLEN_SECT_CNT | 214300207Sken AP_FLAG_TDIR_FROM_DEV, 215300207Sken /*features*/ 0, 216300207Sken /*sector_count*/ 2, 217300207Sken /*lba*/ lba, 218300207Sken /*command*/ ATA_READ_LOG_DMA_EXT, 219300207Sken /*auxiliary*/ 0, 220300207Sken /*data_ptr*/ log_buf, 221300207Sken /*dxfer_len*/ sizeof(log_buf), 222300207Sken /*cdb_storage*/ NULL, 223300207Sken /*cdb_storage_len*/ 0, 224300207Sken /*sense_len*/ SSD_FULL_SIZE, 225300207Sken /*timeout*/ timeout ? timeout : 60000, 226300207Sken /*is48bit*/ 1, 227300207Sken /*devtype*/ devtype); 228300207Sken 229300207Sken if (error != 0) { 230300207Sken warnx("%s: build_ata_cmd() failed, likely programmer error", 231300207Sken __func__); 232300207Sken goto bailout; 233300207Sken } 234300207Sken 235300207Sken if (retry_count > 0) 236300207Sken ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 237300207Sken 238300207Sken error = cam_send_ccb(device, ccb); 239300207Sken if (error != 0) { 240300207Sken warn("error sending ATA READ LOG EXT CCB"); 241300207Sken error = 1; 242300207Sken goto bailout; 243300207Sken } 244300207Sken 245300207Sken if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 246300207Sken cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr); 247300207Sken error = 1; 248300207Sken goto bailout; 249300207Sken } 250300207Sken 251300207Sken idle_log = (struct ata_power_cond_log_idle *)log_buf; 252300207Sken standby_log = 253300207Sken (struct ata_power_cond_log_standby *)&log_buf[sizeof(*idle_log)]; 254300207Sken 255300207Sken printf("ATA Power Conditions Log:\n"); 256300207Sken printf(" Idle power conditions page:\n"); 257300207Sken printf(" Idle A condition:\n"); 258300207Sken epc_print_pcl_desc(&idle_log->idle_a_desc, " "); 259300207Sken printf(" Idle B condition:\n"); 260300207Sken epc_print_pcl_desc(&idle_log->idle_b_desc, " "); 261300207Sken printf(" Idle C condition:\n"); 262300207Sken epc_print_pcl_desc(&idle_log->idle_c_desc, " "); 263300207Sken printf(" Standby power conditions page:\n"); 264300207Sken printf(" Standby Y condition:\n"); 265300207Sken epc_print_pcl_desc(&standby_log->standby_y_desc, " "); 266300207Sken printf(" Standby Z condition:\n"); 267300207Sken epc_print_pcl_desc(&standby_log->standby_z_desc, " "); 268300207Skenbailout: 269300207Sken return (error); 270300207Sken} 271300207Sken 272300207Skenstatic int 273300207Skenepc_getmode(struct cam_device *device, camcontrol_devtype devtype, 274300207Sken union ccb *ccb, int retry_count, int timeout, int power_only) 275300207Sken{ 276300207Sken struct ata_params *ident = NULL; 277300207Sken struct ata_identify_log_sup_cap sup_cap; 278300207Sken const char *mode_name = NULL; 279300207Sken uint8_t error = 0, ata_device = 0, status = 0; 280300207Sken uint16_t count = 0; 281300207Sken uint64_t lba = 0; 282300207Sken uint32_t page_number, log_address; 283300207Sken uint64_t caps = 0; 284300207Sken int avail_bytes = 0; 285300207Sken int res_available = 0; 286300207Sken int retval; 287300207Sken 288300207Sken retval = 0; 289300207Sken 290300207Sken if (power_only != 0) 291300207Sken goto check_power_mode; 292300207Sken 293300207Sken /* 294300207Sken * Get standard ATA Identify data. 295300207Sken */ 296300207Sken retval = ata_do_identify(device, retry_count, timeout, ccb, &ident); 297300207Sken if (retval != 0) { 298300207Sken warnx("Couldn't get identify data"); 299300207Sken goto bailout; 300300207Sken } 301300207Sken 302300207Sken /* 303300207Sken * Get the ATA Identify Data Log (0x30), 304300207Sken * Supported Capabilities Page (0x03). 305300207Sken */ 306300207Sken log_address = ATA_IDENTIFY_DATA_LOG; 307300207Sken page_number = ATA_IDL_SUP_CAP; 308300207Sken lba = (((uint64_t)page_number & 0xff00) << 32) | 309300207Sken ((page_number & 0x00ff) << 8) | 310300207Sken (log_address & 0xff); 311300207Sken 312300207Sken bzero(&sup_cap, sizeof(sup_cap)); 313300207Sken /* 314300207Sken * XXX KDM check the supported protocol. 315300207Sken */ 316300207Sken retval = build_ata_cmd(ccb, 317300207Sken /*retry_count*/ retry_count, 318300207Sken /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS, 319300207Sken /*tag_action*/ MSG_SIMPLE_Q_TAG, 320300207Sken /*protocol*/ AP_PROTO_DMA | 321300207Sken AP_EXTEND, 322300207Sken /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS | 323300207Sken AP_FLAG_TLEN_SECT_CNT | 324300207Sken AP_FLAG_TDIR_FROM_DEV, 325300207Sken /*features*/ 0, 326300207Sken /*sector_count*/ 1, 327300207Sken /*lba*/ lba, 328300207Sken /*command*/ ATA_READ_LOG_DMA_EXT, 329300207Sken /*auxiliary*/ 0, 330300207Sken /*data_ptr*/ (uint8_t *)&sup_cap, 331300207Sken /*dxfer_len*/ sizeof(sup_cap), 332300207Sken /*cdb_storage*/ NULL, 333300207Sken /*cdb_storage_len*/ 0, 334300207Sken /*sense_len*/ SSD_FULL_SIZE, 335300207Sken /*timeout*/ timeout ? timeout : 60000, 336300207Sken /*is48bit*/ 1, 337300207Sken /*devtype*/ devtype); 338300207Sken 339300207Sken if (retval != 0) { 340300207Sken warnx("%s: build_ata_cmd() failed, likely a programmer error", 341300207Sken __func__); 342300207Sken goto bailout; 343300207Sken } 344300207Sken 345300207Sken if (retry_count > 0) 346300207Sken ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 347300207Sken 348300207Sken retval = cam_send_ccb(device, ccb); 349300207Sken if (retval != 0) { 350300207Sken warn("error sending ATA READ LOG CCB"); 351300207Sken retval = 1; 352300207Sken goto bailout; 353300207Sken } 354300207Sken 355300207Sken if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 356300207Sken cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr); 357300207Sken retval = 1; 358300207Sken goto bailout; 359300207Sken } 360300207Sken 361300207Sken if (ccb->ccb_h.func_code == XPT_SCSI_IO) { 362300207Sken avail_bytes = ccb->csio.dxfer_len - ccb->csio.resid; 363300207Sken } else { 364300207Sken avail_bytes = ccb->ataio.dxfer_len - ccb->ataio.resid; 365300207Sken } 366300207Sken if (avail_bytes < (int)sizeof(sup_cap)) { 367300207Sken warnx("Couldn't get enough of the ATA Supported " 368300207Sken "Capabilities log, %d bytes returned", avail_bytes); 369300207Sken retval = 1; 370300207Sken goto bailout; 371300207Sken } 372300207Sken caps = le64dec(sup_cap.sup_cap); 373300207Sken if ((caps & ATA_SUP_CAP_VALID) == 0) { 374300207Sken warnx("Supported capabilities bits are not valid"); 375300207Sken retval = 1; 376300207Sken goto bailout; 377300207Sken } 378300207Sken 379300207Sken printf("APM: %sSupported, %sEnabled\n", 380300207Sken (ident->support.command2 & ATA_SUPPORT_APM) ? "" : "NOT ", 381300207Sken (ident->enabled.command2 & ATA_SUPPORT_APM) ? "" : "NOT "); 382300207Sken printf("EPC: %sSupported, %sEnabled\n", 383300207Sken (ident->support2 & ATA_SUPPORT_EPC) ? "" : "NOT ", 384300207Sken (ident->enabled2 & ATA_ENABLED_EPC) ? "" : "NOT "); 385300207Sken printf("Low Power Standby %sSupported\n", 386300207Sken (caps & ATA_SC_LP_STANDBY_SUP) ? "" : "NOT "); 387300207Sken printf("Set EPC Power Source %sSupported\n", 388300207Sken (caps & ATA_SC_SET_EPC_PS_SUP) ? "" : "NOT "); 389300207Sken 390300207Sken 391300207Skencheck_power_mode: 392300207Sken 393300207Sken retval = build_ata_cmd(ccb, 394300207Sken /*retry_count*/ retry_count, 395300207Sken /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS, 396300207Sken /*tag_action*/ MSG_SIMPLE_Q_TAG, 397300207Sken /*protocol*/ AP_PROTO_NON_DATA | 398300207Sken AP_EXTEND, 399300207Sken /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS | 400300207Sken AP_FLAG_TLEN_NO_DATA | 401300207Sken AP_FLAG_CHK_COND, 402300207Sken /*features*/ ATA_SF_EPC, 403300207Sken /*sector_count*/ 0, 404300207Sken /*lba*/ 0, 405300207Sken /*command*/ ATA_CHECK_POWER_MODE, 406300207Sken /*auxiliary*/ 0, 407300207Sken /*data_ptr*/ NULL, 408300207Sken /*dxfer_len*/ 0, 409300207Sken /*cdb_storage*/ NULL, 410300207Sken /*cdb_storage_len*/ 0, 411300207Sken /*sense_len*/ SSD_FULL_SIZE, 412300207Sken /*timeout*/ timeout ? timeout : 60000, 413300207Sken /*is48bit*/ 0, 414300207Sken /*devtype*/ devtype); 415300207Sken 416300207Sken if (retval != 0) { 417300207Sken warnx("%s: build_ata_cmd() failed, likely a programmer error", 418300207Sken __func__); 419300207Sken goto bailout; 420300207Sken } 421300207Sken 422300207Sken if (retry_count > 0) 423300207Sken ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 424300207Sken 425300207Sken retval = cam_send_ccb(device, ccb); 426300207Sken if (retval != 0) { 427300207Sken warn("error sending ATA CHECK POWER MODE CCB"); 428300207Sken retval = 1; 429300207Sken goto bailout; 430300207Sken } 431300207Sken 432300207Sken /* 433300207Sken * Check to see whether we got the requested ATA result if this 434300207Sken * is an SCSI ATA PASS-THROUGH command. 435300207Sken */ 436300207Sken if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) 437300207Sken && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND)) { 438300207Sken int error_code, sense_key, asc, ascq; 439300207Sken 440300207Sken retval = scsi_extract_sense_ccb(ccb, &error_code, 441300207Sken &sense_key, &asc, &ascq); 442300207Sken if (retval == 0) { 443300207Sken cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, 444300207Sken stderr); 445300207Sken retval = 1; 446300207Sken goto bailout; 447300207Sken } 448300207Sken if ((sense_key == SSD_KEY_RECOVERED_ERROR) 449300207Sken && (asc == 0x00) 450300207Sken && (ascq == 0x1d)) { 451300207Sken res_available = 1; 452300207Sken } 453300207Sken 454300207Sken } 455300207Sken if (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) 456300207Sken && (res_available == 0)) { 457300207Sken cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr); 458300207Sken retval = 1; 459300207Sken goto bailout; 460300207Sken } 461300207Sken 462300207Sken retval = get_ata_status(device, ccb, &error, &count, &lba, &ata_device, 463300207Sken &status); 464300207Sken if (retval != 0) { 465300207Sken warnx("Unable to get ATA CHECK POWER MODE result"); 466300207Sken retval = 1; 467300207Sken goto bailout; 468300207Sken } 469300207Sken 470300207Sken mode_name = scsi_nv_to_str(epc_power_cond_map, 471300207Sken sizeof(epc_power_cond_map) / sizeof(epc_power_cond_map[0]), count); 472300207Sken printf("Current power state: "); 473300207Sken /* Note: ident can be null in power_only mode */ 474300207Sken if ((ident == NULL) 475300207Sken || (ident->enabled2 & ATA_ENABLED_EPC)) { 476300207Sken if (mode_name != NULL) 477300207Sken printf("%s", mode_name); 478300207Sken else if (count == 0xff) { 479300207Sken printf("PM0:Active or PM1:Idle"); 480300207Sken } 481300207Sken } else { 482300207Sken switch (count) { 483300207Sken case 0x00: 484300207Sken printf("PM2:Standby"); 485300207Sken break; 486300207Sken case 0x80: 487300207Sken printf("PM1:Idle"); 488300207Sken break; 489300207Sken case 0xff: 490300207Sken printf("PM0:Active or PM1:Idle"); 491300207Sken break; 492300207Sken } 493300207Sken } 494300207Sken printf("(0x%02x)\n", count); 495300207Sken 496300207Sken if (power_only != 0) 497300207Sken goto bailout; 498300207Sken 499300207Sken if (caps & ATA_SC_LP_STANDBY_SUP) { 500300207Sken uint32_t wait_mode; 501300207Sken 502300207Sken wait_mode = (lba >> 20) & 0xff; 503300207Sken if (wait_mode == 0xff) { 504300207Sken printf("Device not waiting to enter lower power " 505300207Sken "condition"); 506300207Sken } else { 507300207Sken mode_name = scsi_nv_to_str(epc_power_cond_map, 508300207Sken sizeof(epc_power_cond_map) / 509300207Sken sizeof(epc_power_cond_map[0]), wait_mode); 510300207Sken printf("Device waiting to enter mode %s (0x%02x)\n", 511300207Sken (mode_name != NULL) ? mode_name : "Unknown", 512300207Sken wait_mode); 513300207Sken } 514300207Sken printf("Device is %sheld in the current power condition\n", 515300207Sken (lba & 0x80000) ? "" : "NOT "); 516300207Sken } 517300207Skenbailout: 518300207Sken return (retval); 519300207Sken 520300207Sken} 521300207Sken 522300207Skenstatic int 523300207Skenepc_set_features(struct cam_device *device, camcontrol_devtype devtype, 524300207Sken union ccb *ccb, int retry_count, int timeout, int action, 525300207Sken int power_cond, int timer, int enable, int save, 526300207Sken int delayed_entry, int hold, int power_src, int restore_src) 527300207Sken{ 528300207Sken uint64_t lba; 529300207Sken uint16_t count = 0; 530300207Sken int error; 531300207Sken 532300207Sken error = 0; 533300207Sken 534300207Sken lba = action; 535300207Sken 536300207Sken switch (action) { 537300207Sken case ATA_SF_EPC_SET_TIMER: 538300207Sken lba |= ((timer << ATA_SF_EPC_TIMER_SHIFT) & 539300207Sken ATA_SF_EPC_TIMER_MASK); 540300207Sken /* FALLTHROUGH */ 541300207Sken case ATA_SF_EPC_SET_STATE: 542300207Sken lba |= (enable ? ATA_SF_EPC_TIMER_EN : 0) | 543300207Sken (save ? ATA_SF_EPC_TIMER_SAVE : 0); 544300207Sken count = power_cond; 545300207Sken break; 546300207Sken case ATA_SF_EPC_GOTO: 547300207Sken count = power_cond; 548300207Sken lba |= (delayed_entry ? ATA_SF_EPC_GOTO_DELAY : 0) | 549300207Sken (hold ? ATA_SF_EPC_GOTO_HOLD : 0); 550300207Sken break; 551300207Sken case ATA_SF_EPC_RESTORE: 552300207Sken lba |= restore_src | 553300207Sken (save ? ATA_SF_EPC_RST_SAVE : 0); 554300207Sken break; 555300207Sken case ATA_SF_EPC_ENABLE: 556300207Sken case ATA_SF_EPC_DISABLE: 557300207Sken break; 558300207Sken case ATA_SF_EPC_SET_SOURCE: 559300207Sken count = power_src; 560300207Sken break; 561300207Sken } 562300207Sken 563300207Sken error = build_ata_cmd(ccb, 564300207Sken /*retry_count*/ retry_count, 565300207Sken /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS, 566300207Sken /*tag_action*/ MSG_SIMPLE_Q_TAG, 567300207Sken /*protocol*/ AP_PROTO_NON_DATA | AP_EXTEND, 568300207Sken /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS | 569300207Sken AP_FLAG_TLEN_NO_DATA | 570300207Sken AP_FLAG_TDIR_FROM_DEV, 571300207Sken /*features*/ ATA_SF_EPC, 572300207Sken /*sector_count*/ count, 573300207Sken /*lba*/ lba, 574300207Sken /*command*/ ATA_SETFEATURES, 575300207Sken /*auxiliary*/ 0, 576300207Sken /*data_ptr*/ NULL, 577300207Sken /*dxfer_len*/ 0, 578300207Sken /*cdb_storage*/ NULL, 579300207Sken /*cdb_storage_len*/ 0, 580300207Sken /*sense_len*/ SSD_FULL_SIZE, 581300207Sken /*timeout*/ timeout ? timeout : 60000, 582300207Sken /*is48bit*/ 1, 583300207Sken /*devtype*/ devtype); 584300207Sken 585300207Sken if (error != 0) { 586300207Sken warnx("%s: build_ata_cmd() failed, likely a programmer error", 587300207Sken __func__); 588300207Sken goto bailout; 589300207Sken } 590300207Sken 591300207Sken if (retry_count > 0) 592300207Sken ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 593300207Sken 594300207Sken error = cam_send_ccb(device, ccb); 595300207Sken if (error != 0) { 596300207Sken warn("error sending ATA SET FEATURES CCB"); 597300207Sken error = 1; 598300207Sken goto bailout; 599300207Sken } 600300207Sken 601300207Sken if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 602300207Sken cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr); 603300207Sken error = 1; 604300207Sken goto bailout; 605300207Sken } 606300207Sken 607300207Skenbailout: 608300207Sken return (error); 609300207Sken} 610300207Sken 611300207Skenint 612300207Skenepc(struct cam_device *device, int argc, char **argv, char *combinedopt, 613300207Sken int retry_count, int timeout, int verbosemode __unused) 614300207Sken{ 615300207Sken union ccb *ccb = NULL; 616300207Sken int error = 0; 617300207Sken int c; 618300207Sken int action = -1; 619300207Sken camcontrol_devtype devtype; 620300207Sken double timer_val = -1; 621300207Sken int timer_tenths = 0, power_cond = -1; 622300207Sken int delayed_entry = 0, hold = 0; 623300207Sken int enable = -1, save = 0; 624300207Sken int restore_src = -1; 625300207Sken int power_src = -1; 626300207Sken int power_only = 0; 627300207Sken 628300207Sken 629300207Sken ccb = cam_getccb(device); 630300207Sken if (ccb == NULL) { 631300207Sken warnx("%s: error allocating CCB", __func__); 632300207Sken error = 1; 633300207Sken goto bailout; 634300207Sken } 635300207Sken 636300685Struckman CCB_CLEAR_ALL_EXCEPT_HDR(ccb); 637300207Sken 638300207Sken while ((c = getopt(argc, argv, combinedopt)) != -1) { 639300207Sken switch (c) { 640300207Sken case 'c': { 641300207Sken scsi_nv_status status; 642300207Sken int entry_num; 643300207Sken 644300207Sken status = scsi_get_nv(epc_cmd_map, 645300207Sken (sizeof(epc_cmd_map) / sizeof(epc_cmd_map[0])), 646300207Sken optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); 647300207Sken if (status == SCSI_NV_FOUND) 648300207Sken action = epc_cmd_map[entry_num].value; 649300207Sken else { 650300207Sken warnx("%s: %s: %s option %s", __func__, 651300207Sken (status == SCSI_NV_AMBIGUOUS) ? 652300207Sken "ambiguous" : "invalid", "epc command", 653300207Sken optarg); 654300207Sken error = 1; 655300207Sken goto bailout; 656300207Sken } 657300207Sken break; 658300207Sken } 659300207Sken case 'd': 660300207Sken enable = 0; 661300207Sken break; 662300207Sken case 'D': 663300207Sken delayed_entry = 1; 664300207Sken break; 665300207Sken case 'e': 666300207Sken enable = 1; 667300207Sken break; 668300207Sken case 'H': 669300207Sken hold = 1; 670300207Sken break; 671300207Sken case 'p': { 672300207Sken scsi_nv_status status; 673300207Sken int entry_num; 674300207Sken 675300207Sken status = scsi_get_nv(epc_power_cond_map, 676300207Sken (sizeof(epc_power_cond_map) / 677300207Sken sizeof(epc_power_cond_map[0])), optarg, 678300207Sken &entry_num, SCSI_NV_FLAG_IG_CASE); 679300207Sken if (status == SCSI_NV_FOUND) 680300207Sken power_cond =epc_power_cond_map[entry_num].value; 681300207Sken else { 682300207Sken warnx("%s: %s: %s option %s", __func__, 683300207Sken (status == SCSI_NV_AMBIGUOUS) ? 684300207Sken "ambiguous" : "invalid", "power condition", 685300207Sken optarg); 686300207Sken error = 1; 687300207Sken goto bailout; 688300207Sken } 689300207Sken break; 690300207Sken } 691300207Sken case 'P': 692300207Sken power_only = 1; 693300207Sken break; 694300207Sken case 'r': { 695300207Sken scsi_nv_status status; 696300207Sken int entry_num; 697300207Sken 698300207Sken status = scsi_get_nv(epc_rst_val, 699300207Sken (sizeof(epc_rst_val) / 700300207Sken sizeof(epc_rst_val[0])), optarg, 701300207Sken &entry_num, SCSI_NV_FLAG_IG_CASE); 702300207Sken if (status == SCSI_NV_FOUND) 703300207Sken restore_src = epc_rst_val[entry_num].value; 704300207Sken else { 705300207Sken warnx("%s: %s: %s option %s", __func__, 706300207Sken (status == SCSI_NV_AMBIGUOUS) ? 707300207Sken "ambiguous" : "invalid", 708300207Sken "restore value source", optarg); 709300207Sken error = 1; 710300207Sken goto bailout; 711300207Sken } 712300207Sken break; 713300207Sken } 714300207Sken case 's': 715300207Sken save = 1; 716300207Sken break; 717300207Sken case 'S': { 718300207Sken scsi_nv_status status; 719300207Sken int entry_num; 720300207Sken 721300207Sken status = scsi_get_nv(epc_ps_map, 722300207Sken (sizeof(epc_ps_map) / sizeof(epc_ps_map[0])), 723300207Sken optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); 724300207Sken if (status == SCSI_NV_FOUND) 725300207Sken power_src = epc_ps_map[entry_num].value; 726300207Sken else { 727300207Sken warnx("%s: %s: %s option %s", __func__, 728300207Sken (status == SCSI_NV_AMBIGUOUS) ? 729300207Sken "ambiguous" : "invalid", "power source", 730300207Sken optarg); 731300207Sken error = 1; 732300207Sken goto bailout; 733300207Sken } 734300207Sken break; 735300207Sken } 736300207Sken case 'T': { 737300207Sken char *endptr; 738300207Sken 739300207Sken timer_val = strtod(optarg, &endptr); 740300207Sken if (timer_val < 0) { 741300207Sken warnx("Invalid timer value %f", timer_val); 742300207Sken error = 1; 743300207Sken goto bailout; 744300207Sken } else if (*endptr != '\0') { 745300207Sken warnx("Invalid timer value %s", optarg); 746300207Sken error = 1; 747300207Sken goto bailout; 748300207Sken } 749300207Sken timer_tenths = timer_val * 10; 750300207Sken break; 751300207Sken } 752300207Sken default: 753300207Sken break; 754300207Sken } 755300207Sken } 756300207Sken 757300207Sken if (action == -1) { 758300207Sken warnx("Must specify an action"); 759300207Sken error = 1; 760300207Sken goto bailout; 761300207Sken } 762300207Sken 763300207Sken error = get_device_type(device, retry_count, timeout, 764300207Sken /*printerrors*/ 1, &devtype); 765300207Sken if (error != 0) 766300207Sken errx(1, "Unable to determine device type"); 767300207Sken 768300207Sken switch (devtype) { 769300207Sken case CC_DT_ATA: 770350796Smav case CC_DT_SATL: 771300207Sken break; 772300207Sken default: 773300207Sken warnx("The epc subcommand only works with ATA protocol " 774300207Sken "devices"); 775300207Sken error = 1; 776300207Sken goto bailout; 777300207Sken break; /*NOTREACHED*/ 778300207Sken } 779300207Sken 780300207Sken switch (action) { 781300207Sken case ATA_SF_EPC_SET_TIMER: 782300207Sken if (timer_val == -1) { 783300207Sken warnx("Must specify a timer value (-T time)"); 784300207Sken error = 1; 785300207Sken } 786317374Sasomers /* FALLTHROUGH */ 787300207Sken case ATA_SF_EPC_SET_STATE: 788300207Sken if (enable == -1) { 789300207Sken warnx("Must specify enable (-e) or disable (-d)"); 790300207Sken error = 1; 791300207Sken } 792300207Sken /* FALLTHROUGH */ 793300207Sken case ATA_SF_EPC_GOTO: 794300207Sken if (power_cond == -1) { 795300207Sken warnx("Must specify a power condition with -p"); 796300207Sken error = 1; 797300207Sken } 798300207Sken if (error != 0) 799300207Sken goto bailout; 800300207Sken break; 801300207Sken case ATA_SF_EPC_SET_SOURCE: 802300207Sken if (power_src == -1) { 803300207Sken warnx("Must specify a power source (-S battery or " 804300207Sken "-S notbattery) value"); 805300207Sken error = 1; 806300207Sken goto bailout; 807300207Sken } 808300207Sken break; 809300207Sken case ATA_SF_EPC_RESTORE: 810300207Sken if (restore_src == -1) { 811300207Sken warnx("Must specify a source for restored value, " 812300207Sken "-r default or -r saved"); 813300207Sken error = 1; 814300207Sken goto bailout; 815300207Sken } 816300207Sken break; 817300207Sken case ATA_SF_EPC_ENABLE: 818300207Sken case ATA_SF_EPC_DISABLE: 819300207Sken case CCTL_EPC_GET_STATUS: 820300207Sken case CCTL_EPC_LIST: 821300207Sken default: 822300207Sken break; 823300207Sken } 824300207Sken 825300207Sken switch (action) { 826300207Sken case CCTL_EPC_GET_STATUS: 827300207Sken error = epc_getmode(device, devtype, ccb, retry_count, timeout, 828300207Sken power_only); 829300207Sken break; 830300207Sken case CCTL_EPC_LIST: 831300207Sken error = epc_list(device, devtype, ccb, retry_count, timeout); 832300207Sken break; 833300207Sken case ATA_SF_EPC_RESTORE: 834300207Sken case ATA_SF_EPC_GOTO: 835300207Sken case ATA_SF_EPC_SET_TIMER: 836300207Sken case ATA_SF_EPC_SET_STATE: 837300207Sken case ATA_SF_EPC_ENABLE: 838300207Sken case ATA_SF_EPC_DISABLE: 839300207Sken case ATA_SF_EPC_SET_SOURCE: 840300207Sken error = epc_set_features(device, devtype, ccb, retry_count, 841300207Sken timeout, action, power_cond, timer_tenths, enable, save, 842300207Sken delayed_entry, hold, power_src, restore_src); 843300207Sken break; 844300207Sken default: 845300207Sken warnx("Not implemented yet"); 846300207Sken error = 1; 847300207Sken goto bailout; 848300207Sken break; 849300207Sken } 850300207Sken 851300207Sken 852300207Skenbailout: 853300207Sken if (ccb != NULL) 854300207Sken cam_freeccb(ccb); 855300207Sken 856300207Sken return (error); 857300207Sken} 858