xref: /freebsd/sbin/camcontrol/epc.c (revision 5bf5ca772c6de2d53344a78cf461447cc322ccea)
1 /*-
2  * Copyright (c) 2016 Spectra Logic Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions, and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    substantially similar to the "NO WARRANTY" disclaimer below
13  *    ("Disclaimer") and any redistribution must be conditioned upon
14  *    including a substantially similar Disclaimer requirement for further
15  *    binary redistribution.
16  *
17  * NO WARRANTY
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGES.
29  *
30  * Authors: Ken Merry           (Spectra Logic Corporation)
31  */
32 /*
33  * ATA Extended Power Conditions (EPC) support
34  */
35 
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38 
39 #include <sys/ioctl.h>
40 #include <sys/stdint.h>
41 #include <sys/types.h>
42 #include <sys/endian.h>
43 #include <sys/sbuf.h>
44 #include <sys/queue.h>
45 #include <sys/ata.h>
46 
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <inttypes.h>
50 #include <unistd.h>
51 #include <string.h>
52 #include <strings.h>
53 #include <fcntl.h>
54 #include <ctype.h>
55 #include <limits.h>
56 #include <err.h>
57 #include <locale.h>
58 
59 #include <cam/cam.h>
60 #include <cam/cam_debug.h>
61 #include <cam/cam_ccb.h>
62 #include <cam/scsi/scsi_all.h>
63 #include <cam/scsi/scsi_da.h>
64 #include <cam/scsi/scsi_pass.h>
65 #include <cam/scsi/scsi_message.h>
66 #include <camlib.h>
67 #include "camcontrol.h"
68 
69 typedef enum {
70 	EPC_ACTION_NONE		= 0x00,
71 	EPC_ACTION_LIST		= 0x01,
72 	EPC_ACTION_TIMER_SET	= 0x02,
73 	EPC_ACTION_IMMEDIATE	= 0x03,
74 	EPC_ACTION_GETMODE	= 0x04
75 } epc_action;
76 
77 static struct scsi_nv epc_flags[] = {
78 	{ "Supported", ATA_PCL_COND_SUPPORTED },
79 	{ "Saveable", ATA_PCL_COND_SUPPORTED },
80 	{ "Changeable", ATA_PCL_COND_CHANGEABLE },
81 	{ "Default Timer Enabled", ATA_PCL_DEFAULT_TIMER_EN },
82 	{ "Saved Timer Enabled", ATA_PCL_SAVED_TIMER_EN },
83 	{ "Current Timer Enabled", ATA_PCL_CURRENT_TIMER_EN },
84 	{ "Hold Power Condition Not Supported", ATA_PCL_HOLD_PC_NOT_SUP }
85 };
86 
87 static struct scsi_nv epc_power_cond_map[] = {
88 	{ "Standby_z", ATA_EPC_STANDBY_Z },
89 	{ "z", ATA_EPC_STANDBY_Z },
90 	{ "Standby_y", ATA_EPC_STANDBY_Y },
91 	{ "y", ATA_EPC_STANDBY_Y },
92 	{ "Idle_a", ATA_EPC_IDLE_A },
93 	{ "a", ATA_EPC_IDLE_A },
94 	{ "Idle_b", ATA_EPC_IDLE_B },
95 	{ "b", ATA_EPC_IDLE_B },
96 	{ "Idle_c", ATA_EPC_IDLE_C },
97 	{ "c", ATA_EPC_IDLE_C }
98 };
99 
100 static struct scsi_nv epc_rst_val[] = {
101 	{ "default", ATA_SF_EPC_RST_DFLT },
102 	{ "saved", 0}
103 };
104 
105 static struct scsi_nv epc_ps_map[] = {
106 	{ "unknown", ATA_SF_EPC_SRC_UNKNOWN },
107 	{ "battery", ATA_SF_EPC_SRC_BAT },
108 	{ "notbattery", ATA_SF_EPC_SRC_NOT_BAT }
109 };
110 
111 /*
112  * These aren't subcommands of the EPC SET FEATURES subcommand, but rather
113  * commands that determine the current capabilities and status of the drive.
114  * The EPC subcommands are limited to 4 bits, so we won't collide with any
115  * future values.
116  */
117 #define	CCTL_EPC_GET_STATUS	0x8001
118 #define	CCTL_EPC_LIST		0x8002
119 
120 static struct scsi_nv epc_cmd_map[] = {
121 	{ "restore", ATA_SF_EPC_RESTORE },
122 	{ "goto", ATA_SF_EPC_GOTO },
123 	{ "timer", ATA_SF_EPC_SET_TIMER },
124 	{ "state", ATA_SF_EPC_SET_STATE },
125 	{ "enable", ATA_SF_EPC_ENABLE },
126 	{ "disable", ATA_SF_EPC_DISABLE },
127 	{ "source", ATA_SF_EPC_SET_SOURCE },
128 	{ "status", CCTL_EPC_GET_STATUS },
129 	{ "list", CCTL_EPC_LIST }
130 };
131 
132 static int epc_list(struct cam_device *device, camcontrol_devtype devtype,
133 		    union ccb *ccb, int retry_count, int timeout);
134 static void epc_print_pcl_desc(struct ata_power_cond_log_desc *desc,
135 			       const char *prefix);
136 static int epc_getmode(struct cam_device *device, camcontrol_devtype devtype,
137 		       union ccb *ccb, int retry_count, int timeout,
138 		       int power_only);
139 static int epc_set_features(struct cam_device *device,
140 			    camcontrol_devtype devtype, union ccb *ccb,
141 			    int retry_count, int timeout, int action,
142 			    int power_cond, int timer, int enable, int save,
143 			    int delayed_entry, int hold, int power_src,
144 			    int restore_src);
145 
146 static void
147 epc_print_pcl_desc(struct ata_power_cond_log_desc *desc, const char *prefix)
148 {
149 	int first;
150 	unsigned int i,	num_printed, max_chars;
151 
152 	first = 1;
153 	max_chars = 75;
154 
155 	num_printed = printf("%sFlags: ", prefix);
156 	for (i = 0; i < (sizeof(epc_flags) / sizeof(epc_flags[0])); i++) {
157 		if ((desc->flags & epc_flags[i].value) == 0)
158 			continue;
159 		if (first == 0) {
160 			num_printed += printf(", ");
161 		}
162 		if ((num_printed + strlen(epc_flags[i].name)) > max_chars) {
163 			printf("\n");
164 			num_printed = printf("%s       ", prefix);
165 		}
166 		num_printed += printf("%s", epc_flags[i].name);
167 		first = 0;
168 	}
169 	if (first != 0)
170 		printf("None");
171 	printf("\n");
172 
173 	printf("%sDefault timer setting: %.1f sec\n", prefix,
174 	    (double)(le32dec(desc->default_timer) / 10));
175 	printf("%sSaved timer setting: %.1f sec\n", prefix,
176 	    (double)(le32dec(desc->saved_timer) / 10));
177 	printf("%sCurrent timer setting: %.1f sec\n", prefix,
178 	    (double)(le32dec(desc->current_timer) / 10));
179 	printf("%sNominal time to active: %.1f sec\n", prefix,
180 	    (double)(le32dec(desc->nom_time_to_active) / 10));
181 	printf("%sMinimum timer: %.1f sec\n", prefix,
182 	    (double)(le32dec(desc->min_timer) / 10));
183 	printf("%sMaximum timer: %.1f sec\n", prefix,
184 	    (double)(le32dec(desc->max_timer) / 10));
185 	printf("%sNumber of transitions to power condition: %u\n", prefix,
186 	    le32dec(desc->num_transitions_to_pc));
187 	printf("%sHours in power condition: %u\n", prefix,
188 	    le32dec(desc->hours_in_pc));
189 }
190 
191 static int
192 epc_list(struct cam_device *device, camcontrol_devtype devtype, union ccb *ccb,
193 	 int retry_count, int timeout)
194 {
195 	struct ata_power_cond_log_idle *idle_log;
196 	struct ata_power_cond_log_standby *standby_log;
197 	uint8_t log_buf[sizeof(*idle_log) + sizeof(*standby_log)];
198 	uint16_t log_addr = ATA_POWER_COND_LOG;
199 	uint16_t page_number = ATA_PCL_IDLE;
200 	uint64_t lba;
201 	int error = 0;
202 
203 	lba = (((uint64_t)page_number & 0xff00) << 32) |
204 	      ((page_number & 0x00ff) << 8) |
205 	      (log_addr & 0xff);
206 
207 	error = build_ata_cmd(ccb,
208 	    /*retry_count*/ retry_count,
209 	    /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS,
210 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
211 	    /*protocol*/ AP_PROTO_DMA | AP_EXTEND,
212 	    /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS |
213 			  AP_FLAG_TLEN_SECT_CNT |
214 			  AP_FLAG_TDIR_FROM_DEV,
215 	    /*features*/ 0,
216 	    /*sector_count*/ 2,
217 	    /*lba*/ lba,
218 	    /*command*/ ATA_READ_LOG_DMA_EXT,
219 	    /*auxiliary*/ 0,
220 	    /*data_ptr*/ log_buf,
221 	    /*dxfer_len*/ sizeof(log_buf),
222 	    /*cdb_storage*/ NULL,
223 	    /*cdb_storage_len*/ 0,
224 	    /*sense_len*/ SSD_FULL_SIZE,
225 	    /*timeout*/ timeout ? timeout : 60000,
226 	    /*is48bit*/ 1,
227 	    /*devtype*/ devtype);
228 
229 	if (error != 0) {
230 		warnx("%s: build_ata_cmd() failed, likely programmer error",
231 		    __func__);
232 		goto bailout;
233 	}
234 
235 	if (retry_count > 0)
236 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
237 
238 	error = cam_send_ccb(device, ccb);
239 	if (error != 0) {
240 		warn("error sending ATA READ LOG EXT CCB");
241 		error = 1;
242 		goto bailout;
243 	}
244 
245 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
246 		cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr);
247 		error = 1;
248 		goto bailout;
249 	}
250 
251 	idle_log = (struct ata_power_cond_log_idle *)log_buf;
252 	standby_log =
253 	    (struct ata_power_cond_log_standby *)&log_buf[sizeof(*idle_log)];
254 
255 	printf("ATA Power Conditions Log:\n");
256 	printf("  Idle power conditions page:\n");
257 	printf("    Idle A condition:\n");
258 	epc_print_pcl_desc(&idle_log->idle_a_desc, "      ");
259 	printf("    Idle B condition:\n");
260 	epc_print_pcl_desc(&idle_log->idle_b_desc, "      ");
261 	printf("    Idle C condition:\n");
262 	epc_print_pcl_desc(&idle_log->idle_c_desc, "      ");
263 	printf("  Standby power conditions page:\n");
264 	printf("    Standby Y condition:\n");
265 	epc_print_pcl_desc(&standby_log->standby_y_desc, "      ");
266 	printf("    Standby Z condition:\n");
267 	epc_print_pcl_desc(&standby_log->standby_z_desc, "      ");
268 bailout:
269 	return (error);
270 }
271 
272 static int
273 epc_getmode(struct cam_device *device, camcontrol_devtype devtype,
274 	    union ccb *ccb, int retry_count, int timeout, int power_only)
275 {
276 	struct ata_params *ident = NULL;
277 	struct ata_identify_log_sup_cap sup_cap;
278 	const char *mode_name = NULL;
279 	uint8_t error = 0, ata_device = 0, status = 0;
280 	uint16_t count = 0;
281 	uint64_t lba = 0;
282 	uint32_t page_number, log_address;
283 	uint64_t caps = 0;
284 	int avail_bytes = 0;
285 	int res_available = 0;
286 	int retval;
287 
288 	retval = 0;
289 
290 	if (power_only != 0)
291 		goto check_power_mode;
292 
293 	/*
294 	 * Get standard ATA Identify data.
295 	 */
296 	retval = ata_do_identify(device, retry_count, timeout, ccb, &ident);
297 	if (retval != 0) {
298 		warnx("Couldn't get identify data");
299 		goto bailout;
300 	}
301 
302 	/*
303 	 * Get the ATA Identify Data Log (0x30),
304 	 * Supported Capabilities Page (0x03).
305 	 */
306 	log_address = ATA_IDENTIFY_DATA_LOG;
307 	page_number = ATA_IDL_SUP_CAP;
308 	lba = (((uint64_t)page_number & 0xff00) << 32) |
309 	       ((page_number & 0x00ff) << 8) |
310 	       (log_address & 0xff);
311 
312 	bzero(&sup_cap, sizeof(sup_cap));
313 	/*
314 	 * XXX KDM check the supported protocol.
315 	 */
316 	retval = build_ata_cmd(ccb,
317 	    /*retry_count*/ retry_count,
318 	    /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS,
319 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
320 	    /*protocol*/ AP_PROTO_DMA |
321 			 AP_EXTEND,
322 	    /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS |
323 			  AP_FLAG_TLEN_SECT_CNT |
324 			  AP_FLAG_TDIR_FROM_DEV,
325 	    /*features*/ 0,
326 	    /*sector_count*/ 1,
327 	    /*lba*/ lba,
328 	    /*command*/ ATA_READ_LOG_DMA_EXT,
329 	    /*auxiliary*/ 0,
330 	    /*data_ptr*/ (uint8_t *)&sup_cap,
331 	    /*dxfer_len*/ sizeof(sup_cap),
332 	    /*cdb_storage*/ NULL,
333 	    /*cdb_storage_len*/ 0,
334 	    /*sense_len*/ SSD_FULL_SIZE,
335 	    /*timeout*/ timeout ? timeout : 60000,
336 	    /*is48bit*/ 1,
337 	    /*devtype*/ devtype);
338 
339 	if (retval != 0) {
340 		warnx("%s: build_ata_cmd() failed, likely a programmer error",
341 		    __func__);
342 		goto bailout;
343 	}
344 
345 	if (retry_count > 0)
346 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
347 
348 	retval = cam_send_ccb(device, ccb);
349 	if (retval != 0) {
350 		warn("error sending ATA READ LOG CCB");
351 		retval = 1;
352 		goto bailout;
353 	}
354 
355 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
356 		cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr);
357 		retval = 1;
358 		goto bailout;
359 	}
360 
361 	if (ccb->ccb_h.func_code == XPT_SCSI_IO) {
362 		avail_bytes = ccb->csio.dxfer_len - ccb->csio.resid;
363 	} else {
364 		avail_bytes = ccb->ataio.dxfer_len - ccb->ataio.resid;
365 	}
366 	if (avail_bytes < (int)sizeof(sup_cap)) {
367 		warnx("Couldn't get enough of the ATA Supported "
368 		    "Capabilities log, %d bytes returned", avail_bytes);
369 		retval = 1;
370 		goto bailout;
371 	}
372 	caps = le64dec(sup_cap.sup_cap);
373 	if ((caps & ATA_SUP_CAP_VALID) == 0) {
374 		warnx("Supported capabilities bits are not valid");
375 		retval = 1;
376 		goto bailout;
377 	}
378 
379 	printf("APM: %sSupported, %sEnabled\n",
380 	    (ident->support.command2 & ATA_SUPPORT_APM) ? "" : "NOT ",
381 	    (ident->enabled.command2 & ATA_SUPPORT_APM) ? "" : "NOT ");
382 	printf("EPC: %sSupported, %sEnabled\n",
383 	    (ident->support2 & ATA_SUPPORT_EPC) ? "" : "NOT ",
384 	    (ident->enabled2 & ATA_ENABLED_EPC) ? "" : "NOT ");
385 	printf("Low Power Standby %sSupported\n",
386 	    (caps & ATA_SC_LP_STANDBY_SUP) ? "" : "NOT ");
387 	printf("Set EPC Power Source %sSupported\n",
388 	    (caps & ATA_SC_SET_EPC_PS_SUP) ? "" : "NOT ");
389 
390 
391 check_power_mode:
392 
393 	retval = build_ata_cmd(ccb,
394 	    /*retry_count*/ retry_count,
395 	    /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS,
396 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
397 	    /*protocol*/ AP_PROTO_NON_DATA |
398 			 AP_EXTEND,
399 	    /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS |
400 			  AP_FLAG_TLEN_NO_DATA |
401 			  AP_FLAG_CHK_COND,
402 	    /*features*/ ATA_SF_EPC,
403 	    /*sector_count*/ 0,
404 	    /*lba*/ 0,
405 	    /*command*/ ATA_CHECK_POWER_MODE,
406 	    /*auxiliary*/ 0,
407 	    /*data_ptr*/ NULL,
408 	    /*dxfer_len*/ 0,
409 	    /*cdb_storage*/ NULL,
410 	    /*cdb_storage_len*/ 0,
411 	    /*sense_len*/ SSD_FULL_SIZE,
412 	    /*timeout*/ timeout ? timeout : 60000,
413 	    /*is48bit*/ 0,
414 	    /*devtype*/ devtype);
415 
416 	if (retval != 0) {
417 		warnx("%s: build_ata_cmd() failed, likely a programmer error",
418 		    __func__);
419 		goto bailout;
420 	}
421 
422 	if (retry_count > 0)
423 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
424 
425 	retval = cam_send_ccb(device, ccb);
426 	if (retval != 0) {
427 		warn("error sending ATA CHECK POWER MODE CCB");
428 		retval = 1;
429 		goto bailout;
430 	}
431 
432 	/*
433 	 * Check to see whether we got the requested ATA result if this
434 	 * is an SCSI ATA PASS-THROUGH command.
435 	 */
436 	if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR)
437 	 && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND)) {
438 		int error_code, sense_key, asc, ascq;
439 
440 		retval = scsi_extract_sense_ccb(ccb, &error_code,
441 		    &sense_key, &asc, &ascq);
442 		if (retval == 0) {
443 			cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,
444 			    stderr);
445 			retval = 1;
446 			goto bailout;
447 		}
448 		if ((sense_key == SSD_KEY_RECOVERED_ERROR)
449 		 && (asc == 0x00)
450 		 && (ascq == 0x1d)) {
451 			res_available = 1;
452 		}
453 
454 	}
455 	if (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
456 	 && (res_available == 0)) {
457 		cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr);
458 		retval = 1;
459 		goto bailout;
460 	}
461 
462 	retval = get_ata_status(device, ccb, &error, &count, &lba, &ata_device,
463 	    &status);
464 	if (retval != 0) {
465 		warnx("Unable to get ATA CHECK POWER MODE result");
466 		retval = 1;
467 		goto bailout;
468 	}
469 
470 	mode_name = scsi_nv_to_str(epc_power_cond_map,
471 	    sizeof(epc_power_cond_map) / sizeof(epc_power_cond_map[0]), count);
472 	printf("Current power state: ");
473 	/* Note: ident can be null in power_only mode */
474 	if ((ident == NULL)
475 	 || (ident->enabled2 & ATA_ENABLED_EPC)) {
476 		if (mode_name != NULL)
477 			printf("%s", mode_name);
478 		else if (count == 0xff) {
479 			printf("PM0:Active or PM1:Idle");
480 		}
481 	} else {
482 		switch (count) {
483 		case 0x00:
484 			printf("PM2:Standby");
485 			break;
486 		case 0x80:
487 			printf("PM1:Idle");
488 			break;
489 		case 0xff:
490 			printf("PM0:Active or PM1:Idle");
491 			break;
492 		}
493 	}
494 	printf("(0x%02x)\n", count);
495 
496 	if (power_only != 0)
497 		goto bailout;
498 
499 	if (caps & ATA_SC_LP_STANDBY_SUP) {
500 		uint32_t wait_mode;
501 
502 		wait_mode = (lba >> 20) & 0xff;
503 		if (wait_mode == 0xff) {
504 			printf("Device not waiting to enter lower power "
505 			    "condition");
506 		} else {
507 			mode_name = scsi_nv_to_str(epc_power_cond_map,
508 			    sizeof(epc_power_cond_map) /
509 			    sizeof(epc_power_cond_map[0]), wait_mode);
510 			printf("Device waiting to enter mode %s (0x%02x)\n",
511 			    (mode_name != NULL) ? mode_name : "Unknown",
512 			    wait_mode);
513 		}
514 		printf("Device is %sheld in the current power condition\n",
515 		    (lba & 0x80000) ? "" : "NOT ");
516 	}
517 bailout:
518 	return (retval);
519 
520 }
521 
522 static int
523 epc_set_features(struct cam_device *device, camcontrol_devtype devtype,
524 		 union ccb *ccb, int retry_count, int timeout, int action,
525 		 int power_cond, int timer, int enable, int save,
526 		 int delayed_entry, int hold, int power_src, int restore_src)
527 {
528 	uint64_t lba;
529 	uint16_t count = 0;
530 	int error;
531 
532 	error = 0;
533 
534 	lba = action;
535 
536 	switch (action) {
537 	case ATA_SF_EPC_SET_TIMER:
538 		lba |= ((timer << ATA_SF_EPC_TIMER_SHIFT) &
539 			 ATA_SF_EPC_TIMER_MASK);
540 		/* FALLTHROUGH */
541 	case ATA_SF_EPC_SET_STATE:
542 		lba |= (enable ? ATA_SF_EPC_TIMER_EN : 0) |
543 		       (save ? ATA_SF_EPC_TIMER_SAVE : 0);
544 		count = power_cond;
545 		break;
546 	case ATA_SF_EPC_GOTO:
547 		count = power_cond;
548 		lba |= (delayed_entry ? ATA_SF_EPC_GOTO_DELAY : 0) |
549 		       (hold ? ATA_SF_EPC_GOTO_HOLD : 0);
550 		break;
551 	case ATA_SF_EPC_RESTORE:
552 		lba |= restore_src |
553 		       (save ? ATA_SF_EPC_RST_SAVE : 0);
554 		break;
555 	case ATA_SF_EPC_ENABLE:
556 	case ATA_SF_EPC_DISABLE:
557 		break;
558 	case ATA_SF_EPC_SET_SOURCE:
559 		count = power_src;
560 		break;
561 	}
562 
563 	error = build_ata_cmd(ccb,
564 	    /*retry_count*/ retry_count,
565 	    /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS,
566 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
567 	    /*protocol*/ AP_PROTO_NON_DATA | AP_EXTEND,
568 	    /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS |
569 			  AP_FLAG_TLEN_NO_DATA |
570 			  AP_FLAG_TDIR_FROM_DEV,
571 	    /*features*/ ATA_SF_EPC,
572 	    /*sector_count*/ count,
573 	    /*lba*/ lba,
574 	    /*command*/ ATA_SETFEATURES,
575 	    /*auxiliary*/ 0,
576 	    /*data_ptr*/ NULL,
577 	    /*dxfer_len*/ 0,
578 	    /*cdb_storage*/ NULL,
579 	    /*cdb_storage_len*/ 0,
580 	    /*sense_len*/ SSD_FULL_SIZE,
581 	    /*timeout*/ timeout ? timeout : 60000,
582 	    /*is48bit*/ 1,
583 	    /*devtype*/ devtype);
584 
585 	if (error != 0) {
586 		warnx("%s: build_ata_cmd() failed, likely a programmer error",
587 		    __func__);
588 		goto bailout;
589 	}
590 
591 	if (retry_count > 0)
592 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
593 
594 	error = cam_send_ccb(device, ccb);
595 	if (error != 0) {
596 		warn("error sending ATA SET FEATURES CCB");
597 		error = 1;
598 		goto bailout;
599 	}
600 
601 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
602 		cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr);
603 		error = 1;
604 		goto bailout;
605 	}
606 
607 bailout:
608 	return (error);
609 }
610 
611 int
612 epc(struct cam_device *device, int argc, char **argv, char *combinedopt,
613     int retry_count, int timeout, int verbosemode __unused)
614 {
615 	union ccb *ccb = NULL;
616 	int error = 0;
617 	int c;
618 	int action = -1;
619 	camcontrol_devtype devtype;
620 	double timer_val = -1;
621 	int timer_tenths = 0, power_cond = -1;
622 	int delayed_entry = 0, hold = 0;
623 	int enable = -1, save = 0;
624 	int restore_src = -1;
625 	int power_src = -1;
626 	int power_only = 0;
627 
628 
629 	ccb = cam_getccb(device);
630 	if (ccb == NULL) {
631 		warnx("%s: error allocating CCB", __func__);
632 		error = 1;
633 		goto bailout;
634 	}
635 
636 	CCB_CLEAR_ALL_EXCEPT_HDR(ccb);
637 
638 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
639 		switch (c) {
640 		case 'c': {
641 			scsi_nv_status status;
642 			int entry_num;
643 
644 			status = scsi_get_nv(epc_cmd_map,
645 			    (sizeof(epc_cmd_map) / sizeof(epc_cmd_map[0])),
646 			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
647 			if (status == SCSI_NV_FOUND)
648 				action = epc_cmd_map[entry_num].value;
649 			else {
650 				warnx("%s: %s: %s option %s", __func__,
651 				    (status == SCSI_NV_AMBIGUOUS) ?
652 				    "ambiguous" : "invalid", "epc command",
653 				    optarg);
654 				error = 1;
655 				goto bailout;
656 			}
657 			break;
658 		}
659 		case 'd':
660 			enable = 0;
661 			break;
662 		case 'D':
663 			delayed_entry = 1;
664 			break;
665 		case 'e':
666 			enable = 1;
667 			break;
668 		case 'H':
669 			hold = 1;
670 			break;
671 		case 'p': {
672 			scsi_nv_status status;
673 			int entry_num;
674 
675 			status = scsi_get_nv(epc_power_cond_map,
676 			    (sizeof(epc_power_cond_map) /
677 			     sizeof(epc_power_cond_map[0])), optarg,
678 			     &entry_num, SCSI_NV_FLAG_IG_CASE);
679 			if (status == SCSI_NV_FOUND)
680 				power_cond =epc_power_cond_map[entry_num].value;
681 			else {
682 				warnx("%s: %s: %s option %s", __func__,
683 				    (status == SCSI_NV_AMBIGUOUS) ?
684 				    "ambiguous" : "invalid", "power condition",
685 				    optarg);
686 				error = 1;
687 				goto bailout;
688 			}
689 			break;
690 		}
691 		case 'P':
692 			power_only = 1;
693 			break;
694 		case 'r': {
695 			scsi_nv_status status;
696 			int entry_num;
697 
698 			status = scsi_get_nv(epc_rst_val,
699 			    (sizeof(epc_rst_val) /
700 			     sizeof(epc_rst_val[0])), optarg,
701 			     &entry_num, SCSI_NV_FLAG_IG_CASE);
702 			if (status == SCSI_NV_FOUND)
703 				restore_src = epc_rst_val[entry_num].value;
704 			else {
705 				warnx("%s: %s: %s option %s", __func__,
706 				    (status == SCSI_NV_AMBIGUOUS) ?
707 				    "ambiguous" : "invalid",
708 				    "restore value source", optarg);
709 				error = 1;
710 				goto bailout;
711 			}
712 			break;
713 		}
714 		case 's':
715 			save = 1;
716 			break;
717 		case 'S': {
718 			scsi_nv_status status;
719 			int entry_num;
720 
721 			status = scsi_get_nv(epc_ps_map,
722 			    (sizeof(epc_ps_map) / sizeof(epc_ps_map[0])),
723 			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
724 			if (status == SCSI_NV_FOUND)
725 				power_src = epc_ps_map[entry_num].value;
726 			else {
727 				warnx("%s: %s: %s option %s", __func__,
728 				    (status == SCSI_NV_AMBIGUOUS) ?
729 				    "ambiguous" : "invalid", "power source",
730 				    optarg);
731 				error = 1;
732 				goto bailout;
733 			}
734 			break;
735 		}
736 		case 'T': {
737 			char *endptr;
738 
739 			timer_val = strtod(optarg, &endptr);
740 			if (timer_val < 0) {
741 				warnx("Invalid timer value %f", timer_val);
742 				error = 1;
743 				goto bailout;
744 			} else if (*endptr != '\0') {
745 				warnx("Invalid timer value %s", optarg);
746 				error = 1;
747 				goto bailout;
748 			}
749 			timer_tenths = timer_val * 10;
750 			break;
751 		}
752 		default:
753 			break;
754 		}
755 	}
756 
757 	if (action == -1) {
758 		warnx("Must specify an action");
759 		error = 1;
760 		goto bailout;
761 	}
762 
763 	error = get_device_type(device, retry_count, timeout,
764 	    /*printerrors*/ 1, &devtype);
765 	if (error != 0)
766 		errx(1, "Unable to determine device type");
767 
768 	switch (devtype) {
769 	case CC_DT_ATA:
770 	case CC_DT_ATA_BEHIND_SCSI:
771 		break;
772 	default:
773 		warnx("The epc subcommand only works with ATA protocol "
774 		    "devices");
775 		error = 1;
776 		goto bailout;
777 		break; /*NOTREACHED*/
778 	}
779 
780 	switch (action) {
781 	case ATA_SF_EPC_SET_TIMER:
782 		if (timer_val == -1) {
783 			warnx("Must specify a timer value (-T time)");
784 			error = 1;
785 		}
786 		/* FALLTHROUGH */
787 	case ATA_SF_EPC_SET_STATE:
788 		if (enable == -1) {
789 			warnx("Must specify enable (-e) or disable (-d)");
790 			error = 1;
791 		}
792 		/* FALLTHROUGH */
793 	case ATA_SF_EPC_GOTO:
794 		if (power_cond == -1) {
795 			warnx("Must specify a power condition with -p");
796 			error = 1;
797 		}
798 		if (error != 0)
799 			goto bailout;
800 		break;
801 	case ATA_SF_EPC_SET_SOURCE:
802 		if (power_src == -1) {
803 			warnx("Must specify a power source (-S battery or "
804 			    "-S notbattery) value");
805 			error = 1;
806 			goto bailout;
807 		}
808 		break;
809 	case ATA_SF_EPC_RESTORE:
810 		if (restore_src == -1) {
811 			warnx("Must specify a source for restored value, "
812 			    "-r default or -r saved");
813 			error = 1;
814 			goto bailout;
815 		}
816 		break;
817 	case ATA_SF_EPC_ENABLE:
818 	case ATA_SF_EPC_DISABLE:
819 	case CCTL_EPC_GET_STATUS:
820 	case CCTL_EPC_LIST:
821 	default:
822 		break;
823 	}
824 
825 	switch (action) {
826 	case CCTL_EPC_GET_STATUS:
827 		error = epc_getmode(device, devtype, ccb, retry_count, timeout,
828 		    power_only);
829 		break;
830 	case CCTL_EPC_LIST:
831 		error = epc_list(device, devtype, ccb, retry_count, timeout);
832 		break;
833 	case ATA_SF_EPC_RESTORE:
834 	case ATA_SF_EPC_GOTO:
835 	case ATA_SF_EPC_SET_TIMER:
836 	case ATA_SF_EPC_SET_STATE:
837 	case ATA_SF_EPC_ENABLE:
838 	case ATA_SF_EPC_DISABLE:
839 	case ATA_SF_EPC_SET_SOURCE:
840 		error = epc_set_features(device, devtype, ccb, retry_count,
841 		    timeout, action, power_cond, timer_tenths, enable, save,
842 		    delayed_entry, hold, power_src, restore_src);
843 		break;
844 	default:
845 		warnx("Not implemented yet");
846 		error = 1;
847 		goto bailout;
848 		break;
849 	}
850 
851 
852 bailout:
853 	if (ccb != NULL)
854 		cam_freeccb(ccb);
855 
856 	return (error);
857 }
858