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