[Midnightbsd-cvs] src [11237] trunk/sbin/camcontrol: update camcontrol
laffer1 at midnightbsd.org
laffer1 at midnightbsd.org
Sun Jul 1 17:16:47 EDT 2018
Revision: 11237
http://svnweb.midnightbsd.org/src/?rev=11237
Author: laffer1
Date: 2018-07-01 17:16:46 -0400 (Sun, 01 Jul 2018)
Log Message:
-----------
update camcontrol
Modified Paths:
--------------
trunk/sbin/camcontrol/Makefile
trunk/sbin/camcontrol/camcontrol.8
trunk/sbin/camcontrol/camcontrol.c
trunk/sbin/camcontrol/camcontrol.h
trunk/sbin/camcontrol/fwdownload.c
trunk/sbin/camcontrol/modeedit.c
trunk/sbin/camcontrol/progress.c
Modified: trunk/sbin/camcontrol/Makefile
===================================================================
--- trunk/sbin/camcontrol/Makefile 2018-07-01 21:16:14 UTC (rev 11236)
+++ trunk/sbin/camcontrol/Makefile 2018-07-01 21:16:46 UTC (rev 11237)
@@ -1,13 +1,17 @@
# $MidnightBSD$
+# $FreeBSD: stable/10/sbin/camcontrol/Makefile 284435 2015-06-16 02:31:11Z ken $
PROG= camcontrol
SRCS= camcontrol.c util.c
.if !defined(RELEASE_CRUNCH)
-SRCS+= fwdownload.c modeedit.c progress.c
+SRCS+= attrib.c fwdownload.c modeedit.c persist.c progress.c
.else
CFLAGS+= -DMINIMALISTIC
.endif
+# This is verboten
+.if ${MACHINE_CPUARCH} == "arm"
WARNS?= 3
+.endif
DPADD= ${LIBCAM} ${LIBSBUF} ${LIBUTIL}
LDADD= -lcam -lsbuf -lutil
MAN= camcontrol.8
Modified: trunk/sbin/camcontrol/camcontrol.8
===================================================================
--- trunk/sbin/camcontrol/camcontrol.8 2018-07-01 21:16:14 UTC (rev 11236)
+++ trunk/sbin/camcontrol/camcontrol.8 2018-07-01 21:16:46 UTC (rev 11237)
@@ -1,3 +1,4 @@
+.\" $MidnightBSD$
.\"
.\" Copyright (c) 1998, 1999, 2000, 2002, 2005, 2006, 2007 Kenneth D. Merry.
.\" All rights reserved.
@@ -25,9 +26,9 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" $MidnightBSD$
+.\" $FreeBSD: stable/10/sbin/camcontrol/camcontrol.8 317947 2017-05-08 17:02:03Z ken $
.\"
-.Dd April 24, 2013
+.Dd February 17, 2017
.Dt CAMCONTROL 8
.Os
.Sh NAME
@@ -41,6 +42,7 @@
.Op command args
.Nm
.Ic devlist
+.Op Fl b
.Op Fl v
.Nm
.Ic periphlist
@@ -97,11 +99,14 @@
.Op device id
.Op generic args
.Nm
+.Ic reprobe
+.Op device id
+.Nm
.Ic rescan
-.Aq all | bus Ns Op :target:lun
+.Aq all | device id | bus Ns Op :target:lun
.Nm
.Ic reset
-.Aq all | bus Ns Op :target:lun
+.Aq all | device id | bus Ns Op :target:lun
.Nm
.Ic defects
.Op device id
@@ -109,11 +114,15 @@
.Aq Fl f Ar format
.Op Fl P
.Op Fl G
+.Op Fl q
+.Op Fl s
+.Op Fl S Ar offset
+.Op Fl X
.Nm
.Ic modepage
.Op device id
.Op generic args
-.Aq Fl m Ar page | Fl l
+.Aq Fl m Ar page[,subpage] | Fl l
.Op Fl P Ar pgctl
.Op Fl b | Fl e
.Op Fl d
@@ -207,6 +216,19 @@
.Op Fl w
.Op Fl y
.Nm
+.Ic sanitize
+.Op device id
+.Op generic args
+.Aq Fl a Ar overwrite | block | crypto | exitfailure
+.Op Fl c Ar passes
+.Op Fl I
+.Op Fl P Ar pattern
+.Op Fl q
+.Op Fl U
+.Op Fl r
+.Op Fl w
+.Op Fl y
+.Nm
.Ic idle
.Op device id
.Op generic args
@@ -221,12 +243,23 @@
.Op device id
.Op generic args
.Nm
+.Ic apm
+.Op device id
+.Op generic args
+.Op Fl l Ar level
+.Nm
+.Ic aam
+.Op device id
+.Op generic args
+.Op Fl l Ar level
+.Nm
.Ic fwdownload
.Op device id
.Op generic args
.Aq Fl f Ar fw_image
+.Op Fl q
+.Op Fl s
.Op Fl y
-.Op Fl s
.Nm
.Ic security
.Op device id
@@ -255,12 +288,48 @@
.Op Fl U Ar pwd
.Op Fl y
.Nm
+.Ic persist
+.Op device id
+.Op generic args
+.Aq Fl i Ar action | Fl o Ar action
+.Op Fl a
+.Op Fl I Ar trans_id
+.Op Fl k Ar key
+.Op Fl K Ar sa_key
+.Op Fl p
+.Op Fl R Ar rel_tgt_port
+.Op Fl s Ar scope
+.Op Fl S
+.Op Fl T Ar res_type
+.Op Fl U
+.Nm
+.Ic attrib
+.Op device id
+.Op generic args
+.Aq Fl r Ar action | Fl w Ar attrib
+.Op Fl a Ar attr_num
+.Op Fl c
+.Op Fl e Ar elem_addr
+.Op Fl F Ar form1,form2
+.Op Fl p Ar part
+.Op Fl s Ar start_addr
+.Op Fl T Ar elem_type
+.Op Fl V Ar lv_num
+.Nm
+.Ic opcodes
+.Op device id
+.Op generic args
+.Op Fl o Ar opcode
+.Op Fl s Ar service_action
+.Op Fl N
+.Op Fl T
+.Nm
.Ic help
.Sh DESCRIPTION
The
.Nm
utility is designed to provide a way for users to access and control the
-.Mx
+.Fx
CAM subsystem.
.Pp
The
@@ -329,6 +398,17 @@
the command.
.It Fl n Ar dev_name
Specify the device type to operate on, e.g.\& "da", "cd".
+.It Fl Q Ar task_attr
+.Tn SCSI
+task attribute for the command, if it is a
+.Tn SCSI
+command.
+This may be ordered, simple, head, or aca.
+In most cases this is not needed.
+The default is simple, which works with all
+.Tn SCSI
+devices.
+The task attribute may also be specified numerically.
.It Fl t Ar timeout
SCSI command timeout in seconds.
This overrides the default timeout for
@@ -348,6 +428,10 @@
.Fl v
argument, SCSI bus number, adapter name and unit numbers are printed as
well.
+On the other hand, with the
+.Fl b
+argument, only the bus adapter, and unit information will be printed, and
+device information will be omitted.
.It Ic periphlist
List all peripheral drivers attached to a given physical device (logical
unit).
@@ -388,7 +472,7 @@
.It Fl c
Just print out a count of LUNs, not the actual LUN numbers.
.It Fl l
-Just print out the LUNs, and don't print out the count.
+Just print out the LUNs, and do not print out the count.
.It Fl r Ar reporttype
Specify the type of report to request from the target:
.Bl -tag -width 012345678
@@ -449,6 +533,12 @@
Print out the last logical block or the size of the device only, and omit
the blocksize.
.El
+.Pp
+Note that this command only displays the information, it does not update
+the kernel data structures.
+Use the
+.Nm
+reprobe subcommand to do that.
.It Ic start
Send the SCSI Start/Stop Unit (0x1B) command to the given device with the
start bit set.
@@ -464,34 +554,46 @@
.It Ic rescan
Tell the kernel to scan all busses in the system (with the
.Ar all
-argument), the given bus (XPT_SCAN_BUS), or bus:target:lun
+argument), the given bus (XPT_SCAN_BUS), bus:target:lun or device
(XPT_SCAN_LUN) for new devices or devices that have gone away.
The user
may specify a scan of all busses, a single bus, or a lun.
Scanning all luns
on a target is not supported.
+.Pp
+If a device is specified by peripheral name and unit number, for instance
+da4, it may only be rescanned if that device currently exists in the CAM EDT
+(Existing Device Table).
+If the device is no longer there (see
+.Nm
+devlist ),
+you must use the bus:target:lun form to rescan it.
+.It Ic reprobe
+Tell the kernel to refresh the information about the device and
+notify the upper layer,
+.Xr GEOM 4 .
+This includes sending the SCSI READ CAPACITY command and updating
+the disk size visible to the rest of the system.
.It Ic reset
Tell the kernel to reset all busses in the system (with the
.Ar all
-argument) or the given bus (XPT_RESET_BUS) by issuing a SCSI bus
-reset for that bus, or to reset the given bus:target:lun
+argument), the given bus (XPT_RESET_BUS) by issuing a SCSI bus
+reset for that bus, or to reset the given bus:target:lun or device
(XPT_RESET_DEV), typically by issuing a BUS DEVICE RESET message after
connecting to that device.
Note that this can have a destructive impact
on the system.
.It Ic defects
-Send the SCSI READ DEFECT DATA (10) command (0x37) to the given device, and
+Send the
+.Tn SCSI
+READ DEFECT DATA (10) command (0x37) or the
+.Tn SCSI
+READ DEFECT DATA (12) command (0xB7) to the given device, and
print out any combination of: the total number of defects, the primary
defect list (PLIST), and the grown defect list (GLIST).
.Bl -tag -width 11n
.It Fl f Ar format
-The three format options are:
-.Em block ,
-to print out the list as logical blocks,
-.Em bfi ,
-to print out the list in bytes from index format, and
-.Em phys ,
-to print out the list in physical sector format.
+Specify the requested format of the defect list.
The format argument is
required.
Most drives support the physical sector format.
@@ -508,6 +610,29 @@
support the requested format,
.Nm
will probably see the error as a failure to complete the request.
+.Pp
+The format options are:
+.Bl -tag -width 9n
+.It block
+Print out the list as logical blocks.
+This is limited to 32-bit block sizes, and isn't supported by many modern
+drives.
+.It longblock
+Print out the list as logical blocks.
+This option uses a 64-bit block size.
+.It bfi
+Print out the list in bytes from index format.
+.It extbfi
+Print out the list in extended bytes from index format.
+The extended format allows for ranges of blocks to be printed.
+.It phys
+Print out the list in physical sector format.
+Most drives support this format.
+.It extphys
+Print out the list in extended physical sector format.
+The extended format allows for ranges of blocks to be printed.
+.El
+.Pp
.It Fl G
Print out the grown defect list.
This is a list of bad blocks that have
@@ -514,6 +639,23 @@
been remapped since the disk left the factory.
.It Fl P
Print out the primary defect list.
+This is the list of defects that were present in the factory.
+.It Fl q
+When printing status information with
+.Fl s ,
+only print the number of defects.
+.It Fl s
+Just print the number of defects, not the list of defects.
+.It Fl S Ar offset
+Specify the starting offset into the defect list.
+This implies using the
+.Tn SCSI
+READ DEFECT DATA (12) command, as the 10 byte version of the command
+doesn't support the address descriptor index field.
+Not all drives support the 12 byte command, and some drives that support
+the 12 byte command don't support the address descriptor index field.
+.It Fl X
+Print out defects in hexadecimal (base 16) form instead of base 10 form.
.El
.Pp
If neither
@@ -556,9 +698,10 @@
detects that standard input is terminal.
.It Fl l
Lists all available mode pages.
-.It Fl m Ar mode_page
-This specifies the number of the mode page the user would like to view
-and/or edit.
+If specified more then once, also lists subpages.
+.It Fl m Ar page[,subpage]
+This specifies the number of the mode page and optionally subpage the user
+would like to view and/or edit.
This argument is mandatory unless
.Fl l
is specified.
@@ -649,7 +792,8 @@
.Bl -tag -width 17n
.It Fl r Ar len Ar fmt Op args
This specifies the size of the SMP request, without the CRC bytes, and the
-SMP request format. If the format is
+SMP request format.
+If the format is
.Sq - ,
.Ar len
bytes of data will be read from standard input and written as the SMP
@@ -1088,17 +1232,146 @@
will not be asked about the timeout if a timeout is specified on the
command line.
.El
+.It Ic sanitize
+Issue the
+.Tn SCSI
+SANITIZE command to the named device.
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+ALL data in the cache and on the disk will be destroyed or made inaccessible.
+Recovery of the data is not possible.
+Use extreme caution when issuing this command.
+.Pp
+The
+.Sq sanitize
+subcommand takes several arguments that modify its default behavior.
+The
+.Fl q
+and
+.Fl y
+arguments can be useful for scripts.
+.Bl -tag -width 6n
+.It Fl a Ar operation
+Specify the sanitize operation to perform.
+.Bl -tag -width 16n
+.It overwrite
+Perform an overwrite operation by writing a user supplied
+data pattern to the device one or more times.
+The pattern is given by the
+.Fl P
+argument.
+The number of times is given by the
+.Fl c
+argument.
+.It block
+Perform a block erase operation.
+All the device's blocks are set to a vendor defined
+value, typically zero.
+.It crypto
+Perform a cryptographic erase operation.
+The encryption keys are changed to prevent the decryption
+of the data.
+.It exitfailure
+Exits a previously failed sanitize operation.
+A failed sanitize operation can only be exited if it was
+run in the unrestricted completion mode, as provided by the
+.Fl U
+argument.
+.El
+.It Fl c Ar passes
+The number of passes when performing an
+.Sq overwrite
+operation.
+Valid values are between 1 and 31.
+The default is 1.
+.It Fl I
+When performing an
+.Sq overwrite
+operation, the pattern is inverted between consecutive passes.
+.It Fl P Ar pattern
+Path to the file containing the pattern to use when
+performing an
+.Sq overwrite
+operation.
+The pattern is repeated as needed to fill each block.
+.It Fl q
+Be quiet, do not print any status messages.
+This option will not disable
+the questions, however.
+To disable questions, use the
+.Fl y
+argument, below.
+.It Fl U
+Perform the sanitize in the unrestricted completion mode.
+If the operation fails, it can later be exited with the
+.Sq exitfailure
+operation.
+.It Fl r
+Run in
+.Dq report only
+mode.
+This will report status on a sanitize that is already running on the drive.
+.It Fl w
+Issue a non-immediate sanitize command.
+By default,
+.Nm
+issues the SANITIZE command with the immediate bit set.
+This tells the
+device to immediately return the sanitize command, before
+the sanitize has actually completed.
+Then,
+.Nm
+gathers
+.Tn SCSI
+sense information from the device every second to determine how far along
+in the sanitize process it is.
+If the
+.Fl w
+argument is specified,
+.Nm
+will issue a non-immediate sanitize command, and will be unable to print any
+information to let the user know what percentage of the disk has been
+sanitized.
+.It Fl y
+Do not ask any questions.
+By default,
+.Nm
+will ask the user if he/she really wants to sanitize the disk in question,
+and also if the default sanitize command timeout is acceptable.
+The user
+will not be asked about the timeout if a timeout is specified on the
+command line.
+.El
.It Ic idle
-Put ATA device into IDLE state. Optional parameter
+Put ATA device into IDLE state.
+Optional parameter
.Pq Fl t
-specifies automatic standby timer value in seconds. Value 0 disables timer.
+specifies automatic standby timer value in seconds.
+Value 0 disables timer.
.It Ic standby
-Put ATA device into STANDBY state. Optional parameter
+Put ATA device into STANDBY state.
+Optional parameter
.Pq Fl t
-specifies automatic standby timer value in seconds. Value 0 disables timer.
+specifies automatic standby timer value in seconds.
+Value 0 disables timer.
.It Ic sleep
-Put ATA device into SLEEP state. Note that the only way get device out of
+Put ATA device into SLEEP state.
+Note that the only way get device out of
this state may be reset.
+.It Ic apm
+It optional parameter
+.Pq Fl l
+specified, enables and sets advanced power management level, where
+1 -- minimum power, 127 -- maximum performance with standby,
+128 -- minimum power without standby, 254 -- maximum performance.
+If not specified -- APM is disabled.
+.It Ic aam
+It optional parameter
+.Pq Fl l
+specified, enables and sets automatic acoustic management level, where
+1 -- minimum noise, 254 -- maximum performance.
+If not specified -- AAM is disabled.
.It Ic security
Update or report security settings, using an ATA identify command (0xec).
By default,
@@ -1123,7 +1396,8 @@
user data on the device and may take several hours to complete.
.Pp
When this command is used against an SSD drive all its cells will be marked as
-empty, restoring it to factory default write performance. For SSD's this action
+empty, restoring it to factory default write performance.
+For SSD's this action
usually takes just a few seconds.
.It Fl f
.Pp
@@ -1130,7 +1404,8 @@
Freeze the security configuration of the specified device.
.Pp
After command completion any other commands that update the device lock mode
-shall be command aborted. Frozen mode is disabled by power-off or hardware reset.
+shall be command aborted.
+Frozen mode is disabled by power-off or hardware reset.
.It Fl h Ar pwd
.Pp
Enhanced erase the device using the given password for the selected user.
@@ -1137,7 +1412,7 @@
.Pp
.Em WARNING! WARNING! WARNING!
.Pp
-Issuing an enhanced secure erase will
+Issuing an enhanced secure erase will
.Em ERASE ALL
user data on the device and may take several hours to complete.
.Pp
@@ -1152,8 +1427,10 @@
.Pp
Specifies which security level to set when issuing a
.Fl s Ar pwd
-command. The security level determines device behavior when the master
-password is used to unlock the device. When the security level is set to high
+command.
+The security level determines device behavior when the master
+password is used to unlock the device.
+When the security level is set to high
the device requires the unlock command and the master password to unlock.
When the security level is set to maximum the device requires a secure erase
with the master password to unlock.
@@ -1172,7 +1449,8 @@
.It Fl s Ar pwd
.Pp
Password the device (enable security) using the given password for the selected
-user. This option can be combined with other options such as
+user.
+This option can be combined with other options such as
.Fl e Em pwd
.Pp
A master password may be set in a addition to the user password. The purpose of
@@ -1211,7 +1489,7 @@
without prompting for confirmation.
.Pp
.El
-If the password specified for any action commands doesn't match the configured
+If the password specified for any action commands does not match the configured
password for the specified user the command will fail.
.Pp
The password in all cases is limited to 32 characters, longer passwords will
@@ -1268,7 +1546,7 @@
.It Fl U Ar pwd
.Pp
Unlock the HPA configuration of the specified device using the given password.
-If the password specified doesn't match the password configured via
+If the password specified does not match the password configured via
.Fl p Ar pwd
the command will fail.
.Pp
@@ -1284,22 +1562,41 @@
The password for all HPA commands is limited to 32 characters, longer passwords
will fail.
.It Ic fwdownload
-Program firmware of the named SCSI device using the image file provided.
+Program firmware of the named
+.Tn SCSI
+or ATA device using the image file provided.
.Pp
-Current list of supported vendors:
-.Bl -bullet -offset indent -compact
-.It
-HITACHI
-.It
-HP
-.It
-IBM
-.It
-PLEXTOR
-.It
-QUANTUM
-.It
-SEAGATE
+If the device is a
+.Tn SCSI
+device and it provides a recommended timeout for the WRITE BUFFER command
+(see the
+.Nm
+opcodes subcommand), that timeout will be used for the firmware download.
+The drive-recommended timeout value may be overridden on the command line
+with the
+.Fl t
+option.
+.Pp
+Current list of supported vendors for SCSI/SAS drives:
+.Bl -tag -width 10n
+.It HGST
+Tested with 4TB SAS drives, model number HUS724040ALS640.
+.It HITACHI
+.It HP
+.It IBM
+Tested with LTO-5 (ULTRIUM-HH5) and LTO-6 (ULTRIUM-HH6) tape drives.
+There is a separate table entry for hard drives, because the update method
+for hard drives is different than the method for tape drives.
+.It PLEXTOR
+.It QUALSTAR
+.It QUANTUM
+.It SAMSUNG
+Tested with SM1625 SSDs.
+.It SEAGATE
+Tested with Constellation ES (ST32000444SS), ES.2 (ST33000651SS) and
+ES.3 (ST1000NM0023) drives.
+.It SmrtStor
+Tested with 400GB Optimus SSDs (TXA2D20400GA6001).
.El
.Pp
.Em WARNING! WARNING! WARNING!
@@ -1313,24 +1610,521 @@
guarantee it will not break a device from the listed vendors.
Ensure that you have a recent backup of the data on the device before
performing a firmware update.
+.Pp
+Note that unknown
+.Tn SCSI
+protocol devices will not be programmed, since there is little chance of
+the firmware download succeeding.
+.Pp
+.Nm
+will currently attempt a firmware download to any
+.Tn ATA
+or
+.Tn SATA
+device, since the standard
+.Tn ATA
+DOWNLOAD MICROCODE command may work.
+Firmware downloads to
+.Tn ATA
+and
+.Tn SATA
+devices are supported for devices connected
+to standard
+.Tn ATA
+and
+.Tn SATA
+controllers, and devices connected to SAS controllers
+with
+.Tn SCSI
+to
+.Tn ATA
+translation capability.
+In the latter case,
+.Nm
+uses the
+.Tn SCSI
+.Tn ATA
+PASS-THROUGH command to send the
+.Tn ATA
+DOWNLOAD MICROCODE command to the drive.
+Some
+.Tn SCSI
+to
+.Tn ATA
+translation implementations don't work fully when translating
+.Tn SCSI
+WRITE BUFFER commands to
+.Tn ATA
+DOWNLOAD MICROCODE commands, but do support
+.Tn ATA
+passthrough well enough to do a firmware download.
.Bl -tag -width 11n
.It Fl f Ar fw_image
Path to the firmware image file to be downloaded to the specified device.
-.It Fl y
-Do not ask for confirmation.
+.It Fl q
+Do not print informational messages, only print errors.
+This option should be used with the
+.Fl y
+option to suppress all output.
.It Fl s
Run in simulation mode.
-Packet sizes that will be sent are shown, but no actual packet is sent to the
-device.
-No confirmation is asked in simulation mode.
+Device checks are run and the confirmation dialog is shown, but no firmware
+download will occur.
.It Fl v
-Besides showing sense information in case of a failure, the verbose option
-causes
+Show
+.Tn SCSI
+or
+.Tn ATA
+errors in the event of a failure.
+.Pp
+In simulation mode, print out the
+.Tn SCSI
+CDB
+or
+.Tn ATA
+register values that would be used for the firmware download command.
+.It Fl y
+Do not ask for confirmation.
+.El
+.It Ic persist
+Persistent reservation support.
+Persistent reservations are a way to reserve a particular
+.Tn SCSI
+LUN for use by one or more
+.Tn SCSI
+initiators.
+If the
+.Fl i
+option is specified,
.Nm
-to output a line for every firmware segment that is sent to the device by the
-fwdownload command
--- the same as the ones shown in simulation mode.
+will issue the
+.Tn SCSI
+PERSISTENT RESERVE IN
+command using the requested service action.
+If the
+.Fl o
+option is specified,
+.Nm
+will issue the
+.Tn SCSI
+PERSISTENT RESERVE OUT
+command using the requested service action.
+One of those two options is required.
+.Pp
+Persistent reservations are complex, and fully explaining them is outside
+the scope of this manual.
+Please visit
+http://www.t10.org
+and download the latest SPC spec for a full explanation of persistent
+reservations.
+.Bl -tag -width 8n
+.It Fl i Ar mode
+Specify the service action for the PERSISTENT RESERVE IN command.
+Supported service actions:
+.Bl -tag -width 19n
+.It read_keys
+Report the current persistent reservation generation (PRgeneration) and any
+registered keys.
+.It read_reservation
+Report the persistent reservation, if any.
+.It report_capabilities
+Report the persistent reservation capabilities of the LUN.
+.It read_full_status
+Report the full status of persistent reservations on the LUN.
.El
+.It Fl o Ar mode
+Specify the service action for the PERSISTENT RESERVE OUT command.
+For service actions like register that are components of other service
+action names, the entire name must be specified.
+Otherwise, enough of the service action name must be specified to
+distinguish it from other possible service actions.
+Supported service actions:
+.Bl -tag -width 15n
+.It register
+Register a reservation key with the LUN or unregister a reservation key.
+To register a key, specify the requested key as the Service Action
+Reservation Key.
+To unregister a key, specify the previously registered key as the
+Reservation Key.
+To change a key, specify the old key as the Reservation Key and the new
+key as the Service Action Reservation Key.
+.It register_ignore
+This is similar to the register subcommand, except that the Reservation Key
+is ignored.
+The Service Action Reservation Key will overwrite any previous key
+registered for the initiator.
+.It reserve
+Create a reservation.
+A key must be registered with the LUN before the LUN can be reserved, and
+it must be specified as the Reservation Key.
+The type of reservation must also be specified.
+The scope defaults to LUN scope (LU_SCOPE), but may be changed.
+.It release
+Release a reservation.
+The Reservation Key must be specified.
+.It clear
+Release a reservation and remove all keys from the device.
+The Reservation Key must be specified.
+.It preempt
+Remove a reservation belonging to another initiator.
+The Reservation Key must be specified.
+The Service Action Reservation Key may be specified, depending on the
+operation being performed.
+.It preempt_abort
+Remove a reservation belonging to another initiator and abort all
+outstanding commands from that initiator.
+The Reservation Key must be specified.
+The Service Action Reservation Key may be specified, depending on the
+operation being performed.
+.It register_move
+Register another initiator with the LUN, and establish a reservation on the
+LUN for that initiator.
+The Reservation Key and Service Action Reservation Key must be specified.
+.It replace_lost
+Replace Lost Reservation information.
+.El
+.It Fl a
+Set the All Target Ports (ALL_TG_PT) bit.
+This requests that the key registration be applied to all target ports and
+not just the particular target port that receives the command.
+This only applies to the register and register_ignore actions.
+.It Fl I Ar tid
+Specify a Transport ID.
+This only applies to the Register and Register and Move service actions for
+Persistent Reserve Out.
+Multiple Transport IDs may be specified with multiple
+.Fl I
+arguments.
+With the Register service action, specifying one or more Transport IDs
+implicitly enables the
+.Fl S
+option which turns on the SPEC_I_PT bit.
+Transport IDs generally have the format protocol,id.
+.Bl -tag -width 5n
+.It SAS
+A SAS Transport ID consists of
+.Dq sas,
+followed by a 64-bit SAS address.
+For example:
+.Pp
+.Dl sas,0x1234567812345678
+.It FC
+A Fibre Channel Transport ID consists of
+.Dq fcp,
+followed by a 64-bit Fibre Channel World Wide Name.
+For example:
+.Pp
+.Dl fcp,0x1234567812345678
+.It SPI
+A Parallel SCSI address consists of
+.Dq spi,
+followed by a SCSI target ID and a relative target port identifier.
+For example:
+.Pp
+.Dl spi,4,1
+.It 1394
+An IEEE 1394 (Firewire) Transport ID consists of
+.Dq sbp,
+followed by a 64-bit EUI-64 IEEE 1394 node unique identifier.
+For example:
+.Pp
+.Dl sbp,0x1234567812345678
+.It RDMA
+A SCSI over RDMA Transport ID consists of
+.Dq srp,
+followed by a 128-bit RDMA initiator port identifier.
+The port identifier must be exactly 32 or 34 (if the leading 0x is
+included) hexadecimal digits.
+Only hexadecimal (base 16) numbers are supported.
+For example:
+.Pp
+.Dl srp,0x12345678123456781234567812345678
+.It iSCSI
+An iSCSI Transport ID consists an iSCSI name and optionally a separator and
+iSCSI session ID.
+For example, if only the iSCSI name is specified:
+.Pp
+.Dl iqn.2012-06.com.example:target0
+.Pp
+If the iSCSI separator and initiator session ID are specified:
+.Pp
+.Dl iqn.2012-06.com.example:target0,i,0x123
+.It PCIe
+A SCSI over PCIe Transport ID consists of
+.Dq sop,
+followed by a PCIe Routing ID.
+The Routing ID consists of a bus, device and function or in the alternate
+form, a bus and function.
+The bus must be in the range of 0 to 255 inclusive and the device must be
+in the range of 0 to 31 inclusive.
+The function must be in the range of 0 to 7 inclusive if the standard form
+is used, and in the range of 0 to 255 inclusive if the alternate form is
+used.
+For example, if a bus, device and function are specified for the standard
+Routing ID form:
+.Pp
+.Dl sop,4,5,1
+.Pp
+If the alternate Routing ID form is used:
+.Pp
+.Dl sop,4,1
+.El
+.It Fl k Ar key
+Specify the Reservation Key.
+This may be in decimal, octal or hexadecimal format.
+The value is zero by default if not otherwise specified.
+The value must be between 0 and 2^64 - 1, inclusive.
+.It Fl K Ar key
+Specify the Service Action Reservation Key.
+This may be in decimal, octal or hexadecimal format.
+The value is zero by default if not otherwise specified.
+The value must be between 0 and 2^64 - 1, inclusive.
+.It Fl p
+Enable the Activate Persist Through Power Loss bit.
+This is only used for the register and register_ignore actions.
+This requests that the reservation persist across power loss events.
+.It Fl s Ar scope
+Specify the scope of the reservation.
+The scope may be specified by name or by number.
+The scope is ignored for register, register_ignore and clear.
+If the desired scope isn't available by name, you may specify the number.
+.Bl -tag -width 7n
+.It lun
+LUN scope (0x00).
+This encompasses the entire LUN.
+.It extent
+Extent scope (0x01).
+.It element
+Element scope (0x02).
+.El
+.It Fl R Ar rtp
+Specify the Relative Target Port.
+This only applies to the Register and Move service action of the Persistent
+Reserve Out command.
+.It Fl S
+Enable the SPEC_I_PT bit.
+This only applies to the Register service action of Persistent Reserve Out.
+You must also specify at least one Transport ID with
+.Fl I
+if this option is set.
+If you specify a Transport ID, this option is automatically set.
+It is an error to specify this option for any service action other than
+Register.
+.It Fl T Ar type
+Specify the reservation type.
+The reservation type may be specified by name or by number.
+If the desired reservation type isn't available by name, you may specify
+the number.
+Supported reservation type names:
+.Bl -tag -width 11n
+.It read_shared
+Read Shared mode.
+.It wr_ex
+Write Exclusive mode.
+May also be specified as
+.Dq write_exclusive .
+.It rd_ex
+Read Exclusive mode.
+May also be specified as
+.Dq read_exclusive .
+.It ex_ac
+Exclusive access mode.
+May also be specified as
+.Dq exclusive_access .
+.It wr_ex_ro
+Write Exclusive Registrants Only mode.
+May also be specified as
+.Dq write_exclusive_reg_only .
+.It ex_ac_ro
+Exclusive Access Registrants Only mode.
+May also be specified as
+.Dq exclusive_access_reg_only .
+.It wr_ex_ar
+Write Exclusive All Registrants mode.
+May also be specified as
+.Dq write_exclusive_all_regs .
+.It ex_ac_ar
+Exclusive Access All Registrants mode.
+May also be specified as
+.Dq exclusive_access_all_regs .
+.El
+.It Fl U
+Specify that the target should unregister the initiator that sent
+the Register and Move request.
+By default, the target will not unregister the initiator that sends the
+Register and Move request.
+This option only applies to the Register and Move service action of the
+Persistent Reserve Out command.
+.El
+.It Ic attrib
+Issue the
+.Tn SCSI
+READ or WRITE ATTRIBUTE commands.
+These commands are used to read and write attributes in Medium Auxiliary
+Memory (MAM).
+The most common place Medium Auxiliary Memory is found is small flash chips
+included tape cartriges.
+For instance,
+.Tn LTO
+tapes have MAM.
+Either the
+.Fl r
+option or the
+.Fl w
+option must be specified.
+.Bl -tag -width 14n
+.It Fl r Ar action
+Specify the READ ATTRIBUTE service action.
+.Bl -tag -width 11n
+.It attr_values
+Issue the ATTRIBUTE VALUES service action.
+Read and decode the available attributes and their values.
+.It attr_list
+Issue the ATTRIBUTE LIST service action.
+List the attributes that are available to read and write.
+.It lv_list
+Issue the LOGICAL VOLUME LIST service action.
+List the available logical volumes in the MAM.
+.It part_list
+Issue the PARTITION LIST service action.
+List the available partitions in the MAM.
+.It supp_attr
+Issue the SUPPORTED ATTRIBUTES service action.
+List attributes that are supported for reading or writing.
+These attributes may or may not be currently present in the MAM.
+.El
+.It Fl w Ar attr
+Specify an attribute to write to the MAM.
+This option is not yet implemented.
+.It Fl a Ar num
+Specify the attribute number to display.
+This option only works with the attr_values, attr_list and supp_attr
+arguments to
+.Fl r .
+.It Fl c
+Display cached attributes.
+If the device supports this flag, it allows displaying attributes for the
+last piece of media loaded in the drive.
+.It Fl e Ar num
+Specify the element address.
+This is used for specifying which element number in a medium changer to
+access when reading attributes.
+The element number could be for a picker, portal, slot or drive.
+.It Fl F Ar form1,form2
+Specify the output format for the attribute values (attr_val) display as a
+comma separated list of options.
+The default output is currently set to field_all,nonascii_trim,text_raw.
+Once this code is ported to FreeBSD 10, any text fields will be converted
+from their codeset to the user's native codeset with
+.Xr iconv 3 .
+.Pp
+The text options are mutually exclusive; if you specify more than one, you
+will get unpredictable results.
+The nonascii options are also mutually exclusive.
+Most of the field options may be logically ORed together.
+.Bl -tag -width 12n
+.It text_esc
+Print text fields with non-ASCII characters escaped.
+.It text_raw
+Print text fields natively, with no codeset conversion.
+.It nonascii_esc
+If any non-ASCII characters occur in fields that are supposed to be ASCII,
+escape the non-ASCII characters.
+.It nonascii_trim
+If any non-ASCII characters occur in fields that are supposed to be ASCII,
+omit the non-ASCII characters.
+.It nonascii_raw
+If any non-ASCII characters occur in fields that are supposed to be ASCII,
+print them as they are.
+.It field_all
+Print all of the prefix fields: description, attribute number, attribute
+size, and the attribute's readonly status.
+If field_all is specified, specifying any other field options will not have
+an effect.
+.It field_none
+Print none of the prefix fields, and only print out the attribute value.
+If field_none is specified, specifying any other field options will result
+in those fields being printed.
+.It field_desc
+Print out the attribute description.
+.It field_num
+Print out the attribute number.
+.It field_size
+Print out the attribute size.
+.It field_rw
+Print out the attribute's readonly status.
+.El
+.It Fl p Ar part
+Specify the partition.
+When the media has multiple partitions, specifying different partition
+numbers allows seeing the values for each individual partition.
+.It Fl s Ar start_num
+Specify the starting attribute number.
+This requests that the target device return attribute information starting
+at the given number.
+.It Fl T Ar elem_type
+Specify the element type.
+For medium changer devices, this allows specifying the type the element
+referenced in the element address (
+.Fl e ) .
+Valid types are:
+.Dq all ,
+.Dq picker ,
+.Dq slot ,
+.Dq portal ,
+and
+.Dq drive .
+.El
+.It Fl V Ar vol_num
+Specify the number of the logical volume to operate on.
+If the media has multiple logical volumes, this will allow displaying
+or writing attributes on the given logical volume.
+.It Ic opcodes
+Issue the REPORT SUPPORTED OPCODES service action of the
+.Tn SCSI
+MAINTENANCE IN
+command.
+Without arguments, this command will return a list of all
+.Tn SCSI
+commands supported by the device, including service actions of commands
+that support service actions.
+It will also include the
+.Tn SCSI
+CDB (Command Data Block) length for each command, and the description of
+each command if it is known.
+.Bl -tag -width 18n
+.It Fl o Ar opcode
+Request information on a specific opcode instead of the list of supported
+commands.
+If supported, the target will return a CDB-like structure that indicates
+the opcode, service action (if any), and a mask of bits that are supported
+in that CDB.
+.It Fl s Ar service_action
+For commands that support a service action, specify the service action to
+query.
+.It Fl N
+If a service action is specified for a given opcode, and the device does
+not support the given service action, the device should not return a
+.Tn SCSI
+error, but rather indicate in the returned parameter data that the command
+is not supported.
+By default, if a service action is specified for an opcode, and service
+actions are not supported for the opcode in question, the device will
+return an error.
+.It Fl T
+Include timeout values.
+This option works with the default display, which includes all commands
+supported by the device, and with the
+.Fl o
+and
+.Fl s
+options, which request information on a specific command and service
+action.
+This requests that the device report Nominal and Recommended timeout values
+for the given command or commands.
+The timeout values are in seconds.
+The timeout descriptor also includes a command-specific
+.El
.It Ic help
Print out verbose usage information.
.El
@@ -1369,7 +2163,7 @@
.Fl v
switch was not specified.
.Bd -literal -offset indent
-camcontrol tur da1 -E -C 4 -t 50 -v
+camcontrol tur da1 -E -C 4 -t 50 -Q head -v
.Ed
.Pp
Send a test unit ready command to da1.
@@ -1382,6 +2176,9 @@
Since error recovery is turned on, the
disk will be spun up if it is not currently spinning.
The
+.Tn SCSI
+task attribute for the command will be set to Head of Queue.
+The
.Nm
utility will report whether the disk is ready.
.Bd -literal -offset indent
@@ -1457,12 +2254,12 @@
.Pp
Report security support and settings for ada0
.Bd -literal -offset indent
-camcontrol security ada0 -u user -s MyPass
+camcontrol security ada0 -U user -s MyPass
.Ed
.Pp
Enable security on device ada0 with the password MyPass
.Bd -literal -offset indent
-camcontrol security ada0 -u user -e MyPass
+camcontrol security ada0 -U user -e MyPass
.Ed
.Pp
Secure erase ada0 which has had security enabled with user password MyPass
@@ -1473,7 +2270,7 @@
.Em ERASE ALL
data from the device, so backup your data before using!
.Pp
-This command can be used used against an SSD drive to restoring it to
+This command can be used against an SSD drive to restoring it to
factory default write performance.
.Bd -literal -offset indent
camcontrol hpa ada0
@@ -1497,6 +2294,75 @@
.Pp
.Em DO NOT
use this on a device which has an active filesystem!
+.Pp
+.Bd -literal -offset indent
+camcontrol persist da0 -v -i read_keys
+.Ed
+.Pp
+This will read any persistent reservation keys registered with da0, and
+display any errors encountered when sending the PERSISTENT RESERVE IN
+.Tn SCSI
+command.
+.Bd -literal -offset indent
+camcontrol persist da0 -v -o register -a -K 0x12345678
+.Ed
+.Pp
+This will register the persistent reservation key 0x12345678 with da0,
+apply that registration to all ports on da0, and display any errors that
+occur when sending the PERSISTENT RESERVE OUT command.
+.Bd -literal -offset indent
+camcontrol persist da0 -v -o reserve -s lun -k 0x12345678 -T ex_ac
+.Ed
+.Pp
+This will reserve da0 for the exlusive use of the initiator issuing the
+command.
+The scope of the reservation is the entire LUN.
+Any errors sending the PERSISTENT RESERVE OUT command will be displayed.
+.Bd -literal -offset indent
+camcontrol persist da0 -v -i read_full
+.Ed
+.Pp
+This will display the full status of all reservations on da0 and print out
+status if there are any errors.
+.Bd -literal -offset indent
+camcontrol persist da0 -v -o release -k 0x12345678 -T ex_ac
+.Ed
+.Pp
+This will release a reservation on da0 of the type ex_ac
+(Exclusive Access).
+The Reservation Key for this registration is 0x12345678.
+Any errors that occur will be displayed.
+.Bd -literal -offset indent
+camcontrol persist da0 -v -o register -K 0x12345678 -S \e
+ -I sas,0x1234567812345678 -I sas,0x8765432187654321
+.Ed
+.Pp
+This will register the key 0x12345678 with da0, specifying that it applies
+to the SAS initiators with SAS addresses 0x1234567812345678 and
+0x8765432187654321.
+.Bd -literal -offset indent
+camcontrol persist da0 -v -o register_move -k 0x87654321 \e
+ -K 0x12345678 -U -p -R 2 -I fcp,0x1234567812345678
+.Ed
+.Pp
+This will move the registration from the current initiator, whose
+Registration Key is 0x87654321, to the Fibre Channel initiator with the
+Fiber Channel World Wide Node Name 0x1234567812345678.
+A new registration key, 0x12345678, will be registered for the initiator
+with the Fibre Channel World Wide Node Name 0x1234567812345678, and the
+current initiator will be unregistered from the target.
+The reservation will be moved to relative target port 2 on the target
+device.
+The registration will persist across power losses.
+.Pp
+.Bd -literal -offset indent
+camcontrol attrib sa0 -v -i attr_values -p 1
+.Ed
+.Pp
+This will read and decode the attribute values from partition 1 on the tape
+in tape drive sa0, and will display any
+.Tn SCSI
+errors that result.
.Sh SEE ALSO
.Xr cam 3 ,
.Xr cam_cdbparse 3 ,
@@ -1524,7 +2390,7 @@
in
.Fx 2.0.5 .
.Sh AUTHORS
-.An Kenneth Merry Aq ken at FreeBSD.org
+.An Kenneth Merry Aq Mt ken at FreeBSD.org
.Sh BUGS
The code that parses the generic command line arguments does not know that
some of the subcommands take multiple arguments.
Modified: trunk/sbin/camcontrol/camcontrol.c
===================================================================
--- trunk/sbin/camcontrol/camcontrol.c 2018-07-01 21:16:14 UTC (rev 11236)
+++ trunk/sbin/camcontrol/camcontrol.c 2018-07-01 21:16:46 UTC (rev 11237)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*
* Copyright (c) 1997-2007 Kenneth D. Merry
* All rights reserved.
@@ -27,11 +28,12 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sbin/camcontrol/camcontrol.c 320781 2017-07-07 15:22:29Z asomers $");
#include <sys/ioctl.h>
#include <sys/stdint.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <sys/endian.h>
#include <sys/sbuf.h>
@@ -93,7 +95,14 @@
CAM_CMD_SMP_MANINFO = 0x0000001b,
CAM_CMD_DOWNLOAD_FW = 0x0000001c,
CAM_CMD_SECURITY = 0x0000001d,
- CAM_CMD_HPA = 0x0000001e
+ CAM_CMD_HPA = 0x0000001e,
+ CAM_CMD_SANITIZE = 0x0000001f,
+ CAM_CMD_PERSIST = 0x00000020,
+ CAM_CMD_APM = 0x00000021,
+ CAM_CMD_AAM = 0x00000022,
+ CAM_CMD_ATTRIB = 0x00000023,
+ CAM_CMD_OPCODES = 0x00000024,
+ CAM_CMD_REPROBE = 0x00000025
} cam_cmdmask;
typedef enum {
@@ -114,12 +123,9 @@
CAM_ARG_GET_STDINQ = 0x00002000,
CAM_ARG_GET_XFERRATE = 0x00004000,
CAM_ARG_INQ_MASK = 0x00007000,
- CAM_ARG_MODE_EDIT = 0x00008000,
- CAM_ARG_PAGE_CNTL = 0x00010000,
CAM_ARG_TIMEOUT = 0x00020000,
CAM_ARG_CMD_IN = 0x00040000,
CAM_ARG_CMD_OUT = 0x00080000,
- CAM_ARG_DBD = 0x00100000,
CAM_ARG_ERR_RECOVER = 0x00200000,
CAM_ARG_RETRIES = 0x00400000,
CAM_ARG_START_UNIT = 0x00800000,
@@ -163,8 +169,16 @@
u_int16_t reserved2[239];
};
+static struct scsi_nv task_attrs[] = {
+ { "simple", MSG_SIMPLE_Q_TAG },
+ { "head", MSG_HEAD_OF_Q_TAG },
+ { "ordered", MSG_ORDERED_Q_TAG },
+ { "iwr", MSG_IGN_WIDE_RESIDUE },
+ { "aca", MSG_ACA_TASK }
+};
+
static const char scsicmd_opts[] = "a:c:dfi:o:r";
-static const char readdefect_opts[] = "f:GP";
+static const char readdefect_opts[] = "f:GPqsS:X";
static const char negotiate_opts[] = "acD:M:O:qR:T:UW:";
static const char smprg_opts[] = "l";
static const char smppc_opts[] = "a:A:d:lm:M:o:p:s:S:T:";
@@ -183,6 +197,7 @@
{"eject", CAM_CMD_STARTSTOP, CAM_ARG_EJECT, NULL},
{"reportluns", CAM_CMD_REPORTLUNS, CAM_ARG_NONE, "clr:"},
{"readcapacity", CAM_CMD_READCAP, CAM_ARG_NONE, "bhHNqs"},
+ {"reprobe", CAM_CMD_REPROBE, CAM_ARG_NONE, NULL},
#endif /* MINIMALISTIC */
{"rescan", CAM_CMD_RESCAN, CAM_ARG_NONE, NULL},
{"reset", CAM_CMD_RESET, CAM_ARG_NONE, NULL},
@@ -200,7 +215,7 @@
{"defects", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts},
{"defectlist", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts},
#endif /* MINIMALISTIC */
- {"devlist", CAM_CMD_DEVTREE, CAM_ARG_NONE, NULL},
+ {"devlist", CAM_CMD_DEVTREE, CAM_ARG_NONE, "-b"},
#ifndef MINIMALISTIC
{"periphlist", CAM_CMD_DEVLIST, CAM_ARG_NONE, NULL},
{"modepage", CAM_CMD_MODE_PAGE, CAM_ARG_NONE, "bdelm:P:"},
@@ -209,12 +224,18 @@
{"rate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts},
{"debug", CAM_CMD_DEBUG, CAM_ARG_NONE, "IPTSXcp"},
{"format", CAM_CMD_FORMAT, CAM_ARG_NONE, "qrwy"},
+ {"sanitize", CAM_CMD_SANITIZE, CAM_ARG_NONE, "a:c:IP:qrUwy"},
{"idle", CAM_CMD_IDLE, CAM_ARG_NONE, "t:"},
{"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:"},
{"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, ""},
- {"fwdownload", CAM_CMD_DOWNLOAD_FW, CAM_ARG_NONE, "f:ys"},
+ {"apm", CAM_CMD_APM, CAM_ARG_NONE, "l:"},
+ {"aam", CAM_CMD_AAM, CAM_ARG_NONE, "l:"},
+ {"fwdownload", CAM_CMD_DOWNLOAD_FW, CAM_ARG_NONE, "f:qsy"},
{"security", CAM_CMD_SECURITY, CAM_ARG_NONE, "d:e:fh:k:l:qs:T:U:y"},
{"hpa", CAM_CMD_HPA, CAM_ARG_NONE, "Pflp:qs:U:y"},
+ {"persist", CAM_CMD_PERSIST, CAM_ARG_NONE, "ai:I:k:K:o:ps:ST:U"},
+ {"attrib", CAM_CMD_ATTRIB, CAM_ARG_NONE, "a:ce:F:p:r:s:T:w:V:"},
+ {"opcodes", CAM_CMD_OPCODES, CAM_ARG_NONE, "No:s:T"},
#endif /* MINIMALISTIC */
{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
@@ -222,12 +243,6 @@
{NULL, 0, 0, NULL}
};
-typedef enum {
- CC_OR_NOT_FOUND,
- CC_OR_AMBIGUOUS,
- CC_OR_FOUND
-} camcontrol_optret;
-
struct cam_devitem {
struct device_match_result dev_match;
int num_periphs;
@@ -251,28 +266,33 @@
#ifndef MINIMALISTIC
static int getdevlist(struct cam_device *device);
#endif /* MINIMALISTIC */
-static int getdevtree(void);
+static int getdevtree(int argc, char **argv, char *combinedopt);
#ifndef MINIMALISTIC
-static int testunitready(struct cam_device *device, int retry_count,
- int timeout, int quiet);
+static int testunitready(struct cam_device *device, int task_attr,
+ int retry_count, int timeout, int quiet);
static int scsistart(struct cam_device *device, int startstop, int loadeject,
- int retry_count, int timeout);
-static int scsiinquiry(struct cam_device *device, int retry_count, int timeout);
-static int scsiserial(struct cam_device *device, int retry_count, int timeout);
-static int camxferrate(struct cam_device *device);
+ int task_attr, int retry_count, int timeout);
+static int scsiinquiry(struct cam_device *device, int task_attr,
+ int retry_count, int timeout);
+static int scsiserial(struct cam_device *device, int task_attr,
+ int retry_count, int timeout);
#endif /* MINIMALISTIC */
-static int parse_btl(char *tstr, int *bus, int *target, int *lun,
- cam_argmask *arglst);
+static int parse_btl(char *tstr, path_id_t *bus, target_id_t *target,
+ lun_id_t *lun, cam_argmask *arglst);
static int dorescan_or_reset(int argc, char **argv, int rescan);
-static int rescan_or_reset_bus(int bus, int rescan);
-static int scanlun_or_reset_dev(int bus, int target, int lun, int scan);
+static int rescan_or_reset_bus(path_id_t bus, int rescan);
+static int scanlun_or_reset_dev(path_id_t bus, target_id_t target,
+ lun_id_t lun, int scan);
#ifndef MINIMALISTIC
static int readdefects(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout);
+ char *combinedopt, int task_attr, int retry_count,
+ int timeout);
static void modepage(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout);
+ char *combinedopt, int task_attr, int retry_count,
+ int timeout);
static int scsicmd(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout);
+ char *combinedopt, int task_attr, int retry_count,
+ int timeout);
static int smpcmd(struct cam_device *device, int argc, char **argv,
char *combinedopt, int retry_count, int timeout);
static int smpreportgeneral(struct cam_device *device, int argc, char **argv,
@@ -297,14 +317,21 @@
static int get_cgd(struct cam_device *device, struct ccb_getdev *cgd);
static int get_print_cts(struct cam_device *device, int user_settings,
int quiet, struct ccb_trans_settings *cts);
-static int ratecontrol(struct cam_device *device, int retry_count,
- int timeout, int argc, char **argv, char *combinedopt);
+static int ratecontrol(struct cam_device *device, int task_attr,
+ int retry_count, int timeout, int argc, char **argv,
+ char *combinedopt);
static int scsiformat(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout);
+ char *combinedopt, int task_attr, int retry_count,
+ int timeout);
+static int scsisanitize(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int task_attr, int retry_count,
+ int timeout);
static int scsireportluns(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout);
+ char *combinedopt, int task_attr, int retry_count,
+ int timeout);
static int scsireadcapacity(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout);
+ char *combinedopt, int task_attr, int retry_count,
+ int timeout);
static int atapm(struct cam_device *device, int argc, char **argv,
char *combinedopt, int retry_count, int timeout);
static int atasecurity(struct cam_device *device, int retry_count, int timeout,
@@ -311,6 +338,15 @@
int argc, char **argv, char *combinedopt);
static int atahpa(struct cam_device *device, int retry_count, int timeout,
int argc, char **argv, char *combinedopt);
+static int scsiprintoneopcode(struct cam_device *device, int req_opcode,
+ int sa_set, int req_sa, uint8_t *buf,
+ uint32_t valid_len);
+static int scsiprintopcodes(struct cam_device *device, int td_req, uint8_t *buf,
+ uint32_t valid_len);
+static int scsiopcodes(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int task_attr, int retry_count,
+ int timeout, int verbose);
+static int scsireprobe(struct cam_device *device);
#endif /* MINIMALISTIC */
#ifndef min
@@ -406,7 +442,7 @@
#endif /* MINIMALISTIC */
static int
-getdevtree(void)
+getdevtree(int argc, char **argv, char *combinedopt)
{
union ccb ccb;
int bufsize, fd;
@@ -414,7 +450,20 @@
int need_close = 0;
int error = 0;
int skip_device = 0;
+ int busonly = 0;
+ int c;
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch(c) {
+ case 'b':
+ if ((arglist & CAM_ARG_VERBOSE) == 0)
+ busonly = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) {
warn("couldn't open %s", XPT_DEVICE);
return(1);
@@ -473,7 +522,8 @@
* Only print the bus information if the
* user turns on the verbose flag.
*/
- if ((arglist & CAM_ARG_VERBOSE) == 0)
+ if ((busonly == 0) &&
+ (arglist & CAM_ARG_VERBOSE) == 0)
break;
bus_result =
@@ -484,11 +534,12 @@
need_close = 0;
}
- fprintf(stdout, "scbus%d on %s%d bus %d:\n",
+ fprintf(stdout, "scbus%d on %s%d bus %d%s\n",
bus_result->path_id,
bus_result->dev_name,
bus_result->unit_number,
- bus_result->bus_id);
+ bus_result->bus_id,
+ (busonly ? "" : ":"));
break;
}
case DEV_MATCH_DEVICE: {
@@ -496,6 +547,9 @@
char vendor[16], product[48], revision[16];
char fw[5], tmpstr[256];
+ if (busonly == 1)
+ break;
+
dev_result =
&ccb.cdm.matches[i].result.device_result;
@@ -561,11 +615,11 @@
}
fprintf(stdout, "%-33s at scbus%d "
- "target %d lun %d (",
+ "target %d lun %jx (",
tmpstr,
dev_result->path_id,
dev_result->target_id,
- dev_result->target_lun);
+ (uintmax_t)dev_result->target_lun);
need_close = 1;
@@ -577,7 +631,7 @@
periph_result =
&ccb.cdm.matches[i].result.periph_result;
- if (skip_device != 0)
+ if (busonly || skip_device != 0)
break;
if (need_close > 1)
@@ -609,8 +663,8 @@
#ifndef MINIMALISTIC
static int
-testunitready(struct cam_device *device, int retry_count, int timeout,
- int quiet)
+testunitready(struct cam_device *device, int task_attr, int retry_count,
+ int timeout, int quiet)
{
int error = 0;
union ccb *ccb;
@@ -620,7 +674,7 @@
scsi_test_unit_ready(&ccb->csio,
/* retries */ retry_count,
/* cbfcnp */ NULL,
- /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* tag_action */ task_attr,
/* sense_len */ SSD_FULL_SIZE,
/* timeout */ timeout ? timeout : 5000);
@@ -664,7 +718,7 @@
static int
scsistart(struct cam_device *device, int startstop, int loadeject,
- int retry_count, int timeout)
+ int task_attr, int retry_count, int timeout)
{
union ccb *ccb;
int error = 0;
@@ -675,13 +729,19 @@
* If we're stopping, send an ordered tag so the drive in question
* will finish any previously queued writes before stopping. If
* the device isn't capable of tagged queueing, or if tagged
- * queueing is turned off, the tag action is a no-op.
+ * queueing is turned off, the tag action is a no-op. We override
+ * the default simple tag, although this also has the effect of
+ * overriding the user's wishes if he wanted to specify a simple
+ * tag.
*/
+ if ((startstop == 0)
+ && (task_attr == MSG_SIMPLE_Q_TAG))
+ task_attr = MSG_ORDERED_Q_TAG;
+
scsi_start_stop(&ccb->csio,
/* retries */ retry_count,
/* cbfcnp */ NULL,
- /* tag_action */ startstop ? MSG_SIMPLE_Q_TAG :
- MSG_ORDERED_Q_TAG,
+ /* tag_action */ task_attr,
/* start/stop */ startstop,
/* load_eject */ loadeject,
/* immediate */ 0,
@@ -742,7 +802,7 @@
int
scsidoinquiry(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout)
+ char *combinedopt, int task_attr, int retry_count, int timeout)
{
int c;
int error = 0;
@@ -771,17 +831,14 @@
arglist |= CAM_ARG_INQ_MASK;
if (arglist & CAM_ARG_GET_STDINQ)
- error = scsiinquiry(device, retry_count, timeout);
+ error = scsiinquiry(device, task_attr, retry_count, timeout);
if (error != 0)
return(error);
if (arglist & CAM_ARG_GET_SERIAL)
- scsiserial(device, retry_count, timeout);
+ scsiserial(device, task_attr, retry_count, timeout);
- if (error != 0)
- return(error);
-
if (arglist & CAM_ARG_GET_XFERRATE)
error = camxferrate(device);
@@ -789,7 +846,8 @@
}
static int
-scsiinquiry(struct cam_device *device, int retry_count, int timeout)
+scsiinquiry(struct cam_device *device, int task_attr, int retry_count,
+ int timeout)
{
union ccb *ccb;
struct scsi_inquiry_data *inq_buf;
@@ -803,8 +861,7 @@
}
/* cam_getccb cleans up the header, caller has to zero the payload */
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
inq_buf = (struct scsi_inquiry_data *)malloc(
sizeof(struct scsi_inquiry_data));
@@ -852,7 +909,7 @@
scsi_inquiry(&ccb->csio,
/* retries */ retry_count,
/* cbfcnp */ NULL,
- /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* tag_action */ task_attr,
/* inq_buf */ (u_int8_t *)inq_buf,
/* inq_len */ SHORT_INQUIRY_LENGTH,
/* evpd */ 0,
@@ -904,7 +961,8 @@
}
static int
-scsiserial(struct cam_device *device, int retry_count, int timeout)
+scsiserial(struct cam_device *device, int task_attr, int retry_count,
+ int timeout)
{
union ccb *ccb;
struct scsi_vpd_unit_serial_number *serial_buf;
@@ -919,8 +977,7 @@
}
/* cam_getccb cleans up the header, caller has to zero the payload */
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
serial_buf = (struct scsi_vpd_unit_serial_number *)
malloc(sizeof(*serial_buf));
@@ -934,7 +991,7 @@
scsi_inquiry(&ccb->csio,
/*retries*/ retry_count,
/*cbfcnp*/ NULL,
- /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* tag_action */ task_attr,
/* inq_buf */ (u_int8_t *)serial_buf,
/* inq_len */ sizeof(*serial_buf),
/* evpd */ 1,
@@ -992,7 +1049,7 @@
return(0);
}
-static int
+int
camxferrate(struct cam_device *device)
{
struct ccb_pathinq cpi;
@@ -1012,8 +1069,7 @@
return(1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_trans_settings) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cts);
ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
ccb->cts.type = CTS_TYPE_CURRENT_SETTINGS;
@@ -1190,6 +1246,18 @@
}
}
+static int
+atasata(struct ata_params *parm)
+{
+
+
+ if (parm->satacapabilities != 0xffff &&
+ parm->satacapabilities != 0x0000)
+ return 1;
+
+ return 0;
+}
+
static void
atacapprint(struct ata_params *parm)
{
@@ -1346,6 +1414,17 @@
ATA_QUEUE_LEN(parm->queue) + 1);
} else
printf("no\n");
+
+ printf("NCQ Queue Management %s\n", atasata(parm) &&
+ parm->satacapabilities2 & ATA_SUPPORT_NCQ_QMANAGEMENT ?
+ "yes" : "no");
+ printf("NCQ Streaming %s\n", atasata(parm) &&
+ parm->satacapabilities2 & ATA_SUPPORT_NCQ_STREAM ?
+ "yes" : "no");
+ printf("Receive & Send FPDMA Queued %s\n", atasata(parm) &&
+ parm->satacapabilities2 & ATA_SUPPORT_RCVSND_FPDMA_QUEUED ?
+ "yes" : "no");
+
printf("SMART %s %s\n",
parm->support.command1 & ATA_SUPPORT_SMART ? "yes" : "no",
parm->enabled.command1 & ATA_SUPPORT_SMART ? "yes" : "no");
@@ -1363,7 +1442,7 @@
parm->enabled.command2 & ATA_SUPPORT_APM ? "yes" : "no");
if (parm->support.command2 & ATA_SUPPORT_APM) {
printf(" %d/0x%02X\n",
- parm->apm_value, parm->apm_value);
+ parm->apm_value & 0xff, parm->apm_value & 0xff);
} else
printf("\n");
printf("automatic acoustic management %s %s",
@@ -1394,6 +1473,9 @@
printf("unload %s %s\n",
parm->support.extension & ATA_SUPPORT_UNLOAD ? "yes" : "no",
parm->enabled.extension & ATA_SUPPORT_UNLOAD ? "yes" : "no");
+ printf("general purpose logging %s %s\n",
+ parm->support.extension & ATA_SUPPORT_GENLOG ? "yes" : "no",
+ parm->enabled.extension & ATA_SUPPORT_GENLOG ? "yes" : "no");
printf("free-fall %s %s\n",
parm->support2 & ATA_SUPPORT_FREEFALL ? "yes" : "no",
parm->enabled2 & ATA_SUPPORT_FREEFALL ? "yes" : "no");
@@ -1540,8 +1622,7 @@
ata_flags |= AP_FLAG_TLEN_NO_DATA;
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
scsi_ata_pass_16(&ccb->csio,
retries,
@@ -1602,8 +1683,7 @@
timeout, quiet);
}
- bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_ataio) -
- sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->ataio);
cam_fill_ataio(&ccb->ataio,
retries,
NULL,
@@ -1672,8 +1752,7 @@
return (error);
}
- bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_ataio) -
- sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->ataio);
cam_fill_ataio(&ccb->ataio,
retries,
NULL,
@@ -1784,7 +1863,7 @@
error = ata_do_cmd(device,
ccb,
retry_count,
- /*flags*/CAM_DIR_IN,
+ /*flags*/CAM_DIR_NONE,
/*protocol*/protocol,
/*ata_flags*/AP_FLAG_CHK_COND,
/*tag_action*/MSG_SIMPLE_Q_TAG,
@@ -1828,7 +1907,7 @@
error = ata_do_cmd(device,
ccb,
retry_count,
- /*flags*/CAM_DIR_OUT,
+ /*flags*/CAM_DIR_NONE,
/*protocol*/protocol,
/*ata_flags*/AP_FLAG_CHK_COND,
/*tag_action*/MSG_SIMPLE_Q_TAG,
@@ -1895,7 +1974,7 @@
error = ata_do_cmd(device,
ccb,
retry_count,
- /*flags*/CAM_DIR_OUT,
+ /*flags*/CAM_DIR_NONE,
/*protocol*/protocol,
/*ata_flags*/AP_FLAG_CHK_COND,
/*tag_action*/MSG_SIMPLE_Q_TAG,
@@ -1962,7 +2041,7 @@
error = ata_do_cmd(device,
ccb,
retry_count,
- /*flags*/CAM_DIR_OUT,
+ /*flags*/CAM_DIR_NONE,
/*protocol*/protocol,
/*ata_flags*/AP_FLAG_CHK_COND,
/*tag_action*/MSG_SIMPLE_Q_TAG,
@@ -1982,7 +2061,7 @@
}
-static int
+int
ata_do_identify(struct cam_device *device, int retry_count, int timeout,
union ccb *ccb, struct ata_params** ident_bufp)
{
@@ -2158,7 +2237,7 @@
ATA_SECURITY_ACTION_ERASE,
ATA_SECURITY_ACTION_ERASE_ENHANCED,
ATA_SECURITY_ACTION_SET_PASSWORD
-} atasecurity_action;
+};
static void
atasecurity_print_time(u_int16_t tw)
@@ -2743,7 +2822,7 @@
if (strcasecmp(optarg, "user") == 0) {
pwd.ctrl |= ATA_SECURITY_PASSWORD_USER;
pwd.ctrl &= ~ATA_SECURITY_PASSWORD_MASTER;
- } else if (strcasecmp(optarg, "master") != 0) {
+ } else if (strcasecmp(optarg, "master") == 0) {
pwd.ctrl |= ATA_SECURITY_PASSWORD_MASTER;
pwd.ctrl &= ~ATA_SECURITY_PASSWORD_USER;
} else {
@@ -2996,7 +3075,8 @@
* Returns the number of parsed components, or 0.
*/
static int
-parse_btl(char *tstr, int *bus, int *target, int *lun, cam_argmask *arglst)
+parse_btl(char *tstr, path_id_t *bus, target_id_t *target, lun_id_t *lun,
+ cam_argmask *arglst)
{
char *tmpstr;
int convs = 0;
@@ -3032,7 +3112,9 @@
static const char must[] =
"you must specify \"all\", a bus, or a bus:target:lun to %s";
int rv, error = 0;
- int bus = -1, target = -1, lun = -1;
+ path_id_t bus = CAM_BUS_WILDCARD;
+ target_id_t target = CAM_TARGET_WILDCARD;
+ lun_id_t lun = CAM_LUN_WILDCARD;
char *tstr;
if (argc < 3) {
@@ -3045,12 +3127,107 @@
tstr++;
if (strncasecmp(tstr, "all", strlen("all")) == 0)
arglist |= CAM_ARG_BUS;
- else {
+ else if (isdigit(*tstr)) {
rv = parse_btl(argv[optind], &bus, &target, &lun, &arglist);
if (rv != 1 && rv != 3) {
warnx(must, rescan? "rescan" : "reset");
return(1);
}
+ } else {
+ char name[30];
+ int unit;
+ int fd = -1;
+ union ccb ccb;
+
+ /*
+ * Note that resetting or rescanning a device used to
+ * require a bus or bus:target:lun. This is because the
+ * device in question may not exist and you're trying to
+ * get the controller to rescan to find it. It may also be
+ * because the device is hung / unresponsive, and opening
+ * an unresponsive device is not desireable.
+ *
+ * It can be more convenient to reference a device by
+ * peripheral name and unit number, though, and it is
+ * possible to get the bus:target:lun for devices that
+ * currently exist in the EDT. So this can work for
+ * devices that we want to reset, or devices that exist
+ * that we want to rescan, but not devices that do not
+ * exist yet.
+ *
+ * So, we are careful here to look up the bus/target/lun
+ * for the device the user wants to operate on, specified
+ * by peripheral instance (e.g. da0, pass32) without
+ * actually opening that device. The process is similar to
+ * what cam_lookup_pass() does, except that we don't
+ * actually open the passthrough driver instance in the end.
+ */
+
+ if (cam_get_device(tstr, name, sizeof(name), &unit) == -1) {
+ warnx("%s", cam_errbuf);
+ error = 1;
+ goto bailout;
+ }
+
+ if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) {
+ warn("Unable to open %s", XPT_DEVICE);
+ error = 1;
+ goto bailout;
+ }
+
+ bzero(&ccb, sizeof(ccb));
+
+ /*
+ * The function code isn't strictly necessary for the
+ * GETPASSTHRU ioctl.
+ */
+ ccb.ccb_h.func_code = XPT_GDEVLIST;
+
+ /*
+ * These two are necessary for the GETPASSTHRU ioctl to
+ * work.
+ */
+ strlcpy(ccb.cgdl.periph_name, name,
+ sizeof(ccb.cgdl.periph_name));
+ ccb.cgdl.unit_number = unit;
+
+ /*
+ * Attempt to get the passthrough device. This ioctl will
+ * fail if the device name is null, if the device doesn't
+ * exist, or if the passthrough driver isn't in the kernel.
+ */
+ if (ioctl(fd, CAMGETPASSTHRU, &ccb) == -1) {
+ warn("Unable to find bus:target:lun for device %s%d",
+ name, unit);
+ error = 1;
+ close(fd);
+ goto bailout;
+ }
+ if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ const struct cam_status_entry *entry;
+
+ entry = cam_fetch_status_entry(ccb.ccb_h.status);
+ warnx("Unable to find bus:target_lun for device %s%d, "
+ "CAM status: %s (%#x)", name, unit,
+ entry ? entry->status_text : "Unknown",
+ ccb.ccb_h.status);
+ error = 1;
+ close(fd);
+ goto bailout;
+ }
+
+ /*
+ * The kernel fills in the bus/target/lun. We don't
+ * need the passthrough device name and unit number since
+ * we aren't going to open it.
+ */
+ bus = ccb.ccb_h.path_id;
+ target = ccb.ccb_h.target_id;
+ lun = ccb.ccb_h.target_lun;
+
+ arglist |= CAM_ARG_BUS | CAM_ARG_TARGET | CAM_ARG_LUN;
+
+ close(fd);
}
if ((arglist & CAM_ARG_BUS)
@@ -3060,14 +3237,16 @@
else
error = rescan_or_reset_bus(bus, rescan);
+bailout:
+
return(error);
}
static int
-rescan_or_reset_bus(int bus, int rescan)
+rescan_or_reset_bus(path_id_t bus, int rescan)
{
- union ccb ccb, matchccb;
- int fd, retval;
+ union ccb *ccb = NULL, *matchccb = NULL;
+ int fd = -1, retval;
int bufsize;
retval = 0;
@@ -3078,35 +3257,41 @@
return(1);
}
- if (bus != -1) {
- ccb.ccb_h.func_code = rescan ? XPT_SCAN_BUS : XPT_RESET_BUS;
- ccb.ccb_h.path_id = bus;
- ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
- ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
- ccb.crcn.flags = CAM_FLAG_NONE;
+ ccb = malloc(sizeof(*ccb));
+ if (ccb == NULL) {
+ warn("failed to allocate CCB");
+ retval = 1;
+ goto bailout;
+ }
+ bzero(ccb, sizeof(*ccb));
+ if (bus != CAM_BUS_WILDCARD) {
+ ccb->ccb_h.func_code = rescan ? XPT_SCAN_BUS : XPT_RESET_BUS;
+ ccb->ccb_h.path_id = bus;
+ ccb->ccb_h.target_id = CAM_TARGET_WILDCARD;
+ ccb->ccb_h.target_lun = CAM_LUN_WILDCARD;
+ ccb->crcn.flags = CAM_FLAG_NONE;
+
/* run this at a low priority */
- ccb.ccb_h.pinfo.priority = 5;
+ ccb->ccb_h.pinfo.priority = 5;
- if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
+ if (ioctl(fd, CAMIOCOMMAND, ccb) == -1) {
warn("CAMIOCOMMAND ioctl failed");
- close(fd);
- return(1);
+ retval = 1;
+ goto bailout;
}
- if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
fprintf(stdout, "%s of bus %d was successful\n",
rescan ? "Re-scan" : "Reset", bus);
} else {
fprintf(stdout, "%s of bus %d returned error %#x\n",
rescan ? "Re-scan" : "Reset", bus,
- ccb.ccb_h.status & CAM_STATUS_MASK);
+ ccb->ccb_h.status & CAM_STATUS_MASK);
retval = 1;
}
- close(fd);
- return(retval);
-
+ goto bailout;
}
@@ -3120,84 +3305,89 @@
* no-op, sending a rescan to the xpt bus would result in a status of
* CAM_REQ_INVALID.
*/
- bzero(&(&matchccb.ccb_h)[1],
- sizeof(struct ccb_dev_match) - sizeof(struct ccb_hdr));
- matchccb.ccb_h.func_code = XPT_DEV_MATCH;
- matchccb.ccb_h.path_id = CAM_BUS_WILDCARD;
+ matchccb = malloc(sizeof(*matchccb));
+ if (matchccb == NULL) {
+ warn("failed to allocate CCB");
+ retval = 1;
+ goto bailout;
+ }
+ bzero(matchccb, sizeof(*matchccb));
+ matchccb->ccb_h.func_code = XPT_DEV_MATCH;
+ matchccb->ccb_h.path_id = CAM_BUS_WILDCARD;
bufsize = sizeof(struct dev_match_result) * 20;
- matchccb.cdm.match_buf_len = bufsize;
- matchccb.cdm.matches=(struct dev_match_result *)malloc(bufsize);
- if (matchccb.cdm.matches == NULL) {
+ matchccb->cdm.match_buf_len = bufsize;
+ matchccb->cdm.matches=(struct dev_match_result *)malloc(bufsize);
+ if (matchccb->cdm.matches == NULL) {
warnx("can't malloc memory for matches");
retval = 1;
goto bailout;
}
- matchccb.cdm.num_matches = 0;
+ matchccb->cdm.num_matches = 0;
- matchccb.cdm.num_patterns = 1;
- matchccb.cdm.pattern_buf_len = sizeof(struct dev_match_pattern);
+ matchccb->cdm.num_patterns = 1;
+ matchccb->cdm.pattern_buf_len = sizeof(struct dev_match_pattern);
- matchccb.cdm.patterns = (struct dev_match_pattern *)malloc(
- matchccb.cdm.pattern_buf_len);
- if (matchccb.cdm.patterns == NULL) {
+ matchccb->cdm.patterns = (struct dev_match_pattern *)malloc(
+ matchccb->cdm.pattern_buf_len);
+ if (matchccb->cdm.patterns == NULL) {
warnx("can't malloc memory for patterns");
retval = 1;
goto bailout;
}
- matchccb.cdm.patterns[0].type = DEV_MATCH_BUS;
- matchccb.cdm.patterns[0].pattern.bus_pattern.flags = BUS_MATCH_ANY;
+ matchccb->cdm.patterns[0].type = DEV_MATCH_BUS;
+ matchccb->cdm.patterns[0].pattern.bus_pattern.flags = BUS_MATCH_ANY;
do {
unsigned int i;
- if (ioctl(fd, CAMIOCOMMAND, &matchccb) == -1) {
+ if (ioctl(fd, CAMIOCOMMAND, matchccb) == -1) {
warn("CAMIOCOMMAND ioctl failed");
retval = 1;
goto bailout;
}
- if ((matchccb.ccb_h.status != CAM_REQ_CMP)
- || ((matchccb.cdm.status != CAM_DEV_MATCH_LAST)
- && (matchccb.cdm.status != CAM_DEV_MATCH_MORE))) {
+ if ((matchccb->ccb_h.status != CAM_REQ_CMP)
+ || ((matchccb->cdm.status != CAM_DEV_MATCH_LAST)
+ && (matchccb->cdm.status != CAM_DEV_MATCH_MORE))) {
warnx("got CAM error %#x, CDM error %d\n",
- matchccb.ccb_h.status, matchccb.cdm.status);
+ matchccb->ccb_h.status, matchccb->cdm.status);
retval = 1;
goto bailout;
}
- for (i = 0; i < matchccb.cdm.num_matches; i++) {
+ for (i = 0; i < matchccb->cdm.num_matches; i++) {
struct bus_match_result *bus_result;
/* This shouldn't happen. */
- if (matchccb.cdm.matches[i].type != DEV_MATCH_BUS)
+ if (matchccb->cdm.matches[i].type != DEV_MATCH_BUS)
continue;
- bus_result = &matchccb.cdm.matches[i].result.bus_result;
+ bus_result =&matchccb->cdm.matches[i].result.bus_result;
/*
* We don't want to rescan or reset the xpt bus.
* See above.
*/
- if ((int)bus_result->path_id == -1)
+ if (bus_result->path_id == CAM_XPT_PATH_ID)
continue;
- ccb.ccb_h.func_code = rescan ? XPT_SCAN_BUS :
+ ccb->ccb_h.func_code = rescan ? XPT_SCAN_BUS :
XPT_RESET_BUS;
- ccb.ccb_h.path_id = bus_result->path_id;
- ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
- ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
- ccb.crcn.flags = CAM_FLAG_NONE;
+ ccb->ccb_h.path_id = bus_result->path_id;
+ ccb->ccb_h.target_id = CAM_TARGET_WILDCARD;
+ ccb->ccb_h.target_lun = CAM_LUN_WILDCARD;
+ ccb->crcn.flags = CAM_FLAG_NONE;
/* run this at a low priority */
- ccb.ccb_h.pinfo.priority = 5;
+ ccb->ccb_h.pinfo.priority = 5;
- if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
+ if (ioctl(fd, CAMIOCOMMAND, ccb) == -1) {
warn("CAMIOCOMMAND ioctl failed");
retval = 1;
goto bailout;
}
- if ((ccb.ccb_h.status & CAM_STATUS_MASK) ==CAM_REQ_CMP){
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK)==CAM_REQ_CMP){
fprintf(stdout, "%s of bus %d was successful\n",
rescan? "Re-scan" : "Reset",
bus_result->path_id);
@@ -3210,12 +3400,12 @@
fprintf(stderr, "%s of bus %d returned error "
"%#x\n", rescan? "Re-scan" : "Reset",
bus_result->path_id,
- ccb.ccb_h.status & CAM_STATUS_MASK);
+ ccb->ccb_h.status & CAM_STATUS_MASK);
retval = 1;
}
}
- } while ((matchccb.ccb_h.status == CAM_REQ_CMP)
- && (matchccb.cdm.status == CAM_DEV_MATCH_MORE));
+ } while ((matchccb->ccb_h.status == CAM_REQ_CMP)
+ && (matchccb->cdm.status == CAM_DEV_MATCH_MORE));
bailout:
@@ -3222,16 +3412,18 @@
if (fd != -1)
close(fd);
- if (matchccb.cdm.patterns != NULL)
- free(matchccb.cdm.patterns);
- if (matchccb.cdm.matches != NULL)
- free(matchccb.cdm.matches);
+ if (matchccb != NULL) {
+ free(matchccb->cdm.patterns);
+ free(matchccb->cdm.matches);
+ free(matchccb);
+ }
+ free(ccb);
return(retval);
}
static int
-scanlun_or_reset_dev(int bus, int target, int lun, int scan)
+scanlun_or_reset_dev(path_id_t bus, target_id_t target, lun_id_t lun, int scan)
{
union ccb ccb;
struct cam_device *device;
@@ -3239,18 +3431,18 @@
device = NULL;
- if (bus < 0) {
+ if (bus == CAM_BUS_WILDCARD) {
warnx("invalid bus number %d", bus);
return(1);
}
- if (target < 0) {
+ if (target == CAM_TARGET_WILDCARD) {
warnx("invalid target number %d", target);
return(1);
}
- if (lun < 0) {
- warnx("invalid lun number %d", lun);
+ if (lun == CAM_LUN_WILDCARD) {
+ warnx("invalid lun number %jx", (uintmax_t)lun);
return(1);
}
@@ -3308,12 +3500,12 @@
if (((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
|| ((!scan)
&& ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_BDR_SENT))) {
- fprintf(stdout, "%s of %d:%d:%d was successful\n",
- scan? "Re-scan" : "Reset", bus, target, lun);
+ fprintf(stdout, "%s of %d:%d:%jx was successful\n",
+ scan? "Re-scan" : "Reset", bus, target, (uintmax_t)lun);
return(0);
} else {
- fprintf(stdout, "%s of %d:%d:%d returned error %#x\n",
- scan? "Re-scan" : "Reset", bus, target, lun,
+ fprintf(stdout, "%s of %d:%d:%jx returned error %#x\n",
+ scan? "Re-scan" : "Reset", bus, target, (uintmax_t)lun,
ccb.ccb_h.status & CAM_STATUS_MASK);
return(1);
}
@@ -3320,39 +3512,64 @@
}
#ifndef MINIMALISTIC
+
+static struct scsi_nv defect_list_type_map[] = {
+ { "block", SRDD10_BLOCK_FORMAT },
+ { "extbfi", SRDD10_EXT_BFI_FORMAT },
+ { "extphys", SRDD10_EXT_PHYS_FORMAT },
+ { "longblock", SRDD10_LONG_BLOCK_FORMAT },
+ { "bfi", SRDD10_BYTES_FROM_INDEX_FORMAT },
+ { "phys", SRDD10_PHYSICAL_SECTOR_FORMAT }
+};
+
static int
readdefects(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout)
+ char *combinedopt, int task_attr, int retry_count, int timeout)
{
union ccb *ccb = NULL;
- struct scsi_read_defect_data_10 *rdd_cdb;
+ struct scsi_read_defect_data_hdr_10 *hdr10 = NULL;
+ struct scsi_read_defect_data_hdr_12 *hdr12 = NULL;
+ size_t hdr_size = 0, entry_size = 0;
+ int use_12byte = 0;
+ int hex_format = 0;
u_int8_t *defect_list = NULL;
- u_int32_t max_dlist_length = SRDD10_MAX_LENGTH, dlist_length = 0;
- u_int32_t returned_length = 0;
- u_int32_t num_returned = 0;
- u_int8_t returned_format;
+ u_int8_t list_format = 0;
+ int list_type_set = 0;
+ u_int32_t dlist_length = 0;
+ u_int32_t returned_length = 0, valid_len = 0;
+ u_int32_t num_returned = 0, num_valid = 0;
+ u_int32_t max_possible_size = 0, hdr_max = 0;
+ u_int32_t starting_offset = 0;
+ u_int8_t returned_format, returned_type;
unsigned int i;
+ int summary = 0, quiet = 0;
int c, error = 0;
- int lists_specified;
- int get_length = 1;
+ int lists_specified = 0;
+ int get_length = 1, first_pass = 1;
+ int mads = 0;
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch(c){
case 'f':
{
- char *tstr;
- tstr = optarg;
- while (isspace(*tstr) && (*tstr != '\0'))
- tstr++;
- if (strcmp(tstr, "block") == 0)
- arglist |= CAM_ARG_FORMAT_BLOCK;
- else if (strcmp(tstr, "bfi") == 0)
- arglist |= CAM_ARG_FORMAT_BFI;
- else if (strcmp(tstr, "phys") == 0)
- arglist |= CAM_ARG_FORMAT_PHYS;
- else {
+ scsi_nv_status status;
+ int entry_num = 0;
+
+ status = scsi_get_nv(defect_list_type_map,
+ sizeof(defect_list_type_map) /
+ sizeof(defect_list_type_map[0]), optarg,
+ &entry_num, SCSI_NV_FLAG_IG_CASE);
+
+ if (status == SCSI_NV_FOUND) {
+ list_format = defect_list_type_map[
+ entry_num].value;
+ list_type_set = 1;
+ } else {
+ warnx("%s: %s %s option %s", __func__,
+ (status == SCSI_NV_AMBIGUOUS) ?
+ "ambiguous" : "invalid", "defect list type",
+ optarg);
error = 1;
- warnx("invalid defect format %s", tstr);
goto defect_bailout;
}
break;
@@ -3363,84 +3580,101 @@
case 'P':
arglist |= CAM_ARG_PLIST;
break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 's':
+ summary = 1;
+ break;
+ case 'S': {
+ char *endptr;
+
+ starting_offset = strtoul(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ error = 1;
+ warnx("invalid starting offset %s", optarg);
+ goto defect_bailout;
+ }
+ break;
+ }
+ case 'X':
+ hex_format = 1;
+ break;
default:
break;
}
}
- ccb = cam_getccb(device);
-
- /*
- * Eventually we should probably support the 12 byte READ DEFECT
- * DATA command. It supports a longer parameter list, which may be
- * necessary on newer drives with lots of defects. According to
- * the SBC-3 spec, drives are supposed to return an illegal request
- * if they have more defect data than will fit in 64K.
- */
- defect_list = malloc(max_dlist_length);
- if (defect_list == NULL) {
- warnx("can't malloc memory for defect list");
+ if (list_type_set == 0) {
error = 1;
+ warnx("no defect list format specified");
goto defect_bailout;
}
+ if (arglist & CAM_ARG_PLIST) {
+ list_format |= SRDD10_PLIST;
+ lists_specified++;
+ }
+
+ if (arglist & CAM_ARG_GLIST) {
+ list_format |= SRDD10_GLIST;
+ lists_specified++;
+ }
+
/*
+ * This implies a summary, and was the previous behavior.
+ */
+ if (lists_specified == 0)
+ summary = 1;
+
+ ccb = cam_getccb(device);
+
+retry_12byte:
+
+ /*
* We start off asking for just the header to determine how much
* defect data is available. Some Hitachi drives return an error
* if you ask for more data than the drive has. Once we know the
* length, we retry the command with the returned length.
*/
- dlist_length = sizeof(struct scsi_read_defect_data_hdr_10);
+ if (use_12byte == 0)
+ dlist_length = sizeof(*hdr10);
+ else
+ dlist_length = sizeof(*hdr12);
- rdd_cdb =(struct scsi_read_defect_data_10 *)&ccb->csio.cdb_io.cdb_bytes;
-
retry:
+ if (defect_list != NULL) {
+ free(defect_list);
+ defect_list = NULL;
+ }
+ defect_list = malloc(dlist_length);
+ if (defect_list == NULL) {
+ warnx("can't malloc memory for defect list");
+ error = 1;
+ goto defect_bailout;
+ }
- lists_specified = 0;
+next_batch:
+ bzero(defect_list, dlist_length);
/*
* cam_getccb() zeros the CCB header only. So we need to zero the
* payload portion of the ccb.
*/
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
- cam_fill_csio(&ccb->csio,
- /*retries*/ retry_count,
- /*cbfcnp*/ NULL,
- /*flags*/ CAM_DIR_IN | ((arglist & CAM_ARG_ERR_RECOVER) ?
- CAM_PASS_ERR_RECOVER : 0),
- /*tag_action*/ MSG_SIMPLE_Q_TAG,
- /*data_ptr*/ defect_list,
- /*dxfer_len*/ dlist_length,
- /*sense_len*/ SSD_FULL_SIZE,
- /*cdb_len*/ sizeof(struct scsi_read_defect_data_10),
- /*timeout*/ timeout ? timeout : 5000);
+ scsi_read_defects(&ccb->csio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*tag_action*/ task_attr,
+ /*list_format*/ list_format,
+ /*addr_desc_index*/ starting_offset,
+ /*data_ptr*/ defect_list,
+ /*dxfer_len*/ dlist_length,
+ /*minimum_cmd_size*/ use_12byte ? 12 : 0,
+ /*sense_len*/ SSD_FULL_SIZE,
+ /*timeout*/ timeout ? timeout : 5000);
- rdd_cdb->opcode = READ_DEFECT_DATA_10;
- if (arglist & CAM_ARG_FORMAT_BLOCK)
- rdd_cdb->format = SRDD10_BLOCK_FORMAT;
- else if (arglist & CAM_ARG_FORMAT_BFI)
- rdd_cdb->format = SRDD10_BYTES_FROM_INDEX_FORMAT;
- else if (arglist & CAM_ARG_FORMAT_PHYS)
- rdd_cdb->format = SRDD10_PHYSICAL_SECTOR_FORMAT;
- else {
- error = 1;
- warnx("no defect list format specified");
- goto defect_bailout;
- }
- if (arglist & CAM_ARG_PLIST) {
- rdd_cdb->format |= SRDD10_PLIST;
- lists_specified++;
- }
-
- if (arglist & CAM_ARG_GLIST) {
- rdd_cdb->format |= SRDD10_GLIST;
- lists_specified++;
- }
-
- scsi_ulto2b(dlist_length, rdd_cdb->alloc_length);
-
/* Disable freezing the device queue */
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
@@ -3456,9 +3690,62 @@
goto defect_bailout;
}
- returned_length = scsi_2btoul(((struct
- scsi_read_defect_data_hdr_10 *)defect_list)->length);
+ valid_len = ccb->csio.dxfer_len - ccb->csio.resid;
+ if (use_12byte == 0) {
+ hdr10 = (struct scsi_read_defect_data_hdr_10 *)defect_list;
+ hdr_size = sizeof(*hdr10);
+ hdr_max = SRDDH10_MAX_LENGTH;
+
+ if (valid_len >= hdr_size) {
+ returned_length = scsi_2btoul(hdr10->length);
+ returned_format = hdr10->format;
+ } else {
+ returned_length = 0;
+ returned_format = 0;
+ }
+ } else {
+ hdr12 = (struct scsi_read_defect_data_hdr_12 *)defect_list;
+ hdr_size = sizeof(*hdr12);
+ hdr_max = SRDDH12_MAX_LENGTH;
+
+ if (valid_len >= hdr_size) {
+ returned_length = scsi_4btoul(hdr12->length);
+ returned_format = hdr12->format;
+ } else {
+ returned_length = 0;
+ returned_format = 0;
+ }
+ }
+
+ returned_type = returned_format & SRDDH10_DLIST_FORMAT_MASK;
+ switch (returned_type) {
+ case SRDD10_BLOCK_FORMAT:
+ entry_size = sizeof(struct scsi_defect_desc_block);
+ break;
+ case SRDD10_LONG_BLOCK_FORMAT:
+ entry_size = sizeof(struct scsi_defect_desc_long_block);
+ break;
+ case SRDD10_EXT_PHYS_FORMAT:
+ case SRDD10_PHYSICAL_SECTOR_FORMAT:
+ entry_size = sizeof(struct scsi_defect_desc_phys_sector);
+ break;
+ case SRDD10_EXT_BFI_FORMAT:
+ case SRDD10_BYTES_FROM_INDEX_FORMAT:
+ entry_size = sizeof(struct scsi_defect_desc_bytes_from_index);
+ break;
+ default:
+ warnx("Unknown defect format 0x%x\n", returned_type);
+ error = 1;
+ goto defect_bailout;
+ break;
+ }
+
+ max_possible_size = (hdr_max / entry_size) * entry_size;
+ num_returned = returned_length / entry_size;
+ num_valid = min(returned_length, valid_len - hdr_size);
+ num_valid /= entry_size;
+
if (get_length != 0) {
get_length = 0;
@@ -3481,12 +3768,66 @@
if ((sense_key == SSD_KEY_RECOVERED_ERROR)
&& (asc == 0x1c) && (ascq == 0x00)
&& (returned_length > 0)) {
- dlist_length = returned_length +
- sizeof(struct scsi_read_defect_data_hdr_10);
- dlist_length = min(dlist_length,
- SRDD10_MAX_LENGTH);
- } else
- dlist_length = max_dlist_length;
+ if ((use_12byte == 0)
+ && (returned_length >= max_possible_size)) {
+ get_length = 1;
+ use_12byte = 1;
+ goto retry_12byte;
+ }
+ dlist_length = returned_length + hdr_size;
+ } else if ((sense_key == SSD_KEY_RECOVERED_ERROR)
+ && (asc == 0x1f) && (ascq == 0x00)
+ && (returned_length > 0)) {
+ /* Partial defect list transfer */
+ /*
+ * Hitachi drives return this error
+ * along with a partial defect list if they
+ * have more defects than the 10 byte
+ * command can support. Retry with the 12
+ * byte command.
+ */
+ if (use_12byte == 0) {
+ get_length = 1;
+ use_12byte = 1;
+ goto retry_12byte;
+ }
+ dlist_length = returned_length + hdr_size;
+ } else if ((sense_key == SSD_KEY_ILLEGAL_REQUEST)
+ && (asc == 0x24) && (ascq == 0x00)) {
+ /* Invalid field in CDB */
+ /*
+ * SBC-3 says that if the drive has more
+ * defects than can be reported with the
+ * 10 byte command, it should return this
+ * error and no data. Retry with the 12
+ * byte command.
+ */
+ if (use_12byte == 0) {
+ get_length = 1;
+ use_12byte = 1;
+ goto retry_12byte;
+ }
+ dlist_length = returned_length + hdr_size;
+ } else {
+ /*
+ * If we got a SCSI error and no valid length,
+ * just use the 10 byte maximum. The 12
+ * byte maximum is too large.
+ */
+ if (returned_length == 0)
+ dlist_length = SRDD10_MAX_LENGTH;
+ else {
+ if ((use_12byte == 0)
+ && (returned_length >=
+ max_possible_size)) {
+ get_length = 1;
+ use_12byte = 1;
+ goto retry_12byte;
+ }
+ dlist_length = returned_length +
+ hdr_size;
+ }
+ }
} else if ((ccb->ccb_h.status & CAM_STATUS_MASK) !=
CAM_REQ_CMP){
error = 1;
@@ -3496,16 +3837,40 @@
CAM_EPF_ALL, stderr);
goto defect_bailout;
} else {
- dlist_length = returned_length +
- sizeof(struct scsi_read_defect_data_hdr_10);
- dlist_length = min(dlist_length, SRDD10_MAX_LENGTH);
+ if ((use_12byte == 0)
+ && (returned_length >= max_possible_size)) {
+ get_length = 1;
+ use_12byte = 1;
+ goto retry_12byte;
+ }
+ dlist_length = returned_length + hdr_size;
}
+ if (summary != 0) {
+ fprintf(stdout, "%u", num_returned);
+ if (quiet == 0) {
+ fprintf(stdout, " defect%s",
+ (num_returned != 1) ? "s" : "");
+ }
+ fprintf(stdout, "\n");
+ goto defect_bailout;
+ }
+
+ /*
+ * We always limit the list length to the 10-byte maximum
+ * length (0xffff). The reason is that some controllers
+ * can't handle larger I/Os, and we can transfer the entire
+ * 10 byte list in one shot. For drives that support the 12
+ * byte read defects command, we'll step through the list
+ * by specifying a starting offset. For drives that don't
+ * support the 12 byte command's starting offset, we'll
+ * just display the first 64K.
+ */
+ dlist_length = min(dlist_length, SRDD10_MAX_LENGTH);
+
goto retry;
}
- returned_format = ((struct scsi_read_defect_data_hdr_10 *)
- defect_list)->format;
if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR)
&& (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND)
@@ -3522,32 +3887,32 @@
* According to the SCSI spec, if the disk doesn't support
* the requested format, it will generally return a sense
* key of RECOVERED ERROR, and an additional sense code
- * of "DEFECT LIST NOT FOUND". So, we check for that, and
- * also check to make sure that the returned length is
- * greater than 0, and then print out whatever format the
- * disk gave us.
+ * of "DEFECT LIST NOT FOUND". HGST drives also return
+ * Primary/Grown defect list not found errors. So just
+ * check for an ASC of 0x1c.
*/
if ((sense_key == SSD_KEY_RECOVERED_ERROR)
- && (asc == 0x1c) && (ascq == 0x00)
- && (returned_length > 0)) {
- warnx("requested defect format not available");
- switch(returned_format & SRDDH10_DLIST_FORMAT_MASK) {
- case SRDD10_BLOCK_FORMAT:
- warnx("Device returned block format");
- break;
- case SRDD10_BYTES_FROM_INDEX_FORMAT:
- warnx("Device returned bytes from index"
- " format");
- break;
- case SRDD10_PHYSICAL_SECTOR_FORMAT:
- warnx("Device returned physical sector format");
- break;
- default:
+ && (asc == 0x1c)) {
+ const char *format_str;
+
+ format_str = scsi_nv_to_str(defect_list_type_map,
+ sizeof(defect_list_type_map) /
+ sizeof(defect_list_type_map[0]),
+ list_format & SRDD10_DLIST_FORMAT_MASK);
+ warnx("requested defect format %s not available",
+ format_str ? format_str : "unknown");
+
+ format_str = scsi_nv_to_str(defect_list_type_map,
+ sizeof(defect_list_type_map) /
+ sizeof(defect_list_type_map[0]), returned_type);
+ if (format_str != NULL) {
+ warnx("Device returned %s format",
+ format_str);
+ } else {
error = 1;
warnx("Device returned unknown defect"
- " data format %#x", returned_format);
+ " data format %#x", returned_type);
goto defect_bailout;
- break; /* NOTREACHED */
}
} else {
error = 1;
@@ -3566,100 +3931,152 @@
goto defect_bailout;
}
+ if (first_pass != 0) {
+ fprintf(stderr, "Got %d defect", num_returned);
+
+ if ((lists_specified == 0) || (num_returned == 0)) {
+ fprintf(stderr, "s.\n");
+ goto defect_bailout;
+ } else if (num_returned == 1)
+ fprintf(stderr, ":\n");
+ else
+ fprintf(stderr, "s:\n");
+
+ first_pass = 0;
+ }
+
/*
* XXX KDM I should probably clean up the printout format for the
* disk defects.
*/
- switch (returned_format & SRDDH10_DLIST_FORMAT_MASK){
- case SRDDH10_PHYSICAL_SECTOR_FORMAT:
- {
- struct scsi_defect_desc_phys_sector *dlist;
+ switch (returned_type) {
+ case SRDD10_PHYSICAL_SECTOR_FORMAT:
+ case SRDD10_EXT_PHYS_FORMAT:
+ {
+ struct scsi_defect_desc_phys_sector *dlist;
- dlist = (struct scsi_defect_desc_phys_sector *)
- (defect_list +
- sizeof(struct scsi_read_defect_data_hdr_10));
+ dlist = (struct scsi_defect_desc_phys_sector *)
+ (defect_list + hdr_size);
- num_returned = returned_length /
- sizeof(struct scsi_defect_desc_phys_sector);
+ for (i = 0; i < num_valid; i++) {
+ uint32_t sector;
- fprintf(stderr, "Got %d defect", num_returned);
-
- if ((lists_specified == 0) || (num_returned == 0)) {
- fprintf(stderr, "s.\n");
- break;
- } else if (num_returned == 1)
- fprintf(stderr, ":\n");
+ sector = scsi_4btoul(dlist[i].sector);
+ if (returned_type == SRDD10_EXT_PHYS_FORMAT) {
+ mads = (sector & SDD_EXT_PHYS_MADS) ?
+ 0 : 1;
+ sector &= ~SDD_EXT_PHYS_FLAG_MASK;
+ }
+ if (hex_format == 0)
+ fprintf(stdout, "%d:%d:%d%s",
+ scsi_3btoul(dlist[i].cylinder),
+ dlist[i].head,
+ scsi_4btoul(dlist[i].sector),
+ mads ? " - " : "\n");
else
- fprintf(stderr, "s:\n");
-
- for (i = 0; i < num_returned; i++) {
- fprintf(stdout, "%d:%d:%d\n",
+ fprintf(stdout, "0x%x:0x%x:0x%x%s",
scsi_3btoul(dlist[i].cylinder),
dlist[i].head,
- scsi_4btoul(dlist[i].sector));
- }
- break;
+ scsi_4btoul(dlist[i].sector),
+ mads ? " - " : "\n");
+ mads = 0;
}
- case SRDDH10_BYTES_FROM_INDEX_FORMAT:
- {
- struct scsi_defect_desc_bytes_from_index *dlist;
+ if (num_valid < num_returned) {
+ starting_offset += num_valid;
+ goto next_batch;
+ }
+ break;
+ }
+ case SRDD10_BYTES_FROM_INDEX_FORMAT:
+ case SRDD10_EXT_BFI_FORMAT:
+ {
+ struct scsi_defect_desc_bytes_from_index *dlist;
- dlist = (struct scsi_defect_desc_bytes_from_index *)
- (defect_list +
- sizeof(struct scsi_read_defect_data_hdr_10));
+ dlist = (struct scsi_defect_desc_bytes_from_index *)
+ (defect_list + hdr_size);
- num_returned = returned_length /
- sizeof(struct scsi_defect_desc_bytes_from_index);
+ for (i = 0; i < num_valid; i++) {
+ uint32_t bfi;
- fprintf(stderr, "Got %d defect", num_returned);
-
- if ((lists_specified == 0) || (num_returned == 0)) {
- fprintf(stderr, "s.\n");
- break;
- } else if (num_returned == 1)
- fprintf(stderr, ":\n");
+ bfi = scsi_4btoul(dlist[i].bytes_from_index);
+ if (returned_type == SRDD10_EXT_BFI_FORMAT) {
+ mads = (bfi & SDD_EXT_BFI_MADS) ? 1 : 0;
+ bfi &= ~SDD_EXT_BFI_FLAG_MASK;
+ }
+ if (hex_format == 0)
+ fprintf(stdout, "%d:%d:%d%s",
+ scsi_3btoul(dlist[i].cylinder),
+ dlist[i].head,
+ scsi_4btoul(dlist[i].bytes_from_index),
+ mads ? " - " : "\n");
else
- fprintf(stderr, "s:\n");
-
- for (i = 0; i < num_returned; i++) {
- fprintf(stdout, "%d:%d:%d\n",
+ fprintf(stdout, "0x%x:0x%x:0x%x%s",
scsi_3btoul(dlist[i].cylinder),
dlist[i].head,
- scsi_4btoul(dlist[i].bytes_from_index));
- }
- break;
+ scsi_4btoul(dlist[i].bytes_from_index),
+ mads ? " - " : "\n");
+
+ mads = 0;
}
- case SRDDH10_BLOCK_FORMAT:
- {
- struct scsi_defect_desc_block *dlist;
+ if (num_valid < num_returned) {
+ starting_offset += num_valid;
+ goto next_batch;
+ }
+ break;
+ }
+ case SRDDH10_BLOCK_FORMAT:
+ {
+ struct scsi_defect_desc_block *dlist;
- dlist = (struct scsi_defect_desc_block *)(defect_list +
- sizeof(struct scsi_read_defect_data_hdr_10));
+ dlist = (struct scsi_defect_desc_block *)
+ (defect_list + hdr_size);
- num_returned = returned_length /
- sizeof(struct scsi_defect_desc_block);
+ for (i = 0; i < num_valid; i++) {
+ if (hex_format == 0)
+ fprintf(stdout, "%u\n",
+ scsi_4btoul(dlist[i].address));
+ else
+ fprintf(stdout, "0x%x\n",
+ scsi_4btoul(dlist[i].address));
+ }
- fprintf(stderr, "Got %d defect", num_returned);
+ if (num_valid < num_returned) {
+ starting_offset += num_valid;
+ goto next_batch;
+ }
- if ((lists_specified == 0) || (num_returned == 0)) {
- fprintf(stderr, "s.\n");
- break;
- } else if (num_returned == 1)
- fprintf(stderr, ":\n");
+ break;
+ }
+ case SRDD10_LONG_BLOCK_FORMAT:
+ {
+ struct scsi_defect_desc_long_block *dlist;
+
+ dlist = (struct scsi_defect_desc_long_block *)
+ (defect_list + hdr_size);
+
+ for (i = 0; i < num_valid; i++) {
+ if (hex_format == 0)
+ fprintf(stdout, "%ju\n",
+ (uintmax_t)scsi_8btou64(
+ dlist[i].address));
else
- fprintf(stderr, "s:\n");
+ fprintf(stdout, "0x%jx\n",
+ (uintmax_t)scsi_8btou64(
+ dlist[i].address));
+ }
- for (i = 0; i < num_returned; i++)
- fprintf(stdout, "%u\n",
- scsi_4btoul(dlist[i].address));
- break;
+ if (num_valid < num_returned) {
+ starting_offset += num_valid;
+ goto next_batch;
}
- default:
- fprintf(stderr, "Unknown defect format %d\n",
- returned_format & SRDDH10_DLIST_FORMAT_MASK);
- error = 1;
- break;
+ break;
}
+ default:
+ fprintf(stderr, "Unknown defect format 0x%x\n",
+ returned_type);
+ error = 1;
+ break;
+ }
defect_bailout:
if (defect_list != NULL)
@@ -3686,8 +4103,9 @@
#ifndef MINIMALISTIC
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)
+mode_sense(struct cam_device *device, int dbd, int pc, int page, int subpage,
+ int task_attr, int retry_count, int timeout, u_int8_t *data,
+ int datalen)
{
union ccb *ccb;
int retval;
@@ -3697,18 +4115,19 @@
if (ccb == NULL)
errx(1, "mode_sense: couldn't allocate CCB");
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
- scsi_mode_sense(&ccb->csio,
+ scsi_mode_sense_subpage(&ccb->csio,
/* retries */ retry_count,
/* cbfcnp */ NULL,
- /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* tag_action */ task_attr,
/* dbd */ dbd,
- /* page_code */ page_control << 6,
- /* page */ mode_page,
+ /* pc */ pc << 6,
+ /* page */ page,
+ /* subpage */ subpage,
/* param_buf */ data,
/* param_len */ datalen,
+ /* minimum_cmd_size */ 0,
/* sense_len */ SSD_FULL_SIZE,
/* timeout */ timeout ? timeout : 5000);
@@ -3736,8 +4155,8 @@
}
void
-mode_select(struct cam_device *device, int save_pages, int retry_count,
- int timeout, u_int8_t *data, int datalen)
+mode_select(struct cam_device *device, int save_pages, int task_attr,
+ int retry_count, int timeout, u_int8_t *data, int datalen)
{
union ccb *ccb;
int retval;
@@ -3747,13 +4166,12 @@
if (ccb == NULL)
errx(1, "mode_select: couldn't allocate CCB");
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
scsi_mode_select(&ccb->csio,
/* retries */ retry_count,
/* cbfcnp */ NULL,
- /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* tag_action */ task_attr,
/* scsi_page_fmt */ 1,
/* save_pages */ save_pages,
/* param_buf */ data,
@@ -3788,10 +4206,11 @@
void
modepage(struct cam_device *device, int argc, char **argv, char *combinedopt,
- int retry_count, int timeout)
+ int task_attr, int retry_count, int timeout)
{
- int c, mode_page = -1, page_control = 0;
- int binary = 0, list = 0;
+ char *str_subpage;
+ int c, page = -1, subpage = -1, pc = 0;
+ int binary = 0, dbd = 0, edit = 0, list = 0;
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch(c) {
@@ -3799,25 +4218,31 @@
binary = 1;
break;
case 'd':
- arglist |= CAM_ARG_DBD;
+ dbd = 1;
break;
case 'e':
- arglist |= CAM_ARG_MODE_EDIT;
+ edit = 1;
break;
case 'l':
- list = 1;
+ list++;
break;
case 'm':
- mode_page = strtol(optarg, NULL, 0);
- if (mode_page < 0)
- errx(1, "invalid mode page %d", mode_page);
+ str_subpage = optarg;
+ strsep(&str_subpage, ",");
+ page = strtol(optarg, NULL, 0);
+ if (str_subpage)
+ subpage = strtol(str_subpage, NULL, 0);
+ else
+ subpage = 0;
+ if (page < 0)
+ errx(1, "invalid mode page %d", page);
+ if (subpage < 0)
+ errx(1, "invalid mode subpage %d", subpage);
break;
case 'P':
- page_control = strtol(optarg, NULL, 0);
- if ((page_control < 0) || (page_control > 3))
- errx(1, "invalid page control field %d",
- page_control);
- arglist |= CAM_ARG_PAGE_CNTL;
+ pc = strtol(optarg, NULL, 0);
+ if ((pc < 0) || (pc > 3))
+ errx(1, "invalid page control field %d", pc);
break;
default:
break;
@@ -3824,22 +4249,21 @@
}
}
- if (mode_page == -1 && list == 0)
+ if (page == -1 && list == 0)
errx(1, "you must specify a mode page!");
- if (list) {
- mode_list(device, page_control, arglist & CAM_ARG_DBD,
- retry_count, timeout);
+ if (list != 0) {
+ mode_list(device, dbd, pc, list > 1, task_attr, retry_count,
+ timeout);
} else {
- mode_edit(device, mode_page, page_control,
- arglist & CAM_ARG_DBD, arglist & CAM_ARG_MODE_EDIT, binary,
- retry_count, timeout);
+ mode_edit(device, dbd, pc, page, subpage, edit, binary,
+ task_attr, retry_count, timeout);
}
}
static int
scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt,
- int retry_count, int timeout)
+ int task_attr, int retry_count, int timeout)
{
union ccb *ccb;
u_int32_t flags = CAM_DIR_NONE;
@@ -3847,7 +4271,7 @@
u_int8_t cdb[20];
u_int8_t atacmd[12];
struct get_hook hook;
- int c, data_bytes = 0;
+ int c, data_bytes = 0, valid_bytes;
int cdb_len = 0;
int atacmd_len = 0;
int dmacmd = 0;
@@ -3865,8 +4289,7 @@
return(1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(union ccb) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(ccb);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch(c) {
@@ -4085,7 +4508,7 @@
/*retries*/ retry_count,
/*cbfcnp*/ NULL,
/*flags*/ flags,
- /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*tag_action*/ task_attr,
/*data_ptr*/ data_ptr,
/*dxfer_len*/ data_bytes,
/*sense_len*/ SSD_FULL_SIZE,
@@ -4152,16 +4575,20 @@
}
}
+ if (cdb_len)
+ valid_bytes = ccb->csio.dxfer_len - ccb->csio.resid;
+ else
+ valid_bytes = ccb->ataio.dxfer_len - ccb->ataio.resid;
if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
&& (arglist & CAM_ARG_CMD_IN)
- && (data_bytes > 0)) {
+ && (valid_bytes > 0)) {
if (fd_data == 0) {
- buff_decode_visit(data_ptr, data_bytes, datastr,
+ buff_decode_visit(data_ptr, valid_bytes, datastr,
arg_put, NULL);
fprintf(stdout, "\n");
} else {
ssize_t amt_written;
- int amt_to_write = data_bytes;
+ int amt_to_write = valid_bytes;
u_int8_t *buf_ptr = data_ptr;
for (amt_written = 0; (amt_to_write > 0) &&
@@ -4176,7 +4603,7 @@
} else if ((amt_written == 0)
&& (amt_to_write > 0)) {
warnx("only wrote %u bytes out of %u",
- data_bytes - amt_to_write, data_bytes);
+ valid_bytes - amt_to_write, valid_bytes);
}
}
}
@@ -4195,7 +4622,9 @@
camdebug(int argc, char **argv, char *combinedopt)
{
int c, fd;
- int bus = -1, target = -1, lun = -1;
+ path_id_t bus = CAM_BUS_WILDCARD;
+ target_id_t target = CAM_TARGET_WILDCARD;
+ lun_id_t lun = CAM_LUN_WILDCARD;
char *tstr, *tmpstr = NULL;
union ccb ccb;
int error = 0;
@@ -4315,8 +4744,8 @@
} else {
fprintf(stderr,
"Debugging enabled for "
- "%d:%d:%d\n",
- bus, target, lun);
+ "%d:%d:%jx\n",
+ bus, target, (uintmax_t)lun);
}
}
}
@@ -4365,8 +4794,7 @@
cam_path_string(device, pathstr, sizeof(pathstr));
if (numtags >= 0) {
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_relsim) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->crs);
ccb->ccb_h.func_code = XPT_REL_SIMQ;
ccb->ccb_h.flags = CAM_DEV_QFREEZE;
ccb->crs.release_flags = RELSIM_ADJUST_OPENINGS;
@@ -4393,8 +4821,7 @@
pathstr, ccb->crs.openings);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_getdevstats) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cgds);
ccb->ccb_h.func_code = XPT_GDEV_STATS;
@@ -4418,9 +4845,9 @@
fprintf(stdout, "%s", pathstr);
fprintf(stdout, "dev_active %d\n", ccb->cgds.dev_active);
fprintf(stdout, "%s", pathstr);
- fprintf(stdout, "devq_openings %d\n", ccb->cgds.devq_openings);
+ fprintf(stdout, "allocated %d\n", ccb->cgds.allocated);
fprintf(stdout, "%s", pathstr);
- fprintf(stdout, "devq_queued %d\n", ccb->cgds.devq_queued);
+ fprintf(stdout, "queued %d\n", ccb->cgds.queued);
fprintf(stdout, "%s", pathstr);
fprintf(stdout, "held %d\n", ccb->cgds.held);
fprintf(stdout, "%s", pathstr);
@@ -4488,13 +4915,13 @@
&cts->xport_specific.fc;
if (fc->valid & CTS_FC_VALID_WWNN)
- fprintf(stdout, "%sWWNN: 0x%llx", pathstr,
+ fprintf(stdout, "%sWWNN: 0x%llx\n", pathstr,
(long long) fc->wwnn);
if (fc->valid & CTS_FC_VALID_WWPN)
- fprintf(stdout, "%sWWPN: 0x%llx", pathstr,
+ fprintf(stdout, "%sWWPN: 0x%llx\n", pathstr,
(long long) fc->wwpn);
if (fc->valid & CTS_FC_VALID_PORT)
- fprintf(stdout, "%sPortID: 0x%x", pathstr, fc->port);
+ fprintf(stdout, "%sPortID: 0x%x\n", pathstr, fc->port);
if (fc->valid & CTS_FC_VALID_SPEED)
fprintf(stdout, "%stransfer speed: %d.%03dMB/s\n",
pathstr, fc->bitrate / 1000, fc->bitrate % 1000);
@@ -4594,8 +5021,7 @@
warnx("get_cpi: couldn't allocate CCB");
return(1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_pathinq) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cpi);
ccb->ccb_h.func_code = XPT_PATH_INQ;
if (cam_send_ccb(device, ccb) < 0) {
warn("get_cpi: error sending Path Inquiry CCB");
@@ -4633,8 +5059,7 @@
warnx("get_cgd: couldn't allocate CCB");
return(1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_pathinq) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cgd);
ccb->ccb_h.func_code = XPT_GDEV_TYPE;
if (cam_send_ccb(device, ccb) < 0) {
warn("get_cgd: error sending Path Inquiry CCB");
@@ -4658,31 +5083,178 @@
return(retval);
}
-/* return the type of disk (really the command type) */
-static const char *
-get_disk_type(struct cam_device *device)
+/*
+ * Returns 1 if the device has the VPD page, 0 if it does not, and -1 on an
+ * error.
+ */
+int
+dev_has_vpd_page(struct cam_device *dev, uint8_t page_id, int retry_count,
+ int timeout, int verbosemode)
{
- struct ccb_getdev cgd;
+ union ccb *ccb = NULL;
+ struct scsi_vpd_supported_page_list sup_pages;
+ int i;
+ int retval = 0;
- (void) memset(&cgd, 0x0, sizeof(cgd));
- get_cgd(device, &cgd);
- switch(cgd.protocol) {
+ ccb = cam_getccb(dev);
+ if (ccb == NULL) {
+ warn("Unable to allocate CCB");
+ retval = -1;
+ goto bailout;
+ }
+
+ /* cam_getccb cleans up the header, caller has to zero the payload */
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
+
+ bzero(&sup_pages, sizeof(sup_pages));
+
+ scsi_inquiry(&ccb->csio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* inq_buf */ (u_int8_t *)&sup_pages,
+ /* inq_len */ sizeof(sup_pages),
+ /* evpd */ 1,
+ /* page_code */ SVPD_SUPPORTED_PAGE_LIST,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ timeout ? timeout : 5000);
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ if (retry_count != 0)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ if (cam_send_ccb(dev, ccb) < 0) {
+ cam_freeccb(ccb);
+ ccb = NULL;
+ retval = -1;
+ goto bailout;
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (verbosemode != 0)
+ cam_error_print(dev, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ retval = -1;
+ goto bailout;
+ }
+
+ for (i = 0; i < sup_pages.length; i++) {
+ if (sup_pages.list[i] == page_id) {
+ retval = 1;
+ goto bailout;
+ }
+ }
+bailout:
+ if (ccb != NULL)
+ cam_freeccb(ccb);
+
+ return (retval);
+}
+
+/*
+ * devtype is filled in with the type of device.
+ * Returns 0 for success, non-zero for failure.
+ */
+int
+get_device_type(struct cam_device *dev, int retry_count, int timeout,
+ int verbosemode, camcontrol_devtype *devtype)
+{
+ struct ccb_getdev cgd;
+ int retval = 0;
+
+ retval = get_cgd(dev, &cgd);
+ if (retval != 0)
+ goto bailout;
+
+ switch (cgd.protocol) {
case PROTO_SCSI:
- return "scsi";
+ break;
case PROTO_ATA:
case PROTO_ATAPI:
case PROTO_SATAPM:
- return "ata";
+ *devtype = CC_DT_ATA;
+ goto bailout;
+ break; /*NOTREACHED*/
default:
- return "unknown";
+ *devtype = CC_DT_UNKNOWN;
+ goto bailout;
+ break; /*NOTREACHED*/
}
+
+ /*
+ * Check for the ATA Information VPD page (0x89). If this is an
+ * ATA device behind a SCSI to ATA translation layer, this VPD page
+ * should be present.
+ *
+ * If that VPD page isn't present, or we get an error back from the
+ * INQUIRY command, we'll just treat it as a normal SCSI device.
+ */
+ retval = dev_has_vpd_page(dev, SVPD_ATA_INFORMATION, retry_count,
+ timeout, verbosemode);
+ if (retval == 1)
+ *devtype = CC_DT_ATA_BEHIND_SCSI;
+ else
+ *devtype = CC_DT_SCSI;
+
+ retval = 0;
+
+bailout:
+ return (retval);
}
+void
+build_ata_cmd(union ccb *ccb, uint32_t retry_count, uint32_t flags,
+ uint8_t tag_action, uint8_t protocol, uint8_t ata_flags, uint16_t features,
+ uint16_t sector_count, uint64_t lba, uint8_t command, uint8_t *data_ptr,
+ uint16_t dxfer_len, uint8_t sense_len, uint32_t timeout,
+ int is48bit, camcontrol_devtype devtype)
+{
+ if (devtype == CC_DT_ATA) {
+ cam_fill_ataio(&ccb->ataio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*flags*/ flags,
+ /*tag_action*/ tag_action,
+ /*data_ptr*/ data_ptr,
+ /*dxfer_len*/ dxfer_len,
+ /*timeout*/ timeout);
+ if (is48bit || lba > ATA_MAX_28BIT_LBA)
+ ata_48bit_cmd(&ccb->ataio, command, features, lba,
+ sector_count);
+ else
+ ata_28bit_cmd(&ccb->ataio, command, features, lba,
+ sector_count);
+ } else {
+ if (is48bit || lba > ATA_MAX_28BIT_LBA)
+ protocol |= AP_EXTEND;
+
+ scsi_ata_pass_16(&ccb->csio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*flags*/ flags,
+ /*tag_action*/ tag_action,
+ /*protocol*/ protocol,
+ /*ata_flags*/ ata_flags,
+ /*features*/ features,
+ /*sector_count*/ sector_count,
+ /*lba*/ lba,
+ /*command*/ command,
+ /*control*/ 0,
+ /*data_ptr*/ data_ptr,
+ /*dxfer_len*/ dxfer_len,
+ /*sense_len*/ sense_len,
+ /*timeout*/ timeout);
+ }
+}
+
+
static void
cpi_print(struct ccb_pathinq *cpi)
{
char adapter_str[1024];
- int i;
+ uint64_t i;
snprintf(adapter_str, sizeof(adapter_str),
"%s%d:", cpi->dev_name, cpi->unit_number);
@@ -4690,7 +5262,7 @@
fprintf(stdout, "%s SIM/HBA version: %d\n", adapter_str,
cpi->version_num);
- for (i = 1; i < 0xff; i = i << 1) {
+ for (i = 1; i < UINT8_MAX; i = i << 1) {
const char *str;
if ((i & cpi->hba_inquiry) == 0)
@@ -4730,7 +5302,7 @@
fprintf(stdout, "%s\n", str);
}
- for (i = 1; i < 0xff; i = i << 1) {
+ for (i = 1; i < UINT32_MAX; i = i << 1) {
const char *str;
if ((i & cpi->hba_misc) == 0)
@@ -4765,7 +5337,7 @@
fprintf(stdout, "%s\n", str);
}
- for (i = 1; i < 0xff; i = i << 1) {
+ for (i = 1; i < UINT16_MAX; i = i << 1) {
const char *str;
if ((i & cpi->target_sprt) == 0)
@@ -4846,8 +5418,7 @@
return(1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_trans_settings) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cts);
ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
@@ -4888,8 +5459,8 @@
}
static int
-ratecontrol(struct cam_device *device, int retry_count, int timeout,
- int argc, char **argv, char *combinedopt)
+ratecontrol(struct cam_device *device, int task_attr, int retry_count,
+ int timeout, int argc, char **argv, char *combinedopt)
{
int c;
union ccb *ccb;
@@ -4987,8 +5558,7 @@
break;
}
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_pathinq) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cpi);
/*
* Grab path inquiry information, so we can determine whether
* or not the initiator is capable of the things that the user
@@ -5014,8 +5584,7 @@
goto ratecontrol_bailout;
}
bcopy(&ccb->cpi, &cpi, sizeof(struct ccb_pathinq));
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_trans_settings) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cts);
if (quiet == 0) {
fprintf(stdout, "%s parameters:\n",
user_settings ? "User" : "Current");
@@ -5221,7 +5790,7 @@
}
}
if (send_tur) {
- retval = testunitready(device, retry_count, timeout,
+ retval = testunitready(device, task_attr, retry_count, timeout,
(arglist & CAM_ARG_VERBOSE) ? 0 : 1);
/*
* If the TUR didn't succeed, just bail.
@@ -5246,7 +5815,7 @@
static int
scsiformat(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout)
+ char *combinedopt, int task_attr, int retry_count, int timeout)
{
union ccb *ccb;
int c;
@@ -5268,8 +5837,7 @@
return(1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch(c) {
@@ -5296,7 +5864,7 @@
"following device:\n");
error = scsidoinquiry(device, argc, argv, combinedopt,
- retry_count, timeout);
+ task_attr, retry_count, timeout);
if (error != 0) {
warnx("scsiformat: error sending inquiry");
@@ -5368,7 +5936,7 @@
scsi_format_unit(&ccb->csio,
/* retries */ retry_count,
/* cbfcnp */ NULL,
- /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* tag_action */ task_attr,
/* byte2 */ byte2,
/* ileave */ 0,
/* data_ptr */ data_ptr,
@@ -5417,8 +5985,7 @@
do {
cam_status status;
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
/*
* There's really no need to do error recovery or
@@ -5428,7 +5995,7 @@
scsi_test_unit_ready(&ccb->csio,
/* retries */ 0,
/* cbfcnp */ NULL,
- /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* tag_action */ task_attr,
/* sense_len */ SSD_FULL_SIZE,
/* timeout */ 5000);
@@ -5478,15 +6045,15 @@
if ((scsi_get_sks(sense, ccb->csio.sense_len -
ccb->csio.sense_resid, sks) == 0)
&& (quiet == 0)) {
- int val;
+ uint32_t val;
u_int64_t percentage;
val = scsi_2btoul(&sks[1]);
- percentage = 10000 * val;
+ percentage = 10000ull * val;
fprintf(stdout,
"\rFormatting: %ju.%02u %% "
- "(%d/%d) done",
+ "(%u/%d) done",
(uintmax_t)(percentage /
(0x10000 * 100)),
(unsigned)((percentage /
@@ -5537,8 +6104,418 @@
}
static int
+scsisanitize(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int task_attr, int retry_count, int timeout)
+{
+ union ccb *ccb;
+ u_int8_t action = 0;
+ int c;
+ int ycount = 0, quiet = 0;
+ int error = 0, retval = 0;
+ int use_timeout = 10800 * 1000;
+ int immediate = 1;
+ int invert = 0;
+ int passes = 0;
+ int ause = 0;
+ int fd = -1;
+ const char *pattern = NULL;
+ u_int8_t *data_ptr = NULL;
+ u_int32_t dxfer_len = 0;
+ u_int8_t byte2 = 0;
+ int num_warnings = 0;
+ int reportonly = 0;
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("scsisanitize: error allocating ccb");
+ return(1);
+ }
+
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch(c) {
+ case 'a':
+ if (strcasecmp(optarg, "overwrite") == 0)
+ action = SSZ_SERVICE_ACTION_OVERWRITE;
+ else if (strcasecmp(optarg, "block") == 0)
+ action = SSZ_SERVICE_ACTION_BLOCK_ERASE;
+ else if (strcasecmp(optarg, "crypto") == 0)
+ action = SSZ_SERVICE_ACTION_CRYPTO_ERASE;
+ else if (strcasecmp(optarg, "exitfailure") == 0)
+ action = SSZ_SERVICE_ACTION_EXIT_MODE_FAILURE;
+ else {
+ warnx("invalid service operation \"%s\"",
+ optarg);
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+ break;
+ case 'c':
+ passes = strtol(optarg, NULL, 0);
+ if (passes < 1 || passes > 31) {
+ warnx("invalid passes value %d", passes);
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+ break;
+ case 'I':
+ invert = 1;
+ break;
+ case 'P':
+ pattern = optarg;
+ break;
+ case 'q':
+ quiet++;
+ break;
+ case 'U':
+ ause = 1;
+ break;
+ case 'r':
+ reportonly = 1;
+ break;
+ case 'w':
+ immediate = 0;
+ break;
+ case 'y':
+ ycount++;
+ break;
+ }
+ }
+
+ if (reportonly)
+ goto doreport;
+
+ if (action == 0) {
+ warnx("an action is required");
+ error = 1;
+ goto scsisanitize_bailout;
+ } else if (action == SSZ_SERVICE_ACTION_OVERWRITE) {
+ struct scsi_sanitize_parameter_list *pl;
+ struct stat sb;
+ ssize_t sz, amt;
+
+ if (pattern == NULL) {
+ warnx("overwrite action requires -P argument");
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+ fd = open(pattern, O_RDONLY);
+ if (fd < 0) {
+ warn("cannot open pattern file %s", pattern);
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+ if (fstat(fd, &sb) < 0) {
+ warn("cannot stat pattern file %s", pattern);
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+ sz = sb.st_size;
+ if (sz > SSZPL_MAX_PATTERN_LENGTH) {
+ warnx("pattern file size exceeds maximum value %d",
+ SSZPL_MAX_PATTERN_LENGTH);
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+ dxfer_len = sizeof(*pl) + sz;
+ data_ptr = calloc(1, dxfer_len);
+ if (data_ptr == NULL) {
+ warnx("cannot allocate parameter list buffer");
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+
+ amt = read(fd, data_ptr + sizeof(*pl), sz);
+ if (amt < 0) {
+ warn("cannot read pattern file");
+ error = 1;
+ goto scsisanitize_bailout;
+ } else if (amt != sz) {
+ warnx("short pattern file read");
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+
+ pl = (struct scsi_sanitize_parameter_list *)data_ptr;
+ if (passes == 0)
+ pl->byte1 = 1;
+ else
+ pl->byte1 = passes;
+ if (invert != 0)
+ pl->byte1 |= SSZPL_INVERT;
+ scsi_ulto2b(sz, pl->length);
+ } else {
+ const char *arg;
+
+ if (passes != 0)
+ arg = "-c";
+ else if (invert != 0)
+ arg = "-I";
+ else if (pattern != NULL)
+ arg = "-P";
+ else
+ arg = NULL;
+ if (arg != NULL) {
+ warnx("%s argument only valid with overwrite "
+ "operation", arg);
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+ }
+
+ if (quiet == 0) {
+ fprintf(stdout, "You are about to REMOVE ALL DATA from the "
+ "following device:\n");
+
+ error = scsidoinquiry(device, argc, argv, combinedopt,
+ task_attr, retry_count, timeout);
+
+ if (error != 0) {
+ warnx("scsisanitize: error sending inquiry");
+ goto scsisanitize_bailout;
+ }
+ }
+
+ if (ycount == 0) {
+ if (!get_confirmation()) {
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+ }
+
+ if (timeout != 0)
+ use_timeout = timeout;
+
+ if (quiet == 0) {
+ fprintf(stdout, "Current sanitize timeout is %d seconds\n",
+ use_timeout / 1000);
+ }
+
+ /*
+ * If the user hasn't disabled questions and didn't specify a
+ * timeout on the command line, ask them if they want the current
+ * timeout.
+ */
+ if ((ycount == 0)
+ && (timeout == 0)) {
+ char str[1024];
+ int new_timeout = 0;
+
+ fprintf(stdout, "Enter new timeout in seconds or press\n"
+ "return to keep the current timeout [%d] ",
+ use_timeout / 1000);
+
+ if (fgets(str, sizeof(str), stdin) != NULL) {
+ if (str[0] != '\0')
+ new_timeout = atoi(str);
+ }
+
+ if (new_timeout != 0) {
+ use_timeout = new_timeout * 1000;
+ fprintf(stdout, "Using new timeout value %d\n",
+ use_timeout / 1000);
+ }
+ }
+
+ byte2 = action;
+ if (ause != 0)
+ byte2 |= SSZ_UNRESTRICTED_EXIT;
+ if (immediate != 0)
+ byte2 |= SSZ_IMMED;
+
+ scsi_sanitize(&ccb->csio,
+ /* retries */ retry_count,
+ /* cbfcnp */ NULL,
+ /* tag_action */ task_attr,
+ /* byte2 */ byte2,
+ /* control */ 0,
+ /* data_ptr */ data_ptr,
+ /* dxfer_len */ dxfer_len,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ use_timeout);
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ if (arglist & CAM_ARG_ERR_RECOVER)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ warn("error sending sanitize command");
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ struct scsi_sense_data *sense;
+ int error_code, sense_key, asc, ascq;
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
+ CAM_SCSI_STATUS_ERROR) {
+ sense = &ccb->csio.sense_data;
+ scsi_extract_sense_len(sense, ccb->csio.sense_len -
+ ccb->csio.sense_resid, &error_code, &sense_key,
+ &asc, &ascq, /*show_errors*/ 1);
+
+ if (sense_key == SSD_KEY_ILLEGAL_REQUEST &&
+ asc == 0x20 && ascq == 0x00)
+ warnx("sanitize is not supported by "
+ "this device");
+ else
+ warnx("error sanitizing this device");
+ } else
+ warnx("error sanitizing this device");
+
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+
+ /*
+ * If we ran in non-immediate mode, we already checked for errors
+ * above and printed out any necessary information. If we're in
+ * immediate mode, we need to loop through and get status
+ * information periodically.
+ */
+ if (immediate == 0) {
+ if (quiet == 0) {
+ fprintf(stdout, "Sanitize Complete\n");
+ }
+ goto scsisanitize_bailout;
+ }
+
+doreport:
+ do {
+ cam_status status;
+
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
+
+ /*
+ * There's really no need to do error recovery or
+ * retries here, since we're just going to sit in a
+ * loop and wait for the device to finish sanitizing.
+ */
+ scsi_test_unit_ready(&ccb->csio,
+ /* retries */ 0,
+ /* cbfcnp */ NULL,
+ /* tag_action */ task_attr,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ 5000);
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ retval = cam_send_ccb(device, ccb);
+
+ /*
+ * If we get an error from the ioctl, bail out. SCSI
+ * errors are expected.
+ */
+ if (retval < 0) {
+ warn("error sending CAMIOCOMMAND ioctl");
+ if (arglist & CAM_ARG_VERBOSE) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+
+ status = ccb->ccb_h.status & CAM_STATUS_MASK;
+
+ if ((status != CAM_REQ_CMP)
+ && (status == CAM_SCSI_STATUS_ERROR)
+ && ((ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0)) {
+ struct scsi_sense_data *sense;
+ int error_code, sense_key, asc, ascq;
+
+ sense = &ccb->csio.sense_data;
+ scsi_extract_sense_len(sense, ccb->csio.sense_len -
+ ccb->csio.sense_resid, &error_code, &sense_key,
+ &asc, &ascq, /*show_errors*/ 1);
+
+ /*
+ * According to the SCSI-3 spec, a drive that is in the
+ * middle of a sanitize should return NOT READY with an
+ * ASC of "logical unit not ready, sanitize in
+ * progress". The sense key specific bytes will then
+ * be a progress indicator.
+ */
+ if ((sense_key == SSD_KEY_NOT_READY)
+ && (asc == 0x04) && (ascq == 0x1b)) {
+ uint8_t sks[3];
+
+ if ((scsi_get_sks(sense, ccb->csio.sense_len -
+ ccb->csio.sense_resid, sks) == 0)
+ && (quiet == 0)) {
+ int val;
+ u_int64_t percentage;
+
+ val = scsi_2btoul(&sks[1]);
+ percentage = 10000 * val;
+
+ fprintf(stdout,
+ "\rSanitizing: %ju.%02u %% "
+ "(%d/%d) done",
+ (uintmax_t)(percentage /
+ (0x10000 * 100)),
+ (unsigned)((percentage /
+ 0x10000) % 100),
+ val, 0x10000);
+ fflush(stdout);
+ } else if ((quiet == 0)
+ && (++num_warnings <= 1)) {
+ warnx("Unexpected SCSI Sense Key "
+ "Specific value returned "
+ "during sanitize:");
+ scsi_sense_print(device, &ccb->csio,
+ stderr);
+ warnx("Unable to print status "
+ "information, but sanitze will "
+ "proceed.");
+ warnx("will exit when sanitize is "
+ "complete");
+ }
+ sleep(1);
+ } else {
+ warnx("Unexpected SCSI error during sanitize");
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+
+ } else if (status != CAM_REQ_CMP) {
+ warnx("Unexpected CAM status %#x", status);
+ if (arglist & CAM_ARG_VERBOSE)
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ error = 1;
+ goto scsisanitize_bailout;
+ }
+ } while((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP);
+
+ if (quiet == 0)
+ fprintf(stdout, "\nSanitize Complete\n");
+
+scsisanitize_bailout:
+ if (fd >= 0)
+ close(fd);
+ if (data_ptr != NULL)
+ free(data_ptr);
+ cam_freeccb(ccb);
+
+ return(error);
+}
+
+static int
scsireportluns(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout)
+ char *combinedopt, int task_attr, int retry_count, int timeout)
{
union ccb *ccb;
int c, countonly, lunsonly;
@@ -5558,8 +6535,7 @@
return (1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
countonly = 0;
lunsonly = 0;
@@ -5616,7 +6592,7 @@
scsi_report_luns(&ccb->csio,
/*retries*/ retry_count,
/*cbfcnp*/ NULL,
- /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*tag_action*/ task_attr,
/*select_report*/ report_type,
/*rpl_buf*/ lundata,
/*alloc_len*/ alloc_len,
@@ -5781,7 +6757,7 @@
static int
scsireadcapacity(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout)
+ char *combinedopt, int task_attr, int retry_count, int timeout)
{
union ccb *ccb;
int blocksizeonly, humanize, numblocks, quiet, sizeonly, baseten;
@@ -5807,8 +6783,7 @@
return (1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
@@ -5868,7 +6843,7 @@
scsi_read_capacity(&ccb->csio,
/*retries*/ retry_count,
/*cbfcnp*/ NULL,
- /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*tag_action*/ task_attr,
&rcap,
SSD_FULL_SIZE,
/*timeout*/ timeout ? timeout : 5000);
@@ -5910,11 +6885,12 @@
scsi_read_capacity_16(&ccb->csio,
/*retries*/ retry_count,
/*cbfcnp*/ NULL,
- /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*tag_action*/ task_attr,
/*lba*/ 0,
/*reladdr*/ 0,
/*pmi*/ 0,
- &rcaplong,
+ /*rcap_buf*/ (uint8_t *)&rcaplong,
+ /*rcap_buf_len*/ sizeof(rcaplong),
/*sense_len*/ SSD_FULL_SIZE,
/*timeout*/ timeout ? timeout : 5000);
@@ -6011,8 +6987,7 @@
return (1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(union ccb) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->smpio);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
@@ -6205,8 +7180,7 @@
return (1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(union ccb) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->smpio);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
@@ -6268,8 +7242,7 @@
if ((response->long_response & SMP_RG_LONG_RESPONSE)
&& (long_response == 0)) {
ccb->ccb_h.status = CAM_REQ_INPROG;
- bzero(&(&ccb->ccb_h)[1],
- sizeof(union ccb) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->smpio);
long_response = 1;
goto try_long;
}
@@ -6351,8 +7324,7 @@
return (1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(union ccb) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->smpio);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
@@ -6528,7 +7500,7 @@
response = malloc(sizeof(*response));
if (response == NULL) {
warn("%s: unable to allocate %zd bytes", __func__,
- sizeof(*request));
+ sizeof(*response));
retval = 1;
goto bailout;
}
@@ -6609,8 +7581,7 @@
return (1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(union ccb) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->smpio);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
@@ -6704,8 +7675,7 @@
goto bailout;
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(union ccb) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cdai);
/*
* On the first try, we just probe for the size of the data, and
@@ -6714,7 +7684,7 @@
retry:
ccb->ccb_h.func_code = XPT_DEV_ADVINFO;
ccb->ccb_h.flags = CAM_DIR_IN;
- ccb->cdai.flags = 0;
+ ccb->cdai.flags = CDAI_FLAG_NONE;
ccb->cdai.buftype = CDAI_TYPE_SCSI_DEVID;
ccb->cdai.bufsiz = item->device_id_len;
if (item->device_id_len != 0)
@@ -6946,18 +7916,18 @@
struct cam_devitem *item;
STAILQ_FOREACH(item, &devlist->dev_queue, links) {
- uint8_t *item_addr;
+ struct scsi_vpd_id_descriptor *idd;
/*
* XXX KDM look for LUN IDs as well?
*/
- item_addr = scsi_get_devid(item->device_id,
+ idd = scsi_get_devid(item->device_id,
item->device_id_len,
scsi_devid_is_sas_target);
- if (item_addr == NULL)
+ if (idd == NULL)
continue;
- if (scsi_8btou64(item_addr) == sasaddr)
+ if (scsi_8btou64(idd->identifier) == sasaddr)
return (item);
}
@@ -6990,8 +7960,7 @@
return (1);
}
- bzero(&(&ccb->ccb_h)[1],
- sizeof(union ccb) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->smpio);
STAILQ_INIT(&devlist.dev_queue);
rgrequest = malloc(sizeof(*rgrequest));
@@ -7095,8 +8064,7 @@
char tmpstr[256];
int j;
- bzero(&(&ccb->ccb_h)[1],
- sizeof(union ccb) - sizeof(struct ccb_hdr));
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->smpio);
ccb->ccb_h.status = CAM_REQ_INPROG;
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
@@ -7274,45 +8242,580 @@
else
sc = 253;
- cam_fill_ataio(&ccb->ataio,
- retry_count,
- NULL,
- /*flags*/CAM_DIR_NONE,
- MSG_SIMPLE_Q_TAG,
- /*data_ptr*/NULL,
- /*dxfer_len*/0,
- timeout ? timeout : 30 * 1000);
- ata_28bit_cmd(&ccb->ataio, cmd, 0, 0, sc);
+ retval = ata_do_28bit_cmd(device,
+ ccb,
+ /*retries*/retry_count,
+ /*flags*/CAM_DIR_NONE,
+ /*protocol*/AP_PROTO_NON_DATA,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*command*/cmd,
+ /*features*/0,
+ /*lba*/0,
+ /*sector_count*/sc,
+ /*data_ptr*/NULL,
+ /*dxfer_len*/0,
+ /*timeout*/timeout ? timeout : 30 * 1000,
+ /*quiet*/1);
- /* Disable freezing the device queue */
+ cam_freeccb(ccb);
+ return (retval);
+}
+
+static int
+ataaxm(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int retry_count, int timeout)
+{
+ union ccb *ccb;
+ int retval = 0;
+ int l = -1;
+ int c;
+ u_char cmd, sc;
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("%s: error allocating ccb", __func__);
+ return (1);
+ }
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch (c) {
+ case 'l':
+ l = atoi(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+ sc = 0;
+ if (strcmp(argv[1], "apm") == 0) {
+ if (l == -1)
+ cmd = 0x85;
+ else {
+ cmd = 0x05;
+ sc = l;
+ }
+ } else /* aam */ {
+ if (l == -1)
+ cmd = 0xC2;
+ else {
+ cmd = 0x42;
+ sc = l;
+ }
+ }
+
+ retval = ata_do_28bit_cmd(device,
+ ccb,
+ /*retries*/retry_count,
+ /*flags*/CAM_DIR_NONE,
+ /*protocol*/AP_PROTO_NON_DATA,
+ /*tag_action*/MSG_SIMPLE_Q_TAG,
+ /*command*/ATA_SETFEATURES,
+ /*features*/cmd,
+ /*lba*/0,
+ /*sector_count*/sc,
+ /*data_ptr*/NULL,
+ /*dxfer_len*/0,
+ /*timeout*/timeout ? timeout : 30 * 1000,
+ /*quiet*/1);
+
+ cam_freeccb(ccb);
+ return (retval);
+}
+
+int
+scsigetopcodes(struct cam_device *device, int opcode_set, int opcode,
+ int show_sa_errors, int sa_set, int service_action,
+ int timeout_desc, int task_attr, int retry_count, int timeout,
+ int verbosemode, uint32_t *fill_len, uint8_t **data_ptr)
+{
+ union ccb *ccb = NULL;
+ uint8_t *buf = NULL;
+ uint32_t alloc_len = 0, num_opcodes;
+ uint32_t valid_len = 0;
+ uint32_t avail_len = 0;
+ struct scsi_report_supported_opcodes_all *all_hdr;
+ struct scsi_report_supported_opcodes_one *one;
+ int options = 0;
+ int retval = 0;
+
+ /*
+ * Make it clear that we haven't yet allocated or filled anything.
+ */
+ *fill_len = 0;
+ *data_ptr = NULL;
+
+ ccb = cam_getccb(device);
+ if (ccb == NULL) {
+ warnx("couldn't allocate CCB");
+ retval = 1;
+ goto bailout;
+ }
+
+ /* cam_getccb cleans up the header, caller has to zero the payload */
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
+
+ if (opcode_set != 0) {
+ options |= RSO_OPTIONS_OC;
+ num_opcodes = 1;
+ alloc_len = sizeof(*one) + CAM_MAX_CDBLEN;
+ } else {
+ num_opcodes = 256;
+ alloc_len = sizeof(*all_hdr) + (num_opcodes *
+ sizeof(struct scsi_report_supported_opcodes_descr));
+ }
+
+ if (timeout_desc != 0) {
+ options |= RSO_RCTD;
+ alloc_len += num_opcodes *
+ sizeof(struct scsi_report_supported_opcodes_timeout);
+ }
+
+ if (sa_set != 0) {
+ options |= RSO_OPTIONS_OC_SA;
+ if (show_sa_errors != 0)
+ options &= ~RSO_OPTIONS_OC;
+ }
+
+retry_alloc:
+ if (buf != NULL) {
+ free(buf);
+ buf = NULL;
+ }
+
+ buf = malloc(alloc_len);
+ if (buf == NULL) {
+ warn("Unable to allocate %u bytes", alloc_len);
+ retval = 1;
+ goto bailout;
+ }
+ bzero(buf, alloc_len);
+
+ scsi_report_supported_opcodes(&ccb->csio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*tag_action*/ task_attr,
+ /*options*/ options,
+ /*req_opcode*/ opcode,
+ /*req_service_action*/ service_action,
+ /*data_ptr*/ buf,
+ /*dxfer_len*/ alloc_len,
+ /*sense_len*/ SSD_FULL_SIZE,
+ /*timeout*/ timeout ? timeout : 10000);
+
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
- if (arglist & CAM_ARG_ERR_RECOVER)
+ if (retry_count != 0)
ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
if (cam_send_ccb(device, ccb) < 0) {
- warn("error sending command");
+ perror("error sending REPORT SUPPORTED OPERATION CODES");
+ retval = 1;
+ goto bailout;
+ }
- if (arglist & CAM_ARG_VERBOSE)
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (verbosemode != 0)
cam_error_print(device, ccb, CAM_ESF_ALL,
CAM_EPF_ALL, stderr);
+
+ retval = 1;
+ goto bailout;
+ }
+ valid_len = ccb->csio.dxfer_len - ccb->csio.resid;
+
+ if (((options & RSO_OPTIONS_MASK) == RSO_OPTIONS_ALL)
+ && (valid_len >= sizeof(*all_hdr))) {
+ all_hdr = (struct scsi_report_supported_opcodes_all *)buf;
+ avail_len = scsi_4btoul(all_hdr->length) + sizeof(*all_hdr);
+ } else if (((options & RSO_OPTIONS_MASK) != RSO_OPTIONS_ALL)
+ && (valid_len >= sizeof(*one))) {
+ uint32_t cdb_length;
+
+ one = (struct scsi_report_supported_opcodes_one *)buf;
+ cdb_length = scsi_2btoul(one->cdb_length);
+ avail_len = sizeof(*one) + cdb_length;
+ if (one->support & RSO_ONE_CTDP) {
+ struct scsi_report_supported_opcodes_timeout *td;
+
+ td = (struct scsi_report_supported_opcodes_timeout *)
+ &buf[avail_len];
+ if (valid_len >= (avail_len + sizeof(td->length))) {
+ avail_len += scsi_2btoul(td->length) +
+ sizeof(td->length);
+ } else {
+ avail_len += sizeof(*td);
+ }
+ }
+ }
+
+ /*
+ * avail_len could be zero if we didn't get enough data back from
+ * thet target to determine
+ */
+ if ((avail_len != 0)
+ && (avail_len > valid_len)) {
+ alloc_len = avail_len;
+ goto retry_alloc;
+ }
+
+ *fill_len = valid_len;
+ *data_ptr = buf;
+bailout:
+ if (retval != 0)
+ free(buf);
+
+ cam_freeccb(ccb);
+
+ return (retval);
+}
+
+static int
+scsiprintoneopcode(struct cam_device *device, int req_opcode, int sa_set,
+ int req_sa, uint8_t *buf, uint32_t valid_len)
+{
+ struct scsi_report_supported_opcodes_one *one;
+ struct scsi_report_supported_opcodes_timeout *td;
+ uint32_t cdb_len = 0, td_len = 0;
+ const char *op_desc = NULL;
+ unsigned int i;
+ int retval = 0;
+
+ one = (struct scsi_report_supported_opcodes_one *)buf;
+
+ /*
+ * If we don't have the full single opcode descriptor, no point in
+ * continuing.
+ */
+ if (valid_len < __offsetof(struct scsi_report_supported_opcodes_one,
+ cdb_length)) {
+ warnx("Only %u bytes returned, not enough to verify support",
+ valid_len);
retval = 1;
goto bailout;
}
+ op_desc = scsi_op_desc(req_opcode, &device->inq_data);
+
+ printf("%s (0x%02x)", op_desc != NULL ? op_desc : "UNKNOWN",
+ req_opcode);
+ if (sa_set != 0)
+ printf(", SA 0x%x", req_sa);
+ printf(": ");
+
+ switch (one->support & RSO_ONE_SUP_MASK) {
+ case RSO_ONE_SUP_UNAVAIL:
+ printf("No command support information currently available\n");
+ break;
+ case RSO_ONE_SUP_NOT_SUP:
+ printf("Command not supported\n");
+ retval = 1;
+ goto bailout;
+ break; /*NOTREACHED*/
+ case RSO_ONE_SUP_AVAIL:
+ printf("Command is supported, complies with a SCSI standard\n");
+ break;
+ case RSO_ONE_SUP_VENDOR:
+ printf("Command is supported, vendor-specific "
+ "implementation\n");
+ break;
+ default:
+ printf("Unknown command support flags 0x%#x\n",
+ one->support & RSO_ONE_SUP_MASK);
+ break;
+ }
+
+ /*
+ * If we don't have the CDB length, it isn't exactly an error, the
+ * command probably isn't supported.
+ */
+ if (valid_len < __offsetof(struct scsi_report_supported_opcodes_one,
+ cdb_usage))
+ goto bailout;
+
+ cdb_len = scsi_2btoul(one->cdb_length);
+
+ /*
+ * If our valid data doesn't include the full reported length,
+ * return. The caller should have detected this and adjusted his
+ * allocation length to get all of the available data.
+ */
+ if (valid_len < sizeof(*one) + cdb_len) {
+ retval = 1;
+ goto bailout;
+ }
+
+ /*
+ * If all we have is the opcode, there is no point in printing out
+ * the usage bitmap.
+ */
+ if (cdb_len <= 1) {
+ retval = 1;
+ goto bailout;
+ }
+
+ printf("CDB usage bitmap:");
+ for (i = 0; i < cdb_len; i++) {
+ printf(" %02x", one->cdb_usage[i]);
+ }
+ printf("\n");
+
+ /*
+ * If we don't have a timeout descriptor, we're done.
+ */
+ if ((one->support & RSO_ONE_CTDP) == 0)
+ goto bailout;
+
+ /*
+ * If we don't have enough valid length to include the timeout
+ * descriptor length, we're done.
+ */
+ if (valid_len < (sizeof(*one) + cdb_len + sizeof(td->length)))
+ goto bailout;
+
+ td = (struct scsi_report_supported_opcodes_timeout *)
+ &buf[sizeof(*one) + cdb_len];
+ td_len = scsi_2btoul(td->length);
+ td_len += sizeof(td->length);
+
+ /*
+ * If we don't have the full timeout descriptor, we're done.
+ */
+ if (td_len < sizeof(*td))
+ goto bailout;
+
+ /*
+ * If we don't have enough valid length to contain the full timeout
+ * descriptor, we're done.
+ */
+ if (valid_len < (sizeof(*one) + cdb_len + td_len))
+ goto bailout;
+
+ printf("Timeout information:\n");
+ printf("Command-specific: 0x%02x\n", td->cmd_specific);
+ printf("Nominal timeout: %u seconds\n",
+ scsi_4btoul(td->nominal_time));
+ printf("Recommended timeout: %u seconds\n",
+ scsi_4btoul(td->recommended_time));
+
+bailout:
+ return (retval);
+}
+
+static int
+scsiprintopcodes(struct cam_device *device, int td_req, uint8_t *buf,
+ uint32_t valid_len)
+{
+ struct scsi_report_supported_opcodes_all *hdr;
+ struct scsi_report_supported_opcodes_descr *desc;
+ uint32_t avail_len = 0, used_len = 0;
+ uint8_t *cur_ptr;
+ int retval = 0;
+
+ if (valid_len < sizeof(*hdr)) {
+ warnx("%s: not enough returned data (%u bytes) opcode list",
+ __func__, valid_len);
+ retval = 1;
+ goto bailout;
+ }
+ hdr = (struct scsi_report_supported_opcodes_all *)buf;
+ avail_len = scsi_4btoul(hdr->length);
+ avail_len += sizeof(hdr->length);
+ /*
+ * Take the lesser of the amount of data the drive claims is
+ * available, and the amount of data the HBA says was returned.
+ */
+ avail_len = MIN(avail_len, valid_len);
+
+ used_len = sizeof(hdr->length);
+
+ printf("%-6s %4s %8s ",
+ "Opcode", "SA", "CDB len" );
+
+ if (td_req != 0)
+ printf("%5s %6s %6s ", "CS", "Nom", "Rec");
+ printf(" Description\n");
+
+ while ((avail_len - used_len) > sizeof(*desc)) {
+ struct scsi_report_supported_opcodes_timeout *td;
+ uint32_t td_len;
+ const char *op_desc = NULL;
+
+ cur_ptr = &buf[used_len];
+ desc = (struct scsi_report_supported_opcodes_descr *)cur_ptr;
+
+ op_desc = scsi_op_desc(desc->opcode, &device->inq_data);
+ if (op_desc == NULL)
+ op_desc = "UNKNOWN";
+
+ printf("0x%02x %#4x %8u ", desc->opcode,
+ scsi_2btoul(desc->service_action),
+ scsi_2btoul(desc->cdb_length));
+
+ used_len += sizeof(*desc);
+
+ if ((desc->flags & RSO_CTDP) == 0) {
+ printf(" %s\n", op_desc);
+ continue;
+ }
+
+ /*
+ * If we don't have enough space to fit a timeout
+ * descriptor, then we're done.
+ */
+ if (avail_len - used_len < sizeof(*td)) {
+ used_len = avail_len;
+ printf(" %s\n", op_desc);
+ continue;
+ }
+ cur_ptr = &buf[used_len];
+ td = (struct scsi_report_supported_opcodes_timeout *)cur_ptr;
+ td_len = scsi_2btoul(td->length);
+ td_len += sizeof(td->length);
+
+ used_len += td_len;
+ /*
+ * If the given timeout descriptor length is less than what
+ * we understand, skip it.
+ */
+ if (td_len < sizeof(*td)) {
+ printf(" %s\n", op_desc);
+ continue;
+ }
+
+ printf(" 0x%02x %6u %6u %s\n", td->cmd_specific,
+ scsi_4btoul(td->nominal_time),
+ scsi_4btoul(td->recommended_time), op_desc);
+ }
+bailout:
+ return (retval);
+}
+
+static int
+scsiopcodes(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int task_attr, int retry_count, int timeout,
+ int verbosemode)
+{
+ int c;
+ uint32_t opcode = 0, service_action = 0;
+ int td_set = 0, opcode_set = 0, sa_set = 0;
+ int show_sa_errors = 1;
+ uint32_t valid_len = 0;
+ uint8_t *buf = NULL;
+ char *endptr;
+ int retval = 0;
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch (c) {
+ case 'N':
+ show_sa_errors = 0;
+ break;
+ case 'o':
+ opcode = strtoul(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ warnx("Invalid opcode \"%s\", must be a number",
+ optarg);
+ retval = 1;
+ goto bailout;
+ }
+ if (opcode > 0xff) {
+ warnx("Invalid opcode 0x%#x, must be between"
+ "0 and 0xff inclusive", opcode);
+ retval = 1;
+ goto bailout;
+ }
+ opcode_set = 1;
+ break;
+ case 's':
+ service_action = strtoul(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ warnx("Invalid service action \"%s\", must "
+ "be a number", optarg);
+ retval = 1;
+ goto bailout;
+ }
+ if (service_action > 0xffff) {
+ warnx("Invalid service action 0x%#x, must "
+ "be between 0 and 0xffff inclusive",
+ service_action);
+ retval = 1;
+ }
+ sa_set = 1;
+ break;
+ case 'T':
+ td_set = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((sa_set != 0)
+ && (opcode_set == 0)) {
+ warnx("You must specify an opcode with -o if a service "
+ "action is given");
+ retval = 1;
+ goto bailout;
+ }
+ retval = scsigetopcodes(device, opcode_set, opcode, show_sa_errors,
+ sa_set, service_action, td_set, task_attr,
+ retry_count, timeout, verbosemode, &valid_len,
+ &buf);
+ if (retval != 0)
+ goto bailout;
+
+ if ((opcode_set != 0)
+ || (sa_set != 0)) {
+ retval = scsiprintoneopcode(device, opcode, sa_set,
+ service_action, buf, valid_len);
+ } else {
+ retval = scsiprintopcodes(device, td_set, buf, valid_len);
+ }
+
+bailout:
+ free(buf);
+
+ return (retval);
+}
+
+#endif /* MINIMALISTIC */
+
+static int
+scsireprobe(struct cam_device *device)
+{
+ union ccb *ccb;
+ int retval = 0;
+
+ ccb = cam_getccb(device);
+
+ if (ccb == NULL) {
+ warnx("%s: error allocating ccb", __func__);
+ return (1);
+ }
+
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
+
+ ccb->ccb_h.func_code = XPT_REPROBE_LUN;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ warn("error sending XPT_REPROBE_LUN CCB");
+ retval = 1;
+ 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);
retval = 1;
goto bailout;
}
+
bailout:
cam_freeccb(ccb);
+
return (retval);
}
-#endif /* MINIMALISTIC */
-
void
usage(int printlong)
{
@@ -7319,7 +8822,7 @@
fprintf(printlong ? stdout : stderr,
"usage: camcontrol <command> [device id][generic args][command args]\n"
-" camcontrol devlist [-v]\n"
+" camcontrol devlist [-b] [-v]\n"
#ifndef MINIMALISTIC
" camcontrol periphlist [dev_id][-n dev_name] [-u unit]\n"
" camcontrol tur [dev_id][generic args]\n"
@@ -7332,11 +8835,13 @@
" camcontrol stop [dev_id][generic args]\n"
" camcontrol load [dev_id][generic args]\n"
" camcontrol eject [dev_id][generic args]\n"
+" camcontrol reprobe [dev_id][generic args]\n"
#endif /* MINIMALISTIC */
-" camcontrol rescan <all | bus[:target:lun]>\n"
-" camcontrol reset <all | bus[:target:lun]>\n"
+" camcontrol rescan <all | bus[:target:lun] | dev_id>\n"
+" camcontrol reset <all | bus[:target:lun] | dev_id>\n"
#ifndef MINIMALISTIC
" camcontrol defects [dev_id][generic args] <-f format> [-P][-G]\n"
+" [-q][-s][-S offset][-X]\n"
" camcontrol modepage [dev_id][generic args] <-m page | -l>\n"
" [-P pagectl][-e | -b][-d]\n"
" camcontrol cmd [dev_id][generic args]\n"
@@ -7360,10 +8865,17 @@
" [-q][-R syncrate][-v][-T <enable|disable>]\n"
" [-U][-W bus_width]\n"
" camcontrol format [dev_id][generic args][-q][-r][-w][-y]\n"
+" camcontrol sanitize [dev_id][generic args]\n"
+" [-a overwrite|block|crypto|exitfailure]\n"
+" [-c passes][-I][-P pattern][-q][-U][-r][-w]\n"
+" [-y]\n"
" camcontrol idle [dev_id][generic args][-t time]\n"
" camcontrol standby [dev_id][generic args][-t time]\n"
" camcontrol sleep [dev_id][generic args]\n"
-" camcontrol fwdownload [dev_id][generic args] <-f fw_image> [-y][-s]\n"
+" camcontrol apm [dev_id][generic args][-l level]\n"
+" camcontrol aam [dev_id][generic args][-l level]\n"
+" camcontrol fwdownload [dev_id][generic args] <-f fw_image> [-q]\n"
+" [-s][-y]\n"
" camcontrol security [dev_id][generic args]\n"
" <-d pwd | -e pwd | -f | -h pwd | -k pwd>\n"
" [-l <high|maximum>] [-q] [-s pwd] [-T timeout]\n"
@@ -7370,6 +8882,14 @@
" [-U <user|master>] [-y]\n"
" camcontrol hpa [dev_id][generic args] [-f] [-l] [-P] [-p pwd]\n"
" [-q] [-s max_sectors] [-U pwd] [-y]\n"
+" camcontrol persist [dev_id][generic args] <-i action|-o action>\n"
+" [-a][-I tid][-k key][-K sa_key][-p][-R rtp]\n"
+" [-s scope][-S][-T type][-U]\n"
+" camcontrol attrib [dev_id][generic args] <-r action|-w attr>\n"
+" [-a attr_num][-c][-e elem][-F form1,form1]\n"
+" [-p part][-s start][-T type][-V vol]\n"
+" camcontrol opcodes [dev_id][generic args][-o opcode][-s SA]\n"
+" [-N][-T]\n"
#endif /* MINIMALISTIC */
" camcontrol help\n");
if (!printlong)
@@ -7388,8 +8908,9 @@
"stop send a Stop Unit command to the device\n"
"load send a Start Unit command to the device with the load bit set\n"
"eject send a Stop Unit command to the device with the eject bit set\n"
-"rescan rescan all busses, the given bus, or bus:target:lun\n"
-"reset reset all busses, the given bus, or bus:target:lun\n"
+"reprobe update capacity information of the given device\n"
+"rescan rescan all buses, the given bus, bus:target:lun or device\n"
+"reset reset all buses, the given bus, bus:target:lun or device\n"
"defects read the defect list of the specified device\n"
"modepage display or edit (-e) the given mode page\n"
"cmd send the given SCSI command, may need -i or -o as well\n"
@@ -7402,11 +8923,15 @@
"tags report or set the number of transaction slots for a device\n"
"negotiate report or set device negotiation parameters\n"
"format send the SCSI FORMAT UNIT command to the named device\n"
+"sanitize send the SCSI SANITIZE command to the named device\n"
"idle send the ATA IDLE command to the named device\n"
"standby send the ATA STANDBY command to the named device\n"
"sleep send the ATA SLEEP command to the named device\n"
-"fwdownload program firmware of the named device with the given image"
+"fwdownload program firmware of the named device with the given image\n"
"security report or send ATA security commands to the named device\n"
+"persist send the SCSI PERSISTENT RESERVE IN or OUT commands\n"
+"attrib send the SCSI READ or WRITE ATTRIBUTE commands\n"
+"opcodes send the SCSI REPORT SUPPORTED OPCODES command\n"
"help this message\n"
"Device Identifiers:\n"
"bus:target specify the bus and target, lun defaults to 0\n"
@@ -7419,6 +8944,7 @@
"-u unit specify unit number, e.g. \"0\", \"5\"\n"
"-E have the kernel attempt to perform SCSI error recovery\n"
"-C count specify the SCSI command retry count (needs -E to work)\n"
+"-Q task_attr specify ordered, simple or head tag type for SCSI cmds\n"
"modepage arguments:\n"
"-l list all available mode pages\n"
"-m page specify the mode page to view or edit\n"
@@ -7497,13 +9023,24 @@
"-r run in report only mode\n"
"-w don't send immediate format command\n"
"-y don't ask any questions\n"
+"sanitize arguments:\n"
+"-a operation operation mode: overwrite, block, crypto or exitfailure\n"
+"-c passes overwrite passes to perform (1 to 31)\n"
+"-I invert overwrite pattern after each pass\n"
+"-P pattern path to overwrite pattern file\n"
+"-q be quiet, don't print status messages\n"
+"-r run in report only mode\n"
+"-U run operation in unrestricted completion exit mode\n"
+"-w don't send immediate sanitize command\n"
+"-y don't ask any questions\n"
"idle/standby arguments:\n"
"-t <arg> number of seconds before respective state.\n"
"fwdownload arguments:\n"
"-f fw_image path to firmware image file\n"
-"-y don't ask any questions\n"
+"-q don't print informational messages, only errors\n"
"-s run in simulation mode\n"
"-v print info for every firmware segment sent to device\n"
+"-y don't ask any questions\n"
"security arguments:\n"
"-d pwd disable security using the given password for the selected\n"
" user\n"
@@ -7531,6 +9068,41 @@
" device\n"
"-U pwd unlock the HPA configuration of the device\n"
"-y don't ask any questions\n"
+"persist arguments:\n"
+"-i action specify read_keys, read_reservation, report_cap, or\n"
+" read_full_status\n"
+"-o action specify register, register_ignore, reserve, release,\n"
+" clear, preempt, preempt_abort, register_move, replace_lost\n"
+"-a set the All Target Ports (ALL_TG_PT) bit\n"
+"-I tid specify a Transport ID, e.g.: sas,0x1234567812345678\n"
+"-k key specify the Reservation Key\n"
+"-K sa_key specify the Service Action Reservation Key\n"
+"-p set the Activate Persist Through Power Loss bit\n"
+"-R rtp specify the Relative Target Port\n"
+"-s scope specify the scope: lun, extent, element or a number\n"
+"-S specify Transport ID for register, requires -I\n"
+"-T res_type specify the reservation type: read_shared, wr_ex, rd_ex,\n"
+" ex_ac, wr_ex_ro, ex_ac_ro, wr_ex_ar, ex_ac_ar\n"
+"-U unregister the current initiator for register_move\n"
+"attrib arguments:\n"
+"-r action specify attr_values, attr_list, lv_list, part_list, or\n"
+" supp_attr\n"
+"-w attr specify an attribute to write, one -w argument per attr\n"
+"-a attr_num only display this attribute number\n"
+"-c get cached attributes\n"
+"-e elem_addr request attributes for the given element in a changer\n"
+"-F form1,form2 output format, comma separated list: text_esc, text_raw,\n"
+" nonascii_esc, nonascii_trim, nonascii_raw, field_all,\n"
+" field_none, field_desc, field_num, field_size, field_rw\n"
+"-p partition request attributes for the given partition\n"
+"-s start_attr request attributes starting at the given number\n"
+"-T elem_type specify the element type (used with -e)\n"
+"-V logical_vol specify the logical volume ID\n"
+"opcodes arguments:\n"
+"-o opcode specify the individual opcode to list\n"
+"-s service_action specify the service action for the opcode\n"
+"-N do not return SCSI error for unsupported SA\n"
+"-T request nominal and recommended timeout values\n"
);
#endif /* MINIMALISTIC */
}
@@ -7545,13 +9117,16 @@
int timeout = 0, retry_count = 1;
camcontrol_optret optreturn;
char *tstr;
- const char *mainopt = "C:En:t:u:v";
+ const char *mainopt = "C:En:Q:t:u:v";
const char *subopt = NULL;
char combinedopt[256];
int error = 0, optstart = 2;
+ int task_attr = MSG_SIMPLE_Q_TAG;
int devopen = 1;
#ifndef MINIMALISTIC
- int bus, target, lun;
+ path_id_t bus;
+ target_id_t target;
+ lun_id_t lun;
#endif /* MINIMALISTIC */
cmdlist = CAM_CMD_NONE;
@@ -7700,6 +9275,40 @@
tstr++;
device = (char *)strdup(tstr);
break;
+ case 'Q': {
+ char *endptr;
+ int table_entry = 0;
+
+ tstr = optarg;
+ while (isspace(*tstr) && (*tstr != '\0'))
+ tstr++;
+ if (isdigit(*tstr)) {
+ task_attr = strtol(tstr, &endptr, 0);
+ if (*endptr != '\0') {
+ errx(1, "Invalid queue option "
+ "%s", tstr);
+ }
+ } else {
+ size_t table_size;
+ scsi_nv_status status;
+
+ table_size = sizeof(task_attrs) /
+ sizeof(task_attrs[0]);
+ status = scsi_get_nv(task_attrs,
+ table_size, tstr, &table_entry,
+ SCSI_NV_FLAG_IG_CASE);
+ if (status == SCSI_NV_FOUND)
+ task_attr = task_attrs[
+ table_entry].value;
+ else {
+ errx(1, "%s option %s",
+ (status == SCSI_NV_AMBIGUOUS)?
+ "ambiguous" : "invalid",
+ tstr);
+ }
+ }
+ break;
+ }
case 't':
timeout = strtol(optarg, NULL, 0);
if (timeout < 0)
@@ -7761,15 +9370,16 @@
break;
#endif /* MINIMALISTIC */
case CAM_CMD_DEVTREE:
- error = getdevtree();
+ error = getdevtree(argc, argv, combinedopt);
break;
#ifndef MINIMALISTIC
case CAM_CMD_TUR:
- error = testunitready(cam_dev, retry_count, timeout, 0);
+ error = testunitready(cam_dev, task_attr, retry_count,
+ timeout, 0);
break;
case CAM_CMD_INQUIRY:
error = scsidoinquiry(cam_dev, argc, argv, combinedopt,
- retry_count, timeout);
+ task_attr, retry_count, timeout);
break;
case CAM_CMD_IDENTIFY:
error = ataidentify(cam_dev, retry_count, timeout);
@@ -7776,8 +9386,8 @@
break;
case CAM_CMD_STARTSTOP:
error = scsistart(cam_dev, arglist & CAM_ARG_START_UNIT,
- arglist & CAM_ARG_EJECT, retry_count,
- timeout);
+ arglist & CAM_ARG_EJECT, task_attr,
+ retry_count, timeout);
break;
#endif /* MINIMALISTIC */
case CAM_CMD_RESCAN:
@@ -7789,15 +9399,15 @@
#ifndef MINIMALISTIC
case CAM_CMD_READ_DEFECTS:
error = readdefects(cam_dev, argc, argv, combinedopt,
- retry_count, timeout);
+ task_attr, retry_count, timeout);
break;
case CAM_CMD_MODE_PAGE:
modepage(cam_dev, argc, argv, combinedopt,
- retry_count, timeout);
+ task_attr, retry_count, timeout);
break;
case CAM_CMD_SCSI_CMD:
error = scsicmd(cam_dev, argc, argv, combinedopt,
- retry_count, timeout);
+ task_attr, retry_count, timeout);
break;
case CAM_CMD_SMP_CMD:
error = smpcmd(cam_dev, argc, argv, combinedopt,
@@ -7827,22 +9437,23 @@
error = tagcontrol(cam_dev, argc, argv, combinedopt);
break;
case CAM_CMD_RATE:
- error = ratecontrol(cam_dev, retry_count, timeout,
- argc, argv, combinedopt);
+ error = ratecontrol(cam_dev, task_attr, retry_count,
+ timeout, argc, argv, combinedopt);
break;
case CAM_CMD_FORMAT:
error = scsiformat(cam_dev, argc, argv,
- combinedopt, retry_count, timeout);
+ combinedopt, task_attr, retry_count,
+ timeout);
break;
case CAM_CMD_REPORTLUNS:
error = scsireportluns(cam_dev, argc, argv,
- combinedopt, retry_count,
- timeout);
+ combinedopt, task_attr,
+ retry_count, timeout);
break;
case CAM_CMD_READCAP:
error = scsireadcapacity(cam_dev, argc, argv,
- combinedopt, retry_count,
- timeout);
+ combinedopt, task_attr,
+ retry_count, timeout);
break;
case CAM_CMD_IDLE:
case CAM_CMD_STANDBY:
@@ -7850,6 +9461,11 @@
error = atapm(cam_dev, argc, argv,
combinedopt, retry_count, timeout);
break;
+ case CAM_CMD_APM:
+ case CAM_CMD_AAM:
+ error = ataaxm(cam_dev, argc, argv,
+ combinedopt, retry_count, timeout);
+ break;
case CAM_CMD_SECURITY:
error = atasecurity(cam_dev, retry_count, timeout,
argc, argv, combinedopt);
@@ -7856,9 +9472,35 @@
break;
case CAM_CMD_DOWNLOAD_FW:
error = fwdownload(cam_dev, argc, argv, combinedopt,
- arglist & CAM_ARG_VERBOSE, retry_count, timeout,
- get_disk_type(cam_dev));
+ arglist & CAM_ARG_VERBOSE, task_attr, retry_count,
+ timeout);
break;
+ case CAM_CMD_SANITIZE:
+ error = scsisanitize(cam_dev, argc, argv,
+ combinedopt, task_attr,
+ retry_count, timeout);
+ break;
+ case CAM_CMD_PERSIST:
+ error = scsipersist(cam_dev, argc, argv, combinedopt,
+ task_attr, retry_count, timeout,
+ arglist & CAM_ARG_VERBOSE,
+ arglist & CAM_ARG_ERR_RECOVER);
+ break;
+ case CAM_CMD_ATTRIB:
+ error = scsiattrib(cam_dev, argc, argv, combinedopt,
+ task_attr, retry_count, timeout,
+ arglist & CAM_ARG_VERBOSE,
+ arglist & CAM_ARG_ERR_RECOVER);
+ break;
+ case CAM_CMD_OPCODES:
+ error = scsiopcodes(cam_dev, argc, argv, combinedopt,
+ task_attr, retry_count, timeout,
+ arglist & CAM_ARG_VERBOSE);
+ break;
+ case CAM_CMD_REPROBE:
+ error = scsireprobe(cam_dev);
+ break;
+
#endif /* MINIMALISTIC */
case CAM_CMD_USAGE:
usage(1);
Modified: trunk/sbin/camcontrol/camcontrol.h
===================================================================
--- trunk/sbin/camcontrol/camcontrol.h 2018-07-01 21:16:14 UTC (rev 11236)
+++ trunk/sbin/camcontrol/camcontrol.h 2018-07-01 21:16:46 UTC (rev 11237)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*
* Copyright (c) 1998 Kenneth D. Merry.
* All rights reserved.
@@ -25,11 +26,26 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sbin/camcontrol/camcontrol.h 314221 2017-02-24 20:51:39Z ken $
*/
#ifndef _CAMCONTROL_H
#define _CAMCONTROL_H
+
+typedef enum {
+ CC_OR_NOT_FOUND,
+ CC_OR_AMBIGUOUS,
+ CC_OR_FOUND
+} camcontrol_optret;
+
+typedef enum {
+ CC_DT_NONE,
+ CC_DT_SCSI,
+ CC_DT_ATA_BEHIND_SCSI,
+ CC_DT_ATA,
+ CC_DT_UNKNOWN
+} camcontrol_devtype;
+
/*
* get_hook: Structure for evaluating args in a callback.
*/
@@ -42,20 +58,46 @@
extern int verbose;
+int ata_do_identify(struct cam_device *device, int retry_count, int timeout,
+ union ccb *ccb, struct ata_params **ident_bufp);
+int dev_has_vpd_page(struct cam_device *dev, uint8_t page_id, int retry_count,
+ int timeout, int verbosemode);
+int get_device_type(struct cam_device *dev, int retry_count, int timeout,
+ int verbosemode, camcontrol_devtype *devtype);
+void build_ata_cmd(union ccb *ccb, uint32_t retry_count, uint32_t flags,
+ uint8_t tag_action, uint8_t protocol, uint8_t ata_flags,
+ uint16_t features, uint16_t sector_count, uint64_t lba,
+ uint8_t command, uint8_t *data_ptr, uint16_t dxfer_len,
+ uint8_t sense_len, uint32_t timeout, int is48bit,
+ camcontrol_devtype devtype);
+int camxferrate(struct cam_device *device);
int fwdownload(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int printerrors, int retry_count, int timeout,
- const char */*type*/);
-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);
-void mode_select(struct cam_device *device, int save_pages, int retry_count,
- int timeout, u_int8_t *data, int datalen);
-void mode_edit(struct cam_device *device, int page, int page_control, int dbd,
- int edit, int binary, int retry_count, int timeout);
-void mode_list(struct cam_device *device, int page_control, int dbd,
+ char *combinedopt, int printerrors, int task_attr,
int retry_count, int timeout);
+void mode_sense(struct cam_device *device, int dbd, int pc, int page,
+ int subpage, int task_attr, int retry_count, int timeout,
+ uint8_t *data, int datalen);
+void mode_select(struct cam_device *device, int save_pages, int task_attr,
+ int retry_count, int timeout, u_int8_t *data, int datalen);
+void mode_edit(struct cam_device *device, int dbd, int pc, int page,
+ int subpage, int edit, int binary, int task_attr,
+ int retry_count, int timeout);
+void mode_list(struct cam_device *device, int dbd, int pc, int subpages,
+ int task_attr, int retry_count, int timeout);
int scsidoinquiry(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int retry_count, int timeout);
+ char *combinedopt, int task_attr, int retry_count,
+ int timeout);
+int scsigetopcodes(struct cam_device *device, int opcode_set, int opcode,
+ int show_sa_errors, int sa_set, int service_action,
+ int timeout_desc, int task_attr, int retry_count,
+ int timeout, int verbosemode, uint32_t *fill_len,
+ uint8_t **data_ptr);
+int scsipersist(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int task_attr, int retry_count,
+ int timeout, int verbose, int err_recover);
+int scsiattrib(struct cam_device *device, int argc, char **argv,
+ char *combinedopt, int task_attr, int retry_count, int timeout,
+ int verbose, int err_recover);
char *cget(void *hook, char *name);
int iget(void *hook, char *name);
void arg_put(void *hook, int letter, void *arg, int count, char *name);
Modified: trunk/sbin/camcontrol/fwdownload.c
===================================================================
--- trunk/sbin/camcontrol/fwdownload.c 2018-07-01 21:16:14 UTC (rev 11236)
+++ trunk/sbin/camcontrol/fwdownload.c 2018-07-01 21:16:46 UTC (rev 11237)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 2011 Sandvine Incorporated. All rights reserved.
* Copyright (c) 2002-2011 Andre Albsmeier <andre at albsmeier.net>
@@ -48,7 +49,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sbin/camcontrol/fwdownload.c 319266 2017-05-30 22:46:00Z asomers $");
#include <sys/types.h>
#include <sys/stat.h>
@@ -68,9 +69,10 @@
#include "camcontrol.h"
-#define CMD_TIMEOUT 50000 /* 50 seconds */
+#define WB_TIMEOUT 50000 /* 50 seconds */
typedef enum {
+ VENDOR_HGST,
VENDOR_HITACHI,
VENDOR_HP,
VENDOR_IBM,
@@ -77,34 +79,167 @@
VENDOR_PLEXTOR,
VENDOR_QUALSTAR,
VENDOR_QUANTUM,
+ VENDOR_SAMSUNG,
VENDOR_SEAGATE,
+ VENDOR_SMART,
+ VENDOR_ATA,
VENDOR_UNKNOWN
} fw_vendor_t;
+/*
+ * FW_TUR_READY: The drive must return good status for a test unit ready.
+ *
+ * FW_TUR_NOT_READY: The drive must return not ready status for a test unit
+ * ready. You may want this in a removable media drive.
+ *
+ * FW_TUR_NA: It doesn't matter whether the drive is ready or not.
+ * This may be the case for a removable media drive.
+ */
+typedef enum {
+ FW_TUR_NONE,
+ FW_TUR_READY,
+ FW_TUR_NOT_READY,
+ FW_TUR_NA
+} fw_tur_status;
+
+/*
+ * FW_TIMEOUT_DEFAULT: Attempt to probe for a WRITE BUFFER timeout
+ * value from the drive. If we get an answer,
+ * use the Recommended timeout. Otherwise,
+ * use the default value from the table.
+ *
+ * FW_TIMEOUT_DEV_REPORTED: The timeout value was probed directly from
+ * the device.
+ *
+ * FW_TIMEOUT_NO_PROBE: Do not ask the device for a WRITE BUFFER
+ * timeout value. Use the device-specific
+ * value.
+ *
+ * FW_TIMEOUT_USER_SPEC: The user specified a timeout on the command
+ * line with the -t option. This overrides any
+ * probe or default timeout.
+ */
+typedef enum {
+ FW_TIMEOUT_DEFAULT,
+ FW_TIMEOUT_DEV_REPORTED,
+ FW_TIMEOUT_NO_PROBE,
+ FW_TIMEOUT_USER_SPEC
+} fw_timeout_type;
+
+/*
+ * type: Enumeration for the particular vendor.
+ *
+ * pattern: Pattern to match for the Vendor ID from the SCSI
+ * Inquiry data.
+ *
+ * dev_type: SCSI device type to match, or T_ANY to match any
+ * device from the given vendor. Note that if there
+ * is a specific device type listed for a particular
+ * vendor, it must be listed before a T_ANY entry.
+ *
+ * max_pkt_size: Maximum packet size when talking to a device. Note
+ * that although large data sizes may be supported by
+ * the target device, they may not be supported by the
+ * OS or the controller.
+ *
+ * cdb_byte2: This specifies byte 2 (byte 1 when counting from 0)
+ * of the CDB. This is generally the WRITE BUFFER mode.
+ *
+ * cdb_byte2_last: This specifies byte 2 for the last chunk of the
+ * download.
+ *
+ * inc_cdb_buffer_id: Increment the buffer ID by 1 for each chunk sent
+ * down to the drive.
+ *
+ * inc_cdb_offset: Increment the offset field in the CDB with the byte
+ * offset into the firmware file.
+ *
+ * tur_status: Pay attention to whether the device is ready before
+ * upgrading the firmware, or not. See above for the
+ * values.
+ */
struct fw_vendor {
fw_vendor_t type;
const char *pattern;
+ int dev_type;
int max_pkt_size;
u_int8_t cdb_byte2;
u_int8_t cdb_byte2_last;
int inc_cdb_buffer_id;
int inc_cdb_offset;
+ fw_tur_status tur_status;
+ int timeout_ms;
+ fw_timeout_type timeout_type;
};
-static const struct fw_vendor vendors_list[] = {
- {VENDOR_HITACHI, "HITACHI", 0x8000, 0x05, 0x05, 1, 0},
- {VENDOR_HP, "HP", 0x8000, 0x07, 0x07, 0, 1},
- {VENDOR_IBM, "IBM", 0x8000, 0x05, 0x05, 1, 0},
- {VENDOR_PLEXTOR, "PLEXTOR", 0x2000, 0x04, 0x05, 0, 1},
- {VENDOR_QUALSTAR, "QUALSTAR", 0x2030, 0x05, 0x05, 0, 0},
- {VENDOR_QUANTUM, "QUANTUM", 0x2000, 0x04, 0x05, 0, 1},
- {VENDOR_SEAGATE, "SEAGATE", 0x8000, 0x07, 0x07, 0, 1},
- /* the next 2 are SATA disks going through SAS HBA */
- {VENDOR_SEAGATE, "ATA ST", 0x8000, 0x07, 0x07, 0, 1},
- {VENDOR_HITACHI, "ATA HDS", 0x8000, 0x05, 0x05, 1, 0},
- {VENDOR_UNKNOWN, NULL, 0x0000, 0x00, 0x00, 0, 0}
+/*
+ * Vendor notes:
+ *
+ * HGST: The packets need to be sent in multiples of 4K.
+ *
+ * IBM: For LTO and TS drives, the buffer ID is ignored in mode 7 (and
+ * some other modes). It treats the request as a firmware download.
+ * The offset (and therefore the length of each chunk sent) needs
+ * to be a multiple of the offset boundary specified for firmware
+ * (buffer ID 4) in the read buffer command. At least for LTO-6,
+ * that seems to be 0, but using a 32K chunk size should satisfy
+ * most any alignment requirement.
+ *
+ * SmrtStor: Mode 5 is also supported, but since the firmware is 400KB or
+ * so, we can't fit it in a single request in most cases.
+ */
+static struct fw_vendor vendors_list[] = {
+ {VENDOR_HGST, "HGST", T_DIRECT,
+ 0x1000, 0x07, 0x07, 1, 0, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT},
+ {VENDOR_HITACHI, "HITACHI", T_ANY,
+ 0x8000, 0x05, 0x05, 1, 0, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT},
+ {VENDOR_HP, "HP", T_ANY,
+ 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT},
+ {VENDOR_IBM, "IBM", T_SEQUENTIAL,
+ 0x8000, 0x07, 0x07, 0, 1, FW_TUR_NA, 300 * 1000, FW_TIMEOUT_DEFAULT},
+ {VENDOR_IBM, "IBM", T_ANY,
+ 0x8000, 0x05, 0x05, 1, 0, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT},
+ {VENDOR_PLEXTOR, "PLEXTOR", T_ANY,
+ 0x2000, 0x04, 0x05, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT},
+ {VENDOR_QUALSTAR, "QUALSTAR", T_ANY,
+ 0x2030, 0x05, 0x05, 0, 0, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT},
+ {VENDOR_QUANTUM, "QUANTUM", T_ANY,
+ 0x2000, 0x04, 0x05, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT},
+ {VENDOR_SAMSUNG, "SAMSUNG", T_ANY,
+ 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT},
+ {VENDOR_SEAGATE, "SEAGATE", T_ANY,
+ 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT},
+ {VENDOR_SMART, "SmrtStor", T_DIRECT,
+ 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT},
+
+ /*
+ * We match any ATA device. This is really just a placeholder,
+ * since we won't actually send a WRITE BUFFER with any of the
+ * listed parameters. If a SATA device is behind a SAS controller,
+ * the SCSI to ATA translation code (at least for LSI) doesn't
+ * generaly translate a SCSI WRITE BUFFER into an ATA DOWNLOAD
+ * MICROCODE command. So, we use the SCSI ATA PASS_THROUGH command
+ * to send the ATA DOWNLOAD MICROCODE command instead.
+ */
+ {VENDOR_ATA, "ATA", T_ANY,
+ 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT,
+ FW_TIMEOUT_NO_PROBE},
+ {VENDOR_UNKNOWN, NULL, T_ANY,
+ 0x0000, 0x00, 0x00, 0, 0, FW_TUR_NONE, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}
};
+struct fw_timeout_desc {
+ fw_timeout_type timeout_type;
+ const char *timeout_desc;
+};
+
+static const struct fw_timeout_desc fw_timeout_desc_table[] = {
+ { FW_TIMEOUT_DEFAULT, "the default" },
+ { FW_TIMEOUT_DEV_REPORTED, "recommended by this particular device" },
+ { FW_TIMEOUT_NO_PROBE, "the default" },
+ { FW_TIMEOUT_USER_SPEC, "what was specified on the command line" }
+};
+
#ifndef ATA_DOWNLOAD_MICROCODE
#define ATA_DOWNLOAD_MICROCODE 0x92
#endif
@@ -126,43 +261,309 @@
#define UNKNOWN_MAX_PKT_SIZE 0x8000
#endif
-static const struct fw_vendor *fw_get_vendor(struct cam_device *cam_dev);
-static char *fw_read_img(const char *fw_img_path,
- const struct fw_vendor *vp, int *num_bytes);
-static int fw_download_img(struct cam_device *cam_dev,
- const struct fw_vendor *vp, char *buf, int img_size,
- int sim_mode, int printerrors, int retry_count, int timeout,
- const char */*name*/, const char */*type*/);
+static struct fw_vendor *fw_get_vendor(struct cam_device *cam_dev,
+ struct ata_params *ident_buf);
+static int fw_get_timeout(struct cam_device *cam_dev, struct fw_vendor *vp,
+ int task_attr, int retry_count, int timeout);
+static int fw_validate_ibm(struct cam_device *dev, int retry_count,
+ int timeout, int fd, char *buf,
+ const char *fw_img_path, int quiet);
+static char *fw_read_img(struct cam_device *dev, int retry_count,
+ int timeout, int quiet, const char *fw_img_path,
+ struct fw_vendor *vp, int *num_bytes);
+static int fw_check_device_ready(struct cam_device *dev,
+ camcontrol_devtype devtype,
+ struct fw_vendor *vp, int printerrors,
+ int timeout);
+static int fw_download_img(struct cam_device *cam_dev,
+ struct fw_vendor *vp, char *buf, int img_size,
+ int sim_mode, int printerrors, int quiet,
+ int retry_count, int timeout, const char */*name*/,
+ camcontrol_devtype devtype);
/*
* Find entry in vendors list that belongs to
* the vendor of given cam device.
*/
-static const struct fw_vendor *
-fw_get_vendor(struct cam_device *cam_dev)
+static struct fw_vendor *
+fw_get_vendor(struct cam_device *cam_dev, struct ata_params *ident_buf)
{
- char vendor[SID_VENDOR_SIZE + 1];
- const struct fw_vendor *vp;
+ char vendor[42];
+ struct fw_vendor *vp;
if (cam_dev == NULL)
return (NULL);
- cam_strvis((u_char *)vendor, (u_char *)cam_dev->inq_data.vendor,
- sizeof(cam_dev->inq_data.vendor), sizeof(vendor));
+
+ if (ident_buf != NULL) {
+ cam_strvis((u_char *)vendor, ident_buf->model,
+ sizeof(ident_buf->model), sizeof(vendor));
+ for (vp = vendors_list; vp->pattern != NULL; vp++) {
+ if (vp->type == VENDOR_ATA)
+ return (vp);
+ }
+ } else {
+ cam_strvis((u_char *)vendor, (u_char *)cam_dev->inq_data.vendor,
+ sizeof(cam_dev->inq_data.vendor), sizeof(vendor));
+ }
for (vp = vendors_list; vp->pattern != NULL; vp++) {
if (!cam_strmatch((const u_char *)vendor,
- (const u_char *)vp->pattern, strlen(vendor)))
- break;
+ (const u_char *)vp->pattern, strlen(vendor))) {
+ if ((vp->dev_type == T_ANY)
+ || (vp->dev_type == SID_TYPE(&cam_dev->inq_data)))
+ break;
+ }
}
return (vp);
}
+static int
+fw_get_timeout(struct cam_device *cam_dev, struct fw_vendor *vp,
+ int task_attr, int retry_count, int timeout)
+{
+ struct scsi_report_supported_opcodes_one *one;
+ struct scsi_report_supported_opcodes_timeout *td;
+ uint8_t *buf = NULL;
+ uint32_t fill_len = 0, cdb_len = 0, rec_timeout = 0;
+ int retval = 0;
+
+ /*
+ * If the user has specified a timeout on the command line, we let
+ * him override any default or probed value.
+ */
+ if (timeout != 0) {
+ vp->timeout_type = FW_TIMEOUT_USER_SPEC;
+ vp->timeout_ms = timeout;
+ goto bailout;
+ }
+
+ /*
+ * Check to see whether we should probe for a timeout for this
+ * device.
+ */
+ if (vp->timeout_type == FW_TIMEOUT_NO_PROBE)
+ goto bailout;
+
+ retval = scsigetopcodes(/*device*/ cam_dev,
+ /*opcode_set*/ 1,
+ /*opcode*/ WRITE_BUFFER,
+ /*show_sa_errors*/ 1,
+ /*sa_set*/ 0,
+ /*service_action*/ 0,
+ /*timeout_desc*/ 1,
+ /*task_attr*/ task_attr,
+ /*retry_count*/ retry_count,
+ /*timeout*/ 10000,
+ /*verbose*/ 0,
+ /*fill_len*/ &fill_len,
+ /*data_ptr*/ &buf);
+ /*
+ * It isn't an error if we can't get a timeout descriptor. We just
+ * continue on with the default timeout.
+ */
+ if (retval != 0) {
+ retval = 0;
+ goto bailout;
+ }
+
+ /*
+ * Even if the drive didn't return a SCSI error, if we don't have
+ * enough data to contain the one opcode descriptor, the CDB
+ * structure and a timeout descriptor, we don't have the timeout
+ * value we're looking for. So we'll just fall back to the
+ * default value.
+ */
+ if (fill_len < (sizeof(*one) + sizeof(struct scsi_write_buffer) +
+ sizeof(*td)))
+ goto bailout;
+
+ one = (struct scsi_report_supported_opcodes_one *)buf;
+
+ /*
+ * If the drive claims to not support the WRITE BUFFER command...
+ * fall back to the default timeout value and let things fail on
+ * the actual firmware download.
+ */
+ if ((one->support & RSO_ONE_SUP_MASK) == RSO_ONE_SUP_NOT_SUP)
+ goto bailout;
+
+ cdb_len = scsi_2btoul(one->cdb_length);
+ td = (struct scsi_report_supported_opcodes_timeout *)
+ &buf[sizeof(*one) + cdb_len];
+
+ rec_timeout = scsi_4btoul(td->recommended_time);
+ /*
+ * If the recommended timeout is 0, then the device has probably
+ * returned a bogus value.
+ */
+ if (rec_timeout == 0)
+ goto bailout;
+
+ /* CAM timeouts are in ms */
+ rec_timeout *= 1000;
+
+ vp->timeout_ms = rec_timeout;
+ vp->timeout_type = FW_TIMEOUT_DEV_REPORTED;
+
+bailout:
+ return (retval);
+}
+
+#define SVPD_IBM_FW_DESIGNATION 0x03
+
/*
+ * IBM LTO and TS tape drives have an INQUIRY VPD page 0x3 with the following
+ * format:
+ */
+struct fw_ibm_tape_fw_designation {
+ uint8_t device;
+ uint8_t page_code;
+ uint8_t reserved;
+ uint8_t length;
+ uint8_t ascii_length;
+ uint8_t reserved2[3];
+ uint8_t load_id[4];
+ uint8_t fw_rev[4];
+ uint8_t ptf_number[4];
+ uint8_t patch_number[4];
+ uint8_t ru_name[8];
+ uint8_t lib_seq_num[5];
+};
+
+/*
+ * The firmware for IBM tape drives has the following header format. The
+ * load_id and ru_name in the header file should match what is returned in
+ * VPD page 0x3.
+ */
+struct fw_ibm_tape_fw_header {
+ uint8_t unspec[4];
+ uint8_t length[4]; /* Firmware and header! */
+ uint8_t load_id[4];
+ uint8_t fw_rev[4];
+ uint8_t reserved[8];
+ uint8_t ru_name[8];
+};
+
+static int
+fw_validate_ibm(struct cam_device *dev, int retry_count, int timeout, int fd,
+ char *buf, const char *fw_img_path, int quiet)
+{
+ union ccb *ccb;
+ struct fw_ibm_tape_fw_designation vpd_page;
+ struct fw_ibm_tape_fw_header *header;
+ char drive_rev[sizeof(vpd_page.fw_rev) + 1];
+ char file_rev[sizeof(vpd_page.fw_rev) + 1];
+ int retval = 1;
+
+ ccb = cam_getccb(dev);
+ if (ccb == NULL) {
+ warnx("couldn't allocate CCB");
+ goto bailout;
+ }
+
+ /* cam_getccb cleans up the header, caller has to zero the payload */
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
+
+ bzero(&vpd_page, sizeof(vpd_page));
+
+ scsi_inquiry(&ccb->csio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* inq_buf */ (u_int8_t *)&vpd_page,
+ /* inq_len */ sizeof(vpd_page),
+ /* evpd */ 1,
+ /* page_code */ SVPD_IBM_FW_DESIGNATION,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ timeout ? timeout : 5000);
+
+ /* Disable freezing the device queue */
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ if (retry_count != 0)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ if (cam_send_ccb(dev, ccb) < 0) {
+ warn("error getting firmware designation page");
+
+ cam_error_print(dev, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+
+ cam_freeccb(ccb);
+ ccb = NULL;
+ goto bailout;
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ cam_error_print(dev, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ goto bailout;
+ }
+
+ /*
+ * Read the firmware header only.
+ */
+ if (read(fd, buf, sizeof(*header)) != sizeof(*header)) {
+ warn("unable to read %zu bytes from %s", sizeof(*header),
+ fw_img_path);
+ goto bailout;
+ }
+
+ /* Rewind the file back to 0 for the full file read. */
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ warn("Unable to lseek");
+ goto bailout;
+ }
+
+ header = (struct fw_ibm_tape_fw_header *)buf;
+
+ bzero(drive_rev, sizeof(drive_rev));
+ bcopy(vpd_page.fw_rev, drive_rev, sizeof(vpd_page.fw_rev));
+ bzero(file_rev, sizeof(file_rev));
+ bcopy(header->fw_rev, file_rev, sizeof(header->fw_rev));
+
+ if (quiet == 0) {
+ fprintf(stdout, "Current Drive Firmware version: %s\n",
+ drive_rev);
+ fprintf(stdout, "Firmware File version: %s\n", file_rev);
+ }
+
+ /*
+ * For IBM tape drives the load ID and RU name reported by the
+ * drive should match what is in the firmware file.
+ */
+ if (bcmp(vpd_page.load_id, header->load_id,
+ MIN(sizeof(vpd_page.load_id), sizeof(header->load_id))) != 0) {
+ warnx("Drive Firmware load ID 0x%x does not match firmware "
+ "file load ID 0x%x", scsi_4btoul(vpd_page.load_id),
+ scsi_4btoul(header->load_id));
+ goto bailout;
+ }
+
+ if (bcmp(vpd_page.ru_name, header->ru_name,
+ MIN(sizeof(vpd_page.ru_name), sizeof(header->ru_name))) != 0) {
+ warnx("Drive Firmware RU name 0x%jx does not match firmware "
+ "file RU name 0x%jx",
+ (uintmax_t)scsi_8btou64(vpd_page.ru_name),
+ (uintmax_t)scsi_8btou64(header->ru_name));
+ goto bailout;
+ }
+ if (quiet == 0)
+ fprintf(stdout, "Firmware file is valid for this drive.\n");
+ retval = 0;
+bailout:
+ cam_freeccb(ccb);
+
+ return (retval);
+}
+
+/*
* Allocate a buffer and read fw image file into it
* from given path. Number of bytes read is stored
* in num_bytes.
*/
static char *
-fw_read_img(const char *fw_img_path, const struct fw_vendor *vp, int *num_bytes)
+fw_read_img(struct cam_device *dev, int retry_count, int timeout, int quiet,
+ const char *fw_img_path, struct fw_vendor *vp, int *num_bytes)
{
int fd;
struct stat stbuf;
@@ -205,6 +606,14 @@
case VENDOR_QUALSTAR:
skip_bytes = img_size % 1030;
break;
+ case VENDOR_IBM: {
+ if (vp->dev_type != T_SEQUENTIAL)
+ break;
+ if (fw_validate_ibm(dev, retry_count, timeout, fd, buf,
+ fw_img_path, quiet) != 0)
+ goto bailout;
+ break;
+ }
default:
break;
}
@@ -222,6 +631,7 @@
goto bailout;
}
*num_bytes = img_size;
+ close(fd);
return (buf);
bailout:
free(buf);
@@ -231,6 +641,115 @@
return (NULL);
}
+/*
+ * Returns 0 for "success", where success means that the device has met the
+ * requirement in the vendor structure for being ready or not ready when
+ * firmware is downloaded.
+ *
+ * Returns 1 for a failure to be ready to accept a firmware download.
+ * (e.g., a drive needs to be ready, but returns not ready)
+ *
+ * Returns -1 for any other failure.
+ */
+static int
+fw_check_device_ready(struct cam_device *dev, camcontrol_devtype devtype,
+ struct fw_vendor *vp, int printerrors, int timeout)
+{
+ union ccb *ccb;
+ int retval = 0;
+ int16_t *ptr = NULL;
+ size_t dxfer_len = 0;
+
+ if ((ccb = cam_getccb(dev)) == NULL) {
+ warnx("Could not allocate CCB");
+ retval = -1;
+ goto bailout;
+ }
+
+ CCB_CLEAR_ALL_EXCEPT_HDR(ccb);
+
+ if (devtype != CC_DT_SCSI) {
+ dxfer_len = sizeof(struct ata_params);
+
+ ptr = (uint16_t *)malloc(dxfer_len);
+ if (ptr == NULL) {
+ warnx("can't malloc memory for identify");
+ retval = -1;
+ goto bailout;
+ }
+ bzero(ptr, dxfer_len);
+ }
+
+ switch (devtype) {
+ case CC_DT_SCSI:
+ scsi_test_unit_ready(&ccb->csio,
+ /*retries*/ 0,
+ /*cbfcnp*/ NULL,
+ /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*sense_len*/ SSD_FULL_SIZE,
+ /*timeout*/ 5000);
+ break;
+ case CC_DT_ATA_BEHIND_SCSI:
+ case CC_DT_ATA: {
+ build_ata_cmd(ccb,
+ /*retries*/ 1,
+ /*flags*/ CAM_DIR_IN,
+ /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*protocol*/ AP_PROTO_PIO_IN,
+ /*ata_flags*/ AP_FLAG_BYT_BLOK_BYTES |
+ AP_FLAG_TLEN_SECT_CNT |
+ AP_FLAG_TDIR_FROM_DEV,
+ /*features*/ 0,
+ /*sector_count*/ (uint8_t) dxfer_len,
+ /*lba*/ 0,
+ /*command*/ ATA_ATA_IDENTIFY,
+ /*data_ptr*/ (uint8_t *)ptr,
+ /*dxfer_len*/ dxfer_len,
+ /*sense_len*/ SSD_FULL_SIZE,
+ /*timeout*/ timeout ? timeout : 30 * 1000,
+ /*is48bit*/ 0,
+ /*devtype*/ devtype);
+ break;
+ }
+ default:
+ warnx("Unknown disk type %d", devtype);
+ retval = -1;
+ goto bailout;
+ break; /*NOTREACHED*/
+ }
+
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ retval = cam_send_ccb(dev, ccb);
+ if (retval != 0) {
+ warn("error sending %s CCB", (devtype == CC_DT_SCSI) ?
+ "Test Unit Ready" : "Identify");
+ retval = -1;
+ goto bailout;
+ }
+
+ if (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
+ && (vp->tur_status == FW_TUR_READY)) {
+ warnx("Device is not ready");
+ if (printerrors)
+ cam_error_print(dev, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ retval = 1;
+ goto bailout;
+ } else if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
+ && (vp->tur_status == FW_TUR_NOT_READY)) {
+ warnx("Device cannot have media loaded when firmware is "
+ "downloaded");
+ retval = 1;
+ goto bailout;
+ }
+bailout:
+ free(ptr);
+ cam_freeccb(ccb);
+
+ return (retval);
+}
+
/*
* Download firmware stored in buf to cam_dev. If simulation mode
* is enabled, only show what packet sizes would be sent to the
@@ -237,14 +756,15 @@
* device but do not sent any actual packets
*/
static int
-fw_download_img(struct cam_device *cam_dev, const struct fw_vendor *vp,
- char *buf, int img_size, int sim_mode, int printerrors, int retry_count,
- int timeout, const char *imgname, const char *type)
+fw_download_img(struct cam_device *cam_dev, struct fw_vendor *vp,
+ char *buf, int img_size, int sim_mode, int printerrors, int quiet,
+ int retry_count, int timeout, const char *imgname,
+ camcontrol_devtype devtype)
{
struct scsi_write_buffer cdb;
progress_t progress;
- int size;
- union ccb *ccb;
+ int size = 0;
+ union ccb *ccb = NULL;
int pkt_count = 0;
int max_pkt_size;
u_int32_t pkt_size = 0;
@@ -251,64 +771,30 @@
char *pkt_ptr = buf;
u_int32_t offset;
int last_pkt = 0;
- int16_t *ptr;
+ int retval = 0;
+ /*
+ * Check to see whether the device is ready to accept a firmware
+ * download.
+ */
+ retval = fw_check_device_ready(cam_dev, devtype, vp, printerrors,
+ timeout);
+ if (retval != 0)
+ goto bailout;
+
if ((ccb = cam_getccb(cam_dev)) == NULL) {
warnx("Could not allocate CCB");
- return (1);
+ retval = 1;
+ goto bailout;
}
- if (strcmp(type, "scsi") == 0) {
- scsi_test_unit_ready(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG,
- SSD_FULL_SIZE, 5000);
- } else if (strcmp(type, "ata") == 0) {
- /* cam_getccb cleans up the header, caller has to zero the payload */
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_ataio) - sizeof(struct ccb_hdr));
- ptr = (uint16_t *)malloc(sizeof(struct ata_params));
+ CCB_CLEAR_ALL_EXCEPT_HDR(ccb);
- if (ptr == NULL) {
- cam_freeccb(ccb);
- warnx("can't malloc memory for identify\n");
- return(1);
- }
- bzero(ptr, sizeof(struct ata_params));
- cam_fill_ataio(&ccb->ataio,
- 1,
- NULL,
- /*flags*/CAM_DIR_IN,
- MSG_SIMPLE_Q_TAG,
- /*data_ptr*/(uint8_t *)ptr,
- /*dxfer_len*/sizeof(struct ata_params),
- timeout ? timeout : 30 * 1000);
- ata_28bit_cmd(&ccb->ataio, ATA_ATA_IDENTIFY, 0, 0, 0);
- } else {
- warnx("weird disk type '%s'", type);
- return 1;
- }
- /* Disable freezing the device queue. */
- ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
- if (cam_send_ccb(cam_dev, ccb) < 0) {
- warnx("Error sending identify/test unit ready");
- if (printerrors)
- cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
- CAM_EPF_ALL, stderr);
- cam_freeccb(ccb);
- return(1);
- }
- if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
- warnx("Device is not ready");
- if (printerrors)
- cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
- CAM_EPF_ALL, stderr);
- cam_freeccb(ccb);
- return (1);
- }
max_pkt_size = vp->max_pkt_size;
- if (vp->max_pkt_size == 0 && strcmp(type, "ata") == 0) {
+ if (max_pkt_size == 0)
max_pkt_size = UNKNOWN_MAX_PKT_SIZE;
- }
- pkt_size = vp->max_pkt_size;
+
+ pkt_size = max_pkt_size;
progress_init(&progress, imgname, size = img_size);
/* Download single fw packets. */
do {
@@ -317,9 +803,12 @@
pkt_size = img_size;
}
progress_update(&progress, size - img_size);
- progress_draw(&progress);
+ if (((sim_mode == 0) && (quiet == 0))
+ || ((sim_mode != 0) && (printerrors == 0)))
+ progress_draw(&progress);
bzero(&cdb, sizeof(cdb));
- if (strcmp(type, "scsi") == 0) {
+ switch (devtype) {
+ case CC_DT_SCSI:
cdb.opcode = WRITE_BUFFER;
cdb.control = 0;
/* Parameter list length. */
@@ -326,98 +815,119 @@
scsi_ulto3b(pkt_size, &cdb.length[0]);
offset = vp->inc_cdb_offset ? (pkt_ptr - buf) : 0;
scsi_ulto3b(offset, &cdb.offset[0]);
- cdb.byte2 = last_pkt ? vp->cdb_byte2_last : vp->cdb_byte2;
+ cdb.byte2 = last_pkt ? vp->cdb_byte2_last :
+ vp->cdb_byte2;
cdb.buffer_id = vp->inc_cdb_buffer_id ? pkt_count : 0;
/* Zero out payload of ccb union after ccb header. */
- bzero((u_char *)ccb + sizeof(struct ccb_hdr),
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
- /* Copy previously constructed cdb into ccb_scsiio struct. */
+ CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
+ /*
+ * Copy previously constructed cdb into ccb_scsiio
+ * struct.
+ */
bcopy(&cdb, &ccb->csio.cdb_io.cdb_bytes[0],
sizeof(struct scsi_write_buffer));
/* Fill rest of ccb_scsiio struct. */
- if (!sim_mode) {
- cam_fill_csio(&ccb->csio, /* ccb_scsiio */
- retry_count, /* retries */
- NULL, /* cbfcnp */
- CAM_DIR_OUT | CAM_DEV_QFRZDIS, /* flags */
- CAM_TAG_ACTION_NONE, /* tag_action */
- (u_char *)pkt_ptr, /* data_ptr */
- pkt_size, /* dxfer_len */
- SSD_FULL_SIZE, /* sense_len */
- sizeof(struct scsi_write_buffer), /* cdb_len */
- timeout ? timeout : CMD_TIMEOUT); /* timeout */
- }
- } else if (strcmp(type, "ata") == 0) {
- bzero(&(&ccb->ccb_h)[1],
- sizeof(struct ccb_ataio) - sizeof(struct ccb_hdr));
- if (!sim_mode) {
- uint32_t off;
+ cam_fill_csio(&ccb->csio, /* ccb_scsiio*/
+ retry_count, /* retries*/
+ NULL, /* cbfcnp*/
+ CAM_DIR_OUT | CAM_DEV_QFRZDIS, /* flags*/
+ CAM_TAG_ACTION_NONE, /* tag_action*/
+ (u_char *)pkt_ptr, /* data_ptr*/
+ pkt_size, /* dxfer_len*/
+ SSD_FULL_SIZE, /* sense_len*/
+ sizeof(struct scsi_write_buffer), /* cdb_len*/
+ timeout ? timeout : WB_TIMEOUT); /* timeout*/
+ break;
+ case CC_DT_ATA:
+ case CC_DT_ATA_BEHIND_SCSI: {
+ uint32_t off;
- cam_fill_ataio(&ccb->ataio,
- (last_pkt) ? 256 : retry_count,
- NULL,
- /*flags*/CAM_DIR_OUT | CAM_DEV_QFRZDIS,
- CAM_TAG_ACTION_NONE,
- /*data_ptr*/(uint8_t *)pkt_ptr,
- /*dxfer_len*/pkt_size,
- timeout ? timeout : 30 * 1000);
- off = (uint32_t)(pkt_ptr - buf);
- ata_28bit_cmd(&ccb->ataio, ATA_DOWNLOAD_MICROCODE,
- USE_OFFSETS_FEATURE,
- ATA_MAKE_LBA(off, pkt_size),
- ATA_MAKE_SECTORS(pkt_size));
- }
+ off = (uint32_t)(pkt_ptr - buf);
+
+ build_ata_cmd(ccb,
+ /*retry_count*/ retry_count,
+ /*flags*/ CAM_DIR_OUT | CAM_DEV_QFRZDIS,
+ /*tag_action*/ CAM_TAG_ACTION_NONE,
+ /*protocol*/ AP_PROTO_PIO_OUT,
+ /*ata_flags*/ AP_FLAG_BYT_BLOK_BYTES |
+ AP_FLAG_TLEN_SECT_CNT |
+ AP_FLAG_TDIR_TO_DEV,
+ /*features*/ USE_OFFSETS_FEATURE,
+ /*sector_count*/ ATA_MAKE_SECTORS(pkt_size),
+ /*lba*/ ATA_MAKE_LBA(off, pkt_size),
+ /*command*/ ATA_DOWNLOAD_MICROCODE,
+ /*data_ptr*/ (uint8_t *)pkt_ptr,
+ /*dxfer_len*/ pkt_size,
+ /*sense_len*/ SSD_FULL_SIZE,
+ /*timeout*/ timeout ? timeout : WB_TIMEOUT,
+ /*is48bit*/ 0,
+ /*devtype*/ devtype);
+ break;
}
+ default:
+ warnx("Unknown device type %d", devtype);
+ retval = 1;
+ goto bailout;
+ break; /*NOTREACHED*/
+ }
if (!sim_mode) {
/* Execute the command. */
- if (cam_send_ccb(cam_dev, ccb) < 0) {
+ if (cam_send_ccb(cam_dev, ccb) < 0 ||
+ (ccb->ccb_h.status & CAM_STATUS_MASK) !=
+ CAM_REQ_CMP) {
warnx("Error writing image to device");
if (printerrors)
- cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
- CAM_EPF_ALL, stderr);
+ cam_error_print(cam_dev, ccb,
+ CAM_ESF_ALL, CAM_EPF_ALL, stderr);
+ retval = 1;
goto bailout;
}
- if (ccb->ataio.res.status != 0 /*&& !last_pkt*/) {
- cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
- CAM_EPF_ALL, stderr);
- }
+ } else if (printerrors) {
+ cam_error_print(cam_dev, ccb, CAM_ESF_COMMAND, 0,
+ stdout);
}
+
/* Prepare next round. */
pkt_count++;
pkt_ptr += pkt_size;
img_size -= pkt_size;
} while(!last_pkt);
- progress_complete(&progress, size - img_size);
- cam_freeccb(ccb);
- return (0);
bailout:
- progress_complete(&progress, size - img_size);
+ if (quiet == 0)
+ progress_complete(&progress, size - img_size);
cam_freeccb(ccb);
- return (1);
+ return (retval);
}
int
fwdownload(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int printerrors, int retry_count, int timeout,
- const char *type)
+ char *combinedopt, int printerrors, int task_attr, int retry_count,
+ int timeout)
{
- const struct fw_vendor *vp;
+ union ccb *ccb = NULL;
+ struct fw_vendor *vp;
char *fw_img_path = NULL;
- char *buf;
+ struct ata_params *ident_buf = NULL;
+ camcontrol_devtype devtype;
+ char *buf = NULL;
int img_size;
int c;
int sim_mode = 0;
int confirmed = 0;
+ int quiet = 0;
+ int retval = 0;
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
+ case 'f':
+ fw_img_path = optarg;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
case 's':
sim_mode = 1;
- confirmed = 1;
break;
- case 'f':
- fw_img_path = optarg;
- break;
case 'y':
confirmed = 1;
break;
@@ -427,42 +937,98 @@
}
if (fw_img_path == NULL)
- errx(1, "you must specify a firmware image file using -f option");
+ errx(1, "you must specify a firmware image file using -f "
+ "option");
- vp = fw_get_vendor(device);
- if (vp == NULL)
- errx(1, "NULL vendor");
- if (vp->type == VENDOR_UNKNOWN)
- warnx("Unsupported device - flashing through an HBA?");
+ retval = get_device_type(device, retry_count, timeout, printerrors,
+ &devtype);
+ if (retval != 0)
+ errx(1, "Unable to determine device type");
- buf = fw_read_img(fw_img_path, vp, &img_size);
- if (buf == NULL)
- goto fail;
+ if ((devtype == CC_DT_ATA)
+ || (devtype == CC_DT_ATA_BEHIND_SCSI)) {
+ ccb = cam_getccb(device);
+ if (ccb == NULL) {
+ warnx("couldn't allocate CCB");
+ retval = 1;
+ goto bailout;
+ }
+ if (ata_do_identify(device, retry_count, timeout, ccb,
+ &ident_buf) != 0) {
+ retval = 1;
+ goto bailout;
+ }
+ } else if (devtype != CC_DT_SCSI)
+ errx(1, "Unsupported device type %d", devtype);
+
+ vp = fw_get_vendor(device, ident_buf);
+ /*
+ * Bail out if we have an unknown vendor and this isn't an ATA
+ * disk. For a SCSI disk, we have no chance of working properly
+ * with the default values in the VENDOR_UNKNOWN case. For an ATA
+ * disk connected via an ATA transport, we may work for drives that
+ * support the ATA_DOWNLOAD_MICROCODE command.
+ */
+ if (((vp == NULL)
+ || (vp->type == VENDOR_UNKNOWN))
+ && (devtype == CC_DT_SCSI))
+ errx(1, "Unsupported device");
+
+ retval = fw_get_timeout(device, vp, task_attr, retry_count, timeout);
+ if (retval != 0) {
+ warnx("Unable to get a firmware download timeout value");
+ goto bailout;
+ }
+
+ buf = fw_read_img(device, retry_count, timeout, quiet, fw_img_path,
+ vp, &img_size);
+ if (buf == NULL) {
+ retval = 1;
+ goto bailout;
+ }
+
if (!confirmed) {
fprintf(stdout, "You are about to download firmware image (%s)"
" into the following device:\n",
fw_img_path);
+ if (devtype == CC_DT_SCSI) {
+ if (scsidoinquiry(device, argc, argv, combinedopt,
+ MSG_SIMPLE_Q_TAG, 0, 5000) != 0) {
+ warnx("Error sending inquiry");
+ retval = 1;
+ goto bailout;
+ }
+ } else {
+ printf("%s%d: ", device->device_name,
+ device->dev_unit_num);
+ ata_print_ident(ident_buf);
+ camxferrate(device);
+ free(ident_buf);
+ }
+ fprintf(stdout, "Using a timeout of %u ms, which is %s.\n",
+ vp->timeout_ms,
+ fw_timeout_desc_table[vp->timeout_type].timeout_desc);
fprintf(stdout, "\nIt may damage your drive. ");
- if (!get_confirmation())
- goto fail;
+ if (!get_confirmation()) {
+ retval = 1;
+ goto bailout;
+ }
}
- if (sim_mode)
+ if ((sim_mode != 0) && (quiet == 0))
fprintf(stdout, "Running in simulation mode\n");
if (fw_download_img(device, vp, buf, img_size, sim_mode, printerrors,
- retry_count, timeout, fw_img_path, type) != 0) {
+ quiet, retry_count, vp->timeout_ms, fw_img_path, devtype) != 0) {
fprintf(stderr, "Firmware download failed\n");
- goto fail;
- }
- else
+ retval = 1;
+ goto bailout;
+ } else if (quiet == 0)
fprintf(stdout, "Firmware download successful\n");
+bailout:
+ cam_freeccb(ccb);
free(buf);
- return (0);
-fail:
- if (buf != NULL)
- free(buf);
- return (1);
+ return (retval);
}
Modified: trunk/sbin/camcontrol/modeedit.c
===================================================================
--- trunk/sbin/camcontrol/modeedit.c 2018-07-01 21:16:14 UTC (rev 11236)
+++ trunk/sbin/camcontrol/modeedit.c 2018-07-01 21:16:46 UTC (rev 11237)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 2000 Kelly Yancey <kbyanc at posi.net>
* Derived from work done by Julian Elischer <julian at tfs.com,
@@ -27,7 +28,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sbin/camcontrol/modeedit.c 317966 2017-05-08 18:30:56Z ken $");
#include <sys/queue.h>
#include <sys/types.h>
@@ -66,10 +67,7 @@
#define MODE_PAGE_HEADER(mh) \
(struct scsi_mode_page_header *)find_mode_page_6(mh)
-#define MODE_PAGE_DATA(mph) \
- (u_int8_t *)(mph) + sizeof(struct scsi_mode_page_header)
-
struct editentry {
STAILQ_ENTRY(editentry) link;
char *name;
@@ -86,7 +84,8 @@
struct pagename {
SLIST_ENTRY(pagename) link;
- int pagenum;
+ int page;
+ int subpage;
char *name;
};
static SLIST_HEAD(, pagename) namelist; /* Page number to name mappings. */
@@ -106,21 +105,23 @@
static struct editentry *editentry_lookup(char *name);
static int editentry_set(char *name, char *newvalue,
int editonly);
-static void editlist_populate(struct cam_device *device,
- int modepage, int page_control,
- int dbd, int retries, int timeout);
-static void editlist_save(struct cam_device *device, int modepage,
- int page_control, int dbd, int retries,
- int timeout);
-static void nameentry_create(int pagenum, char *name);
-static struct pagename *nameentry_lookup(int pagenum);
-static int load_format(const char *pagedb_path, int page);
+static void editlist_populate(struct cam_device *device, int dbd,
+ int pc, int page, int subpage,
+ int task_attr, int retries,
+ int timeout);
+static void editlist_save(struct cam_device *device, int dbd,
+ int pc, int page, int subpage,
+ int task_attr, int retries, int timeout);
+static void nameentry_create(int page, int subpage, char *name);
+static struct pagename *nameentry_lookup(int page, int subpage);
+static int load_format(const char *pagedb_path, int lpage,
+ int lsubpage);
static int modepage_write(FILE *file, int editonly);
static int modepage_read(FILE *file);
static void modepage_edit(void);
-static void modepage_dump(struct cam_device *device, int page,
- int page_control, int dbd, int retries,
- int timeout);
+static void modepage_dump(struct cam_device *device, int dbd,
+ int pc, int page, int subpage, int task_attr,
+ int retries, int timeout);
static void cleanup_editfile(void);
@@ -193,7 +194,14 @@
struct editentry *src; /* Entry value to save. */
src = editentry_lookup(name);
- assert(src != NULL);
+ if (src == 0) {
+ /*
+ * This happens if field does not fit into read page size.
+ * It also means that this field won't be written, so the
+ * returned value does not really matter.
+ */
+ return (0);
+ }
switch (src->type) {
case 'i': /* Byte-sized integral type. */
@@ -246,7 +254,7 @@
* currently workaround it (even for int64's), so we have to kludge it.
*/
#define RESOLUTION_MAX(size) ((resolution * (size) == 32)? \
- (int)0xffffffff: (1 << (resolution * (size))) - 1)
+ INT_MAX: (1 << (resolution * (size))) - 1)
assert(newvalue != NULL);
if (*newvalue == '\0')
@@ -318,10 +326,10 @@
}
static void
-nameentry_create(int pagenum, char *name) {
+nameentry_create(int page, int subpage, char *name) {
struct pagename *newentry;
- if (pagenum < 0 || name == NULL || name[0] == '\0')
+ if (page < 0 || subpage < 0 || name == NULL || name[0] == '\0')
return;
/* Allocate memory for the new entry and a copy of the entry name. */
@@ -332,16 +340,17 @@
/* Trim any trailing whitespace for the page name. */
RTRIM(newentry->name);
- newentry->pagenum = pagenum;
+ newentry->page = page;
+ newentry->subpage = subpage;
SLIST_INSERT_HEAD(&namelist, newentry, link);
}
static struct pagename *
-nameentry_lookup(int pagenum) {
+nameentry_lookup(int page, int subpage) {
struct pagename *scan;
SLIST_FOREACH(scan, &namelist, link) {
- if (pagenum == scan->pagenum)
+ if (page == scan->page && subpage == scan->subpage)
return (scan);
}
@@ -350,12 +359,14 @@
}
static int
-load_format(const char *pagedb_path, int page)
+load_format(const char *pagedb_path, int lpage, int lsubpage)
{
FILE *pagedb;
- char str_pagenum[MAX_PAGENUM_LEN];
+ char str_page[MAX_PAGENUM_LEN];
+ char *str_subpage;
char str_pagename[MAX_PAGENAME_LEN];
- int pagenum;
+ int page;
+ int subpage;
int depth; /* Quoting depth. */
int found;
int lineno;
@@ -364,9 +375,10 @@
char c;
#define SETSTATE_LOCATE do { \
- str_pagenum[0] = '\0'; \
+ str_page[0] = '\0'; \
str_pagename[0] = '\0'; \
- pagenum = -1; \
+ page = -1; \
+ subpage = -1; \
state = LOCATE; \
} while (0)
@@ -443,32 +455,46 @@
* modes without providing a mode definition).
*/
/* Record the name of this page. */
- pagenum = strtol(str_pagenum, NULL, 0);
- nameentry_create(pagenum, str_pagename);
+ str_subpage = str_page;
+ strsep(&str_subpage, ",");
+ page = strtol(str_page, NULL, 0);
+ if (str_subpage)
+ subpage = strtol(str_subpage, NULL, 0);
+ else
+ subpage = 0;
+ nameentry_create(page, subpage, str_pagename);
SETSTATE_LOCATE;
} else if (depth == 0 && c == PAGENAME_START) {
SETSTATE_PAGENAME;
} else if (c == PAGEDEF_START) {
- pagenum = strtol(str_pagenum, NULL, 0);
+ str_subpage = str_page;
+ strsep(&str_subpage, ",");
+ page = strtol(str_page, NULL, 0);
+ if (str_subpage)
+ subpage = strtol(str_subpage, NULL, 0);
+ else
+ subpage = 0;
if (depth == 1) {
/* Record the name of this page. */
- nameentry_create(pagenum, str_pagename);
+ nameentry_create(page, subpage,
+ str_pagename);
/*
* Only record the format if this is
* the page we are interested in.
*/
- if (page == pagenum && !found)
+ if (lpage == page &&
+ lsubpage == subpage && !found)
SETSTATE_PAGEDEF;
}
} else if (c == PAGEDEF_END) {
/* Reset the processor state. */
SETSTATE_LOCATE;
- } else if (depth == 0 && ! BUFFERFULL(str_pagenum)) {
- strncat(str_pagenum, &c, 1);
+ } else if (depth == 0 && ! BUFFERFULL(str_page)) {
+ strncat(str_page, &c, 1);
} else if (depth == 0) {
errx(EX_OSFILE, "%s:%d: %s %zd %s", pagedb_path,
lineno, "page identifier exceeds",
- sizeof(str_pagenum) - 1, "characters");
+ sizeof(str_page) - 1, "characters");
}
break;
@@ -484,7 +510,7 @@
} else {
errx(EX_OSFILE, "%s:%d: %s %zd %s", pagedb_path,
lineno, "page name exceeds",
- sizeof(str_pagenum) - 1, "characters");
+ sizeof(str_page) - 1, "characters");
}
break;
@@ -525,88 +551,111 @@
}
static void
-editlist_populate(struct cam_device *device, int modepage, int page_control,
- int dbd, int retries, int timeout)
+editlist_populate(struct cam_device *device, int dbd, int pc, int page,
+ int subpage, int task_attr, int retries, int timeout)
{
u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
u_int8_t *mode_pars; /* Pointer to modepage params. */
struct scsi_mode_header_6 *mh; /* Location of mode header. */
struct scsi_mode_page_header *mph;
+ struct scsi_mode_page_header_sp *mphsp;
+ size_t len;
STAILQ_INIT(&editlist);
/* Fetch changeable values; use to build initial editlist. */
- mode_sense(device, modepage, 1, dbd, retries, timeout, data,
- sizeof(data));
+ mode_sense(device, dbd, 1, page, subpage, task_attr, retries, timeout,
+ data, sizeof(data));
mh = (struct scsi_mode_header_6 *)data;
mph = MODE_PAGE_HEADER(mh);
- mode_pars = MODE_PAGE_DATA(mph);
+ if ((mph->page_code & SMPH_SPF) == 0) {
+ mode_pars = (uint8_t *)(mph + 1);
+ len = mph->page_length;
+ } else {
+ mphsp = (struct scsi_mode_page_header_sp *)mph;
+ mode_pars = (uint8_t *)(mphsp + 1);
+ len = scsi_2btoul(mphsp->page_length);
+ }
+ len = MIN(len, sizeof(data) - (mode_pars - data));
/* Decode the value data, creating edit_entries for each value. */
- buff_decode_visit(mode_pars, mh->data_length, format,
- editentry_create, 0);
+ buff_decode_visit(mode_pars, len, format, editentry_create, 0);
/* Fetch the current/saved values; use to set editentry values. */
- mode_sense(device, modepage, page_control, dbd, retries, timeout, data,
- sizeof(data));
- buff_decode_visit(mode_pars, mh->data_length, format,
- editentry_update, 0);
+ mode_sense(device, dbd, pc, page, subpage, task_attr, retries, timeout,
+ data, sizeof(data));
+ buff_decode_visit(mode_pars, len, format, editentry_update, 0);
}
static void
-editlist_save(struct cam_device *device, int modepage, int page_control,
- int dbd, int retries, int timeout)
+editlist_save(struct cam_device *device, int dbd, int pc, int page,
+ int subpage, int task_attr, int retries, int timeout)
{
u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
u_int8_t *mode_pars; /* Pointer to modepage params. */
struct scsi_mode_header_6 *mh; /* Location of mode header. */
struct scsi_mode_page_header *mph;
+ struct scsi_mode_page_header_sp *mphsp;
+ size_t len, hlen;
/* Make sure that something changed before continuing. */
if (! editlist_changed)
return;
- /*
- * Preload the CDB buffer with the current mode page data.
- * XXX If buff_encode_visit would return the number of bytes encoded
- * we *should* use that to build a header from scratch. As it is
- * now, we need mode_sense to find out the page length.
- */
- mode_sense(device, modepage, page_control, dbd, retries, timeout, data,
- sizeof(data));
+ /* Preload the CDB buffer with the current mode page data. */
+ mode_sense(device, dbd, pc, page, subpage, task_attr, retries, timeout,
+ data, sizeof(data));
/* Initial headers & offsets. */
mh = (struct scsi_mode_header_6 *)data;
mph = MODE_PAGE_HEADER(mh);
- mode_pars = MODE_PAGE_DATA(mph);
+ if ((mph->page_code & SMPH_SPF) == 0) {
+ hlen = sizeof(*mph);
+ mode_pars = (uint8_t *)(mph + 1);
+ len = mph->page_length;
+ } else {
+ mphsp = (struct scsi_mode_page_header_sp *)mph;
+ hlen = sizeof(*mphsp);
+ mode_pars = (uint8_t *)(mphsp + 1);
+ len = scsi_2btoul(mphsp->page_length);
+ }
+ len = MIN(len, sizeof(data) - (mode_pars - data));
/* Encode the value data to be passed back to the device. */
- buff_encode_visit(mode_pars, mh->data_length, format,
- editentry_save, 0);
+ buff_encode_visit(mode_pars, len, format, editentry_save, 0);
/* Eliminate block descriptors. */
- bcopy(mph, ((u_int8_t *)mh) + sizeof(*mh),
- sizeof(*mph) + mph->page_length);
+ bcopy(mph, mh + 1, hlen + len);
/* Recalculate headers & offsets. */
+ mh->data_length = 0; /* Reserved for MODE SELECT command. */
mh->blk_desc_len = 0; /* No block descriptors. */
- mh->dev_spec = 0; /* Clear device-specific parameters. */
+ /*
+ * Tape drives include write protect (WP), Buffered Mode and Speed
+ * settings in the device-specific parameter. Clearing this
+ * parameter on a mode select can have the effect of turning off
+ * write protect or buffered mode, or changing the speed setting of
+ * the tape drive.
+ *
+ * Disks report DPO/FUA support via the device specific parameter
+ * for MODE SENSE, but the bit is reserved for MODE SELECT. So we
+ * clear this for disks (and other non-tape devices) to avoid
+ * potential errors from the target device.
+ */
+ if (device->pd_type != T_SEQUENTIAL)
+ mh->dev_spec = 0;
mph = MODE_PAGE_HEADER(mh);
- mode_pars = MODE_PAGE_DATA(mph);
+ mph->page_code &= ~SMPH_PS; /* Reserved for MODE SELECT command. */
- mph->page_code &= SMS_PAGE_CODE;/* Isolate just the page code. */
- mh->data_length = 0; /* Reserved for MODE SELECT command. */
-
/*
* Write the changes back to the device. If the user editted control
* page 3 (saved values) then request the changes be permanently
* recorded.
*/
- mode_select(device,
- (page_control << PAGE_CTRL_SHIFT == SMS_PAGE_CTRL_SAVED),
- retries, timeout, (u_int8_t *)mh,
- sizeof(*mh) + mh->blk_desc_len + sizeof(*mph) + mph->page_length);
+ mode_select(device, (pc << PAGE_CTRL_SHIFT == SMS_PAGE_CTRL_SAVED),
+ task_attr, retries, timeout, (u_int8_t *)mh,
+ sizeof(*mh) + hlen + len);
}
static int
@@ -775,24 +824,33 @@
}
static void
-modepage_dump(struct cam_device *device, int page, int page_control, int dbd,
- int retries, int timeout)
+modepage_dump(struct cam_device *device, int dbd, int pc, int page, int subpage,
+ int task_attr, int retries, int timeout)
{
u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
u_int8_t *mode_pars; /* Pointer to modepage params. */
struct scsi_mode_header_6 *mh; /* Location of mode header. */
struct scsi_mode_page_header *mph;
- int indx; /* Index for scanning mode params. */
+ struct scsi_mode_page_header_sp *mphsp;
+ size_t indx, len;
- mode_sense(device, page, page_control, dbd, retries, timeout, data,
- sizeof(data));
+ mode_sense(device, dbd, pc, page, subpage, task_attr, retries, timeout,
+ data, sizeof(data));
mh = (struct scsi_mode_header_6 *)data;
mph = MODE_PAGE_HEADER(mh);
- mode_pars = MODE_PAGE_DATA(mph);
+ if ((mph->page_code & SMPH_SPF) == 0) {
+ mode_pars = (uint8_t *)(mph + 1);
+ len = mph->page_length;
+ } else {
+ mphsp = (struct scsi_mode_page_header_sp *)mph;
+ mode_pars = (uint8_t *)(mphsp + 1);
+ len = scsi_2btoul(mphsp->page_length);
+ }
+ len = MIN(len, sizeof(data) - (mode_pars - data));
/* Print the raw mode page data with newlines each 8 bytes. */
- for (indx = 0; indx < mph->page_length; indx++) {
+ for (indx = 0; indx < len; indx++) {
printf("%02x%c",mode_pars[indx],
(((indx + 1) % 8) == 0) ? '\n' : ' ');
}
@@ -810,8 +868,8 @@
}
void
-mode_edit(struct cam_device *device, int page, int page_control, int dbd,
- int edit, int binary, int retry_count, int timeout)
+mode_edit(struct cam_device *device, int dbd, int pc, int page, int subpage,
+ int edit, int binary, int task_attr, int retry_count, int timeout)
{
const char *pagedb_path; /* Path to modepage database. */
@@ -822,7 +880,8 @@
if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
pagedb_path = DEFAULT_SCSI_MODE_DB;
- if (load_format(pagedb_path, page) != 0 && (edit || verbose)) {
+ if (load_format(pagedb_path, page, subpage) != 0 &&
+ (edit || verbose)) {
if (errno == ENOENT) {
/* Modepage database file not found. */
warn("cannot open modepage database \"%s\"",
@@ -829,8 +888,9 @@
pagedb_path);
} else if (errno == ESRCH) {
/* Modepage entry not found in database. */
- warnx("modepage %d not found in database"
- "\"%s\"", page, pagedb_path);
+ warnx("modepage 0x%02x,0x%02x not found in "
+ "database \"%s\"", page, subpage,
+ pagedb_path);
}
/* We can recover in display mode, otherwise we exit. */
if (!edit) {
@@ -840,22 +900,22 @@
exit(EX_OSFILE);
}
- editlist_populate(device, page, page_control, dbd, retry_count,
- timeout);
+ editlist_populate(device, dbd, pc, page, subpage, task_attr,
+ retry_count, timeout);
}
if (edit) {
- if (page_control << PAGE_CTRL_SHIFT != SMS_PAGE_CTRL_CURRENT &&
- page_control << PAGE_CTRL_SHIFT != SMS_PAGE_CTRL_SAVED)
+ if (pc << PAGE_CTRL_SHIFT != SMS_PAGE_CTRL_CURRENT &&
+ pc << PAGE_CTRL_SHIFT != SMS_PAGE_CTRL_SAVED)
errx(EX_USAGE, "it only makes sense to edit page 0 "
"(current) or page 3 (saved values)");
modepage_edit();
- editlist_save(device, page, page_control, dbd, retry_count,
- timeout);
+ editlist_save(device, dbd, pc, page, subpage, task_attr,
+ retry_count, timeout);
} else if (binary || STAILQ_EMPTY(&editlist)) {
/* Display without formatting information. */
- modepage_dump(device, page, page_control, dbd, retry_count,
- timeout);
+ modepage_dump(device, dbd, pc, page, subpage, task_attr,
+ retry_count, timeout);
} else {
/* Display with format. */
modepage_write(stdout, 0);
@@ -863,44 +923,55 @@
}
void
-mode_list(struct cam_device *device, int page_control, int dbd,
- int retry_count, int timeout)
+mode_list(struct cam_device *device, int dbd, int pc, int subpages,
+ int task_attr, int retry_count, int timeout)
{
u_int8_t data[MAX_COMMAND_SIZE];/* Buffer to hold sense data. */
struct scsi_mode_header_6 *mh; /* Location of mode header. */
struct scsi_mode_page_header *mph;
+ struct scsi_mode_page_header_sp *mphsp;
struct pagename *nameentry;
const char *pagedb_path;
- int len;
+ int len, page, subpage;
if ((pagedb_path = getenv("SCSI_MODES")) == NULL)
pagedb_path = DEFAULT_SCSI_MODE_DB;
- if (load_format(pagedb_path, 0) != 0 && verbose && errno == ENOENT) {
+ if (load_format(pagedb_path, 0, 0) != 0 && verbose && errno == ENOENT) {
/* Modepage database file not found. */
warn("cannot open modepage database \"%s\"", pagedb_path);
}
/* Build the list of all mode pages by querying the "all pages" page. */
- mode_sense(device, SMS_ALL_PAGES_PAGE, page_control, dbd, retry_count,
- timeout, data, sizeof(data));
+ mode_sense(device, dbd, pc, SMS_ALL_PAGES_PAGE,
+ subpages ? SMS_SUBPAGE_ALL : 0,
+ task_attr, retry_count, timeout, data, sizeof(data));
mh = (struct scsi_mode_header_6 *)data;
- len = mh->blk_desc_len; /* Skip block descriptors. */
+ len = sizeof(*mh) + mh->blk_desc_len; /* Skip block descriptors. */
/* Iterate through the pages in the reply. */
while (len < mh->data_length) {
/* Locate the next mode page header. */
- mph = (struct scsi_mode_page_header *)
- ((intptr_t)mh + sizeof(*mh) + len);
+ mph = (struct scsi_mode_page_header *)((intptr_t)mh + len);
- mph->page_code &= SMS_PAGE_CODE;
- nameentry = nameentry_lookup(mph->page_code);
+ if ((mph->page_code & SMPH_SPF) == 0) {
+ page = mph->page_code & SMS_PAGE_CODE;
+ subpage = 0;
+ len += sizeof(*mph) + mph->page_length;
+ } else {
+ mphsp = (struct scsi_mode_page_header_sp *)mph;
+ page = mphsp->page_code & SMS_PAGE_CODE;
+ subpage = mphsp->subpage;
+ len += sizeof(*mphsp) + scsi_2btoul(mphsp->page_length);
+ }
- if (nameentry == NULL || nameentry->name == NULL)
- printf("0x%02x\n", mph->page_code);
- else
- printf("0x%02x\t%s\n", mph->page_code,
- nameentry->name);
- len += mph->page_length + sizeof(*mph);
+ nameentry = nameentry_lookup(page, subpage);
+ if (subpage == 0) {
+ printf("0x%02x\t%s\n", page,
+ nameentry ? nameentry->name : "");
+ } else {
+ printf("0x%02x,0x%02x\t%s\n", page, subpage,
+ nameentry ? nameentry->name : "");
+ }
}
}
Modified: trunk/sbin/camcontrol/progress.c
===================================================================
--- trunk/sbin/camcontrol/progress.c 2018-07-01 21:16:14 UTC (rev 11236)
+++ trunk/sbin/camcontrol/progress.c 2018-07-01 21:16:46 UTC (rev 11237)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/* $NetBSD: progressbar.c,v 1.21 2009/04/12 10:18:52 lukem Exp $ */
/*-
@@ -42,7 +43,7 @@
#include <unistd.h>
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sbin/camcontrol/progress.c 287203 2015-08-27 13:17:05Z ken $");
#include "progress.h"
@@ -124,6 +125,7 @@
* stars should contain at least
* sizeof(buf) - BAROVERHEAD entries
*/
+#define MIN_BAR_LEN 10
static const char stars[] =
"*****************************************************************************"
"*****************************************************************************"
@@ -134,13 +136,31 @@
uint64_t bytespersec;
uint64_t abbrevsize;
int64_t secsleft;
- size_t barlength;
+ ssize_t barlength;
size_t starc;
char hours[12];
char buf[256];
int len;
+ int prefix_len;
- barlength = MIN(sizeof(buf) - 1, (unsigned)prog->ttywidth) - BAROVERHEAD - strlen(prog->prefix);
+ prefix_len = strlen(prog->prefix);
+ barlength = MIN(sizeof(buf) - 1, (unsigned)prog->ttywidth) -
+ BAROVERHEAD - prefix_len;
+ if (barlength < MIN_BAR_LEN) {
+ int tmp_prefix_len;
+ /*
+ * In this case the TTY width is too small or the prefix is
+ * too large for a progress bar. We'll try decreasing the
+ * prefix length.
+ */
+ barlength = MIN_BAR_LEN;
+ tmp_prefix_len = MIN(sizeof(buf) - 1,(unsigned)prog->ttywidth) -
+ BAROVERHEAD - MIN_BAR_LEN;
+ if (tmp_prefix_len > 0)
+ prefix_len = tmp_prefix_len;
+ else
+ prefix_len = 0;
+ }
starc = (barlength * prog->percent) / 100;
abbrevsize = prog->done;
for (bytesabbrev = 0; abbrevsize >= 100000 && bytesabbrev < NSUFFIXES; bytesabbrev++) {
@@ -171,8 +191,8 @@
}
secs = secsleft % SECSPERHOUR;
len = snprintf(buf, sizeof(buf),
- "\r%s %3lld%% |%.*s%*s| %5lld %-3s %3lld.%02d %.2sB/s %s%02d:%02d ETA",
- (prog->prefix) ? prog->prefix : "",
+ "\r%.*s %3lld%% |%.*s%*s| %5lld %-3s %3lld.%02d %.2sB/s %s%02d:%02d ETA",
+ prefix_len, (prog->prefix) ? prog->prefix : "",
(long long)prog->percent,
(int)starc, stars, (int)(barlength - starc), "",
(long long)abbrevsize,
More information about the Midnightbsd-cvs
mailing list