2 * Copyright (c) 2016 Spectra Logic Corporation
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 * Authors: Ken Merry (Spectra Logic Corporation)
31 * Reid Linnemann (Spectra Logic Corporation)
32 * Samuel Klopsch (Spectra Logic Corporation)
35 * SCSI tape drive timestamp support
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
41 #include <sys/types.h>
53 #include <cam/cam_debug.h>
54 #include <cam/cam_ccb.h>
55 #include <cam/scsi/scsi_all.h>
56 #include <cam/scsi/scsi_message.h>
58 #include "camcontrol.h"
60 #define TIMESTAMP_REPORT 0
61 #define TIMESTAMP_SET 1
62 #define MIL "milliseconds"
65 static int set_restore_flags(struct cam_device *device, uint8_t *flags,
66 int set_flag, int task_attr, int retry_count,
68 static int report_timestamp(struct cam_device *device, uint64_t *ts,
69 int task_attr, int retry_count, int timeout);
70 static int set_timestamp(struct cam_device *device, char *format_string,
71 char *timestamp_string, int task_attr, int retry_count,
75 set_restore_flags(struct cam_device *device, uint8_t *flags, int set_flag,
76 int task_attr, int retry_count, int timeout)
78 unsigned long blk_desc_length, hdr_and_blk_length;
80 struct scsi_control_ext_page *control_page = NULL;
81 struct scsi_mode_header_10 *mode_hdr = NULL;
82 struct scsi_mode_sense_10 *cdb = NULL;
83 union ccb *ccb = NULL;
84 unsigned long mode_buf_size = sizeof(struct scsi_mode_header_10) +
85 sizeof(struct scsi_mode_blk_desc) +
86 sizeof(struct scsi_control_ext_page);
87 uint8_t mode_buf[mode_buf_size];
89 ccb = cam_getccb(device);
91 warnx("%s: error allocating CCB", __func__);
96 * Get the control extension subpage, we'll send it back modified to
97 * enable SCSI control over the tape drive's timestamp
99 scsi_mode_sense_len(&ccb->csio,
100 /*retries*/ retry_count,
102 /*tag_action*/ task_attr,
104 /*page_control*/ SMS_PAGE_CTRL_CURRENT,
105 /*page*/ SCEP_PAGE_CODE,
106 /*param_buf*/ &mode_buf[0],
107 /*param_len*/ mode_buf_size,
108 /*minimum_cmd_size*/ 10,
109 /*sense_len*/ SSD_FULL_SIZE,
110 /*timeout*/ timeout ? timeout : 5000);
112 * scsi_mode_sense_len does not have a subpage argument at the moment,
113 * so we have to manually set the subpage code before calling
116 cdb = (struct scsi_mode_sense_10 *)ccb->csio.cdb_io.cdb_bytes;
117 cdb->subpage = SCEP_SUBPAGE_CODE;
119 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
121 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
123 error = cam_send_ccb(device, ccb);
125 warn("error sending Mode Sense");
129 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
130 cam_error_print(device, ccb, CAM_ESF_ALL,
131 CAM_EPF_ALL, stderr);
136 mode_hdr = (struct scsi_mode_header_10 *)&mode_buf[0];
137 blk_desc_length = scsi_2btoul(mode_hdr->blk_desc_len);
138 hdr_and_blk_length = sizeof(struct scsi_mode_header_10)+blk_desc_length;
140 * Create the control page at the correct point in the mode_buf, it
141 * starts after the header and the blk description.
143 assert(hdr_and_blk_length <=
144 sizeof(mode_buf) - sizeof(struct scsi_control_ext_page));
145 control_page = (struct scsi_control_ext_page *)&mode_buf
146 [hdr_and_blk_length];
148 *flags = control_page->flags;
150 * Set the SCSIP flag to enable SCSI to change the
151 * tape drive's timestamp.
153 control_page->flags |= SCEP_SCSIP;
155 control_page->flags = *flags;
158 scsi_mode_select_len(&ccb->csio,
159 /*retries*/ retry_count,
161 /*tag_action*/ task_attr,
164 /*param_buf*/ &mode_buf[0],
165 /*param_len*/ mode_buf_size,
166 /*minimum_cmd_size*/ 10,
167 /*sense_len*/ SSD_FULL_SIZE,
168 /*timeout*/ timeout ? timeout : 5000);
170 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
172 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
174 error = cam_send_ccb(device, ccb);
176 warn("error sending Mode Select");
180 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
181 cam_error_print(device, ccb, CAM_ESF_ALL,
182 CAM_EPF_ALL, stderr);
195 report_timestamp(struct cam_device *device, uint64_t *ts, int task_attr,
196 int retry_count, int timeout)
199 struct scsi_report_timestamp_data *report_buf = malloc(
200 sizeof(struct scsi_report_timestamp_data));
201 uint8_t temp_timestamp[8];
202 uint32_t report_buf_size = sizeof(
203 struct scsi_report_timestamp_data);
204 union ccb *ccb = NULL;
206 ccb = cam_getccb(device);
208 warnx("%s: error allocating CCB", __func__);
213 scsi_report_timestamp(&ccb->csio,
214 /*retries*/ retry_count,
216 /*tag_action*/ task_attr,
219 /*buf_len*/ report_buf_size,
220 /*sense_len*/ SSD_FULL_SIZE,
221 /*timeout*/ timeout ? timeout : 5000);
223 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
225 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
227 error = cam_send_ccb(device, ccb);
229 warn("error sending Report Timestamp");
232 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
233 cam_error_print(device, ccb, CAM_ESF_ALL,
234 CAM_EPF_ALL, stderr);
239 bzero(temp_timestamp, sizeof(temp_timestamp));
240 memcpy(&temp_timestamp[2], &report_buf->timestamp, 6);
242 *ts = scsi_8btou64(temp_timestamp);
253 set_timestamp(struct cam_device *device, char *format_string,
254 char *timestamp_string, int task_attr, int retry_count,
257 struct scsi_set_timestamp_parameters ts_p;
259 struct tm time_struct;
263 union ccb *ccb = NULL;
264 int do_restore_flags = 0;
266 error = set_restore_flags(device, &flags, /*set_flag*/ 1, task_attr,
267 retry_count, timeout);
271 do_restore_flags = 1;
273 ccb = cam_getccb(device);
275 warnx("%s: error allocating CCB", __func__);
280 if (strcmp(format_string, UTC) == 0) {
282 ts = (uint64_t) time_value;
284 bzero(&time_struct, sizeof(struct tm));
285 strptime(timestamp_string, format_string, &time_struct);
286 time_value = mktime(&time_struct);
287 ts = (uint64_t) time_value;
289 /* Convert time from seconds to milliseconds */
291 scsi_create_timestamp(ts_p.timestamp, ts);
293 scsi_set_timestamp(&ccb->csio,
294 /*retries*/ retry_count,
296 /*tag_action*/ task_attr,
298 /*buf_len*/ sizeof(ts_p),
299 /*sense_len*/ SSD_FULL_SIZE,
300 /*timeout*/ timeout ? timeout : 5000);
302 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
304 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
306 error = cam_send_ccb(device, ccb);
308 warn("error sending Set Timestamp");
312 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
313 cam_error_print(device, ccb, CAM_ESF_ALL,
314 CAM_EPF_ALL, stderr);
319 printf("Timestamp set to %ju\n", (uintmax_t)ts);
322 if (do_restore_flags != 0)
323 error = set_restore_flags(device, &flags, /*set_flag*/ 0,
324 task_attr, retry_count, timeout);
332 timestamp(struct cam_device *device, int argc, char **argv, char *combinedopt,
333 int task_attr, int retry_count, int timeout, int verbosemode __unused)
337 char *format_string = NULL;
338 char *timestamp_string = NULL;
344 while ((c = getopt(argc, argv, combinedopt)) != -1) {
348 warnx("Use only one -r or only one -s");
352 action = TIMESTAMP_REPORT;
357 warnx("Use only one -r or only one -s");
361 action = TIMESTAMP_SET;
367 format_string = strdup(optarg);
368 if (format_string == NULL) {
369 warn("Error allocating memory for format "
379 format_string = strdup(MIL);
380 if (format_string == NULL) {
381 warn("Error allocating memory");
392 free(timestamp_string);
393 timestamp_string = strdup(optarg);
394 if (timestamp_string == NULL) {
395 warn("Error allocating memory for format "
407 warnx("Must specify an action, either -r or -s");
412 if (single_arg > 1) {
413 warnx("Select only one: %s",
414 (action == TIMESTAMP_REPORT) ?
415 "-f format or -m for the -r flag" :
416 "-f format -T time or -U for the -s flag");
421 if (action == TIMESTAMP_SET) {
422 if ((format_string == NULL)
424 warnx("Must specify either -f format or -U for "
425 "setting the timestamp");
427 } else if ((format_string != NULL)
429 warnx("Must specify only one of -f or -U to set "
432 } else if ((format_string != NULL)
433 && (strcmp(format_string, MIL) == 0)) {
434 warnx("-m is not allowed for setting the "
437 } else if ((do_utc == 0)
438 && (timestamp_string == NULL)) {
439 warnx("Must specify the time (-T) to set as the "
445 } else if (action == TIMESTAMP_REPORT) {
446 if (format_string == NULL) {
447 format_string = strdup("%c %Z");
448 if (format_string == NULL) {
449 warn("Error allocating memory for format "
457 if (action == TIMESTAMP_REPORT) {
458 error = report_timestamp(device, &ts, task_attr, retry_count,
462 } else if (strcmp(format_string, MIL) == 0) {
463 printf("Timestamp in milliseconds: %ju\n",
466 char temp_timestamp_string[100];
467 time_t time_var = ts / 1000;
468 const struct tm *restrict cur_time;
470 setlocale(LC_TIME, "");
472 cur_time = gmtime(&time_var);
474 cur_time = localtime(&time_var);
476 strftime(temp_timestamp_string,
477 sizeof(temp_timestamp_string), format_string,
479 printf("Formatted timestamp: %s\n",
480 temp_timestamp_string);
482 } else if (action == TIMESTAMP_SET) {
484 format_string = strdup(UTC);
485 if (format_string == NULL) {
486 warn("Error allocating memory for format "
493 error = set_timestamp(device, format_string, timestamp_string,
494 task_attr, retry_count, timeout);
499 free(timestamp_string);