Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/camcontrol/epc.c
39478 views
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
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
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
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
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
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
854