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