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