128db0a5eSKenneth D. Merry /*-
228db0a5eSKenneth D. Merry * Copyright (c) 2016 Spectra Logic Corporation
328db0a5eSKenneth D. Merry * All rights reserved.
428db0a5eSKenneth D. Merry *
528db0a5eSKenneth D. Merry * Redistribution and use in source and binary forms, with or without
628db0a5eSKenneth D. Merry * modification, are permitted provided that the following conditions
728db0a5eSKenneth D. Merry * are met:
828db0a5eSKenneth D. Merry * 1. Redistributions of source code must retain the above copyright
928db0a5eSKenneth D. Merry * notice, this list of conditions, and the following disclaimer,
1028db0a5eSKenneth D. Merry * without modification.
1128db0a5eSKenneth D. Merry * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1228db0a5eSKenneth D. Merry * substantially similar to the "NO WARRANTY" disclaimer below
1328db0a5eSKenneth D. Merry * ("Disclaimer") and any redistribution must be conditioned upon
1428db0a5eSKenneth D. Merry * including a substantially similar Disclaimer requirement for further
1528db0a5eSKenneth D. Merry * binary redistribution.
1628db0a5eSKenneth D. Merry *
1728db0a5eSKenneth D. Merry * NO WARRANTY
1828db0a5eSKenneth D. Merry * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1928db0a5eSKenneth D. Merry * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2028db0a5eSKenneth D. Merry * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
2128db0a5eSKenneth D. Merry * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2228db0a5eSKenneth D. Merry * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2328db0a5eSKenneth D. Merry * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2428db0a5eSKenneth D. Merry * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2528db0a5eSKenneth D. Merry * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2628db0a5eSKenneth D. Merry * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
2728db0a5eSKenneth D. Merry * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2828db0a5eSKenneth D. Merry * POSSIBILITY OF SUCH DAMAGES.
2928db0a5eSKenneth D. Merry *
3028db0a5eSKenneth D. Merry * Authors: Ken Merry (Spectra Logic Corporation)
3128db0a5eSKenneth D. Merry * Reid Linnemann (Spectra Logic Corporation)
3228db0a5eSKenneth D. Merry * Samuel Klopsch (Spectra Logic Corporation)
3328db0a5eSKenneth D. Merry */
3428db0a5eSKenneth D. Merry /*
3528db0a5eSKenneth D. Merry * SCSI tape drive timestamp support
3628db0a5eSKenneth D. Merry */
3728db0a5eSKenneth D. Merry
3828db0a5eSKenneth D. Merry #include <sys/types.h>
3928db0a5eSKenneth D. Merry
4077d9b0efSAlan Somers #include <assert.h>
4128db0a5eSKenneth D. Merry #include <stdio.h>
4228db0a5eSKenneth D. Merry #include <stdlib.h>
4328db0a5eSKenneth D. Merry #include <unistd.h>
4428db0a5eSKenneth D. Merry #include <string.h>
4528db0a5eSKenneth D. Merry #include <err.h>
4628db0a5eSKenneth D. Merry #include <time.h>
4728db0a5eSKenneth D. Merry #include <locale.h>
4828db0a5eSKenneth D. Merry
4928db0a5eSKenneth D. Merry #include <cam/cam.h>
5028db0a5eSKenneth D. Merry #include <cam/cam_debug.h>
5128db0a5eSKenneth D. Merry #include <cam/cam_ccb.h>
5228db0a5eSKenneth D. Merry #include <cam/scsi/scsi_all.h>
5328db0a5eSKenneth D. Merry #include <cam/scsi/scsi_message.h>
5428db0a5eSKenneth D. Merry #include <camlib.h>
5528db0a5eSKenneth D. Merry #include "camcontrol.h"
5628db0a5eSKenneth D. Merry
5728db0a5eSKenneth D. Merry #define TIMESTAMP_REPORT 0
5828db0a5eSKenneth D. Merry #define TIMESTAMP_SET 1
5928db0a5eSKenneth D. Merry #define MIL "milliseconds"
6028db0a5eSKenneth D. Merry #define UTC "utc"
6128db0a5eSKenneth D. Merry
6228db0a5eSKenneth D. Merry static int set_restore_flags(struct cam_device *device, uint8_t *flags,
63492a2ef5SKenneth D. Merry int set_flag, int task_attr, int retry_count,
64492a2ef5SKenneth D. Merry int timeout);
6528db0a5eSKenneth D. Merry static int report_timestamp(struct cam_device *device, uint64_t *ts,
66492a2ef5SKenneth D. Merry int task_attr, int retry_count, int timeout);
6728db0a5eSKenneth D. Merry static int set_timestamp(struct cam_device *device, char *format_string,
68492a2ef5SKenneth D. Merry char *timestamp_string, int task_attr, int retry_count,
69492a2ef5SKenneth D. Merry int timeout);
7028db0a5eSKenneth D. Merry
7128db0a5eSKenneth D. Merry static int
set_restore_flags(struct cam_device * device,uint8_t * flags,int set_flag,int task_attr,int retry_count,int timeout)7228db0a5eSKenneth D. Merry set_restore_flags(struct cam_device *device, uint8_t *flags, int set_flag,
73492a2ef5SKenneth D. Merry int task_attr, int retry_count, int timeout)
7428db0a5eSKenneth D. Merry {
7528db0a5eSKenneth D. Merry unsigned long blk_desc_length, hdr_and_blk_length;
7628db0a5eSKenneth D. Merry int error = 0;
7728db0a5eSKenneth D. Merry struct scsi_control_ext_page *control_page = NULL;
7828db0a5eSKenneth D. Merry struct scsi_mode_header_10 *mode_hdr = NULL;
7928db0a5eSKenneth D. Merry union ccb *ccb = NULL;
8028db0a5eSKenneth D. Merry unsigned long mode_buf_size = sizeof(struct scsi_mode_header_10) +
8128db0a5eSKenneth D. Merry sizeof(struct scsi_mode_blk_desc) +
8228db0a5eSKenneth D. Merry sizeof(struct scsi_control_ext_page);
8328db0a5eSKenneth D. Merry uint8_t mode_buf[mode_buf_size];
8428db0a5eSKenneth D. Merry
8528db0a5eSKenneth D. Merry ccb = cam_getccb(device);
8628db0a5eSKenneth D. Merry if (ccb == NULL) {
8728db0a5eSKenneth D. Merry warnx("%s: error allocating CCB", __func__);
8828db0a5eSKenneth D. Merry error = 1;
8928db0a5eSKenneth D. Merry goto bailout;
9028db0a5eSKenneth D. Merry }
9128db0a5eSKenneth D. Merry /*
9228db0a5eSKenneth D. Merry * Get the control extension subpage, we'll send it back modified to
9328db0a5eSKenneth D. Merry * enable SCSI control over the tape drive's timestamp
9428db0a5eSKenneth D. Merry */
95*df8aa95bSAlexander Motin scsi_mode_sense_subpage(&ccb->csio,
9628db0a5eSKenneth D. Merry /*retries*/ retry_count,
9728db0a5eSKenneth D. Merry /*cbfcnp*/ NULL,
98492a2ef5SKenneth D. Merry /*tag_action*/ task_attr,
9928db0a5eSKenneth D. Merry /*dbd*/ 0,
10028db0a5eSKenneth D. Merry /*page_control*/ SMS_PAGE_CTRL_CURRENT,
10128db0a5eSKenneth D. Merry /*page*/ SCEP_PAGE_CODE,
102*df8aa95bSAlexander Motin /*subpage*/ SCEP_SUBPAGE_CODE,
10328db0a5eSKenneth D. Merry /*param_buf*/ &mode_buf[0],
10428db0a5eSKenneth D. Merry /*param_len*/ mode_buf_size,
10528db0a5eSKenneth D. Merry /*minimum_cmd_size*/ 10,
10628db0a5eSKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE,
10728db0a5eSKenneth D. Merry /*timeout*/ timeout ? timeout : 5000);
10828db0a5eSKenneth D. Merry
10928db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
11028db0a5eSKenneth D. Merry if (retry_count > 0)
11128db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
11228db0a5eSKenneth D. Merry
11328db0a5eSKenneth D. Merry error = cam_send_ccb(device, ccb);
11428db0a5eSKenneth D. Merry if (error != 0) {
11528db0a5eSKenneth D. Merry warn("error sending Mode Sense");
11628db0a5eSKenneth D. Merry goto bailout;
11728db0a5eSKenneth D. Merry }
11828db0a5eSKenneth D. Merry
11928db0a5eSKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
12028db0a5eSKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL,
12128db0a5eSKenneth D. Merry CAM_EPF_ALL, stderr);
12228db0a5eSKenneth D. Merry error = 1;
12328db0a5eSKenneth D. Merry goto bailout;
12428db0a5eSKenneth D. Merry }
12528db0a5eSKenneth D. Merry
12628db0a5eSKenneth D. Merry mode_hdr = (struct scsi_mode_header_10 *)&mode_buf[0];
12728db0a5eSKenneth D. Merry blk_desc_length = scsi_2btoul(mode_hdr->blk_desc_len);
12828db0a5eSKenneth D. Merry hdr_and_blk_length = sizeof(struct scsi_mode_header_10)+blk_desc_length;
12928db0a5eSKenneth D. Merry /*
13028db0a5eSKenneth D. Merry * Create the control page at the correct point in the mode_buf, it
13128db0a5eSKenneth D. Merry * starts after the header and the blk description.
13228db0a5eSKenneth D. Merry */
13377d9b0efSAlan Somers assert(hdr_and_blk_length <=
13477d9b0efSAlan Somers sizeof(mode_buf) - sizeof(struct scsi_control_ext_page));
13528db0a5eSKenneth D. Merry control_page = (struct scsi_control_ext_page *)&mode_buf
13628db0a5eSKenneth D. Merry [hdr_and_blk_length];
13728db0a5eSKenneth D. Merry if (set_flag != 0) {
13828db0a5eSKenneth D. Merry *flags = control_page->flags;
13928db0a5eSKenneth D. Merry /*
14028db0a5eSKenneth D. Merry * Set the SCSIP flag to enable SCSI to change the
14128db0a5eSKenneth D. Merry * tape drive's timestamp.
14228db0a5eSKenneth D. Merry */
14328db0a5eSKenneth D. Merry control_page->flags |= SCEP_SCSIP;
14428db0a5eSKenneth D. Merry } else {
14528db0a5eSKenneth D. Merry control_page->flags = *flags;
14628db0a5eSKenneth D. Merry }
14728db0a5eSKenneth D. Merry
14828db0a5eSKenneth D. Merry scsi_mode_select_len(&ccb->csio,
14928db0a5eSKenneth D. Merry /*retries*/ retry_count,
15028db0a5eSKenneth D. Merry /*cbfcnp*/ NULL,
151492a2ef5SKenneth D. Merry /*tag_action*/ task_attr,
15228db0a5eSKenneth D. Merry /*scsi_page_fmt*/ 1,
15328db0a5eSKenneth D. Merry /*save_pages*/ 0,
15428db0a5eSKenneth D. Merry /*param_buf*/ &mode_buf[0],
15528db0a5eSKenneth D. Merry /*param_len*/ mode_buf_size,
15628db0a5eSKenneth D. Merry /*minimum_cmd_size*/ 10,
15728db0a5eSKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE,
15828db0a5eSKenneth D. Merry /*timeout*/ timeout ? timeout : 5000);
15928db0a5eSKenneth D. Merry
16028db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
16128db0a5eSKenneth D. Merry if (retry_count > 0)
16228db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
16328db0a5eSKenneth D. Merry
16428db0a5eSKenneth D. Merry error = cam_send_ccb(device, ccb);
16528db0a5eSKenneth D. Merry if (error != 0) {
16628db0a5eSKenneth D. Merry warn("error sending Mode Select");
16728db0a5eSKenneth D. Merry goto bailout;
16828db0a5eSKenneth D. Merry }
16928db0a5eSKenneth D. Merry
17028db0a5eSKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
17128db0a5eSKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL,
17228db0a5eSKenneth D. Merry CAM_EPF_ALL, stderr);
17328db0a5eSKenneth D. Merry error = 1;
17428db0a5eSKenneth D. Merry goto bailout;
17528db0a5eSKenneth D. Merry }
17628db0a5eSKenneth D. Merry
17728db0a5eSKenneth D. Merry bailout:
17828db0a5eSKenneth D. Merry if (ccb != NULL)
17928db0a5eSKenneth D. Merry cam_freeccb(ccb);
18028db0a5eSKenneth D. Merry
18128db0a5eSKenneth D. Merry return error;
18228db0a5eSKenneth D. Merry }
18328db0a5eSKenneth D. Merry
18428db0a5eSKenneth D. Merry static int
report_timestamp(struct cam_device * device,uint64_t * ts,int task_attr,int retry_count,int timeout)185492a2ef5SKenneth D. Merry report_timestamp(struct cam_device *device, uint64_t *ts, int task_attr,
18628db0a5eSKenneth D. Merry int retry_count, int timeout)
18728db0a5eSKenneth D. Merry {
18828db0a5eSKenneth D. Merry int error = 0;
18928db0a5eSKenneth D. Merry struct scsi_report_timestamp_data *report_buf = malloc(
19028db0a5eSKenneth D. Merry sizeof(struct scsi_report_timestamp_data));
19128db0a5eSKenneth D. Merry uint8_t temp_timestamp[8];
19228db0a5eSKenneth D. Merry uint32_t report_buf_size = sizeof(
19328db0a5eSKenneth D. Merry struct scsi_report_timestamp_data);
19428db0a5eSKenneth D. Merry union ccb *ccb = NULL;
19528db0a5eSKenneth D. Merry
19628db0a5eSKenneth D. Merry ccb = cam_getccb(device);
19728db0a5eSKenneth D. Merry if (ccb == NULL) {
19828db0a5eSKenneth D. Merry warnx("%s: error allocating CCB", __func__);
19928db0a5eSKenneth D. Merry error = 1;
20028db0a5eSKenneth D. Merry goto bailout;
20128db0a5eSKenneth D. Merry }
20228db0a5eSKenneth D. Merry
20328db0a5eSKenneth D. Merry scsi_report_timestamp(&ccb->csio,
20428db0a5eSKenneth D. Merry /*retries*/ retry_count,
20528db0a5eSKenneth D. Merry /*cbfcnp*/ NULL,
206492a2ef5SKenneth D. Merry /*tag_action*/ task_attr,
20728db0a5eSKenneth D. Merry /*pdf*/ 0,
20828db0a5eSKenneth D. Merry /*buf*/ report_buf,
20928db0a5eSKenneth D. Merry /*buf_len*/ report_buf_size,
21028db0a5eSKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE,
21128db0a5eSKenneth D. Merry /*timeout*/ timeout ? timeout : 5000);
21228db0a5eSKenneth D. Merry
21328db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
21428db0a5eSKenneth D. Merry if (retry_count > 0)
21528db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
21628db0a5eSKenneth D. Merry
21728db0a5eSKenneth D. Merry error = cam_send_ccb(device, ccb);
21828db0a5eSKenneth D. Merry if (error != 0) {
21928db0a5eSKenneth D. Merry warn("error sending Report Timestamp");
22028db0a5eSKenneth D. Merry goto bailout;
22128db0a5eSKenneth D. Merry }
22228db0a5eSKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
22328db0a5eSKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL,
22428db0a5eSKenneth D. Merry CAM_EPF_ALL, stderr);
22528db0a5eSKenneth D. Merry error = 1;
22628db0a5eSKenneth D. Merry goto bailout;
22728db0a5eSKenneth D. Merry }
22828db0a5eSKenneth D. Merry
22928db0a5eSKenneth D. Merry bzero(temp_timestamp, sizeof(temp_timestamp));
23028db0a5eSKenneth D. Merry memcpy(&temp_timestamp[2], &report_buf->timestamp, 6);
23128db0a5eSKenneth D. Merry
23228db0a5eSKenneth D. Merry *ts = scsi_8btou64(temp_timestamp);
23328db0a5eSKenneth D. Merry
23428db0a5eSKenneth D. Merry bailout:
23528db0a5eSKenneth D. Merry if (ccb != NULL)
23628db0a5eSKenneth D. Merry cam_freeccb(ccb);
23777d9b0efSAlan Somers free(report_buf);
23828db0a5eSKenneth D. Merry
23928db0a5eSKenneth D. Merry return error;
24028db0a5eSKenneth D. Merry }
24128db0a5eSKenneth D. Merry
24228db0a5eSKenneth D. Merry static int
set_timestamp(struct cam_device * device,char * format_string,char * timestamp_string,int task_attr,int retry_count,int timeout)24328db0a5eSKenneth D. Merry set_timestamp(struct cam_device *device, char *format_string,
244492a2ef5SKenneth D. Merry char *timestamp_string, int task_attr, int retry_count,
24528db0a5eSKenneth D. Merry int timeout)
24628db0a5eSKenneth D. Merry {
24728db0a5eSKenneth D. Merry struct scsi_set_timestamp_parameters ts_p;
24828db0a5eSKenneth D. Merry time_t time_value;
24928db0a5eSKenneth D. Merry struct tm time_struct;
25028db0a5eSKenneth D. Merry uint8_t flags = 0;
25128db0a5eSKenneth D. Merry int error = 0;
25228db0a5eSKenneth D. Merry uint64_t ts = 0;
25328db0a5eSKenneth D. Merry union ccb *ccb = NULL;
25428db0a5eSKenneth D. Merry int do_restore_flags = 0;
25528db0a5eSKenneth D. Merry
256492a2ef5SKenneth D. Merry error = set_restore_flags(device, &flags, /*set_flag*/ 1, task_attr,
257492a2ef5SKenneth D. Merry retry_count, timeout);
25828db0a5eSKenneth D. Merry if (error != 0)
25928db0a5eSKenneth D. Merry goto bailout;
26028db0a5eSKenneth D. Merry
26128db0a5eSKenneth D. Merry do_restore_flags = 1;
26228db0a5eSKenneth D. Merry
26328db0a5eSKenneth D. Merry ccb = cam_getccb(device);
26428db0a5eSKenneth D. Merry if (ccb == NULL) {
26528db0a5eSKenneth D. Merry warnx("%s: error allocating CCB", __func__);
26628db0a5eSKenneth D. Merry error = 1;
26728db0a5eSKenneth D. Merry goto bailout;
26828db0a5eSKenneth D. Merry }
26928db0a5eSKenneth D. Merry
27028db0a5eSKenneth D. Merry if (strcmp(format_string, UTC) == 0) {
27128db0a5eSKenneth D. Merry time(&time_value);
27228db0a5eSKenneth D. Merry ts = (uint64_t) time_value;
27328db0a5eSKenneth D. Merry } else {
27428db0a5eSKenneth D. Merry bzero(&time_struct, sizeof(struct tm));
275d618624cSKenneth D. Merry if (strptime(timestamp_string, format_string,
276d618624cSKenneth D. Merry &time_struct) == NULL) {
277d618624cSKenneth D. Merry warnx("%s: strptime(3) failed", __func__);
278d618624cSKenneth D. Merry error = 1;
279d618624cSKenneth D. Merry goto bailout;
280d618624cSKenneth D. Merry }
28128db0a5eSKenneth D. Merry time_value = mktime(&time_struct);
28228db0a5eSKenneth D. Merry ts = (uint64_t) time_value;
28328db0a5eSKenneth D. Merry }
28428db0a5eSKenneth D. Merry /* Convert time from seconds to milliseconds */
28528db0a5eSKenneth D. Merry ts *= 1000;
286d618624cSKenneth D. Merry bzero(&ts_p, sizeof(ts_p));
28728db0a5eSKenneth D. Merry scsi_create_timestamp(ts_p.timestamp, ts);
28828db0a5eSKenneth D. Merry
28928db0a5eSKenneth D. Merry scsi_set_timestamp(&ccb->csio,
29028db0a5eSKenneth D. Merry /*retries*/ retry_count,
29128db0a5eSKenneth D. Merry /*cbfcnp*/ NULL,
292492a2ef5SKenneth D. Merry /*tag_action*/ task_attr,
29328db0a5eSKenneth D. Merry /*buf*/ &ts_p,
29428db0a5eSKenneth D. Merry /*buf_len*/ sizeof(ts_p),
29528db0a5eSKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE,
29628db0a5eSKenneth D. Merry /*timeout*/ timeout ? timeout : 5000);
29728db0a5eSKenneth D. Merry
29828db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
29928db0a5eSKenneth D. Merry if (retry_count > 0)
30028db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
30128db0a5eSKenneth D. Merry
30228db0a5eSKenneth D. Merry error = cam_send_ccb(device, ccb);
30328db0a5eSKenneth D. Merry if (error != 0) {
30428db0a5eSKenneth D. Merry warn("error sending Set Timestamp");
30528db0a5eSKenneth D. Merry goto bailout;
30628db0a5eSKenneth D. Merry }
30728db0a5eSKenneth D. Merry
30828db0a5eSKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
30928db0a5eSKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL,
31028db0a5eSKenneth D. Merry CAM_EPF_ALL, stderr);
31128db0a5eSKenneth D. Merry error = 1;
31228db0a5eSKenneth D. Merry goto bailout;
31328db0a5eSKenneth D. Merry }
31428db0a5eSKenneth D. Merry
31528db0a5eSKenneth D. Merry printf("Timestamp set to %ju\n", (uintmax_t)ts);
31628db0a5eSKenneth D. Merry
31728db0a5eSKenneth D. Merry bailout:
31828db0a5eSKenneth D. Merry if (do_restore_flags != 0)
31928db0a5eSKenneth D. Merry error = set_restore_flags(device, &flags, /*set_flag*/ 0,
320492a2ef5SKenneth D. Merry task_attr, retry_count, timeout);
32128db0a5eSKenneth D. Merry if (ccb != NULL)
32228db0a5eSKenneth D. Merry cam_freeccb(ccb);
32328db0a5eSKenneth D. Merry
32428db0a5eSKenneth D. Merry return error;
32528db0a5eSKenneth D. Merry }
32628db0a5eSKenneth D. Merry
32728db0a5eSKenneth D. Merry int
timestamp(struct cam_device * device,int argc,char ** argv,char * combinedopt,int task_attr,int retry_count,int timeout,int verbosemode __unused)32828db0a5eSKenneth D. Merry timestamp(struct cam_device *device, int argc, char **argv, char *combinedopt,
329492a2ef5SKenneth D. Merry int task_attr, int retry_count, int timeout, int verbosemode __unused)
33028db0a5eSKenneth D. Merry {
33128db0a5eSKenneth D. Merry int c;
332a1fa2391SAdrian Chadd uint64_t ts = 0;
33328db0a5eSKenneth D. Merry char *format_string = NULL;
33428db0a5eSKenneth D. Merry char *timestamp_string = NULL;
33528db0a5eSKenneth D. Merry int action = -1;
33628db0a5eSKenneth D. Merry int error = 0;
33728db0a5eSKenneth D. Merry int single_arg = 0;
33828db0a5eSKenneth D. Merry int do_utc = 0;
33928db0a5eSKenneth D. Merry
34028db0a5eSKenneth D. Merry while ((c = getopt(argc, argv, combinedopt)) != -1) {
34128db0a5eSKenneth D. Merry switch (c) {
34228db0a5eSKenneth D. Merry case 'r': {
34328db0a5eSKenneth D. Merry if (action != -1) {
34428db0a5eSKenneth D. Merry warnx("Use only one -r or only one -s");
34528db0a5eSKenneth D. Merry error =1;
34628db0a5eSKenneth D. Merry goto bailout;
34728db0a5eSKenneth D. Merry }
34828db0a5eSKenneth D. Merry action = TIMESTAMP_REPORT;
34928db0a5eSKenneth D. Merry break;
35028db0a5eSKenneth D. Merry }
35128db0a5eSKenneth D. Merry case 's': {
35228db0a5eSKenneth D. Merry if (action != -1) {
35328db0a5eSKenneth D. Merry warnx("Use only one -r or only one -s");
35428db0a5eSKenneth D. Merry error = 1;
35528db0a5eSKenneth D. Merry goto bailout;
35628db0a5eSKenneth D. Merry }
35728db0a5eSKenneth D. Merry action = TIMESTAMP_SET;
35828db0a5eSKenneth D. Merry break;
35928db0a5eSKenneth D. Merry }
36028db0a5eSKenneth D. Merry case 'f': {
36128db0a5eSKenneth D. Merry single_arg++;
362f2a12bceSEnji Cooper free(format_string);
36328db0a5eSKenneth D. Merry format_string = strdup(optarg);
36428db0a5eSKenneth D. Merry if (format_string == NULL) {
36528db0a5eSKenneth D. Merry warn("Error allocating memory for format "
36628db0a5eSKenneth D. Merry "argument");
36728db0a5eSKenneth D. Merry error = 1;
36828db0a5eSKenneth D. Merry goto bailout;
36928db0a5eSKenneth D. Merry }
37028db0a5eSKenneth D. Merry break;
37128db0a5eSKenneth D. Merry }
37228db0a5eSKenneth D. Merry case 'm': {
37328db0a5eSKenneth D. Merry single_arg++;
374f2a12bceSEnji Cooper free(format_string);
37528db0a5eSKenneth D. Merry format_string = strdup(MIL);
37628db0a5eSKenneth D. Merry if (format_string == NULL) {
37728db0a5eSKenneth D. Merry warn("Error allocating memory");
37828db0a5eSKenneth D. Merry error = 1;
37928db0a5eSKenneth D. Merry goto bailout;
38028db0a5eSKenneth D. Merry }
38128db0a5eSKenneth D. Merry break;
38228db0a5eSKenneth D. Merry }
38328db0a5eSKenneth D. Merry case 'U': {
38428db0a5eSKenneth D. Merry do_utc = 1;
38528db0a5eSKenneth D. Merry break;
38628db0a5eSKenneth D. Merry }
38728db0a5eSKenneth D. Merry case 'T':
388f2a12bceSEnji Cooper free(timestamp_string);
38928db0a5eSKenneth D. Merry timestamp_string = strdup(optarg);
39028db0a5eSKenneth D. Merry if (timestamp_string == NULL) {
39128db0a5eSKenneth D. Merry warn("Error allocating memory for format "
39228db0a5eSKenneth D. Merry "argument");
39328db0a5eSKenneth D. Merry error = 1;
39428db0a5eSKenneth D. Merry goto bailout;
39528db0a5eSKenneth D. Merry }
39628db0a5eSKenneth D. Merry break;
39728db0a5eSKenneth D. Merry default:
39828db0a5eSKenneth D. Merry break;
39928db0a5eSKenneth D. Merry }
40028db0a5eSKenneth D. Merry }
40128db0a5eSKenneth D. Merry
40228db0a5eSKenneth D. Merry if (action == -1) {
40328db0a5eSKenneth D. Merry warnx("Must specify an action, either -r or -s");
40428db0a5eSKenneth D. Merry error = 1;
40528db0a5eSKenneth D. Merry goto bailout;
40628db0a5eSKenneth D. Merry }
40728db0a5eSKenneth D. Merry
40828db0a5eSKenneth D. Merry if (single_arg > 1) {
40928db0a5eSKenneth D. Merry warnx("Select only one: %s",
41028db0a5eSKenneth D. Merry (action == TIMESTAMP_REPORT) ?
41128db0a5eSKenneth D. Merry "-f format or -m for the -r flag" :
41228db0a5eSKenneth D. Merry "-f format -T time or -U for the -s flag");
41328db0a5eSKenneth D. Merry error = 1;
41428db0a5eSKenneth D. Merry goto bailout;
41528db0a5eSKenneth D. Merry }
41628db0a5eSKenneth D. Merry
41728db0a5eSKenneth D. Merry if (action == TIMESTAMP_SET) {
41828db0a5eSKenneth D. Merry if ((format_string == NULL)
41928db0a5eSKenneth D. Merry && (do_utc == 0)) {
42028db0a5eSKenneth D. Merry warnx("Must specify either -f format or -U for "
42128db0a5eSKenneth D. Merry "setting the timestamp");
42228db0a5eSKenneth D. Merry error = 1;
42328db0a5eSKenneth D. Merry } else if ((format_string != NULL)
42428db0a5eSKenneth D. Merry && (do_utc != 0)) {
42528db0a5eSKenneth D. Merry warnx("Must specify only one of -f or -U to set "
42628db0a5eSKenneth D. Merry "the timestamp");
42728db0a5eSKenneth D. Merry error = 1;
42828db0a5eSKenneth D. Merry } else if ((format_string != NULL)
42928db0a5eSKenneth D. Merry && (strcmp(format_string, MIL) == 0)) {
43028db0a5eSKenneth D. Merry warnx("-m is not allowed for setting the "
43128db0a5eSKenneth D. Merry "timestamp");
43228db0a5eSKenneth D. Merry error = 1;
43328db0a5eSKenneth D. Merry } else if ((do_utc == 0)
43428db0a5eSKenneth D. Merry && (timestamp_string == NULL)) {
43528db0a5eSKenneth D. Merry warnx("Must specify the time (-T) to set as the "
43628db0a5eSKenneth D. Merry "timestamp");
43728db0a5eSKenneth D. Merry error = 1;
43828db0a5eSKenneth D. Merry }
43928db0a5eSKenneth D. Merry if (error != 0)
44028db0a5eSKenneth D. Merry goto bailout;
44128db0a5eSKenneth D. Merry } else if (action == TIMESTAMP_REPORT) {
44228db0a5eSKenneth D. Merry if (format_string == NULL) {
44328db0a5eSKenneth D. Merry format_string = strdup("%c %Z");
44428db0a5eSKenneth D. Merry if (format_string == NULL) {
44528db0a5eSKenneth D. Merry warn("Error allocating memory for format "
44628db0a5eSKenneth D. Merry "string");
44728db0a5eSKenneth D. Merry error = 1;
44828db0a5eSKenneth D. Merry goto bailout;
44928db0a5eSKenneth D. Merry }
45028db0a5eSKenneth D. Merry }
45128db0a5eSKenneth D. Merry }
45228db0a5eSKenneth D. Merry
45328db0a5eSKenneth D. Merry if (action == TIMESTAMP_REPORT) {
454492a2ef5SKenneth D. Merry error = report_timestamp(device, &ts, task_attr, retry_count,
45528db0a5eSKenneth D. Merry timeout);
45628db0a5eSKenneth D. Merry if (error != 0) {
45728db0a5eSKenneth D. Merry goto bailout;
45828db0a5eSKenneth D. Merry } else if (strcmp(format_string, MIL) == 0) {
45928db0a5eSKenneth D. Merry printf("Timestamp in milliseconds: %ju\n",
46028db0a5eSKenneth D. Merry (uintmax_t)ts);
46128db0a5eSKenneth D. Merry } else {
46228db0a5eSKenneth D. Merry char temp_timestamp_string[100];
46328db0a5eSKenneth D. Merry time_t time_var = ts / 1000;
46428db0a5eSKenneth D. Merry const struct tm *restrict cur_time;
46528db0a5eSKenneth D. Merry
46628db0a5eSKenneth D. Merry setlocale(LC_TIME, "");
46728db0a5eSKenneth D. Merry if (do_utc != 0)
46828db0a5eSKenneth D. Merry cur_time = gmtime(&time_var);
46928db0a5eSKenneth D. Merry else
47028db0a5eSKenneth D. Merry cur_time = localtime(&time_var);
47128db0a5eSKenneth D. Merry
47228db0a5eSKenneth D. Merry strftime(temp_timestamp_string,
47328db0a5eSKenneth D. Merry sizeof(temp_timestamp_string), format_string,
47428db0a5eSKenneth D. Merry cur_time);
47528db0a5eSKenneth D. Merry printf("Formatted timestamp: %s\n",
47628db0a5eSKenneth D. Merry temp_timestamp_string);
47728db0a5eSKenneth D. Merry }
47828db0a5eSKenneth D. Merry } else if (action == TIMESTAMP_SET) {
47928db0a5eSKenneth D. Merry if (do_utc != 0) {
48028db0a5eSKenneth D. Merry format_string = strdup(UTC);
48128db0a5eSKenneth D. Merry if (format_string == NULL) {
48228db0a5eSKenneth D. Merry warn("Error allocating memory for format "
48328db0a5eSKenneth D. Merry "string");
48428db0a5eSKenneth D. Merry error = 1;
48528db0a5eSKenneth D. Merry goto bailout;
48628db0a5eSKenneth D. Merry }
48728db0a5eSKenneth D. Merry }
48828db0a5eSKenneth D. Merry
48928db0a5eSKenneth D. Merry error = set_timestamp(device, format_string, timestamp_string,
490492a2ef5SKenneth D. Merry task_attr, retry_count, timeout);
49128db0a5eSKenneth D. Merry }
49228db0a5eSKenneth D. Merry
49328db0a5eSKenneth D. Merry bailout:
49428db0a5eSKenneth D. Merry free(format_string);
49528db0a5eSKenneth D. Merry free(timestamp_string);
49628db0a5eSKenneth D. Merry
49728db0a5eSKenneth D. Merry return (error);
49828db0a5eSKenneth D. Merry }
499