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