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