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>
52 #include <cam/cam_debug.h>
53 #include <cam/cam_ccb.h>
54 #include <cam/scsi/scsi_all.h>
55 #include <cam/scsi/scsi_message.h>
57 #include "camcontrol.h"
59 #define TIMESTAMP_REPORT 0
60 #define TIMESTAMP_SET 1
61 #define MIL "milliseconds"
64 static int set_restore_flags(struct cam_device *device, uint8_t *flags,
65 int set_flag, int retry_count, int timeout);
66 static int report_timestamp(struct cam_device *device, uint64_t *ts,
67 int retry_count, int timeout);
68 static int set_timestamp(struct cam_device *device, char *format_string,
69 char *timestamp_string,
70 int retry_count, int timeout);
73 set_restore_flags(struct cam_device *device, uint8_t *flags, int set_flag,
74 int retry_count, int timeout)
76 unsigned long blk_desc_length, hdr_and_blk_length;
78 struct scsi_control_ext_page *control_page = NULL;
79 struct scsi_mode_header_10 *mode_hdr = NULL;
80 struct scsi_mode_sense_10 *cdb = NULL;
81 union ccb *ccb = NULL;
82 unsigned long mode_buf_size = sizeof(struct scsi_mode_header_10) +
83 sizeof(struct scsi_mode_blk_desc) +
84 sizeof(struct scsi_control_ext_page);
85 uint8_t mode_buf[mode_buf_size];
87 ccb = cam_getccb(device);
89 warnx("%s: error allocating CCB", __func__);
94 * Get the control extension subpage, we'll send it back modified to
95 * enable SCSI control over the tape drive's timestamp
97 scsi_mode_sense_len(&ccb->csio,
98 /*retries*/ retry_count,
100 /*tag_action*/ MSG_SIMPLE_Q_TAG,
102 /*page_control*/ SMS_PAGE_CTRL_CURRENT,
103 /*page*/ SCEP_PAGE_CODE,
104 /*param_buf*/ &mode_buf[0],
105 /*param_len*/ mode_buf_size,
106 /*minimum_cmd_size*/ 10,
107 /*sense_len*/ SSD_FULL_SIZE,
108 /*timeout*/ timeout ? timeout : 5000);
110 * scsi_mode_sense_len does not have a subpage argument at the moment,
111 * so we have to manually set the subpage code before calling
114 cdb = (struct scsi_mode_sense_10 *)ccb->csio.cdb_io.cdb_bytes;
115 cdb->subpage = SCEP_SUBPAGE_CODE;
117 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
119 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
121 error = cam_send_ccb(device, ccb);
123 warn("error sending Mode Sense");
127 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
128 cam_error_print(device, ccb, CAM_ESF_ALL,
129 CAM_EPF_ALL, stderr);
134 mode_hdr = (struct scsi_mode_header_10 *)&mode_buf[0];
135 blk_desc_length = scsi_2btoul(mode_hdr->blk_desc_len);
136 hdr_and_blk_length = sizeof(struct scsi_mode_header_10)+blk_desc_length;
138 * Create the control page at the correct point in the mode_buf, it
139 * starts after the header and the blk description.
141 control_page = (struct scsi_control_ext_page *)&mode_buf
142 [hdr_and_blk_length];
144 *flags = control_page->flags;
146 * Set the SCSIP flag to enable SCSI to change the
147 * tape drive's timestamp.
149 control_page->flags |= SCEP_SCSIP;
151 control_page->flags = *flags;
154 scsi_mode_select_len(&ccb->csio,
155 /*retries*/ retry_count,
157 /*tag_action*/ MSG_SIMPLE_Q_TAG,
160 /*param_buf*/ &mode_buf[0],
161 /*param_len*/ mode_buf_size,
162 /*minimum_cmd_size*/ 10,
163 /*sense_len*/ SSD_FULL_SIZE,
164 /*timeout*/ timeout ? timeout : 5000);
166 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
168 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
170 error = cam_send_ccb(device, ccb);
172 warn("error sending Mode Select");
176 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
177 cam_error_print(device, ccb, CAM_ESF_ALL,
178 CAM_EPF_ALL, stderr);
191 report_timestamp(struct cam_device *device, uint64_t *ts,
192 int retry_count, int timeout)
195 struct scsi_report_timestamp_data *report_buf = malloc(
196 sizeof(struct scsi_report_timestamp_data));
197 uint8_t temp_timestamp[8];
198 uint32_t report_buf_size = sizeof(
199 struct scsi_report_timestamp_data);
200 union ccb *ccb = NULL;
202 ccb = cam_getccb(device);
204 warnx("%s: error allocating CCB", __func__);
209 scsi_report_timestamp(&ccb->csio,
210 /*retries*/ retry_count,
212 /*tag_action*/ MSG_SIMPLE_Q_TAG,
215 /*buf_len*/ report_buf_size,
216 /*sense_len*/ SSD_FULL_SIZE,
217 /*timeout*/ timeout ? timeout : 5000);
219 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
221 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
223 error = cam_send_ccb(device, ccb);
225 warn("error sending Report Timestamp");
228 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
229 cam_error_print(device, ccb, CAM_ESF_ALL,
230 CAM_EPF_ALL, stderr);
235 bzero(temp_timestamp, sizeof(temp_timestamp));
236 memcpy(&temp_timestamp[2], &report_buf->timestamp, 6);
238 *ts = scsi_8btou64(temp_timestamp);
248 set_timestamp(struct cam_device *device, char *format_string,
249 char *timestamp_string, int retry_count,
252 struct scsi_set_timestamp_parameters ts_p;
254 struct tm time_struct;
258 union ccb *ccb = NULL;
259 int do_restore_flags = 0;
261 error = set_restore_flags(device, &flags, /*set_flag*/ 1, retry_count,
266 do_restore_flags = 1;
268 ccb = cam_getccb(device);
270 warnx("%s: error allocating CCB", __func__);
275 if (strcmp(format_string, UTC) == 0) {
277 ts = (uint64_t) time_value;
279 bzero(&time_struct, sizeof(struct tm));
280 strptime(timestamp_string, format_string, &time_struct);
281 time_value = mktime(&time_struct);
282 ts = (uint64_t) time_value;
284 /* Convert time from seconds to milliseconds */
286 scsi_create_timestamp(ts_p.timestamp, ts);
288 scsi_set_timestamp(&ccb->csio,
289 /*retries*/ retry_count,
291 /*tag_action*/ MSG_SIMPLE_Q_TAG,
293 /*buf_len*/ sizeof(ts_p),
294 /*sense_len*/ SSD_FULL_SIZE,
295 /*timeout*/ timeout ? timeout : 5000);
297 ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
299 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
301 error = cam_send_ccb(device, ccb);
303 warn("error sending Set Timestamp");
307 if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
308 cam_error_print(device, ccb, CAM_ESF_ALL,
309 CAM_EPF_ALL, stderr);
314 printf("Timestamp set to %ju\n", (uintmax_t)ts);
317 if (do_restore_flags != 0)
318 error = set_restore_flags(device, &flags, /*set_flag*/ 0,
319 retry_count, timeout);
327 timestamp(struct cam_device *device, int argc, char **argv, char *combinedopt,
328 int retry_count, int timeout, int verbosemode __unused)
332 char *format_string = NULL;
333 char *timestamp_string = NULL;
339 while ((c = getopt(argc, argv, combinedopt)) != -1) {
343 warnx("Use only one -r or only one -s");
347 action = TIMESTAMP_REPORT;
352 warnx("Use only one -r or only one -s");
356 action = TIMESTAMP_SET;
362 format_string = strdup(optarg);
363 if (format_string == NULL) {
364 warn("Error allocating memory for format "
374 format_string = strdup(MIL);
375 if (format_string == NULL) {
376 warn("Error allocating memory");
387 free(timestamp_string);
388 timestamp_string = strdup(optarg);
389 if (timestamp_string == NULL) {
390 warn("Error allocating memory for format "
402 warnx("Must specify an action, either -r or -s");
407 if (single_arg > 1) {
408 warnx("Select only one: %s",
409 (action == TIMESTAMP_REPORT) ?
410 "-f format or -m for the -r flag" :
411 "-f format -T time or -U for the -s flag");
416 if (action == TIMESTAMP_SET) {
417 if ((format_string == NULL)
419 warnx("Must specify either -f format or -U for "
420 "setting the timestamp");
422 } else if ((format_string != NULL)
424 warnx("Must specify only one of -f or -U to set "
427 } else if ((format_string != NULL)
428 && (strcmp(format_string, MIL) == 0)) {
429 warnx("-m is not allowed for setting the "
432 } else if ((do_utc == 0)
433 && (timestamp_string == NULL)) {
434 warnx("Must specify the time (-T) to set as the "
440 } else if (action == TIMESTAMP_REPORT) {
441 if (format_string == NULL) {
442 format_string = strdup("%c %Z");
443 if (format_string == NULL) {
444 warn("Error allocating memory for format "
452 if (action == TIMESTAMP_REPORT) {
453 error = report_timestamp(device, &ts, retry_count,
457 } else if (strcmp(format_string, MIL) == 0) {
458 printf("Timestamp in milliseconds: %ju\n",
461 char temp_timestamp_string[100];
462 time_t time_var = ts / 1000;
463 const struct tm *restrict cur_time;
465 setlocale(LC_TIME, "");
467 cur_time = gmtime(&time_var);
469 cur_time = localtime(&time_var);
471 strftime(temp_timestamp_string,
472 sizeof(temp_timestamp_string), format_string,
474 printf("Formatted timestamp: %s\n",
475 temp_timestamp_string);
477 } else if (action == TIMESTAMP_SET) {
479 format_string = strdup(UTC);
480 if (format_string == NULL) {
481 warn("Error allocating memory for format "
488 error = set_timestamp(device, format_string, timestamp_string,
489 retry_count, timeout);
494 free(timestamp_string);