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/cdefs.h> 3928db0a5eSKenneth D. Merry __FBSDID("$FreeBSD$"); 4028db0a5eSKenneth D. Merry 4128db0a5eSKenneth D. Merry #include <sys/types.h> 4228db0a5eSKenneth D. Merry 4328db0a5eSKenneth D. Merry #include <stdio.h> 4428db0a5eSKenneth D. Merry #include <stdlib.h> 4528db0a5eSKenneth D. Merry #include <unistd.h> 4628db0a5eSKenneth D. Merry #include <string.h> 4728db0a5eSKenneth D. Merry #include <err.h> 4828db0a5eSKenneth D. Merry #include <time.h> 4928db0a5eSKenneth D. Merry #include <locale.h> 5028db0a5eSKenneth D. Merry 5128db0a5eSKenneth D. Merry #include <cam/cam.h> 5228db0a5eSKenneth D. Merry #include <cam/cam_debug.h> 5328db0a5eSKenneth D. Merry #include <cam/cam_ccb.h> 5428db0a5eSKenneth D. Merry #include <cam/scsi/scsi_all.h> 5528db0a5eSKenneth D. Merry #include <cam/scsi/scsi_message.h> 5628db0a5eSKenneth D. Merry #include <camlib.h> 5728db0a5eSKenneth D. Merry #include "camcontrol.h" 5828db0a5eSKenneth D. Merry 5928db0a5eSKenneth D. Merry #define TIMESTAMP_REPORT 0 6028db0a5eSKenneth D. Merry #define TIMESTAMP_SET 1 6128db0a5eSKenneth D. Merry #define MIL "milliseconds" 6228db0a5eSKenneth D. Merry #define UTC "utc" 6328db0a5eSKenneth D. Merry 6428db0a5eSKenneth D. Merry static int set_restore_flags(struct cam_device *device, uint8_t *flags, 6528db0a5eSKenneth D. Merry int set_flag, int retry_count, int timeout); 6628db0a5eSKenneth D. Merry static int report_timestamp(struct cam_device *device, uint64_t *ts, 6728db0a5eSKenneth D. Merry int retry_count, int timeout); 6828db0a5eSKenneth D. Merry static int set_timestamp(struct cam_device *device, char *format_string, 6928db0a5eSKenneth D. Merry char *timestamp_string, 7028db0a5eSKenneth D. Merry int retry_count, int timeout); 7128db0a5eSKenneth D. Merry 7228db0a5eSKenneth D. Merry static int 7328db0a5eSKenneth D. Merry set_restore_flags(struct cam_device *device, uint8_t *flags, int set_flag, 7428db0a5eSKenneth D. Merry int retry_count, int timeout) 7528db0a5eSKenneth D. Merry { 7628db0a5eSKenneth D. Merry unsigned long blk_desc_length, hdr_and_blk_length; 7728db0a5eSKenneth D. Merry int error = 0; 7828db0a5eSKenneth D. Merry struct scsi_control_ext_page *control_page = NULL; 7928db0a5eSKenneth D. Merry struct scsi_mode_header_10 *mode_hdr = NULL; 8028db0a5eSKenneth D. Merry struct scsi_mode_sense_10 *cdb = NULL; 8128db0a5eSKenneth D. Merry union ccb *ccb = NULL; 8228db0a5eSKenneth D. Merry unsigned long mode_buf_size = sizeof(struct scsi_mode_header_10) + 8328db0a5eSKenneth D. Merry sizeof(struct scsi_mode_blk_desc) + 8428db0a5eSKenneth D. Merry sizeof(struct scsi_control_ext_page); 8528db0a5eSKenneth D. Merry uint8_t mode_buf[mode_buf_size]; 8628db0a5eSKenneth D. Merry 8728db0a5eSKenneth D. Merry ccb = cam_getccb(device); 8828db0a5eSKenneth D. Merry if (ccb == NULL) { 8928db0a5eSKenneth D. Merry warnx("%s: error allocating CCB", __func__); 9028db0a5eSKenneth D. Merry error = 1; 9128db0a5eSKenneth D. Merry goto bailout; 9228db0a5eSKenneth D. Merry } 9328db0a5eSKenneth D. Merry /* 9428db0a5eSKenneth D. Merry * Get the control extension subpage, we'll send it back modified to 9528db0a5eSKenneth D. Merry * enable SCSI control over the tape drive's timestamp 9628db0a5eSKenneth D. Merry */ 9728db0a5eSKenneth D. Merry scsi_mode_sense_len(&ccb->csio, 9828db0a5eSKenneth D. Merry /*retries*/ retry_count, 9928db0a5eSKenneth D. Merry /*cbfcnp*/ NULL, 10028db0a5eSKenneth D. Merry /*tag_action*/ MSG_SIMPLE_Q_TAG, 10128db0a5eSKenneth D. Merry /*dbd*/ 0, 10228db0a5eSKenneth D. Merry /*page_control*/ SMS_PAGE_CTRL_CURRENT, 10328db0a5eSKenneth D. Merry /*page*/ SCEP_PAGE_CODE, 10428db0a5eSKenneth D. Merry /*param_buf*/ &mode_buf[0], 10528db0a5eSKenneth D. Merry /*param_len*/ mode_buf_size, 10628db0a5eSKenneth D. Merry /*minimum_cmd_size*/ 10, 10728db0a5eSKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE, 10828db0a5eSKenneth D. Merry /*timeout*/ timeout ? timeout : 5000); 10928db0a5eSKenneth D. Merry /* 11028db0a5eSKenneth D. Merry * scsi_mode_sense_len does not have a subpage argument at the moment, 11128db0a5eSKenneth D. Merry * so we have to manually set the subpage code before calling 11228db0a5eSKenneth D. Merry * cam_send_ccb(). 11328db0a5eSKenneth D. Merry */ 11428db0a5eSKenneth D. Merry cdb = (struct scsi_mode_sense_10 *)ccb->csio.cdb_io.cdb_bytes; 11528db0a5eSKenneth D. Merry cdb->subpage = SCEP_SUBPAGE_CODE; 11628db0a5eSKenneth D. Merry 11728db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 11828db0a5eSKenneth D. Merry if (retry_count > 0) 11928db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 12028db0a5eSKenneth D. Merry 12128db0a5eSKenneth D. Merry error = cam_send_ccb(device, ccb); 12228db0a5eSKenneth D. Merry if (error != 0) { 12328db0a5eSKenneth D. Merry warn("error sending Mode Sense"); 12428db0a5eSKenneth D. Merry goto bailout; 12528db0a5eSKenneth D. Merry } 12628db0a5eSKenneth D. Merry 12728db0a5eSKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 12828db0a5eSKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL, 12928db0a5eSKenneth D. Merry CAM_EPF_ALL, stderr); 13028db0a5eSKenneth D. Merry error = 1; 13128db0a5eSKenneth D. Merry goto bailout; 13228db0a5eSKenneth D. Merry } 13328db0a5eSKenneth D. Merry 13428db0a5eSKenneth D. Merry mode_hdr = (struct scsi_mode_header_10 *)&mode_buf[0]; 13528db0a5eSKenneth D. Merry blk_desc_length = scsi_2btoul(mode_hdr->blk_desc_len); 13628db0a5eSKenneth D. Merry hdr_and_blk_length = sizeof(struct scsi_mode_header_10)+blk_desc_length; 13728db0a5eSKenneth D. Merry /* 13828db0a5eSKenneth D. Merry * Create the control page at the correct point in the mode_buf, it 13928db0a5eSKenneth D. Merry * starts after the header and the blk description. 14028db0a5eSKenneth D. Merry */ 14128db0a5eSKenneth D. Merry control_page = (struct scsi_control_ext_page *)&mode_buf 14228db0a5eSKenneth D. Merry [hdr_and_blk_length]; 14328db0a5eSKenneth D. Merry if (set_flag != 0) { 14428db0a5eSKenneth D. Merry *flags = control_page->flags; 14528db0a5eSKenneth D. Merry /* 14628db0a5eSKenneth D. Merry * Set the SCSIP flag to enable SCSI to change the 14728db0a5eSKenneth D. Merry * tape drive's timestamp. 14828db0a5eSKenneth D. Merry */ 14928db0a5eSKenneth D. Merry control_page->flags |= SCEP_SCSIP; 15028db0a5eSKenneth D. Merry } else { 15128db0a5eSKenneth D. Merry control_page->flags = *flags; 15228db0a5eSKenneth D. Merry } 15328db0a5eSKenneth D. Merry 15428db0a5eSKenneth D. Merry scsi_mode_select_len(&ccb->csio, 15528db0a5eSKenneth D. Merry /*retries*/ retry_count, 15628db0a5eSKenneth D. Merry /*cbfcnp*/ NULL, 15728db0a5eSKenneth D. Merry /*tag_action*/ MSG_SIMPLE_Q_TAG, 15828db0a5eSKenneth D. Merry /*scsi_page_fmt*/ 1, 15928db0a5eSKenneth D. Merry /*save_pages*/ 0, 16028db0a5eSKenneth D. Merry /*param_buf*/ &mode_buf[0], 16128db0a5eSKenneth D. Merry /*param_len*/ mode_buf_size, 16228db0a5eSKenneth D. Merry /*minimum_cmd_size*/ 10, 16328db0a5eSKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE, 16428db0a5eSKenneth D. Merry /*timeout*/ timeout ? timeout : 5000); 16528db0a5eSKenneth D. Merry 16628db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 16728db0a5eSKenneth D. Merry if (retry_count > 0) 16828db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 16928db0a5eSKenneth D. Merry 17028db0a5eSKenneth D. Merry error = cam_send_ccb(device, ccb); 17128db0a5eSKenneth D. Merry if (error != 0) { 17228db0a5eSKenneth D. Merry warn("error sending Mode Select"); 17328db0a5eSKenneth D. Merry goto bailout; 17428db0a5eSKenneth D. Merry } 17528db0a5eSKenneth D. Merry 17628db0a5eSKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 17728db0a5eSKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL, 17828db0a5eSKenneth D. Merry CAM_EPF_ALL, stderr); 17928db0a5eSKenneth D. Merry error = 1; 18028db0a5eSKenneth D. Merry goto bailout; 18128db0a5eSKenneth D. Merry } 18228db0a5eSKenneth D. Merry 18328db0a5eSKenneth D. Merry bailout: 18428db0a5eSKenneth D. Merry if (ccb != NULL) 18528db0a5eSKenneth D. Merry cam_freeccb(ccb); 18628db0a5eSKenneth D. Merry 18728db0a5eSKenneth D. Merry return error; 18828db0a5eSKenneth D. Merry } 18928db0a5eSKenneth D. Merry 19028db0a5eSKenneth D. Merry static int 19128db0a5eSKenneth D. Merry report_timestamp(struct cam_device *device, uint64_t *ts, 19228db0a5eSKenneth D. Merry int retry_count, int timeout) 19328db0a5eSKenneth D. Merry { 19428db0a5eSKenneth D. Merry int error = 0; 19528db0a5eSKenneth D. Merry struct scsi_report_timestamp_data *report_buf = malloc( 19628db0a5eSKenneth D. Merry sizeof(struct scsi_report_timestamp_data)); 19728db0a5eSKenneth D. Merry uint8_t temp_timestamp[8]; 19828db0a5eSKenneth D. Merry uint32_t report_buf_size = sizeof( 19928db0a5eSKenneth D. Merry struct scsi_report_timestamp_data); 20028db0a5eSKenneth D. Merry union ccb *ccb = NULL; 20128db0a5eSKenneth D. Merry 20228db0a5eSKenneth D. Merry ccb = cam_getccb(device); 20328db0a5eSKenneth D. Merry if (ccb == NULL) { 20428db0a5eSKenneth D. Merry warnx("%s: error allocating CCB", __func__); 20528db0a5eSKenneth D. Merry error = 1; 20628db0a5eSKenneth D. Merry goto bailout; 20728db0a5eSKenneth D. Merry } 20828db0a5eSKenneth D. Merry 20928db0a5eSKenneth D. Merry scsi_report_timestamp(&ccb->csio, 21028db0a5eSKenneth D. Merry /*retries*/ retry_count, 21128db0a5eSKenneth D. Merry /*cbfcnp*/ NULL, 21228db0a5eSKenneth D. Merry /*tag_action*/ MSG_SIMPLE_Q_TAG, 21328db0a5eSKenneth D. Merry /*pdf*/ 0, 21428db0a5eSKenneth D. Merry /*buf*/ report_buf, 21528db0a5eSKenneth D. Merry /*buf_len*/ report_buf_size, 21628db0a5eSKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE, 21728db0a5eSKenneth D. Merry /*timeout*/ timeout ? timeout : 5000); 21828db0a5eSKenneth D. Merry 21928db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 22028db0a5eSKenneth D. Merry if (retry_count > 0) 22128db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 22228db0a5eSKenneth D. Merry 22328db0a5eSKenneth D. Merry error = cam_send_ccb(device, ccb); 22428db0a5eSKenneth D. Merry if (error != 0) { 22528db0a5eSKenneth D. Merry warn("error sending Report Timestamp"); 22628db0a5eSKenneth D. Merry goto bailout; 22728db0a5eSKenneth D. Merry } 22828db0a5eSKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 22928db0a5eSKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL, 23028db0a5eSKenneth D. Merry CAM_EPF_ALL, stderr); 23128db0a5eSKenneth D. Merry error = 1; 23228db0a5eSKenneth D. Merry goto bailout; 23328db0a5eSKenneth D. Merry } 23428db0a5eSKenneth D. Merry 23528db0a5eSKenneth D. Merry bzero(temp_timestamp, sizeof(temp_timestamp)); 23628db0a5eSKenneth D. Merry memcpy(&temp_timestamp[2], &report_buf->timestamp, 6); 23728db0a5eSKenneth D. Merry 23828db0a5eSKenneth D. Merry *ts = scsi_8btou64(temp_timestamp); 23928db0a5eSKenneth D. Merry 24028db0a5eSKenneth D. Merry bailout: 24128db0a5eSKenneth D. Merry if (ccb != NULL) 24228db0a5eSKenneth D. Merry cam_freeccb(ccb); 24328db0a5eSKenneth D. Merry 24428db0a5eSKenneth D. Merry return error; 24528db0a5eSKenneth D. Merry } 24628db0a5eSKenneth D. Merry 24728db0a5eSKenneth D. Merry static int 24828db0a5eSKenneth D. Merry set_timestamp(struct cam_device *device, char *format_string, 24928db0a5eSKenneth D. Merry char *timestamp_string, int retry_count, 25028db0a5eSKenneth D. Merry int timeout) 25128db0a5eSKenneth D. Merry { 25228db0a5eSKenneth D. Merry struct scsi_set_timestamp_parameters ts_p; 25328db0a5eSKenneth D. Merry time_t time_value; 25428db0a5eSKenneth D. Merry struct tm time_struct; 25528db0a5eSKenneth D. Merry uint8_t flags = 0; 25628db0a5eSKenneth D. Merry int error = 0; 25728db0a5eSKenneth D. Merry uint64_t ts = 0; 25828db0a5eSKenneth D. Merry union ccb *ccb = NULL; 25928db0a5eSKenneth D. Merry int do_restore_flags = 0; 26028db0a5eSKenneth D. Merry 26128db0a5eSKenneth D. Merry error = set_restore_flags(device, &flags, /*set_flag*/ 1, retry_count, 26228db0a5eSKenneth D. Merry timeout); 26328db0a5eSKenneth D. Merry if (error != 0) 26428db0a5eSKenneth D. Merry goto bailout; 26528db0a5eSKenneth D. Merry 26628db0a5eSKenneth D. Merry do_restore_flags = 1; 26728db0a5eSKenneth D. Merry 26828db0a5eSKenneth D. Merry ccb = cam_getccb(device); 26928db0a5eSKenneth D. Merry if (ccb == NULL) { 27028db0a5eSKenneth D. Merry warnx("%s: error allocating CCB", __func__); 27128db0a5eSKenneth D. Merry error = 1; 27228db0a5eSKenneth D. Merry goto bailout; 27328db0a5eSKenneth D. Merry } 27428db0a5eSKenneth D. Merry 27528db0a5eSKenneth D. Merry if (strcmp(format_string, UTC) == 0) { 27628db0a5eSKenneth D. Merry time(&time_value); 27728db0a5eSKenneth D. Merry ts = (uint64_t) time_value; 27828db0a5eSKenneth D. Merry } else { 27928db0a5eSKenneth D. Merry bzero(&time_struct, sizeof(struct tm)); 28028db0a5eSKenneth D. Merry strptime(timestamp_string, format_string, &time_struct); 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; 28628db0a5eSKenneth D. Merry scsi_create_timestamp(ts_p.timestamp, ts); 28728db0a5eSKenneth D. Merry 28828db0a5eSKenneth D. Merry scsi_set_timestamp(&ccb->csio, 28928db0a5eSKenneth D. Merry /*retries*/ retry_count, 29028db0a5eSKenneth D. Merry /*cbfcnp*/ NULL, 29128db0a5eSKenneth D. Merry /*tag_action*/ MSG_SIMPLE_Q_TAG, 29228db0a5eSKenneth D. Merry /*buf*/ &ts_p, 29328db0a5eSKenneth D. Merry /*buf_len*/ sizeof(ts_p), 29428db0a5eSKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE, 29528db0a5eSKenneth D. Merry /*timeout*/ timeout ? timeout : 5000); 29628db0a5eSKenneth D. Merry 29728db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 29828db0a5eSKenneth D. Merry if (retry_count > 0) 29928db0a5eSKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; 30028db0a5eSKenneth D. Merry 30128db0a5eSKenneth D. Merry error = cam_send_ccb(device, ccb); 30228db0a5eSKenneth D. Merry if (error != 0) { 30328db0a5eSKenneth D. Merry warn("error sending Set Timestamp"); 30428db0a5eSKenneth D. Merry goto bailout; 30528db0a5eSKenneth D. Merry } 30628db0a5eSKenneth D. Merry 30728db0a5eSKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 30828db0a5eSKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL, 30928db0a5eSKenneth D. Merry CAM_EPF_ALL, stderr); 31028db0a5eSKenneth D. Merry error = 1; 31128db0a5eSKenneth D. Merry goto bailout; 31228db0a5eSKenneth D. Merry } 31328db0a5eSKenneth D. Merry 31428db0a5eSKenneth D. Merry printf("Timestamp set to %ju\n", (uintmax_t)ts); 31528db0a5eSKenneth D. Merry 31628db0a5eSKenneth D. Merry bailout: 31728db0a5eSKenneth D. Merry if (do_restore_flags != 0) 31828db0a5eSKenneth D. Merry error = set_restore_flags(device, &flags, /*set_flag*/ 0, 31928db0a5eSKenneth D. Merry retry_count, timeout); 32028db0a5eSKenneth D. Merry if (ccb != NULL) 32128db0a5eSKenneth D. Merry cam_freeccb(ccb); 32228db0a5eSKenneth D. Merry 32328db0a5eSKenneth D. Merry return error; 32428db0a5eSKenneth D. Merry } 32528db0a5eSKenneth D. Merry 32628db0a5eSKenneth D. Merry int 32728db0a5eSKenneth D. Merry timestamp(struct cam_device *device, int argc, char **argv, char *combinedopt, 32828db0a5eSKenneth D. Merry int retry_count, int timeout, int verbosemode __unused) 32928db0a5eSKenneth D. Merry { 33028db0a5eSKenneth D. Merry int c; 331a1fa2391SAdrian Chadd uint64_t ts = 0; 33228db0a5eSKenneth D. Merry char *format_string = NULL; 33328db0a5eSKenneth D. Merry char *timestamp_string = NULL; 33428db0a5eSKenneth D. Merry int action = -1; 33528db0a5eSKenneth D. Merry int error = 0; 33628db0a5eSKenneth D. Merry int single_arg = 0; 33728db0a5eSKenneth D. Merry int do_utc = 0; 33828db0a5eSKenneth D. Merry 33928db0a5eSKenneth D. Merry while ((c = getopt(argc, argv, combinedopt)) != -1) { 34028db0a5eSKenneth D. Merry switch (c) { 34128db0a5eSKenneth D. Merry case 'r': { 34228db0a5eSKenneth D. Merry if (action != -1) { 34328db0a5eSKenneth D. Merry warnx("Use only one -r or only one -s"); 34428db0a5eSKenneth D. Merry error =1; 34528db0a5eSKenneth D. Merry goto bailout; 34628db0a5eSKenneth D. Merry } 34728db0a5eSKenneth D. Merry action = TIMESTAMP_REPORT; 34828db0a5eSKenneth D. Merry break; 34928db0a5eSKenneth D. Merry } 35028db0a5eSKenneth D. Merry case 's': { 35128db0a5eSKenneth D. Merry if (action != -1) { 35228db0a5eSKenneth D. Merry warnx("Use only one -r or only one -s"); 35328db0a5eSKenneth D. Merry error = 1; 35428db0a5eSKenneth D. Merry goto bailout; 35528db0a5eSKenneth D. Merry } 35628db0a5eSKenneth D. Merry action = TIMESTAMP_SET; 35728db0a5eSKenneth D. Merry break; 35828db0a5eSKenneth D. Merry } 35928db0a5eSKenneth D. Merry case 'f': { 36028db0a5eSKenneth D. Merry single_arg++; 361*f2a12bceSEnji Cooper free(format_string); 36228db0a5eSKenneth D. Merry format_string = strdup(optarg); 36328db0a5eSKenneth D. Merry if (format_string == NULL) { 36428db0a5eSKenneth D. Merry warn("Error allocating memory for format " 36528db0a5eSKenneth D. Merry "argument"); 36628db0a5eSKenneth D. Merry error = 1; 36728db0a5eSKenneth D. Merry goto bailout; 36828db0a5eSKenneth D. Merry } 36928db0a5eSKenneth D. Merry break; 37028db0a5eSKenneth D. Merry } 37128db0a5eSKenneth D. Merry case 'm': { 37228db0a5eSKenneth D. Merry single_arg++; 373*f2a12bceSEnji Cooper free(format_string); 37428db0a5eSKenneth D. Merry format_string = strdup(MIL); 37528db0a5eSKenneth D. Merry if (format_string == NULL) { 37628db0a5eSKenneth D. Merry warn("Error allocating memory"); 37728db0a5eSKenneth D. Merry error = 1; 37828db0a5eSKenneth D. Merry goto bailout; 37928db0a5eSKenneth D. Merry } 38028db0a5eSKenneth D. Merry break; 38128db0a5eSKenneth D. Merry } 38228db0a5eSKenneth D. Merry case 'U': { 38328db0a5eSKenneth D. Merry do_utc = 1; 38428db0a5eSKenneth D. Merry break; 38528db0a5eSKenneth D. Merry } 38628db0a5eSKenneth D. Merry case 'T': 387*f2a12bceSEnji Cooper free(timestamp_string); 38828db0a5eSKenneth D. Merry timestamp_string = strdup(optarg); 38928db0a5eSKenneth D. Merry if (timestamp_string == NULL) { 39028db0a5eSKenneth D. Merry warn("Error allocating memory for format " 39128db0a5eSKenneth D. Merry "argument"); 39228db0a5eSKenneth D. Merry error = 1; 39328db0a5eSKenneth D. Merry goto bailout; 39428db0a5eSKenneth D. Merry } 39528db0a5eSKenneth D. Merry break; 39628db0a5eSKenneth D. Merry default: 39728db0a5eSKenneth D. Merry break; 39828db0a5eSKenneth D. Merry } 39928db0a5eSKenneth D. Merry } 40028db0a5eSKenneth D. Merry 40128db0a5eSKenneth D. Merry if (action == -1) { 40228db0a5eSKenneth D. Merry warnx("Must specify an action, either -r or -s"); 40328db0a5eSKenneth D. Merry error = 1; 40428db0a5eSKenneth D. Merry goto bailout; 40528db0a5eSKenneth D. Merry } 40628db0a5eSKenneth D. Merry 40728db0a5eSKenneth D. Merry if (single_arg > 1) { 40828db0a5eSKenneth D. Merry warnx("Select only one: %s", 40928db0a5eSKenneth D. Merry (action == TIMESTAMP_REPORT) ? 41028db0a5eSKenneth D. Merry "-f format or -m for the -r flag" : 41128db0a5eSKenneth D. Merry "-f format -T time or -U for the -s flag"); 41228db0a5eSKenneth D. Merry error = 1; 41328db0a5eSKenneth D. Merry goto bailout; 41428db0a5eSKenneth D. Merry } 41528db0a5eSKenneth D. Merry 41628db0a5eSKenneth D. Merry if (action == TIMESTAMP_SET) { 41728db0a5eSKenneth D. Merry if ((format_string == NULL) 41828db0a5eSKenneth D. Merry && (do_utc == 0)) { 41928db0a5eSKenneth D. Merry warnx("Must specify either -f format or -U for " 42028db0a5eSKenneth D. Merry "setting the timestamp"); 42128db0a5eSKenneth D. Merry error = 1; 42228db0a5eSKenneth D. Merry } else if ((format_string != NULL) 42328db0a5eSKenneth D. Merry && (do_utc != 0)) { 42428db0a5eSKenneth D. Merry warnx("Must specify only one of -f or -U to set " 42528db0a5eSKenneth D. Merry "the timestamp"); 42628db0a5eSKenneth D. Merry error = 1; 42728db0a5eSKenneth D. Merry } else if ((format_string != NULL) 42828db0a5eSKenneth D. Merry && (strcmp(format_string, MIL) == 0)) { 42928db0a5eSKenneth D. Merry warnx("-m is not allowed for setting the " 43028db0a5eSKenneth D. Merry "timestamp"); 43128db0a5eSKenneth D. Merry error = 1; 43228db0a5eSKenneth D. Merry } else if ((do_utc == 0) 43328db0a5eSKenneth D. Merry && (timestamp_string == NULL)) { 43428db0a5eSKenneth D. Merry warnx("Must specify the time (-T) to set as the " 43528db0a5eSKenneth D. Merry "timestamp"); 43628db0a5eSKenneth D. Merry error = 1; 43728db0a5eSKenneth D. Merry } 43828db0a5eSKenneth D. Merry if (error != 0) 43928db0a5eSKenneth D. Merry goto bailout; 44028db0a5eSKenneth D. Merry } else if (action == TIMESTAMP_REPORT) { 44128db0a5eSKenneth D. Merry if (format_string == NULL) { 44228db0a5eSKenneth D. Merry format_string = strdup("%c %Z"); 44328db0a5eSKenneth D. Merry if (format_string == NULL) { 44428db0a5eSKenneth D. Merry warn("Error allocating memory for format " 44528db0a5eSKenneth D. Merry "string"); 44628db0a5eSKenneth D. Merry error = 1; 44728db0a5eSKenneth D. Merry goto bailout; 44828db0a5eSKenneth D. Merry } 44928db0a5eSKenneth D. Merry } 45028db0a5eSKenneth D. Merry } 45128db0a5eSKenneth D. Merry 45228db0a5eSKenneth D. Merry if (action == TIMESTAMP_REPORT) { 45328db0a5eSKenneth D. Merry error = report_timestamp(device, &ts, retry_count, 45428db0a5eSKenneth D. Merry timeout); 45528db0a5eSKenneth D. Merry if (error != 0) { 45628db0a5eSKenneth D. Merry goto bailout; 45728db0a5eSKenneth D. Merry } else if (strcmp(format_string, MIL) == 0) { 45828db0a5eSKenneth D. Merry printf("Timestamp in milliseconds: %ju\n", 45928db0a5eSKenneth D. Merry (uintmax_t)ts); 46028db0a5eSKenneth D. Merry } else { 46128db0a5eSKenneth D. Merry char temp_timestamp_string[100]; 46228db0a5eSKenneth D. Merry time_t time_var = ts / 1000; 46328db0a5eSKenneth D. Merry const struct tm *restrict cur_time; 46428db0a5eSKenneth D. Merry 46528db0a5eSKenneth D. Merry setlocale(LC_TIME, ""); 46628db0a5eSKenneth D. Merry if (do_utc != 0) 46728db0a5eSKenneth D. Merry cur_time = gmtime(&time_var); 46828db0a5eSKenneth D. Merry else 46928db0a5eSKenneth D. Merry cur_time = localtime(&time_var); 47028db0a5eSKenneth D. Merry 47128db0a5eSKenneth D. Merry strftime(temp_timestamp_string, 47228db0a5eSKenneth D. Merry sizeof(temp_timestamp_string), format_string, 47328db0a5eSKenneth D. Merry cur_time); 47428db0a5eSKenneth D. Merry printf("Formatted timestamp: %s\n", 47528db0a5eSKenneth D. Merry temp_timestamp_string); 47628db0a5eSKenneth D. Merry } 47728db0a5eSKenneth D. Merry } else if (action == TIMESTAMP_SET) { 47828db0a5eSKenneth D. Merry if (do_utc != 0) { 47928db0a5eSKenneth D. Merry format_string = strdup(UTC); 48028db0a5eSKenneth D. Merry if (format_string == NULL) { 48128db0a5eSKenneth D. Merry warn("Error allocating memory for format " 48228db0a5eSKenneth D. Merry "string"); 48328db0a5eSKenneth D. Merry error = 1; 48428db0a5eSKenneth D. Merry goto bailout; 48528db0a5eSKenneth D. Merry } 48628db0a5eSKenneth D. Merry } 48728db0a5eSKenneth D. Merry 48828db0a5eSKenneth D. Merry error = set_timestamp(device, format_string, timestamp_string, 48928db0a5eSKenneth D. Merry retry_count, timeout); 49028db0a5eSKenneth D. Merry } 49128db0a5eSKenneth D. Merry 49228db0a5eSKenneth D. Merry bailout: 49328db0a5eSKenneth D. Merry free(format_string); 49428db0a5eSKenneth D. Merry free(timestamp_string); 49528db0a5eSKenneth D. Merry 49628db0a5eSKenneth D. Merry return (error); 49728db0a5eSKenneth D. Merry } 498