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: stable/11/sbin/camcontrol/epc.c 350796 2019-08-08 21:55:49Z mav $");
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
69typedef 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
77static 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
87static 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
100static struct scsi_nv epc_rst_val[] = {
101	{ "default", ATA_SF_EPC_RST_DFLT },
102	{ "saved", 0}
103};
104
105static 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
120static 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
132static int epc_list(struct cam_device *device, camcontrol_devtype devtype,
133		    union ccb *ccb, int retry_count, int timeout);
134static void epc_print_pcl_desc(struct ata_power_cond_log_desc *desc,
135			       const char *prefix);
136static int epc_getmode(struct cam_device *device, camcontrol_devtype devtype,
137		       union ccb *ccb, int retry_count, int timeout,
138		       int power_only);
139static 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
146static void
147epc_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
191static int
192epc_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, "      ");
268bailout:
269	return (error);
270}
271
272static int
273epc_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
391check_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	}
517bailout:
518	return (retval);
519
520}
521
522static int
523epc_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
607bailout:
608	return (error);
609}
610
611int
612epc(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_SATL:
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
852bailout:
853	if (ccb != NULL)
854		cam_freeccb(ccb);
855
856	return (error);
857}
858