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 if (strptime(timestamp_string, format_string,
286 &time_struct) == NULL) {
287 warnx("%s: strptime(3) failed", __func__);
291 time_value = mktime(&time_struct);
292 ts = (uint64_t) time_value;
294 /* Convert time from seconds to milliseconds */
296 bzero(&ts_p, sizeof(ts_p));
297 scsi_create_timestamp(ts_p.timestamp, ts);
299 scsi_set_timestamp(&ccb->csio,
300 /*retries*/ retry_count,
302 /*tag_action*/ task_attr,
304 /*buf_len*/ sizeof(ts_p),
305 /*sense_len*/ SSD_FULL_SIZE,
306 /*timeout*/ timeout ? timeout : 5000);
308 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
310 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
312 error = cam_send_ccb(device, ccb);
314 warn("error sending Set Timestamp");
318 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
319 cam_error_print(device, ccb, CAM_ESF_ALL,
320 CAM_EPF_ALL, stderr);
325 printf("Timestamp set to %ju\n", (uintmax_t)ts);
328 if (do_restore_flags != 0)
329 error = set_restore_flags(device, &flags, /*set_flag*/ 0,
330 task_attr, retry_count, timeout);
338 timestamp(struct cam_device *device, int argc, char **argv, char *combinedopt,
339 int task_attr, int retry_count, int timeout, int verbosemode __unused)
343 char *format_string = NULL;
344 char *timestamp_string = NULL;
350 while ((c = getopt(argc, argv, combinedopt)) != -1) {
354 warnx("Use only one -r or only one -s");
358 action = TIMESTAMP_REPORT;
363 warnx("Use only one -r or only one -s");
367 action = TIMESTAMP_SET;
373 format_string = strdup(optarg);
374 if (format_string == NULL) {
375 warn("Error allocating memory for format "
385 format_string = strdup(MIL);
386 if (format_string == NULL) {
387 warn("Error allocating memory");
398 free(timestamp_string);
399 timestamp_string = strdup(optarg);
400 if (timestamp_string == NULL) {
401 warn("Error allocating memory for format "
413 warnx("Must specify an action, either -r or -s");
418 if (single_arg > 1) {
419 warnx("Select only one: %s",
420 (action == TIMESTAMP_REPORT) ?
421 "-f format or -m for the -r flag" :
422 "-f format -T time or -U for the -s flag");
427 if (action == TIMESTAMP_SET) {
428 if ((format_string == NULL)
430 warnx("Must specify either -f format or -U for "
431 "setting the timestamp");
433 } else if ((format_string != NULL)
435 warnx("Must specify only one of -f or -U to set "
438 } else if ((format_string != NULL)
439 && (strcmp(format_string, MIL) == 0)) {
440 warnx("-m is not allowed for setting the "
443 } else if ((do_utc == 0)
444 && (timestamp_string == NULL)) {
445 warnx("Must specify the time (-T) to set as the "
451 } else if (action == TIMESTAMP_REPORT) {
452 if (format_string == NULL) {
453 format_string = strdup("%c %Z");
454 if (format_string == NULL) {
455 warn("Error allocating memory for format "
463 if (action == TIMESTAMP_REPORT) {
464 error = report_timestamp(device, &ts, task_attr, retry_count,
468 } else if (strcmp(format_string, MIL) == 0) {
469 printf("Timestamp in milliseconds: %ju\n",
472 char temp_timestamp_string[100];
473 time_t time_var = ts / 1000;
474 const struct tm *restrict cur_time;
476 setlocale(LC_TIME, "");
478 cur_time = gmtime(&time_var);
480 cur_time = localtime(&time_var);
482 strftime(temp_timestamp_string,
483 sizeof(temp_timestamp_string), format_string,
485 printf("Formatted timestamp: %s\n",
486 temp_timestamp_string);
488 } else if (action == TIMESTAMP_SET) {
490 format_string = strdup(UTC);
491 if (format_string == NULL) {
492 warn("Error allocating memory for format "
499 error = set_timestamp(device, format_string, timestamp_string,
500 task_attr, retry_count, timeout);
505 free(timestamp_string);