diff --git a/sbin/camcontrol/Makefile b/sbin/camcontrol/Makefile index dc9320ac3170..a1ad69309c79 100644 --- a/sbin/camcontrol/Makefile +++ b/sbin/camcontrol/Makefile @@ -4,7 +4,7 @@ PACKAGE=runtime PROG= camcontrol SRCS= camcontrol.c util.c .if !defined(RELEASE_CRUNCH) -SRCS+= attrib.c epc.c fwdownload.c modeedit.c persist.c progress.c zone.c +SRCS+= attrib.c epc.c fwdownload.c modeedit.c persist.c progress.c timestamp.c zone.c .else CFLAGS+= -DMINIMALISTIC .endif diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8 index 5997e1f2c318..ee44d55772c6 100644 --- a/sbin/camcontrol/camcontrol.8 +++ b/sbin/camcontrol/camcontrol.8 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 16, 2016 +.Dd November 30, 2016 .Dt CAMCONTROL 8 .Os .Sh NAME @@ -343,6 +343,11 @@ .Op Fl S Ar power_src .Op Fl T Ar timer .Nm +.Ic timestamp +.Op device id +.Op generic args +.Ao Fl r Oo Ns Fl f Ar format | Fl m | Fl U Oc | Fl s Ao Fl f Ar format Fl T Ar time | Fl U Ac Ac +.Nm .Ic help .Sh DESCRIPTION The @@ -2417,6 +2422,54 @@ supports, and a number of parameters about each condition, including whether it is enabled and what the timer value is. .El .El +.It Ic timestamp +Issue REPORT TIMESTAMP or SET TIMESTAMP +.Tn SCSI +commands. Either the +.Fl r +option or the +.Fl s +option must be specified. +.Bl -tag -width 6n +.It Fl r +Report the device's timestamp. +If no more arguments are specified, the timestamp will be reported using +the national representation of the date and time, followed by the time +zone. +.Bl -tag -width 9n +.It Fl f Ar format +Specify the strftime format string, as documented in strftime(3), to be used +to format the reported timestamp. +.It Fl m +Report the timestamp as milliseconds since the epoch. +.It Fl U +Report the timestamp using the national representation of the date and +time, but override the system time zone and use UTC instead. +.El +.El +.Bl -tag -width 6n +.It Fl s +Set the device's timestamp. Either the +.Fl f +and +.Fl T +options or the +.Fl U +option must be specified. +.Bl -tag -width 9n +.It Fl f Ar format +Specify the strptime format string, as documented in strptime(3). +The time must also be specified with the +.Fl T +option. +.It Fl T +Provide the time in the format specified with the +.Fl f +option. +.It Fl U +Set the timestamp to the host system's time in UTC. +.El +.El .It Ic help Print out verbose usage information. .El @@ -2730,6 +2783,18 @@ camcontrol epc ada0 -c list Display the ATA Power Conditions log (Log Address 0x08) for drive .Pa ada0 . +.Pp +.Bd -literal -offset indent +camcontrol timestamp sa0 -s -f "%A %c" \e + -T "Wednesday Wed Oct 26 21:43:57 2016" +.Ed +.Pp +Set the timestamp of drive +.Pa sa0 +using a +.Xr strptime 3 +format string followed by a time string +that was created using this format string. .Sh SEE ALSO .Xr cam 3 , .Xr cam_cdbparse 3 , diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index 3a443a9c1f3e..8fdb45ca9cf6 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -103,7 +103,8 @@ typedef enum { CAM_CMD_OPCODES = 0x00000024, CAM_CMD_REPROBE = 0x00000025, CAM_CMD_ZONE = 0x00000026, - CAM_CMD_EPC = 0x00000027 + CAM_CMD_EPC = 0x00000027, + CAM_CMD_TIMESTAMP = 0x00000028 } cam_cmdmask; typedef enum { @@ -234,6 +235,7 @@ static struct camcontrol_opts option_table[] = { {"opcodes", CAM_CMD_OPCODES, CAM_ARG_NONE, "No:s:T"}, {"zone", CAM_CMD_ZONE, CAM_ARG_NONE, "ac:l:No:P:"}, {"epc", CAM_CMD_EPC, CAM_ARG_NONE, "c:dDeHp:Pr:sS:T:"}, + {"timestamp", CAM_CMD_TIMESTAMP, CAM_ARG_NONE, "f:mrsUT:"}, #endif /* MINIMALISTIC */ {"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, {"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, @@ -8922,6 +8924,9 @@ usage(int printlong) " camcontrol epc [dev_id][generic_args]<-c cmd> [-d] [-D] [-e]\n" " [-H] [-p power_cond] [-P] [-r rst_src] [-s]\n" " [-S power_src] [-T timer]\n" +" camcontrol timestamp [dev_id][generic_args] <-r [-f format|-m|-U]>|\n" +" <-s <-f format -T time | -U >>\n" +" \n" #endif /* MINIMALISTIC */ " camcontrol help\n"); if (!printlong) @@ -8966,6 +8971,7 @@ usage(int printlong) "opcodes send the SCSI REPORT SUPPORTED OPCODES command\n" "zone manage Zoned Block (Shingled) devices\n" "epc send ATA Extended Power Conditions commands\n" +"timestamp report or set the device's timestamp\n" "help this message\n" "Device Identifiers:\n" "bus:target specify the bus and target, lun defaults to 0\n" @@ -9157,6 +9163,17 @@ usage(int printlong) "-s save mode (timer, state, restore)\n" "-S power_src set power source: battery, nonbattery (source)\n" "-T timer set timer, seconds, .1 sec resolution (timer)\n" +"timestamp arguments:\n" +"-r report the timestamp of the device\n" +"-f format report the timestamp of the device with the given\n" +" strftime(3) format string\n" +"-m report the timestamp of the device as milliseconds since\n" +" January 1st, 1970\n" +"-U report the time with UTC instead of the local time zone\n" +"-s set the timestamp of the device\n" +"-f format the format of the time string passed into strptime(3)\n" +"-T time the time value passed into strptime(3)\n" +"-U set the timestamp of the device to UTC time\n" ); #endif /* MINIMALISTIC */ } @@ -9520,6 +9537,10 @@ main(int argc, char **argv) error = epc(cam_dev, argc, argv, combinedopt, retry_count, timeout, arglist & CAM_ARG_VERBOSE); break; + case CAM_CMD_TIMESTAMP: + error = timestamp(cam_dev, argc, argv, combinedopt, + retry_count, timeout, arglist & CAM_ARG_VERBOSE); + break; #endif /* MINIMALISTIC */ case CAM_CMD_USAGE: usage(1); diff --git a/sbin/camcontrol/camcontrol.h b/sbin/camcontrol/camcontrol.h index 76b7f9b43f5d..a9a661e728a5 100644 --- a/sbin/camcontrol/camcontrol.h +++ b/sbin/camcontrol/camcontrol.h @@ -81,6 +81,9 @@ int zone(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout, int verbosemode); int epc(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout, int verbosemode); +int timestamp(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout, + int verbosemode); void mode_sense(struct cam_device *device, int mode_page, int page_control, int dbd, int retry_count, int timeout, u_int8_t *data, int datalen); diff --git a/sbin/camcontrol/timestamp.c b/sbin/camcontrol/timestamp.c new file mode 100644 index 000000000000..f452affd1818 --- /dev/null +++ b/sbin/camcontrol/timestamp.c @@ -0,0 +1,494 @@ +/*- + * Copyright (c) 2016 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Ken Merry (Spectra Logic Corporation) + * Reid Linnemann (Spectra Logic Corporation) + * Samuel Klopsch (Spectra Logic Corporation) + */ +/* + * SCSI tape drive timestamp support + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "camcontrol.h" + +#define TIMESTAMP_REPORT 0 +#define TIMESTAMP_SET 1 +#define MIL "milliseconds" +#define UTC "utc" + +static int set_restore_flags(struct cam_device *device, uint8_t *flags, + int set_flag, int retry_count, int timeout); +static int report_timestamp(struct cam_device *device, uint64_t *ts, + int retry_count, int timeout); +static int set_timestamp(struct cam_device *device, char *format_string, + char *timestamp_string, + int retry_count, int timeout); + +static int +set_restore_flags(struct cam_device *device, uint8_t *flags, int set_flag, + int retry_count, int timeout) +{ + unsigned long blk_desc_length, hdr_and_blk_length; + int error = 0; + struct scsi_control_ext_page *control_page = NULL; + struct scsi_mode_header_10 *mode_hdr = NULL; + struct scsi_mode_sense_10 *cdb = NULL; + union ccb *ccb = NULL; + unsigned long mode_buf_size = sizeof(struct scsi_mode_header_10) + + sizeof(struct scsi_mode_blk_desc) + + sizeof(struct scsi_control_ext_page); + uint8_t mode_buf[mode_buf_size]; + + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + error = 1; + goto bailout; + } + /* + * Get the control extension subpage, we'll send it back modified to + * enable SCSI control over the tape drive's timestamp + */ + scsi_mode_sense_len(&ccb->csio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*dbd*/ 0, + /*page_control*/ SMS_PAGE_CTRL_CURRENT, + /*page*/ SCEP_PAGE_CODE, + /*param_buf*/ &mode_buf[0], + /*param_len*/ mode_buf_size, + /*minimum_cmd_size*/ 10, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ timeout ? timeout : 5000); + /* + * scsi_mode_sense_len does not have a subpage argument at the moment, + * so we have to manually set the subpage code before calling + * cam_send_ccb(). + */ + cdb = (struct scsi_mode_sense_10 *)ccb->csio.cdb_io.cdb_bytes; + cdb->subpage = SCEP_SUBPAGE_CODE; + + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + if (retry_count > 0) + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; + + error = cam_send_ccb(device, ccb); + if (error != 0) { + warn("error sending Mode Sense"); + goto bailout; + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + error = 1; + goto bailout; + } + + mode_hdr = (struct scsi_mode_header_10 *)&mode_buf[0]; + blk_desc_length = scsi_2btoul(mode_hdr->blk_desc_len); + hdr_and_blk_length = sizeof(struct scsi_mode_header_10)+blk_desc_length; + /* + * Create the control page at the correct point in the mode_buf, it + * starts after the header and the blk description. + */ + control_page = (struct scsi_control_ext_page *)&mode_buf + [hdr_and_blk_length]; + if (set_flag != 0) { + *flags = control_page->flags; + /* + * Set the SCSIP flag to enable SCSI to change the + * tape drive's timestamp. + */ + control_page->flags |= SCEP_SCSIP; + } else { + control_page->flags = *flags; + } + + scsi_mode_select_len(&ccb->csio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*scsi_page_fmt*/ 1, + /*save_pages*/ 0, + /*param_buf*/ &mode_buf[0], + /*param_len*/ mode_buf_size, + /*minimum_cmd_size*/ 10, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ timeout ? timeout : 5000); + + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + if (retry_count > 0) + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; + + error = cam_send_ccb(device, ccb); + if (error != 0) { + warn("error sending Mode Select"); + goto bailout; + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + error = 1; + goto bailout; + } + +bailout: + if (ccb != NULL) + cam_freeccb(ccb); + + return error; +} + +static int +report_timestamp(struct cam_device *device, uint64_t *ts, + int retry_count, int timeout) +{ + int error = 0; + struct scsi_report_timestamp_data *report_buf = malloc( + sizeof(struct scsi_report_timestamp_data)); + uint8_t temp_timestamp[8]; + uint32_t report_buf_size = sizeof( + struct scsi_report_timestamp_data); + union ccb *ccb = NULL; + + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + error = 1; + goto bailout; + } + + scsi_report_timestamp(&ccb->csio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*pdf*/ 0, + /*buf*/ report_buf, + /*buf_len*/ report_buf_size, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ timeout ? timeout : 5000); + + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + if (retry_count > 0) + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; + + error = cam_send_ccb(device, ccb); + if (error != 0) { + warn("error sending Report Timestamp"); + goto bailout; + } + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + error = 1; + goto bailout; + } + + bzero(temp_timestamp, sizeof(temp_timestamp)); + memcpy(&temp_timestamp[2], &report_buf->timestamp, 6); + + *ts = scsi_8btou64(temp_timestamp); + +bailout: + if (ccb != NULL) + cam_freeccb(ccb); + + return error; +} + +static int +set_timestamp(struct cam_device *device, char *format_string, + char *timestamp_string, int retry_count, + int timeout) +{ + struct scsi_set_timestamp_parameters ts_p; + time_t time_value; + struct tm time_struct; + uint8_t flags = 0; + int error = 0; + uint64_t ts = 0; + union ccb *ccb = NULL; + int do_restore_flags = 0; + + error = set_restore_flags(device, &flags, /*set_flag*/ 1, retry_count, + timeout); + if (error != 0) + goto bailout; + + do_restore_flags = 1; + + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("%s: error allocating CCB", __func__); + error = 1; + goto bailout; + } + + if (strcmp(format_string, UTC) == 0) { + time(&time_value); + ts = (uint64_t) time_value; + } else { + bzero(&time_struct, sizeof(struct tm)); + strptime(timestamp_string, format_string, &time_struct); + time_value = mktime(&time_struct); + ts = (uint64_t) time_value; + } + /* Convert time from seconds to milliseconds */ + ts *= 1000; + scsi_create_timestamp(ts_p.timestamp, ts); + + scsi_set_timestamp(&ccb->csio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*buf*/ &ts_p, + /*buf_len*/ sizeof(ts_p), + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ timeout ? timeout : 5000); + + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + if (retry_count > 0) + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; + + error = cam_send_ccb(device, ccb); + if (error != 0) { + warn("error sending Set Timestamp"); + goto bailout; + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + error = 1; + goto bailout; + } + + printf("Timestamp set to %ju\n", (uintmax_t)ts); + +bailout: + if (do_restore_flags != 0) + error = set_restore_flags(device, &flags, /*set_flag*/ 0, + retry_count, timeout); + if (ccb != NULL) + cam_freeccb(ccb); + + return error; +} + +int +timestamp(struct cam_device *device, int argc, char **argv, char *combinedopt, + int retry_count, int timeout, int verbosemode __unused) +{ + int c; + uint64_t ts; + char *format_string = NULL; + char *timestamp_string = NULL; + int action = -1; + int error = 0; + int single_arg = 0; + int do_utc = 0; + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'r': { + if (action != -1) { + warnx("Use only one -r or only one -s"); + error =1; + goto bailout; + } + action = TIMESTAMP_REPORT; + break; + } + case 's': { + if (action != -1) { + warnx("Use only one -r or only one -s"); + error = 1; + goto bailout; + } + action = TIMESTAMP_SET; + break; + } + case 'f': { + single_arg++; + format_string = strdup(optarg); + if (format_string == NULL) { + warn("Error allocating memory for format " + "argument"); + error = 1; + goto bailout; + } + break; + } + case 'm': { + single_arg++; + format_string = strdup(MIL); + if (format_string == NULL) { + warn("Error allocating memory"); + error = 1; + goto bailout; + } + break; + } + case 'U': { + do_utc = 1; + break; + } + case 'T': + timestamp_string = strdup(optarg); + if (timestamp_string == NULL) { + warn("Error allocating memory for format " + "argument"); + error = 1; + goto bailout; + } + break; + default: + break; + } + } + + if (action == -1) { + warnx("Must specify an action, either -r or -s"); + error = 1; + goto bailout; + } + + if (single_arg > 1) { + warnx("Select only one: %s", + (action == TIMESTAMP_REPORT) ? + "-f format or -m for the -r flag" : + "-f format -T time or -U for the -s flag"); + error = 1; + goto bailout; + } + + if (action == TIMESTAMP_SET) { + if ((format_string == NULL) + && (do_utc == 0)) { + warnx("Must specify either -f format or -U for " + "setting the timestamp"); + error = 1; + } else if ((format_string != NULL) + && (do_utc != 0)) { + warnx("Must specify only one of -f or -U to set " + "the timestamp"); + error = 1; + } else if ((format_string != NULL) + && (strcmp(format_string, MIL) == 0)) { + warnx("-m is not allowed for setting the " + "timestamp"); + error = 1; + } else if ((do_utc == 0) + && (timestamp_string == NULL)) { + warnx("Must specify the time (-T) to set as the " + "timestamp"); + error = 1; + } + if (error != 0) + goto bailout; + } else if (action == TIMESTAMP_REPORT) { + if (format_string == NULL) { + format_string = strdup("%c %Z"); + if (format_string == NULL) { + warn("Error allocating memory for format " + "string"); + error = 1; + goto bailout; + } + } + } + + if (action == TIMESTAMP_REPORT) { + error = report_timestamp(device, &ts, retry_count, + timeout); + if (error != 0) { + goto bailout; + } else if (strcmp(format_string, MIL) == 0) { + printf("Timestamp in milliseconds: %ju\n", + (uintmax_t)ts); + } else { + char temp_timestamp_string[100]; + time_t time_var = ts / 1000; + const struct tm *restrict cur_time; + + setlocale(LC_TIME, ""); + if (do_utc != 0) + cur_time = gmtime(&time_var); + else + cur_time = localtime(&time_var); + + strftime(temp_timestamp_string, + sizeof(temp_timestamp_string), format_string, + cur_time); + printf("Formatted timestamp: %s\n", + temp_timestamp_string); + } + } else if (action == TIMESTAMP_SET) { + if (do_utc != 0) { + format_string = strdup(UTC); + if (format_string == NULL) { + warn("Error allocating memory for format " + "string"); + error = 1; + goto bailout; + } + } + + error = set_timestamp(device, format_string, timestamp_string, + retry_count, timeout); + } + +bailout: + free(format_string); + free(timestamp_string); + + return (error); +} diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c index ee45852811c7..0e152152c2d9 100644 --- a/sys/cam/scsi/scsi_all.c +++ b/sys/cam/scsi/scsi_all.c @@ -7946,6 +7946,32 @@ scsi_report_target_group(struct ccb_scsiio *csio, u_int32_t retries, scsi_ulto4b(alloc_len, scsi_cmd->length); } +void +scsi_report_timestamp(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, u_int8_t pdf, + void *buf, u_int32_t alloc_len, + u_int8_t sense_len, u_int32_t timeout) +{ + struct scsi_timestamp *scsi_cmd; + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_IN, + tag_action, + /*data_ptr*/(u_int8_t *)buf, + /*dxfer_len*/alloc_len, + sense_len, + sizeof(*scsi_cmd), + timeout); + scsi_cmd = (struct scsi_timestamp *)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + scsi_cmd->opcode = MAINTENANCE_IN; + scsi_cmd->service_action = REPORT_TIMESTAMP | pdf; + scsi_ulto4b(alloc_len, scsi_cmd->length); +} + void scsi_set_target_group(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), @@ -7971,6 +7997,45 @@ scsi_set_target_group(struct ccb_scsiio *csio, u_int32_t retries, scsi_ulto4b(alloc_len, scsi_cmd->length); } +void +scsi_create_timestamp(uint8_t *timestamp_6b_buf, + uint64_t timestamp) +{ + uint8_t buf[8]; + scsi_u64to8b(timestamp, buf); + /* + * Using memcopy starting at buf[2] because the set timestamp parameters + * only has six bytes for the timestamp to fit into, and we don't have a + * scsi_u64to6b function. + */ + memcpy(timestamp_6b_buf, &buf[2], 6); +} + +void +scsi_set_timestamp(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int8_t tag_action, void *buf, u_int32_t alloc_len, + u_int8_t sense_len, u_int32_t timeout) +{ + struct scsi_timestamp *scsi_cmd; + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_OUT, + tag_action, + /*data_ptr*/(u_int8_t *) buf, + /*dxfer_len*/alloc_len, + sense_len, + sizeof(*scsi_cmd), + timeout); + scsi_cmd = (struct scsi_timestamp *)&csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + scsi_cmd->opcode = MAINTENANCE_OUT; + scsi_cmd->service_action = SET_TIMESTAMP; + scsi_ulto4b(alloc_len, scsi_cmd->length); +} + /* * Syncronize the media to the contents of the cache for * the given lba/count pair. Specifying 0/0 means sync diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h index a23554b6c355..9c1376cfd293 100644 --- a/sys/cam/scsi/scsi_all.h +++ b/sys/cam/scsi/scsi_all.h @@ -702,7 +702,9 @@ struct scsi_control_page { struct scsi_control_ext_page { uint8_t page_code; +#define SCEP_PAGE_CODE 0x0a uint8_t subpage_code; +#define SCEP_SUBPAGE_CODE 0x01 uint8_t page_length[2]; uint8_t flags; #define SCEP_TCMOS 0x04 /* Timestamp Changeable by */ @@ -2971,6 +2973,31 @@ struct scsi_target_group uint8_t control; }; +struct scsi_timestamp +{ + uint8_t opcode; + uint8_t service_action; + uint8_t reserved1[4]; + uint8_t length[4]; + uint8_t reserved2; + uint8_t control; +}; + +struct scsi_set_timestamp_parameters +{ + uint8_t reserved1[4]; + uint8_t timestamp[6]; + uint8_t reserved2[4]; +}; + +struct scsi_report_timestamp_parameter_data +{ + uint8_t length[2]; + uint8_t reserved1[2]; + uint8_t timestamp[6]; + uint8_t reserved2[2]; +}; + struct scsi_target_port_descriptor { uint8_t reserved[2]; uint8_t relative_target_port_identifier[2]; @@ -3966,12 +3993,29 @@ void scsi_report_target_group(struct ccb_scsiio *csio, u_int32_t retries, u_int32_t alloc_len, u_int8_t sense_len, u_int32_t timeout); +void scsi_report_timestamp(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, + union ccb *), u_int8_t tag_action, + u_int8_t pdf, + void *buf, + u_int32_t alloc_len, u_int8_t sense_len, + u_int32_t timeout); + void scsi_set_target_group(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, void *buf, u_int32_t alloc_len, u_int8_t sense_len, u_int32_t timeout); +void scsi_create_timestamp(uint8_t *timestamp_6b_buf, + uint64_t timestamp); + +void scsi_set_timestamp(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, + union ccb *), u_int8_t tag_action, + void *buf, u_int32_t alloc_len, + u_int8_t sense_len, u_int32_t timeout); + void scsi_synchronize_cache(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *,