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 union ccb *ccb = NULL;
83 unsigned long mode_buf_size = sizeof(struct scsi_mode_header_10) +
84 sizeof(struct scsi_mode_blk_desc) +
85 sizeof(struct scsi_control_ext_page);
86 uint8_t mode_buf[mode_buf_size];
88 ccb = cam_getccb(device);
90 warnx("%s: error allocating CCB", __func__);
95 * Get the control extension subpage, we'll send it back modified to
96 * enable SCSI control over the tape drive's timestamp
98 scsi_mode_sense_subpage(&ccb->csio,
99 /*retries*/ retry_count,
101 /*tag_action*/ task_attr,
103 /*page_control*/ SMS_PAGE_CTRL_CURRENT,
104 /*page*/ SCEP_PAGE_CODE,
105 /*subpage*/ SCEP_SUBPAGE_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 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
114 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
116 error = cam_send_ccb(device, ccb);
118 warn("error sending Mode Sense");
122 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
123 cam_error_print(device, ccb, CAM_ESF_ALL,
124 CAM_EPF_ALL, stderr);
129 mode_hdr = (struct scsi_mode_header_10 *)&mode_buf[0];
130 blk_desc_length = scsi_2btoul(mode_hdr->blk_desc_len);
131 hdr_and_blk_length = sizeof(struct scsi_mode_header_10)+blk_desc_length;
133 * Create the control page at the correct point in the mode_buf, it
134 * starts after the header and the blk description.
136 assert(hdr_and_blk_length <=
137 sizeof(mode_buf) - sizeof(struct scsi_control_ext_page));
138 control_page = (struct scsi_control_ext_page *)&mode_buf
139 [hdr_and_blk_length];
141 *flags = control_page->flags;
143 * Set the SCSIP flag to enable SCSI to change the
144 * tape drive's timestamp.
146 control_page->flags |= SCEP_SCSIP;
148 control_page->flags = *flags;
151 scsi_mode_select_len(&ccb->csio,
152 /*retries*/ retry_count,
154 /*tag_action*/ task_attr,
157 /*param_buf*/ &mode_buf[0],
158 /*param_len*/ mode_buf_size,
159 /*minimum_cmd_size*/ 10,
160 /*sense_len*/ SSD_FULL_SIZE,
161 /*timeout*/ timeout ? timeout : 5000);
163 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
165 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
167 error = cam_send_ccb(device, ccb);
169 warn("error sending Mode Select");
173 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
174 cam_error_print(device, ccb, CAM_ESF_ALL,
175 CAM_EPF_ALL, stderr);
188 report_timestamp(struct cam_device *device, uint64_t *ts, int task_attr,
189 int retry_count, int timeout)
192 struct scsi_report_timestamp_data *report_buf = malloc(
193 sizeof(struct scsi_report_timestamp_data));
194 uint8_t temp_timestamp[8];
195 uint32_t report_buf_size = sizeof(
196 struct scsi_report_timestamp_data);
197 union ccb *ccb = NULL;
199 ccb = cam_getccb(device);
201 warnx("%s: error allocating CCB", __func__);
206 scsi_report_timestamp(&ccb->csio,
207 /*retries*/ retry_count,
209 /*tag_action*/ task_attr,
212 /*buf_len*/ report_buf_size,
213 /*sense_len*/ SSD_FULL_SIZE,
214 /*timeout*/ timeout ? timeout : 5000);
216 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
218 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
220 error = cam_send_ccb(device, ccb);
222 warn("error sending Report Timestamp");
225 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
226 cam_error_print(device, ccb, CAM_ESF_ALL,
227 CAM_EPF_ALL, stderr);
232 bzero(temp_timestamp, sizeof(temp_timestamp));
233 memcpy(&temp_timestamp[2], &report_buf->timestamp, 6);
235 *ts = scsi_8btou64(temp_timestamp);
246 set_timestamp(struct cam_device *device, char *format_string,
247 char *timestamp_string, int task_attr, int retry_count,
250 struct scsi_set_timestamp_parameters ts_p;
252 struct tm time_struct;
256 union ccb *ccb = NULL;
257 int do_restore_flags = 0;
259 error = set_restore_flags(device, &flags, /*set_flag*/ 1, task_attr,
260 retry_count, timeout);
264 do_restore_flags = 1;
266 ccb = cam_getccb(device);
268 warnx("%s: error allocating CCB", __func__);
273 if (strcmp(format_string, UTC) == 0) {
275 ts = (uint64_t) time_value;
277 bzero(&time_struct, sizeof(struct tm));
278 if (strptime(timestamp_string, format_string,
279 &time_struct) == NULL) {
280 warnx("%s: strptime(3) failed", __func__);
284 time_value = mktime(&time_struct);
285 ts = (uint64_t) time_value;
287 /* Convert time from seconds to milliseconds */
289 bzero(&ts_p, sizeof(ts_p));
290 scsi_create_timestamp(ts_p.timestamp, ts);
292 scsi_set_timestamp(&ccb->csio,
293 /*retries*/ retry_count,
295 /*tag_action*/ task_attr,
297 /*buf_len*/ sizeof(ts_p),
298 /*sense_len*/ SSD_FULL_SIZE,
299 /*timeout*/ timeout ? timeout : 5000);
301 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
303 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
305 error = cam_send_ccb(device, ccb);
307 warn("error sending Set Timestamp");
311 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
312 cam_error_print(device, ccb, CAM_ESF_ALL,
313 CAM_EPF_ALL, stderr);
318 printf("Timestamp set to %ju\n", (uintmax_t)ts);
321 if (do_restore_flags != 0)
322 error = set_restore_flags(device, &flags, /*set_flag*/ 0,
323 task_attr, retry_count, timeout);
331 timestamp(struct cam_device *device, int argc, char **argv, char *combinedopt,
332 int task_attr, int retry_count, int timeout, int verbosemode __unused)
336 char *format_string = NULL;
337 char *timestamp_string = NULL;
343 while ((c = getopt(argc, argv, combinedopt)) != -1) {
347 warnx("Use only one -r or only one -s");
351 action = TIMESTAMP_REPORT;
356 warnx("Use only one -r or only one -s");
360 action = TIMESTAMP_SET;
366 format_string = strdup(optarg);
367 if (format_string == NULL) {
368 warn("Error allocating memory for format "
378 format_string = strdup(MIL);
379 if (format_string == NULL) {
380 warn("Error allocating memory");
391 free(timestamp_string);
392 timestamp_string = strdup(optarg);
393 if (timestamp_string == NULL) {
394 warn("Error allocating memory for format "
406 warnx("Must specify an action, either -r or -s");
411 if (single_arg > 1) {
412 warnx("Select only one: %s",
413 (action == TIMESTAMP_REPORT) ?
414 "-f format or -m for the -r flag" :
415 "-f format -T time or -U for the -s flag");
420 if (action == TIMESTAMP_SET) {
421 if ((format_string == NULL)
423 warnx("Must specify either -f format or -U for "
424 "setting the timestamp");
426 } else if ((format_string != NULL)
428 warnx("Must specify only one of -f or -U to set "
431 } else if ((format_string != NULL)
432 && (strcmp(format_string, MIL) == 0)) {
433 warnx("-m is not allowed for setting the "
436 } else if ((do_utc == 0)
437 && (timestamp_string == NULL)) {
438 warnx("Must specify the time (-T) to set as the "
444 } else if (action == TIMESTAMP_REPORT) {
445 if (format_string == NULL) {
446 format_string = strdup("%c %Z");
447 if (format_string == NULL) {
448 warn("Error allocating memory for format "
456 if (action == TIMESTAMP_REPORT) {
457 error = report_timestamp(device, &ts, task_attr, retry_count,
461 } else if (strcmp(format_string, MIL) == 0) {
462 printf("Timestamp in milliseconds: %ju\n",
465 char temp_timestamp_string[100];
466 time_t time_var = ts / 1000;
467 const struct tm *restrict cur_time;
469 setlocale(LC_TIME, "");
471 cur_time = gmtime(&time_var);
473 cur_time = localtime(&time_var);
475 strftime(temp_timestamp_string,
476 sizeof(temp_timestamp_string), format_string,
478 printf("Formatted timestamp: %s\n",
479 temp_timestamp_string);
481 } else if (action == TIMESTAMP_SET) {
483 format_string = strdup(UTC);
484 if (format_string == NULL) {
485 warn("Error allocating memory for format "
492 error = set_timestamp(device, format_string, timestamp_string,
493 task_attr, retry_count, timeout);
498 free(timestamp_string);