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
66typedef 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
74static 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
84static 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
97static struct scsi_nv epc_rst_val[] = {
98	{ "default", ATA_SF_EPC_RST_DFLT },
99	{ "saved", 0}
100};
101
102static 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
117static 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
129static int epc_list(struct cam_device *device, camcontrol_devtype devtype,
130		    union ccb *ccb, int retry_count, int timeout);
131static void epc_print_pcl_desc(struct ata_power_cond_log_desc *desc,
132			       const char *prefix);
133static int epc_getmode(struct cam_device *device, camcontrol_devtype devtype,
134		       union ccb *ccb, int retry_count, int timeout,
135		       int power_only);
136static 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
143static void
144epc_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
188static int
189epc_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, "      ");
265bailout:
266	return (error);
267}
268
269static int
270epc_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
388check_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	}
514bailout:
515	return (retval);
516
517}
518
519static int
520epc_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
604bailout:
605	return (error);
606}
607
608int
609epc(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
847bailout:
848	if (ccb != NULL)
849		cam_freeccb(ccb);
850
851	return (error);
852}
853