├── TODO ├── Makefile.am ├── .gitignore ├── README ├── src ├── lsfirewire.8.in ├── lsfirewirephy.8.in ├── firewire-request.8.in ├── firewire-phy-command.8.in ├── lsfirewire.in ├── lsfirewirephy.c ├── firewire-phy-command.c ├── crpp └── firewire-request.c ├── configure.ac └── COPYING /TODO: -------------------------------------------------------------------------------- 1 | tools for: 2 | * bus manager: 3 | - make sure SPLIT_TIMEOUT registers are consistent 4 | - initialize BUS_TIME 5 | - set PRIORITY_BUDGET 6 | (higher priority for SBP-x devices, or configurable?) 7 | - optimize gap counts by PHY pinging 8 | (also take over table-based optimization from the kernel) 9 | - disable cycle master when not needed 10 | - IEEE1212-2001 7.5.4.2 with a cute penguin 11 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS := subdir-objects 2 | 3 | bin_SCRIPTS = src/lsfirewire 4 | bin_PROGRAMS = src/firewire-request 5 | 6 | pkglibexec_SCRIPTS = src/crpp 7 | 8 | man_MANS = src/lsfirewire.8 src/firewire-request.8 9 | 10 | if HAVE_CDEV_4 11 | bin_PROGRAMS += src/lsfirewirephy src/firewire-phy-command 12 | man_MANS += src/lsfirewirephy.8 src/firewire-phy-command.8 13 | endif 14 | 15 | EXTRA_DIST = README src/crpp 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # generated by aclocal 2 | aclocal.m4 3 | autom4te.cache 4 | # generated by automake 5 | Makefile.in 6 | compile 7 | depcomp 8 | install-sh 9 | missing 10 | # generated by autoconf 11 | configure 12 | # generated by configure 13 | Makefile 14 | config.log 15 | config.status 16 | src/lsfirewire 17 | src/lsfirewire.8 18 | src/lsfirewirephy.8 19 | src/firewire-request.8 20 | src/firewire-phy-command.8 21 | # generated by make 22 | .deps 23 | .dirstamp 24 | *.o 25 | src/lsfirewirephy 26 | src/firewire-request 27 | src/firewire-phy-command 28 | # generated by make dist 29 | linux-firewire-utils-*.tar.gz 30 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Linux FireWire utilities 2 | ======================== 3 | 4 | What's this? 5 | ------------ 6 | 7 | The linux-firewire-utils package contains Linux FireWire utilities for 8 | listing devices (lsfirewire, lsfirewirephy) and for querying and 9 | configuring devices (firewire-request, firewire-phy-command). 10 | 11 | 12 | Installation 13 | ------------ 14 | 15 | This package uses GNU Autoconf; you can use the standard installation 16 | sequence: 17 | 18 | configure 19 | make 20 | make install 21 | 22 | 23 | Authors 24 | ------- 25 | 26 | Clemens Ladisch 27 | Stefan Richter 28 | 29 | 30 | License 31 | ------- 32 | 33 | GNU GPL v2; see the file COPYING. 34 | 35 | 36 | Links 37 | ----- 38 | 39 | Report bugs to . 40 | 41 | linux-firewire-utils home page: . 42 | -------------------------------------------------------------------------------- /src/lsfirewire.8.in: -------------------------------------------------------------------------------- 1 | .TH lsfirewire 8 "14 Mar 2011" "@PACKAGE_STRING@" 2 | .IX lsfirewire 3 | .SH NAME 4 | lsfirewire \- list FireWire devices 5 | .SH SYNOPSIS 6 | .B lsfirewire 7 | .RI [ options ] 8 | .SH DESCRIPTION 9 | The 10 | .B lsfirewire 11 | utility displays information about all FireWire devices, i.e., 12 | the system's FireWire controllers and the devices connected to them. 13 | .SH OPTIONS 14 | .TP 15 | .B \-v, \-\-verbose 16 | Be more verbose and display detailed information about the devices. 17 | Use this option twice to additionally display the contents of the devices' 18 | configuration ROM. 19 | .TP 20 | .B \-\-help 21 | Print a summary of the command-line options and exit. 22 | .TP 23 | .B \-\-version 24 | Print the version number of 25 | .B lsfirewire 26 | on the standard output and exit. 27 | .SH FILES 28 | .IR /sys/bus/firewire/devices/ * 29 | .SH BUGS 30 | Report bugs to <@PACKAGE_BUGREPORT@>. 31 | .br 32 | @PACKAGE_NAME@ home page: <@PACKAGE_URL@>. 33 | .SH SEE ALSO 34 | .BR firewire\-request (8), 35 | .BR lsfirewirephy (8) 36 | -------------------------------------------------------------------------------- /src/lsfirewirephy.8.in: -------------------------------------------------------------------------------- 1 | .TH lsfirewirephy 8 "2 Mar 2011" "@PACKAGE_STRING@" 2 | .IX lsfirewirephy 3 | .SH NAME 4 | lsfirewirephy \- list PHYs of FireWire devices 5 | .SH SYNOPSIS 6 | .B lsfirewirephy 7 | .RI [ options ] 8 | .RI [ devicenode 9 | .RI [ phyid ]] 10 | .SH DESCRIPTION 11 | The 12 | .B lsfirewirephy 13 | utility prints the device IDs of the PHYs of one or more FireWire devices. 14 | .PP 15 | The 16 | .I devicenode 17 | parameter specifies the device file 18 | .RI ( /dev/fw *) 19 | of the device whose PHY ID you want to print. 20 | .PP 21 | The 22 | .I phyid 23 | parameter specifies the node number of the device; 24 | in this case, 25 | .I devicenode 26 | identifies the bus and must be a local node. 27 | . 28 | The node number is zero-based and cannot be larger than 62. 29 | . 30 | Use this to address a single device that does not have a 31 | .IR /dev/fw * 32 | file, 33 | such as a repeater, a powered-down device, or a VersaPHY device. 34 | .PP 35 | Without any parameters, 36 | .B lsfirewirephy 37 | prints the PHY IDs of all devices on all buses. 38 | .SH OPTIONS 39 | .TP 40 | .B \-\-help 41 | Print a summary of the command-line options and exit. 42 | .TP 43 | .B \-\-version 44 | Print the version number of 45 | .B lsfirewirephy 46 | on the standard output and exit. 47 | .SH BUGS 48 | Report bugs to <@PACKAGE_BUGREPORT@>. 49 | .br 50 | @PACKAGE_NAME@ home page: <@PACKAGE_URL@>. 51 | .SH SEE ALSO 52 | .BR firewire-phy-command (8), 53 | .BR lsfirewire (8) 54 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([Linux FireWire utilities], [0.4], [linux1394-devel@lists.sourceforge.net], 2 | [linux-firewire-utils], [https://github.com/cladisch/linux-firewire-utils]) 3 | AC_CONFIG_SRCDIR([src/lsfirewire.in]) 4 | AM_INIT_AUTOMAKE([foreign no-define silent-rules]) 5 | 6 | # enable silent rules by default 7 | AS_IF([test "${enable_silent_rules+set}" != set], 8 | [AM_DEFAULT_VERBOSITY=0]) 9 | 10 | AC_PROG_CC 11 | 12 | AC_CHECK_HEADERS_ONCE([ 13 | asm/byteorder.h 14 | linux/firewire-cdev.h 15 | linux/firewire-constants.h 16 | ]) 17 | 18 | AS_IF([test "$ac_cv_header_asm_byteorder_h" != yes], 19 | [AC_MSG_ERROR([Linux kernel headers not found])]) 20 | 21 | JUJU=1 22 | test "$ac_cv_header_linux_firewire_cdev_h" = yes || JUJU= 23 | test "$ac_cv_header_linux_firewire_constants_h" = yes || JUJU= 24 | AS_IF([test -z "$JUJU"], 25 | [AC_MSG_ERROR([required Linux FireWire headers not found])]) 26 | 27 | AC_CHECK_DECL([FW_CDEV_EVENT_REQUEST2], [], [], 28 | [#include ]) 29 | AC_MSG_CHECKING([for cdev ABI version 4]) 30 | juju4=$ac_cv_have_decl_FW_CDEV_EVENT_REQUEST2 31 | AC_MSG_RESULT([$juju4]) 32 | AS_IF([test "$juju4" = yes], 33 | [AC_DEFINE([HAVE_CDEV_4], [1])]) 34 | AM_CONDITIONAL([HAVE_CDEV_4], 35 | [test "$juju4" = yes]) 36 | 37 | AC_OUTPUT([ 38 | Makefile 39 | src/lsfirewire 40 | src/lsfirewire.8 41 | src/lsfirewirephy.8 42 | src/firewire-request.8 43 | src/firewire-phy-command.8 44 | ]) 45 | 46 | AS_IF([test "$juju4" != yes], 47 | [AC_MSG_WARN([to enable all utilities, install Linux 2.6.36 headers])]) 48 | -------------------------------------------------------------------------------- /src/firewire-request.8.in: -------------------------------------------------------------------------------- 1 | .TH firewire\-request 8 "22 May 2011" "@PACKAGE_STRING@" 2 | .IX firewire\-request 3 | .SH NAME 4 | firewire\-request \- query and configure FireWire devices 5 | .SH SYNOPSIS 6 | .B firewire\-request 7 | .RI [ options ] 8 | .I device 9 | .I command 10 | .RI [ parameters ] 11 | .SH DESCRIPTION 12 | .B firewire\-request 13 | is a utility to query and configure FireWire devices. 14 | .PP 15 | The 16 | .I device 17 | parameter specifies the device file 18 | .RB ( /dev/fw *) 19 | of the device that is to be accessed. 20 | .PP 21 | All numbers must be specified in hexadecimal notation. 22 | . 23 | When specifying data blocks, you can separate bytes or quadlets with spaces, 24 | but then you have to remember to quote them in the shell 25 | so that all bytes are recognized as a single parameter. 26 | .PP 27 | In the following commands, 28 | .I address 29 | is either a 48-bit address or a register name. 30 | .PP 31 | The following commands are available: 32 | .TP 33 | \fBfirewire\-request\fP \fIdevice\fP \fBread\fP \fIaddress\fP [\fIlength\fP] 34 | Send a read request to the device, 35 | and print the value returned by the device, if successful. 36 | .IP 37 | If 38 | .I length 39 | is not specified, it is derived from 40 | .IR address : 41 | for a named register, the register's length is used; 42 | for a numerical address, a default length of one quadlet (4\~bytes) is used. 43 | .TP 44 | \fBfirewire\-request\fP \fIdevice\fP \fBwrite\fP|\fBbroadcast\fP \fIaddress\fP \fIdata\fP 45 | Send a write request to the device. 46 | .IP 47 | Broadcasts are allowed only for a 48 | .I device 49 | that corresponds to a local controller, 50 | and are sent to all the other devices on the bus. 51 | .TP 52 | \fBfirewire\-request\fP \fIdevice locktype address data \fP[\fIdata2\fP] 53 | Execute a lock transaction (an atomic change) on the device. 54 | .IP 55 | .I locktype 56 | can be any of the following: 57 | .RS 58 | .TP 59 | .B mask_swap 60 | Change the bits set in 61 | .I data 62 | to the value in 63 | .IR data2 . 64 | .TP 65 | .B compare_swap 66 | If the register has the same value as 67 | .IR data , 68 | set it to 69 | .IR data2 . 70 | .TP 71 | .BR add [ _big ] 72 | Increase the register by 73 | .IR data . 74 | .TP 75 | .B add_little 76 | Increase the little-endian register by 77 | .IR data . 78 | .TP 79 | .BR bounded_add [ _big ] 80 | If the register has not the the same value as 81 | .IR data , 82 | increase it by 83 | .IR data2 . 84 | .TP 85 | .BR wrap_add [ _big ] 86 | If the register has not the the same value as 87 | .IR data , 88 | increase it by 89 | .IR data2 , 90 | else set it to 91 | .IR data2 . 92 | .RE 93 | .IP 94 | All transaction types except 95 | .BR add / add_big / add_little 96 | require two parameters. 97 | .IP 98 | .B firewire\-request 99 | will print the value returned by the device, 100 | which is the old register value at the beginning of the transaction. 101 | .TP 102 | \fBfirewire\-request\fP \fIdevice\fP \fBfcp\fP \fIdata\fP 103 | Send the message 104 | .I data 105 | to the device's FCP command register, 106 | and print the response returned by the device 107 | to the local FCP response register. 108 | .TP 109 | \fBfirewire\-request\fP \fIdevice\fP \fBreset\fP|\fBlong_reset\fP 110 | Issue a bus reset on the bus connected to 111 | .IR device . 112 | .SH OPTIONS 113 | .TP 114 | .B \-D, \-\-dump\-register\-names 115 | Print register names that can be used as 116 | .IR address , 117 | and exit. 118 | .TP 119 | .B \-v, \-\-verbose 120 | When used together with 121 | .BR \-\-dump\-register\-names , 122 | print the complete list of register names. 123 | .TP 124 | .B \-h, \-\-help 125 | Print a summary of the command-line options and exit. 126 | .TP 127 | .B \-V, \-\-version 128 | Print the version number of 129 | .B firewire\-request 130 | on the standard output and exit. 131 | .SH NOTES 132 | Whether you can access a device depends on the permissions set for its device file. 133 | . 134 | Usually, most devices require root privileges. 135 | .SH BUGS 136 | Report bugs to <@PACKAGE_BUGREPORT@>. 137 | .br 138 | @PACKAGE_NAME@ home page: <@PACKAGE_URL@>. 139 | .SH SEE ALSO 140 | .BR lsfirewire (8), 141 | .BR firewire-phy-command (8) 142 | -------------------------------------------------------------------------------- /src/firewire-phy-command.8.in: -------------------------------------------------------------------------------- 1 | .TH firewire\-phy\-command 8 "5 Jun 2011" "@PACKAGE_STRING@" 2 | .IX firewire\-phy\-command 3 | .SH NAME 4 | firewire\-phy\-command \- low-level FireWire bus and PHY configuration 5 | .SH SYNOPSIS 6 | .B firewire\-phy\-command 7 | .RI [ options ] 8 | .I command 9 | .RI [ parameters ] 10 | .SH DESCRIPTION 11 | .B firewire\-phy\-command 12 | is a utility to set low-level configuration parameters 13 | and to manage port power saving. 14 | .PP 15 | In the following commands, 16 | numbers must be specified in decimal, 17 | or in hexadecimal with a "0x" prefix. 18 | .PP 19 | .I node 20 | is either the device file 21 | .RB ( /dev/fw *) 22 | of the node that is to be accessed, 23 | or the node number. 24 | .PP 25 | The following commands are available: 26 | .TP 27 | \fBfirewire\-phy\-command\fP \fBconfig\fP [\fBroot\fP \fInode\fP] [\fBgapcount\fP \fIgapcount\fP] 28 | The 29 | .B root 30 | parameter specifies the node that is forced to become the next root node. 31 | .IP 32 | The 33 | .B gapcount 34 | parameter specifies a timing parameter used to optimize performance. 35 | This must be derived from the actual worst-case round-trip delay 36 | between any two nodes. 37 | .IP 38 | At least one of 39 | .B root 40 | or 41 | .B gapcount 42 | must be specified. 43 | .IP 44 | The configuration changes take effect after the next bus reset, 45 | which must be issued immediately after this command; see the 46 | .B reset 47 | command below. 48 | .TP 49 | \fBfirewire\-phy\-command\fP \fBping\fP \fInode\fP 50 | Send a ping packet to node 51 | .IR node , 52 | and print 53 | .IR node 's 54 | answer (its self ID) together with the round-trip time. 55 | .TP 56 | \fBfirewire\-phy\-command\fP \fBread\fP \fInode\fP [\fIpage\fP \fIport\fP] \fIregister\fP 57 | Read a PHY register on node 58 | .I node 59 | and print the register value. 60 | .IP 61 | Registers 0 to 7 are global; 62 | registers 8 to 15 are paged and require both a page number and a port number. 63 | .TP 64 | \fBfirewire\-phy\-command\fP \fBnop\fP \fInode\fP \fIport\fP 65 | .TQ 66 | \fBfirewire\-phy\-command\fP \fBdisable\fP \fInode\fP \fIport\fP 67 | .TQ 68 | \fBfirewire\-phy\-command\fP \fBenable\fP \fInode\fP \fIport\fP 69 | .TQ 70 | \fBfirewire\-phy\-command\fP \fBsuspend\fP \fInode\fP \fIport\fP 71 | .TQ 72 | \fBfirewire\-phy\-command\fP \fBresume\fP \fInode\fP \fIport\fP 73 | .TQ 74 | \fBfirewire\-phy\-command\fP \fBclear\fP \fInode\fP \fIport\fP 75 | Send a remote command packet to port 76 | .I port 77 | of node 78 | .IR node , 79 | and print the new port status. 80 | .RS 81 | .TP 82 | .B nop 83 | Do nothing, just return the status. 84 | This can be used to read the port status, 85 | or to check whether a port exists. 86 | .TP 87 | .B disable 88 | Disable the port and its connected peer port. 89 | This is the equivalent of breaking the connection 90 | between the port and the cable. 91 | .TP 92 | .B enable 93 | Re-enable a disabled port. 94 | .TP 95 | .B suspend 96 | Suspend the connection between the port and its peer port, 97 | and recursively suspend all other active ports on the peer port's node. 98 | .TP 99 | .B resume 100 | Resume the port and all other ports in the suspended domain. 101 | .TP 102 | .B clear 103 | Clear the port's fault status bit(s). 104 | If a port entered the suspended state in a faulted condition 105 | because its peer port did not suspend correctly, 106 | clearing the fault is required after the 107 | .B resume 108 | to continue normal operation. 109 | .RE 110 | .TP 111 | \fBfirewire\-phy\-command\fP \fBresume\fP 112 | Broadcast a resume packet to all ports on the bus. 113 | .TP 114 | \fBfirewire\-phy\-command\fP \fBlinkon\fP \fInode\fP 115 | Send a link-on packet, which instructs node 116 | .I node 117 | to switch on, i.e., to come out of powersaving mode. 118 | .TP 119 | \fBfirewire\-phy\-command\fP \fBreset\fP 120 | Issue a bus reset. 121 | .SH OPTIONS 122 | .TP 123 | \fB\-b\fP, \fB\-\-bus\fP=\fInode\fP 124 | If there are multiple FireWire buses, 125 | i.e., if the computer has multiple FireWire controllers, 126 | this option specifies on which bus the command is sent. 127 | .I node 128 | must be the device file of the controller, 129 | or the controller's card number. 130 | .IP 131 | This option is not needed if a command's 132 | .I node 133 | parameter specifies a device node, 134 | as that parameter already implies the bus to use. 135 | .TP 136 | .BR \-h ", " \-\-help 137 | Print a summary of the command-line options and exit. 138 | .TP 139 | .BR \-V ", " \-\-version 140 | Print the version number of 141 | .B firewire\-phy\-command 142 | on the standard output and exit. 143 | .SH NOTES 144 | This program needs to access all FireWire device files, 145 | which usually requires root privileges. 146 | .SH BUGS 147 | Report bugs to <@PACKAGE_BUGREPORT@>. 148 | .br 149 | @PACKAGE_NAME@ home page: <@PACKAGE_URL@>. 150 | .SH SEE ALSO 151 | .BR lsfirewirephy (8), 152 | .BR firewire-request (8) 153 | -------------------------------------------------------------------------------- /src/lsfirewire.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # lsfirewire - list FireWire devices, as detected by the Linux kernel 4 | # Copyright (C) 2010-2011 Clemens Ladisch 5 | # 6 | # This script is licensed under the terms of the GNU General 7 | # Public License, version 2. 8 | 9 | SYSFS=/sys 10 | SYSFS_BUS=$SYSFS/bus/firewire 11 | SYSFS_DEVICES=$SYSFS_BUS/devices 12 | SYSFS_LEGACY_BUS=$SYSFS/bus/ieee1394 13 | 14 | show_help() { 15 | cat >&2 <<-HELP 16 | Usage: lsfirewire [options] 17 | Options: 18 | -v, --verbose Show properties of the devices. 19 | Use twice to also show the configuration ROM. 20 | --help Print this message and exit. 21 | --version Print the version number and exit. 22 | 23 | Report bugs to <@PACKAGE_BUGREPORT@>. 24 | @PACKAGE_NAME@ home page: <@PACKAGE_URL@>. 25 | HELP 26 | } 27 | 28 | # Parameters: list of file names. 29 | # Reads into $string the contents of the first existing and nonempty file. 30 | read_string_from() { 31 | string= 32 | while [ $# -gt 0 ]; do 33 | if [ -e "$1" ]; then 34 | read -r string < "$1" 35 | if [ -n "$string" ]; then 36 | return 37 | fi 38 | fi 39 | shift 40 | done 41 | } 42 | 43 | # Parameter: sysfs device path. 44 | # Prints a non-verbose device identification. 45 | show_device() { 46 | # The vendor and model names can be in either the root directory or the 47 | # (first) unit directory, so try both. Try the vendor/model number as 48 | # last resort. 49 | read_string_from "$1/vendor_name" "$1.0/vendor_name" "$1/vendor" 50 | vendor=$string 51 | 52 | read_string_from "$1/model_name" "$1.0/model_name" "$1/model" 53 | model=$string 54 | 55 | echo "$1: $vendor $model" 56 | } 57 | 58 | # Parameters: sysfs property file name; property name. 59 | # Prints a sysfs property, if it exists and is nonempty. 60 | show_property() { 61 | read_string_from "$1" 62 | if [ -n "$string" ]; then 63 | echo "$indent$2: $string" 64 | fi 65 | } 66 | 67 | # Parameter: sysfs device path. 68 | # Prints device information verbosely. 69 | show_device_verbose() { 70 | echo "device $1:" 71 | indent=' ' 72 | show_property "$1/vendor" "vendor ID" 73 | show_property "$1/model" "model ID" 74 | show_property "$1/hardware_version" "hardware version ID" 75 | show_property "$1/vendor_name" "vendor" 76 | show_property "$1/model_name" "model" 77 | show_property "$1/hardware_version_name" "hardware version" 78 | show_property "$1/specifier_id" "specifier ID" 79 | show_property "$1/version" "version" 80 | show_property "$1/guid" "guid" 81 | show_property "$1/units" "units" 82 | 83 | # Sort unit names by number: 84 | units=`echo $1.* | tr ' ' '\n' | sort -n -t. -k2` 85 | indent=' ' 86 | for unit in $units; do 87 | if [ -d "$unit" ]; then 88 | echo " unit $unit:" 89 | show_property "$unit/vendor" "vendor ID" 90 | show_property "$unit/model" "model ID" 91 | show_property "$unit/hardware_version" "hardware version ID" 92 | show_property "$unit/vendor_name" "vendor" 93 | show_property "$unit/model_name" "model" 94 | show_property "$unit/hardware_version_name" "hardware version" 95 | show_property "$unit/specifier_id" "specifier ID" 96 | show_property "$unit/version" "version" 97 | fi 98 | done 99 | } 100 | 101 | # Parameter: sysfs device path. 102 | # Prints the contents of the device's configuration ROM. 103 | show_device_config_rom() { 104 | if [ -z "$CRPP" ]; then 105 | MY_DIR=$(dirname "$0") 106 | if [ "$MY_DIR" == "${MY_DIR#/}" ]; then # relative path? 107 | MY_DIR="$ORIGINAL_PWD/$MY_DIR" 108 | fi 109 | 110 | CRPP="$MY_DIR/../libexec/linux-firewire-utils/crpp" 111 | if [ ! -x "$CRPP" ]; then 112 | CRPP="$MY_DIR/crpp" 113 | if [ ! -x "$CRPP" ]; then 114 | CRPP=crpp 115 | fi 116 | fi 117 | fi 118 | 119 | "$CRPP" < "$1/config_rom" 120 | } 121 | 122 | 123 | # Parse script parameters. 124 | 125 | verbose= 126 | 127 | while [ $# -gt 0 ]; do 128 | case "$1" in 129 | (-v|--verbose) 130 | verbose=${verbose}1 131 | ;; 132 | (-vv) 133 | verbose=${verbose}11 134 | ;; 135 | (--help) 136 | show_help 137 | exit 0 138 | ;; 139 | (--version) 140 | echo "lsfirewire version @PACKAGE_VERSION@" 141 | exit 0 142 | ;; 143 | (*) 144 | echo "Unknown option: $1" >&2 145 | show_help 146 | exit 1 147 | esac 148 | shift 149 | done 150 | 151 | # Check that the firewire-core is loaded. 152 | 153 | if [ ! -d "$SYSFS_BUS" ]; then 154 | if [ -d "$SYSFS_LEGACY_BUS" ]; then 155 | echo "This program does not work with the old ieee1394 stack." >&2 156 | echo "Try unloading the ieee1394 module and then loading firewire-ohci." >&2 157 | else 158 | echo "Directory $SYSFS_BUS not found." >&2 159 | echo "Try loading the firewire-ohci module." >&2 160 | fi 161 | exit 1 162 | fi 163 | 164 | ORIGINAL_PWD="$PWD" 165 | cd "$SYSFS_DEVICES" || exit 0 166 | 167 | # Enumerate devices and print them. 168 | 169 | # Filter for devices "fwX", ignoring units "fwX.Y"; sort device names by number: 170 | devices=`echo fw* | tr ' ' '\n' | grep '^fw[0-9]\+$' | sort -n -tw -k2` 171 | for dev in $devices; do 172 | if [ -z "$verbose" ]; then 173 | show_device "$dev" 174 | else 175 | show_device_verbose "$dev" 176 | if [ ${#verbose} -ge 2 ]; then 177 | show_device_config_rom "$dev" 178 | fi 179 | fi 180 | done 181 | -------------------------------------------------------------------------------- /src/lsfirewirephy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lsfirewirephy.c - list the PHYs on a bus 3 | * 4 | * Copyright 2010-2011 Clemens Ladisch 5 | * 6 | * licensed under the terms of version 2 of the GNU General Public License 7 | */ 8 | 9 | #define _GNU_SOURCE 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define ptr_to_u64(p) ((uintptr_t)(p)) 29 | 30 | #define PHY_REMOTE_ACCESS_PAGED(phy_id, page, port, reg) \ 31 | (((phy_id) << 24) | (5 << 18) | ((page) << 15) | ((port) << 11) | ((reg) << 8)) 32 | #define PHY_REMOTE_REPLY_PAGED(phy_id, page, port, reg, data) \ 33 | (((phy_id) << 24) | (7 << 18) | ((page) << 15) | ((port) << 11) | ((reg) << 8) | (data)) 34 | 35 | typedef __u8 u8; 36 | typedef __u32 u32; 37 | typedef __u64 u64; 38 | 39 | typedef u32 u24; 40 | 41 | static const struct vendor { 42 | u24 oui; 43 | const char *name; 44 | const struct phy { 45 | u24 id; 46 | const char *name; 47 | u24 mask; 48 | } *phys; 49 | } vendors[] = { 50 | { 51 | .oui = 0x00000e, 52 | .name = "Fujitsu", 53 | .phys = (const struct phy[]) { 54 | { 0x086613, "MB86613" }, 55 | {} 56 | } 57 | }, 58 | { 59 | .oui = 0x00004c, 60 | .name = "NEC", 61 | .phys = (const struct phy[]) { 62 | { 0x000201, "PD7286x" }, 63 | { 0x050160, "PD7287x" }, 64 | {} 65 | } 66 | }, 67 | { 68 | .oui = 0x00053d, 69 | .name = "Agere (LSI)", 70 | .phys = (const struct phy[]) { 71 | { 0x053300, "FW533E", 0xffff00 }, 72 | { 0x064300, "FW643(E)", 0xffff00 }, 73 | { 0x084300, "FW843", 0xffff00 }, 74 | {} 75 | } 76 | }, 77 | { 78 | .oui = 0x000cc2, 79 | .name = "ControlNet India (O2Micro)", 80 | .phys = (const struct phy[]) { 81 | { 0x401104, "OZxxx" }, 82 | {} 83 | } 84 | }, 85 | { 86 | .oui = 0x001018, 87 | .name = "Broadcom", 88 | .phys = (const struct phy[]) { 89 | {} 90 | } 91 | }, 92 | { 93 | /* 94 | * This OUI actually belongs to System S.p.A.; 95 | * VIA's OUI is 0x004063. 96 | */ 97 | .oui = 0x001163, 98 | .name = "VIA", 99 | .phys = (const struct phy[]) { 100 | { 0x306001, "VT63xx" }, 101 | {} 102 | } 103 | }, 104 | { 105 | .oui = 0x001454, 106 | .name = "Symwave", 107 | .phys = (const struct phy[]) { 108 | { 0x003181, "SW3080" }, 109 | {} 110 | } 111 | }, 112 | { 113 | .oui = 0x001b8c, 114 | .name = "JMicron", 115 | .phys = (const struct phy[]) { 116 | { 0x038100, "JMB38x" }, 117 | {} 118 | } 119 | }, 120 | { 121 | .oui = 0x00601d, 122 | .name = "Lucent (LSI)", 123 | .phys = (const struct phy[]) { 124 | { 0x032200, "FW322", 0xffff00 }, 125 | { 0x032300, "FW323", 0xffff00 }, 126 | { 0x080200, "FW802", 0xffff00 }, 127 | {} 128 | } 129 | }, 130 | { 131 | .oui = 0x006037, 132 | .name = "Philips (NXP)", 133 | .phys = (const struct phy[]) { 134 | { 0x412801, "PDI1394P25" }, 135 | { 0x422001, "PDI1394P23" }, 136 | { 0x423900, "PDI1394P24", 0xffff0f }, 137 | { 0x431000, "PDI1394P21" }, 138 | { 0x431100, "PDI1394P22" }, 139 | {} 140 | } 141 | }, 142 | { 143 | .oui = 0x00c02d, 144 | .name = "Fujifilm", 145 | .phys = (const struct phy[]) { 146 | { 0x303562, "MD8405B" }, 147 | { 0x303565, "MD8405E" }, 148 | {} 149 | } 150 | }, 151 | { 152 | .oui = 0x080028, 153 | .name = "Texas Instruments", 154 | .phys = (const struct phy[]) { 155 | { 0x42308a, "TSB41LV02A" }, 156 | { 0x424296, "TSB41AB1/2" }, 157 | { 0x424499, "TSB43AB22(A)" }, 158 | { 0x424729, "XIO2200A" }, 159 | { 0x434195, "TSB41AB3" }, 160 | { 0x434615, "TSB43CB43A" }, 161 | { 0x46318a, "TSB41LV06A" }, 162 | { 0x831304, "TSB81BA3(A)" }, 163 | { 0x831306, "TSB81BA3D" }, 164 | { 0x831307, "TSB81BA3E/XIO2213" }, 165 | { 0x833005, "TSB41BA3D" }, 166 | {} 167 | } 168 | }, 169 | { 170 | .oui = 0x10005a, 171 | .name = "IBM", 172 | .phys = (const struct phy[]) { 173 | { 0x218600, "IBM21S860", 0xfffff0 }, 174 | { 0x218610, "IBM21S861", 0xfffff0 }, 175 | { 0x218620, "IBM21S862", 0xfffff0 }, 176 | {} 177 | } 178 | }, 179 | {} 180 | }; 181 | 182 | static char *device_file_name; 183 | static int list_phy_id = -1; 184 | static int fd; 185 | static bool any_unknown_phys; 186 | struct fw_cdev_get_info get_info; 187 | struct fw_cdev_event_bus_reset bus_reset; 188 | 189 | static void help(void) 190 | { 191 | fputs("Usage: lsfirewirephy [options] [devicenode [phyid]]\n" 192 | "Options:\n" 193 | " -h, --help show this message and exit\n" 194 | " -V, --version show version number and exit\n" 195 | "\n" 196 | "Report bugs to <" PACKAGE_BUGREPORT ">.\n" 197 | PACKAGE_NAME " home page: <" PACKAGE_URL ">.\n", 198 | stderr); 199 | } 200 | 201 | static void parse_parameters(int argc, char *argv[]) 202 | { 203 | static const char short_options[] = "hV"; 204 | static const struct option long_options[] = { 205 | { "help", 0, NULL, 'h' }, 206 | { "version", 0, NULL, 'V' }, 207 | {} 208 | }; 209 | int c; 210 | char *endptr; 211 | 212 | while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { 213 | switch (c) { 214 | case 'h': 215 | help(); 216 | exit(EXIT_SUCCESS); 217 | case 'V': 218 | puts("lsfirewirephy version " PACKAGE_VERSION); 219 | exit(EXIT_SUCCESS); 220 | default: 221 | syntax_error: 222 | help(); 223 | exit(EXIT_FAILURE); 224 | } 225 | } 226 | 227 | if (optind < argc) { 228 | device_file_name = strdup(argv[optind++]); 229 | if (!device_file_name) { 230 | fputs("out of memory\n", stderr); 231 | exit(EXIT_FAILURE); 232 | } 233 | 234 | if (optind < argc) { 235 | list_phy_id = strtol(argv[optind], &endptr, 0); 236 | if (argv[optind][0] == '\0' || *endptr != '\0') 237 | goto syntax_error; 238 | if (list_phy_id < 0 || list_phy_id >= 63) { 239 | fputs("phy-id must be between 0 and 62\n", stderr); 240 | exit(EXIT_FAILURE); 241 | } 242 | ++optind; 243 | 244 | if (optind < argc) 245 | goto syntax_error; 246 | } 247 | } 248 | } 249 | 250 | static struct dirent **fw_dirents; 251 | static int fw_dirent_count; 252 | static int fw_dirent_index; 253 | 254 | static int fw_filter(const struct dirent *dirent) 255 | { 256 | return dirent->d_name[0] == 'f' && 257 | dirent->d_name[1] == 'w' && 258 | isdigit(dirent->d_name[2]); 259 | } 260 | 261 | static void init_enumerated_fw_devs(void) 262 | { 263 | fw_dirent_count = scandir("/dev", &fw_dirents, fw_filter, versionsort); 264 | if (fw_dirent_count < 0) { 265 | perror("cannot read /dev"); 266 | exit(EXIT_FAILURE); 267 | } 268 | fw_dirent_index = 0; 269 | } 270 | 271 | static void cleanup_enumerated_fw_devs(void) 272 | { 273 | int i; 274 | 275 | for (i = 0; i < fw_dirent_count; ++i) 276 | free(fw_dirents[i]); 277 | free(fw_dirents); 278 | fw_dirents = NULL; 279 | fw_dirent_count = 0; 280 | } 281 | 282 | static bool has_enumerated_fw_dev(void) 283 | { 284 | free(device_file_name); 285 | device_file_name = NULL; 286 | 287 | if (fw_dirent_index < fw_dirent_count) { 288 | if (asprintf(&device_file_name, "/dev/%s", 289 | fw_dirents[fw_dirent_index]->d_name) < 0) { 290 | perror("asprintf failed"); 291 | exit(EXIT_FAILURE); 292 | } 293 | return true; 294 | } else { 295 | cleanup_enumerated_fw_devs(); 296 | return false; 297 | } 298 | } 299 | 300 | static void next_enumerated_fw_dev(void) 301 | { 302 | ++fw_dirent_index; 303 | } 304 | 305 | static bool open_device(bool force) 306 | { 307 | fd = open(device_file_name, O_RDWR); 308 | if (fd == -1) { 309 | if (!force && errno == ENODEV) 310 | return false; 311 | perror(device_file_name); 312 | exit(EXIT_FAILURE); 313 | } 314 | 315 | get_info.version = 4; 316 | get_info.rom_length = 0; 317 | get_info.rom = 0; 318 | get_info.bus_reset = ptr_to_u64(&bus_reset); 319 | get_info.bus_reset_closure = 0; 320 | if (ioctl(fd, FW_CDEV_IOC_GET_INFO, &get_info) < 0) { 321 | perror("GET_INFO ioctl failed"); 322 | exit(EXIT_FAILURE); 323 | } 324 | if (get_info.version < 4) { 325 | fputs("this kernel is too old\n", stderr); 326 | exit(EXIT_FAILURE); 327 | } 328 | 329 | return true; 330 | } 331 | 332 | static void enable_phy_packets(void) 333 | { 334 | struct fw_cdev_receive_phy_packets receive_phy_packets; 335 | 336 | receive_phy_packets.closure = 0; 337 | if (ioctl(fd, FW_CDEV_IOC_RECEIVE_PHY_PACKETS, &receive_phy_packets) < 0) { 338 | perror("RECEIVE_PHY_PACKETS ioctl failed"); 339 | exit(EXIT_FAILURE); 340 | } 341 | } 342 | 343 | static bool device_is_local_node(void) 344 | { 345 | return bus_reset.node_id == bus_reset.local_node_id; 346 | } 347 | 348 | static void check_local_node(void) 349 | { 350 | if (!device_is_local_node()) { 351 | fprintf(stderr, "%s: not a local node\n", device_file_name); 352 | exit(EXIT_FAILURE); 353 | } 354 | } 355 | 356 | static const struct vendor *search_vendor(u24 oui) 357 | { 358 | const struct vendor *vendor; 359 | 360 | for (vendor = vendors; vendor->name; ++vendor) 361 | if (vendor->oui== oui) 362 | return vendor; 363 | return NULL; 364 | } 365 | 366 | static const struct phy *search_phy(const struct vendor *vendor, u24 id) 367 | { 368 | const struct phy *phy; 369 | u24 mask; 370 | 371 | for (phy = vendor->phys; phy->name; ++phy) { 372 | mask = phy->mask ?: 0xffffff; 373 | if ((phy->id & mask) == (id & mask)) 374 | return phy; 375 | } 376 | return NULL; 377 | } 378 | 379 | static void list_phy(void) 380 | { 381 | struct fw_cdev_send_phy_packet send_phy_packet; 382 | unsigned int reg, regs_read; 383 | int ready, r; 384 | struct pollfd pfd; 385 | u8 buf[256]; 386 | struct fw_cdev_event_common *event; 387 | u8 reg_values[6]; 388 | u24 oui, id; 389 | const struct vendor *vendor; 390 | const struct phy *phy; 391 | 392 | send_phy_packet.closure = 0; 393 | send_phy_packet.generation = bus_reset.generation; 394 | for (reg = 2; reg <= 7; ++reg) { 395 | send_phy_packet.data[0] = PHY_REMOTE_ACCESS_PAGED(list_phy_id, 1, 0, reg); 396 | send_phy_packet.data[1] = ~send_phy_packet.data[0]; 397 | if (ioctl(fd, FW_CDEV_IOC_SEND_PHY_PACKET, &send_phy_packet) < 0) { 398 | perror("SEND_PHY_PACKET ioctl failed"); 399 | exit(EXIT_FAILURE); 400 | } 401 | } 402 | 403 | pfd.fd = fd; 404 | pfd.events = POLLIN; 405 | regs_read = 0; 406 | while (regs_read != 0xfc) { 407 | ready = poll(&pfd, 1, 123); 408 | if (ready < 0) { 409 | perror("poll failed"); 410 | exit(EXIT_FAILURE); 411 | } 412 | if (!ready) { 413 | fputs("timeout\n", stderr); 414 | return; /* try next PHY */ 415 | } 416 | r = read(fd, buf, sizeof buf); 417 | if (r < sizeof(struct fw_cdev_event_common)) { 418 | fputs("short read\n", stderr); 419 | exit(EXIT_FAILURE); 420 | } 421 | event = (void *)buf; 422 | if (event->type == FW_CDEV_EVENT_BUS_RESET) { 423 | /* TODO: retry */ 424 | fputs("bus reset\n", stderr); 425 | exit(EXIT_FAILURE); 426 | } 427 | if (event->type == FW_CDEV_EVENT_PHY_PACKET_SENT) { 428 | struct fw_cdev_event_phy_packet *phy_packet = (void *)buf; 429 | if (phy_packet->rcode != RCODE_COMPLETE) { 430 | fprintf(stderr, "PHY packet failed: rcode %u\n", 431 | (unsigned int)phy_packet->rcode); 432 | exit(EXIT_FAILURE); 433 | } 434 | } else if (event->type == FW_CDEV_EVENT_PHY_PACKET_RECEIVED) { 435 | struct fw_cdev_event_phy_packet *phy_packet = (void *)buf; 436 | if (phy_packet->length == 8 && 437 | (phy_packet->data[0] & 0xffff8000) 438 | == PHY_REMOTE_REPLY_PAGED(list_phy_id, 1, 0, 0, 0)) { 439 | reg = (phy_packet->data[0] >> 8) & 7; 440 | if (reg >= 2) { 441 | reg_values[reg - 2] = phy_packet->data[0] & 0xff; 442 | regs_read |= 1 << reg; 443 | } 444 | } 445 | } 446 | } 447 | 448 | oui = (reg_values[0] << 16) | (reg_values[1] << 8) | reg_values[2]; 449 | id = (reg_values[3] << 16) | (reg_values[4] << 8) | reg_values[5]; 450 | vendor = search_vendor(oui); 451 | phy = vendor ? search_phy(vendor, id) : NULL; 452 | 453 | printf("bus %u, node %d: %06x:%06x ", get_info.card, list_phy_id, oui, id); 454 | if (vendor) 455 | printf("%s %s\n", vendor->name, phy ? phy->name : "(unknown)"); 456 | else 457 | printf("%s\n", "(unknown)"); 458 | 459 | if (!phy) 460 | any_unknown_phys = true; 461 | } 462 | 463 | static void list_one_phy(void) 464 | { 465 | open_device(true); 466 | check_local_node(); 467 | enable_phy_packets(); 468 | list_phy(); 469 | close(fd); 470 | } 471 | 472 | static void list_device(void) 473 | { 474 | unsigned int list_card; 475 | 476 | open_device(true); 477 | list_phy_id = bus_reset.node_id & 0x3f; 478 | list_card = get_info.card; 479 | if (!device_is_local_node()) { 480 | close(fd); 481 | for (init_enumerated_fw_devs(); 482 | has_enumerated_fw_dev(); 483 | next_enumerated_fw_dev()) { 484 | if (!open_device(false)) 485 | continue; 486 | if (get_info.card == list_card && 487 | device_is_local_node()) 488 | goto found; 489 | close(fd); 490 | } 491 | fprintf(stderr, "local node for card %u not found\n", list_card); 492 | exit(EXIT_FAILURE); 493 | } 494 | found: 495 | enable_phy_packets(); 496 | list_phy(); 497 | close(fd); 498 | } 499 | 500 | static void list_all_buses(void) 501 | { 502 | for (init_enumerated_fw_devs(); 503 | has_enumerated_fw_dev(); 504 | next_enumerated_fw_dev()) { 505 | if (!open_device(false)) 506 | continue; 507 | if (device_is_local_node()) { 508 | int root_phy_id = bus_reset.root_node_id & 0x3f; 509 | enable_phy_packets(); 510 | for (list_phy_id = 0; list_phy_id <= root_phy_id; ++list_phy_id) 511 | list_phy(); 512 | } 513 | close(fd); 514 | } 515 | } 516 | 517 | int main(int argc, char *argv[]) 518 | { 519 | parse_parameters(argc, argv); 520 | if (device_file_name) 521 | if (list_phy_id >= 0) 522 | list_one_phy(); 523 | else 524 | list_device(); 525 | else 526 | list_all_buses(); 527 | if (any_unknown_phys) 528 | fputs(" Please check this web page for updated PHY IDs:\n" 529 | " http://code.google.com/p/jujuutils/wiki/PhyIds\n", stderr); 530 | return 0; 531 | } 532 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 19yy 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) 19yy name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /src/firewire-phy-command.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewire-phy-command.c - send PHY packets 3 | * 4 | * Copyright 2011 Clemens Ladisch 5 | * 6 | * licensed under the terms of the GNU General Public License, version 2 7 | */ 8 | 9 | #define _GNU_SOURCE 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #ifndef FW_CDEV_IOC_SEND_PHY_PACKET 27 | #error kernel headers too old 28 | #endif 29 | 30 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof *(a)) 31 | 32 | #define ptr_to_u64(p) ((uintptr_t)(p)) 33 | 34 | typedef __u8 u8; 35 | typedef __u32 u32; 36 | typedef __u64 u64; 37 | 38 | struct node { 39 | struct node *next; 40 | char *name; 41 | int fd; 42 | u32 card; 43 | u32 id; 44 | u32 generation; 45 | bool is_local; 46 | }; 47 | 48 | static struct node *nodes; 49 | static struct node *local_node; 50 | static u32 param_node_id; 51 | static u32 ping_time; 52 | static u32 self_ids[3]; 53 | 54 | static void help(void) 55 | { 56 | fputs("Usage: firewire-phy-command [options] command [parameters]\n" 57 | "Commands:\n" 58 | " config [root ] [gapcount ]\n" 59 | " ping \n" 60 | " read [ ] \n" 61 | " nop|disable|suspend|clear|enable|resume \n" 62 | " resume\n" 63 | " linkon \n" 64 | " reset\n" 65 | "Options:\n" 66 | " -b, --bus=node bus to send packet on\n" 67 | " -h, --help show this message and exit\n" 68 | " -V, --version show version number and exit\n" 69 | "\n" 70 | "Report bugs to <" PACKAGE_BUGREPORT ">.\n" 71 | PACKAGE_NAME " home page: <" PACKAGE_URL ">.\n", 72 | stderr); 73 | } 74 | 75 | static int fw_filter(const struct dirent *dirent) 76 | { 77 | unsigned int i; 78 | 79 | if (dirent->d_name[0] != 'f' || 80 | dirent->d_name[1] != 'w') 81 | return false; 82 | i = 2; 83 | do { 84 | if (!isdigit(dirent->d_name[i])) 85 | return false; 86 | } while (dirent->d_name[++i]); 87 | return true; 88 | } 89 | 90 | static void open_all_nodes(void) 91 | { 92 | struct dirent **ents; 93 | int count, i; 94 | struct node *node; 95 | struct fw_cdev_get_info get_info; 96 | struct fw_cdev_event_bus_reset bus_reset; 97 | bool eacces = false; 98 | 99 | count = scandir("/dev", &ents, fw_filter, versionsort); 100 | if (count < 0) { 101 | perror("cannot read /dev"); 102 | exit(EXIT_FAILURE); 103 | } 104 | 105 | for (i = count - 1; i >= 0; --i) { 106 | node = malloc(sizeof(*node)); 107 | if (!node) { 108 | fputs("out of memory\n", stderr); 109 | exit(EXIT_FAILURE); 110 | } 111 | 112 | if (asprintf(&node->name, "/dev/%s", ents[i]->d_name) < 0) { 113 | perror("asprintf failed"); 114 | exit(EXIT_FAILURE); 115 | } 116 | node->fd = open(node->name, O_RDWR); 117 | if (node->fd == -1) { 118 | if (errno == EACCES) 119 | eacces = true; 120 | free(node->name); 121 | free(node); 122 | continue; 123 | } 124 | 125 | get_info.version = 4; 126 | get_info.rom_length = 0; 127 | get_info.rom = 0; 128 | get_info.bus_reset = ptr_to_u64(&bus_reset); 129 | get_info.bus_reset_closure = 0; 130 | if (ioctl(node->fd, FW_CDEV_IOC_GET_INFO, &get_info) < 0) { 131 | close(node->fd); 132 | free(node->name); 133 | free(node); 134 | continue; 135 | } 136 | node->card = get_info.card; 137 | node->id = bus_reset.node_id; 138 | node->generation = bus_reset.generation; 139 | node->is_local = bus_reset.node_id == bus_reset.local_node_id; 140 | 141 | node->next = nodes; 142 | nodes = node; 143 | free(ents[i]); 144 | } 145 | free(ents); 146 | 147 | if (!nodes) { 148 | if (eacces) { 149 | errno = EACCES; 150 | perror("/dev/fw*"); 151 | } else { 152 | fputs("no fw devices found\n", stderr); 153 | } 154 | exit(EXIT_FAILURE); 155 | } 156 | } 157 | 158 | static void close_all_nodes(void) 159 | { 160 | struct node *node, *next; 161 | 162 | for (node = nodes; node; node = next) { 163 | close(node->fd); 164 | free(node->name); 165 | next = node->next; 166 | free(node); 167 | } 168 | } 169 | 170 | static void find_local_node(const char *bus_name) 171 | { 172 | int bus_card; 173 | char *endptr; 174 | int fd; 175 | struct fw_cdev_get_info get_info; 176 | struct node *node; 177 | 178 | if (bus_name) { 179 | bus_card = strtol(bus_name, &endptr, 0); 180 | if (!*endptr) { 181 | if (bus_card < 0) { 182 | fputs("invalid bus number\n", stderr); 183 | exit(EXIT_FAILURE); 184 | } 185 | } else { 186 | bus_card = -1; 187 | for (node = nodes; node; node = node->next) 188 | if (!strcmp(node->name, bus_name)) { 189 | bus_card = node->card; 190 | break; 191 | } 192 | if (bus_card < 0) { 193 | fd = open(bus_name, O_RDWR); 194 | if (fd == -1) { 195 | perror(bus_name); 196 | exit(EXIT_FAILURE); 197 | } 198 | get_info.version = 4; 199 | get_info.rom_length = 0; 200 | get_info.rom = 0; 201 | get_info.bus_reset = 0; 202 | get_info.bus_reset_closure = 0; 203 | if (ioctl(fd, FW_CDEV_IOC_GET_INFO, &get_info) < 0) { 204 | fprintf(stderr, "%s: not a fw device\n", bus_name); 205 | exit(EXIT_FAILURE); 206 | } 207 | close(fd); 208 | bus_card = get_info.card; 209 | } 210 | } 211 | } else { 212 | bus_card = -1; 213 | } 214 | 215 | for (node = nodes; node; node = node->next) { 216 | if (node->is_local && 217 | (bus_card == -1 || node->card == bus_card)) { 218 | local_node = node; 219 | return; 220 | } 221 | } 222 | 223 | if (bus_card == -1) 224 | fputs("local node not found\n", stderr); 225 | else 226 | fprintf(stderr, "local node for card %d not found\n", bus_card); 227 | exit(EXIT_FAILURE); 228 | } 229 | 230 | static void find_param_node(const char *name) 231 | { 232 | int id; 233 | char *endptr; 234 | struct node *node; 235 | int fd; 236 | struct fw_cdev_get_info get_info; 237 | struct fw_cdev_event_bus_reset bus_reset; 238 | char card_str[16]; 239 | 240 | id = strtol(name, &endptr, 0); 241 | if (!*endptr) { 242 | if (id < 0 || id > 63) { 243 | fputs("invalid node id\n", stderr); 244 | exit(EXIT_FAILURE); 245 | } 246 | param_node_id = id; 247 | return; 248 | } 249 | 250 | for (node = nodes; node; node = node->next) 251 | if (!strcmp(node->name, name)) { 252 | param_node_id = node->id & 0x3f; 253 | sprintf(card_str, "%u", node->card); 254 | find_local_node(card_str); 255 | return; 256 | } 257 | 258 | fd = open(name, O_RDWR); 259 | if (fd == -1) { 260 | perror(name); 261 | exit(EXIT_FAILURE); 262 | } 263 | get_info.version = 4; 264 | get_info.rom_length = 0; 265 | get_info.rom = 0; 266 | get_info.bus_reset = ptr_to_u64(&bus_reset); 267 | get_info.bus_reset_closure = 0; 268 | if (ioctl(fd, FW_CDEV_IOC_GET_INFO, &get_info) < 0) { 269 | fprintf(stderr, "%s: not a fw device\n", name); 270 | exit(EXIT_FAILURE); 271 | } 272 | close(fd); 273 | param_node_id = bus_reset.node_id & 0x3f; 274 | sprintf(card_str, "%u", get_info.card); 275 | find_local_node(card_str); 276 | } 277 | 278 | static u32 _send_packet(u32 quadlet0, u32 quadlet1, u32 response_mask, u32 response_bits) 279 | { 280 | struct fw_cdev_receive_phy_packets receive_phy_packets; 281 | struct fw_cdev_send_phy_packet send_phy_packet; 282 | struct pollfd pollfd; 283 | int poll_result; 284 | ssize_t bytes; 285 | union fw_cdev_event event; 286 | bool wait_for_sent = true; 287 | bool wait_for_response = response_mask != 0; 288 | unsigned int self_id_index = 0; 289 | u32 response = 0; 290 | 291 | if (wait_for_response) { 292 | receive_phy_packets.closure = 0; 293 | if (ioctl(local_node->fd, FW_CDEV_IOC_RECEIVE_PHY_PACKETS, &receive_phy_packets) < 0) { 294 | perror("RECEIVE_PHY_PACKETS ioctl failed"); 295 | exit(EXIT_FAILURE); 296 | } 297 | } 298 | 299 | send_phy_packet.closure = 0; 300 | send_phy_packet.data[0] = quadlet0; 301 | send_phy_packet.data[1] = quadlet1; 302 | send_phy_packet.generation = local_node->generation; 303 | if (ioctl(local_node->fd, FW_CDEV_IOC_SEND_PHY_PACKET, &send_phy_packet) < 0) { 304 | perror("SEND_PHY_PACKET ioctl failed"); 305 | exit(EXIT_FAILURE); 306 | } 307 | 308 | pollfd.fd = local_node->fd; 309 | pollfd.events = POLLIN; 310 | while (wait_for_sent || wait_for_response) { 311 | poll_result = poll(&pollfd, 1, 100); 312 | if (poll_result < 0) { 313 | perror("poll failed"); 314 | exit(EXIT_FAILURE); 315 | } 316 | if (poll_result == 0) { 317 | fputs("timeout\n", stderr); 318 | exit(EXIT_FAILURE); 319 | } 320 | bytes = read(local_node->fd, &event, sizeof(event)); 321 | if (bytes < sizeof(struct fw_cdev_event_common)) { 322 | fputs("short read\n", stderr); 323 | exit(EXIT_FAILURE); 324 | } 325 | switch (event.common.type) { 326 | case FW_CDEV_EVENT_BUS_RESET: 327 | fputs("bus reset\n", stderr); 328 | exit(EXIT_FAILURE); 329 | case FW_CDEV_EVENT_PHY_PACKET_SENT: 330 | wait_for_sent = false; 331 | if (event.phy_packet.length >= 4) 332 | ping_time = event.phy_packet.data[0]; 333 | break; 334 | case FW_CDEV_EVENT_PHY_PACKET_RECEIVED: 335 | if ((event.phy_packet.data[0] & response_mask) == response_bits) { 336 | response = event.phy_packet.data[0]; 337 | if ((response & 0xc0000000) == 0x80000000) { 338 | self_ids[self_id_index++] = response; 339 | if (self_id_index >= ARRAY_SIZE(self_ids) || 340 | !(response & 1)) 341 | wait_for_response = false; 342 | } else { 343 | wait_for_response = false; 344 | } 345 | } 346 | break; 347 | } 348 | } 349 | 350 | return response; 351 | } 352 | 353 | static u32 send_packet(u32 quadlet, u32 response_mask, u32 response_bits) 354 | { 355 | return _send_packet(quadlet, ~quadlet, response_mask, response_bits); 356 | } 357 | 358 | static void command_config(char *args[]) 359 | { 360 | bool new_root = false; 361 | int gap_count = -1; 362 | char *endptr; 363 | u32 packet; 364 | 365 | if (!args[0]) { 366 | fputs("missing configuration parameter\n", stderr); 367 | syntax_error: 368 | help(); 369 | exit(EXIT_FAILURE); 370 | } 371 | 372 | do { 373 | if (!strcmp(args[0], "root")) { 374 | if (!args[1]) { 375 | fputs("no root node specified\n", stderr); 376 | goto syntax_error; 377 | } 378 | find_param_node(args[1]); 379 | new_root = true; 380 | } else if (!strcmp(args[0], "gapcount")) { 381 | if (!args[1]) { 382 | fputs("no gap count specified\n", stderr); 383 | goto syntax_error; 384 | } 385 | gap_count = strtol(args[1], &endptr, 0); 386 | if (*endptr) { 387 | fputs("gap count is not a number\n", stderr); 388 | exit(EXIT_FAILURE); 389 | } else if (gap_count < 0 || gap_count > 63) { 390 | fputs("gap count out of range\n", stderr); 391 | exit(EXIT_FAILURE); 392 | } 393 | } else { 394 | fprintf(stderr, "unknown configuration parameter `%s'\n", args[0]); 395 | goto syntax_error; 396 | } 397 | args += 2; 398 | } while (args[0]); 399 | 400 | packet = 0; 401 | if (new_root) 402 | packet |= (1 << 23) | (param_node_id << 24); 403 | if (gap_count >= 0) 404 | packet |= (1 << 22) | (gap_count << 16); 405 | send_packet(packet, 0, 0); 406 | } 407 | 408 | static void print_port(u32 self_id, unsigned int shift) 409 | { 410 | static const char *const port[] = { 411 | [0] = "", 412 | [1] = "-", 413 | [2] = "p", 414 | [3] = "c", 415 | }; 416 | 417 | fputs(port[(self_id >> shift) & 3], stdout); 418 | } 419 | 420 | static void command_ping(char *args[]) 421 | { 422 | static const char *const speed[] = { 423 | [0] = "S100", 424 | [1] = "S200", 425 | [2] = "S400", 426 | [3] = "beta", 427 | }; 428 | static const char *const power[] = { 429 | [0] = "+0W", 430 | [1] = "+15W", 431 | [2] = "+30W", 432 | [3] = "+45W", 433 | [4] = "-3W", 434 | [5] = " ?W", 435 | [6] = "-3..-6W", 436 | [7] = "-3..-10W", 437 | }; 438 | unsigned int i; 439 | 440 | if (!args[0]) { 441 | fputs("missing destination node\n", stderr); 442 | syntax_error: 443 | help(); 444 | exit(EXIT_FAILURE); 445 | } 446 | find_param_node(args[0]); 447 | 448 | if (args[1]) { 449 | fprintf(stderr, "unexpected parameter `%s'\n", args[1]); 450 | goto syntax_error; 451 | } 452 | 453 | send_packet((0 << 18) | (param_node_id << 24), 454 | 0xff000000, 455 | (2 << 30) | (param_node_id << 24)); 456 | printf("time: %u ticks (%llu ns)", 457 | ping_time, (ping_time * 1000000uLL + 12288u) / 24576u); 458 | printf(", selfID: phy %u %s gc=%u %s %s%s%s [", 459 | (self_ids[0] >> 24) & 0x3f, 460 | speed[(self_ids[0] >> 14) & 3], 461 | (self_ids[0] >> 16) & 0x3f, 462 | power[(self_ids[0] >> 8) & 7], 463 | self_ids[0] & (1 << 22) ? "L" : "", 464 | self_ids[0] & (1 << 11) ? "c" : "", 465 | self_ids[0] & (1 << 1) ? "i" : ""); 466 | print_port(self_ids[0], 6); 467 | print_port(self_ids[0], 4); 468 | print_port(self_ids[0], 2); 469 | if (self_ids[0] & 1) { 470 | for (i = 1; i < ARRAY_SIZE(self_ids); ++i) { 471 | print_port(self_ids[i], 16); 472 | print_port(self_ids[i], 14); 473 | print_port(self_ids[i], 12); 474 | print_port(self_ids[i], 10); 475 | print_port(self_ids[i], 8); 476 | print_port(self_ids[i], 6); 477 | print_port(self_ids[i], 4); 478 | print_port(self_ids[i], 2); 479 | if (!(self_ids[i] & 1)) 480 | break; 481 | } 482 | } 483 | puts("]"); 484 | } 485 | 486 | static void command_read(char *args[]) 487 | { 488 | unsigned int page, port, reg; 489 | char *endptr; 490 | u32 packet; 491 | u32 response; 492 | 493 | if (!args[0]) { 494 | fputs("missing destination node\n", stderr); 495 | syntax_error: 496 | help(); 497 | exit(EXIT_FAILURE); 498 | } 499 | find_param_node(args[0]); 500 | 501 | if (!args[1]) { 502 | fputs("missing register number\n", stderr); 503 | goto syntax_error; 504 | } 505 | if (args[2]) { 506 | if (!args[3]) { 507 | fputs("missing register number\n", stderr); 508 | goto syntax_error; 509 | } 510 | 511 | page = strtol(args[1], &endptr, 0); 512 | if (*endptr) { 513 | fputs("invalid page number\n", stderr); 514 | exit(EXIT_FAILURE); 515 | } 516 | if (page > 7) { 517 | fputs("page number out of range\n", stderr); 518 | exit(EXIT_FAILURE); 519 | } 520 | 521 | port = strtol(args[2], &endptr, 0); 522 | if (*endptr) { 523 | fputs("invalid port number\n", stderr); 524 | exit(EXIT_FAILURE); 525 | } 526 | if (port > 15) { 527 | fputs("port number out of range\n", stderr); 528 | exit(EXIT_FAILURE); 529 | } 530 | 531 | reg = strtol(args[3], &endptr, 0); 532 | if (*endptr) { 533 | fputs("invalid register number\n", stderr); 534 | exit(EXIT_FAILURE); 535 | } 536 | if (reg < 8 || reg > 15) { 537 | fputs("register number out of range\n", stderr); 538 | exit(EXIT_FAILURE); 539 | } 540 | 541 | if (args[4]) { 542 | fprintf(stderr, "unexpected parameter `%s'\n", args[4]); 543 | goto syntax_error; 544 | } 545 | } else { 546 | page = 0; 547 | port = 0; 548 | 549 | reg = strtol(args[1], &endptr, 0); 550 | if (*endptr) { 551 | fputs("invalid register number\n", stderr); 552 | exit(EXIT_FAILURE); 553 | } 554 | if (reg > 7) { 555 | fputs("register number out of range\n", stderr); 556 | exit(EXIT_FAILURE); 557 | } 558 | 559 | if (args[2]) { 560 | fprintf(stderr, "unexpected parameter `%s'\n", args[2]); 561 | goto syntax_error; 562 | } 563 | } 564 | 565 | packet = reg < 8 ? 1 << 18 : 5 << 18; 566 | packet |= page << 15; 567 | packet |= port << 11; 568 | packet |= (reg & 7) << 8; 569 | packet |= param_node_id << 24; 570 | response = send_packet(packet, 0xffffff00, packet | (2 << 18)); 571 | printf("value: 0x%02x\n", response & 0xff); 572 | } 573 | 574 | static void command_remote_cmd(char *args[], u32 cmd) 575 | { 576 | unsigned int port; 577 | char *endptr; 578 | u32 response; 579 | 580 | if (!args[0]) { 581 | fputs("missing destination node\n", stderr); 582 | syntax_error: 583 | help(); 584 | exit(EXIT_FAILURE); 585 | } 586 | find_param_node(args[0]); 587 | 588 | if (!args[1]) { 589 | fputs("missing port number\n", stderr); 590 | goto syntax_error; 591 | } 592 | port = strtol(args[1], &endptr, 0); 593 | if (*endptr) { 594 | fputs("invalid port number\n", stderr); 595 | exit(EXIT_FAILURE); 596 | } 597 | if (port > 15) { 598 | fputs("port number out of range\n", stderr); 599 | exit(EXIT_FAILURE); 600 | } 601 | 602 | if (args[2]) { 603 | fprintf(stderr, "unexpected parameter `%s'\n", args[2]); 604 | goto syntax_error; 605 | } 606 | 607 | response = send_packet((0x8 << 18) | cmd | (port << 11) | (param_node_id << 24), 608 | 0xff3ff807, 609 | (0xa << 18) | cmd | (port << 11) | (param_node_id << 24)); 610 | if (!(response & (1 << 3))) 611 | puts("command rejected"); 612 | else if (!(response & 0x1f0)) 613 | fputs("port status: ok\n", stdout); 614 | else 615 | printf("port status:%s%s%s%s%s\n", 616 | response & (1 << 4) ? " disabled" : "", 617 | response & (1 << 5) ? " bias" : "", 618 | response & (1 << 6) ? " connected" : "", 619 | response & (1 << 7) ? " fault" : "", 620 | response & (1 << 8) ? " standby_fault" : ""); 621 | } 622 | 623 | static void command_nop(char *args[]) 624 | { 625 | command_remote_cmd(args, 0); 626 | } 627 | 628 | static void command_disable(char *args[]) 629 | { 630 | command_remote_cmd(args, 1); 631 | } 632 | 633 | static void command_suspend(char *args[]) 634 | { 635 | command_remote_cmd(args, 2); 636 | } 637 | 638 | static void command_clear(char *args[]) 639 | { 640 | command_remote_cmd(args, 4); 641 | } 642 | 643 | static void command_enable(char *args[]) 644 | { 645 | command_remote_cmd(args, 5); 646 | } 647 | 648 | static void command_resume(char *args[]) 649 | { 650 | if (args[0]) 651 | command_remote_cmd(args, 6); 652 | else 653 | send_packet((0xf << 18) | (local_node->id << 24), 0, 0); 654 | } 655 | 656 | static void command_standby(char *args[]) 657 | { 658 | command_remote_cmd(args, 7 | (1 << 15)); 659 | } 660 | 661 | static void command_restore(char *args[]) 662 | { 663 | command_remote_cmd(args, 7 | (2 << 15)); 664 | } 665 | 666 | static void command_linkon(char *args[]) 667 | { 668 | if (!args[0]) { 669 | fputs("missing destination node\n", stderr); 670 | syntax_error: 671 | help(); 672 | exit(EXIT_FAILURE); 673 | } 674 | find_param_node(args[0]); 675 | 676 | if (args[1]) { 677 | fprintf(stderr, "unexpected parameter `%s'\n", args[1]); 678 | goto syntax_error; 679 | } 680 | 681 | send_packet((1 << 30) | (param_node_id << 24), 0, 0); 682 | } 683 | 684 | static void command_versaphy(char *args[]) 685 | { 686 | u32 q0, q1; 687 | char *endptr; 688 | 689 | if (!args[0] || !args[1]) { 690 | fputs("missing data\n", stderr); 691 | syntax_error: 692 | help(); 693 | exit(EXIT_FAILURE); 694 | } 695 | 696 | q0 = strtol(args[0], &endptr, 16); 697 | if (*endptr) { 698 | fputs("invalid data quadlet\n", stderr); 699 | exit(EXIT_FAILURE); 700 | } 701 | q1 = strtol(args[1], &endptr, 16); 702 | if (*endptr) { 703 | fputs("invalid data quadlet\n", stderr); 704 | exit(EXIT_FAILURE); 705 | } 706 | if ((q0 & 0xc0000000) != 0xc0000000) { 707 | fputs("not a VersaPHY packet\n", stderr); 708 | exit(EXIT_FAILURE); 709 | } 710 | 711 | if (args[2]) { 712 | fprintf(stderr, "unexpected parameter `%s'\n", args[2]); 713 | goto syntax_error; 714 | } 715 | 716 | _send_packet(q0, q1, 0, 0); 717 | } 718 | 719 | static void command_reset(char *args[]) 720 | { 721 | struct fw_cdev_initiate_bus_reset initiate_bus_reset; 722 | 723 | if (args[0]) { 724 | fprintf(stderr, "unexpected parameter `%s'\n", args[0]); 725 | help(); 726 | exit(EXIT_FAILURE); 727 | } 728 | 729 | initiate_bus_reset.type = FW_CDEV_SHORT_RESET; 730 | if (ioctl(local_node->fd, FW_CDEV_IOC_INITIATE_BUS_RESET, &initiate_bus_reset) < 0) { 731 | perror("INITIATE_BUS_RESET ioctl failed"); 732 | exit(EXIT_FAILURE); 733 | } 734 | } 735 | 736 | int main(int argc, char *argv[]) 737 | { 738 | static const char short_options[] = "b:hV"; 739 | static const struct option long_options[] = { 740 | { "bus", 1, NULL, 'b' }, 741 | { "help", 0, NULL, 'h' }, 742 | { "version", 0, NULL, 'V' }, 743 | {} 744 | }; 745 | static const struct { 746 | const char *name; 747 | void (*fn)(char *args[]); 748 | } commands[] = { 749 | { "config", command_config }, 750 | { "ping", command_ping }, 751 | { "read", command_read }, 752 | { "nop", command_nop }, 753 | { "disable", command_disable }, 754 | { "suspend", command_suspend }, 755 | { "clear", command_clear }, 756 | { "enable", command_enable }, 757 | { "resume", command_resume }, 758 | { "standby", command_standby }, 759 | { "restore", command_restore }, 760 | { "linkon", command_linkon }, 761 | { "link-on", command_linkon }, 762 | { "link_on", command_linkon }, 763 | { "versaphy", command_versaphy }, 764 | { "reset", command_reset }, 765 | }; 766 | const char *bus_name = NULL; 767 | unsigned int i; 768 | int c; 769 | 770 | while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { 771 | switch (c) { 772 | case 'b': 773 | bus_name = optarg; 774 | break; 775 | case 'h': 776 | help(); 777 | return 0; 778 | case 'V': 779 | puts("firewire-phy-command version " PACKAGE_VERSION); 780 | return 0; 781 | default: 782 | syntax_error: 783 | help(); 784 | return 1; 785 | } 786 | } 787 | 788 | if (optind >= argc) { 789 | fputs("missing command\n", stderr); 790 | goto syntax_error; 791 | } 792 | for (i = 0; i < ARRAY_SIZE(commands); ++i) 793 | if (!strcmp(commands[i].name, argv[optind])) { 794 | open_all_nodes(); 795 | find_local_node(bus_name); 796 | commands[i].fn(argv + optind + 1); 797 | close_all_nodes(); 798 | return 0; 799 | } 800 | 801 | fprintf(stderr, "unknown command `%s'\n", argv[optind]); 802 | return 1; 803 | } 804 | -------------------------------------------------------------------------------- /src/crpp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """IEEE 1212/ IEEE 1394 Configuration ROM pretty printer 4 | 5 | Copyright 2010 Stefan Richter 6 | You may freely use, modify, and/or redistribute this program. 7 | 8 | Reads Configuration ROM data from stdin and writes a human-readable 9 | annotated representation to stdout. The data may be 10 | - binary data, i.e. a big endian or little endian quadlet array, 11 | - a firewire-ohci debug log, 12 | - read results from the tool firecontrol. 13 | 14 | Usage examples: 15 | 16 | - Read a configuration ROM as seen in sysfs: 17 | (plug device in, find out which fw* it became) 18 | crpp < /sys/bus/firewire/devices/fw1/config_rom 19 | 20 | - Read a debug log from when the drivers attempt to probe the device: 21 | echo 1 > /sys/module/firewire_ohci/parameters/debug 22 | (plug device in, wait a few seconds) 23 | dmesg | crpp 24 | echo 0 > /sys/module/firewire_ohci/parameters/debug 25 | 26 | - Read the configuration ROM by means of firecontrol. The example 27 | assumes that the device has the node ID ffc0 and that it is 28 | attached to the first of all installed FireWire controllers: 29 | { for i in {4,5,6,7}{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f}{0,4,8,c}; 30 | do echo "r . 0 0xfffff0000$i 4"; 31 | done } | firecontrol | crpp 32 | 33 | If you want company IDs being translated to names, you need a file 34 | called oui.db in /usr/share/misc/ or in the current working directory. 35 | This file can be obtained from linux-2.6.20 or older kernel soures in 36 | linux/drivers/ieee1394/ or can be freshly generated by 37 | wget -O - http://standards.ieee.org/regauth/oui/oui.txt | 38 | grep -E '(base 16).*\w+.*$' | 39 | sed -e 's/^\s*//' -e 's/\s*(base 16)\s*/ /' > oui.db 40 | which will download ~3 MB data and result in a ~0.6 MB large oui.db. 41 | 42 | History: 43 | 2010-02-16: initial release 44 | 2010-02-18: small fixes; added protocol entries, OUI lookup, CRC checks 45 | 2010-04-25: added firecontrol input 46 | 2010-07-19: added vendor-defined specifier/version IDs 47 | 2012-04-23: added IIDC2 v1.0.0 protocol entries 48 | 2012-06-12: updated dmesg reader to log format of kernel 3.4 49 | 2015-01-31: updated comment on oui.db: let sed squash leading whitespace 50 | """ 51 | 52 | ouidb_search_paths = ( 53 | "/usr/share/misc/oui.db", 54 | "oui.db", 55 | ) 56 | 57 | def crc16(rom, i, l): 58 | def nextcrc(c, q): 59 | for j in xrange(28, -1, -4): 60 | s = (c >> 12 ^ q >> j) & 0xf 61 | c = c << 4 ^ s << 12 ^ s << 5 ^ s 62 | return c & 0xffff 63 | return reduce(nextcrc, rom[i:min(i + l, 256)], 0) 64 | 65 | def u8_to_char(n): 66 | # separator/ terminator 67 | if n == 0: 68 | return "" 69 | # printable character from minimal ASCII 70 | if n in range(0x20, 0x23) + range(0x25, 0x5b) + [0x5f] + range(0x61, 0x7b): 71 | return chr(n) 72 | # other character 73 | return "~" 74 | 75 | def u32_to_string(n): 76 | if n == 0: 77 | return "" 78 | s = "\"" 79 | for i in 24, 16, 8, 0: 80 | s += u8_to_char(n >> i & 0xff) 81 | # add separator in c0c0, c0cc, c00c, or 0c0c 82 | if n & 0xff000000 and n & 0x0000ffff and not n & 0x00ff0000 or \ 83 | n & 0x00ff0000 and n & 0x000000ff and not n & 0xff00ff00: 84 | s = s[:2] + "\", \"" + s[2:] 85 | # add separator in cc0c 86 | if n & 0xff000000 and n & 0x00ff0000 and n & 0xff and not n & 0xff00: 87 | s = s[:3] + "\", \"" + s[3] 88 | return s + "\"" 89 | 90 | def language(n): 91 | n &= 0xffff 92 | if n == 0: 93 | return "en" 94 | s = "" 95 | for i in 10, 5, 0: 96 | c = n >> i & 0x1f 97 | s += char(ord("a") - 1 + c) if c >= 1 and c <= 26 else " " 98 | return s 99 | 100 | def bcd_version_details(n): 101 | return "v%s%d.%d%s" % ( 102 | "%d" % v >> 20 & 0xf if v & 0xf00000 else "", v >> 16 & 0xf, 103 | v >> 12 & 0xf, "%d" % v >> 8 & 0xf if v & 0xf00 else ""), 104 | 105 | def csr_address(offset): 106 | return 0xfffff0000000 + 4 * offset 107 | 108 | dpp111_protocol_entries = { 109 | 0x38: lambda v: "command set spec id" + (": " + 110 | protocols[v][0] if v in protocols else ""), 111 | 0x39: lambda v: "command set" + {0xb081f2: ": DPC", 112 | 0x020000: ": FTC"}.get(v, ""), 113 | 0x3a: lambda v: "command set details", 114 | 0x3c: lambda v: "write transaction interval %dms" % (v), 115 | 0x3d: lambda v: "unit sw details: %s, sdu_write_order %d" % ( 116 | bcd_version_details(v), v & 1), 117 | 0x7b: lambda v: "connection CSR at %012x" % (csr_address(v)), 118 | 0xd4: lambda v: "command set directory", 119 | } 120 | 121 | iicp_protocol_entries = { 122 | 0x38: lambda v: "IICP details: %s" % (bcd_version_details(v)), 123 | 0x39: lambda v: "command set spec id" + (": " + 124 | protocols[v][0] if v in protocols else ""), 125 | 0x3a: lambda v: "command set" + {0x4b661f: ": IICP only", 126 | 0xc27f10: ": IICP488"}.get(v, ""), 127 | 0x3b: lambda v: "command set details: %s" % (bcd_version_details(v)), 128 | 0x3d: lambda v: "IICP capabilities: hl proto %d, IICP %d, " \ 129 | "ccli %d, cmgr %d, maxIntLength %s" % ( 130 | v >> 16, v >> 6 & 0x3f, v >> 5 & 1, v >> 4 & 1, 131 | "%d bytes" % (2 << (v & 0xf)) if v & 0xf else "-"), 132 | 0x7c: lambda v: "connection CSR at %012x" % (csr_address(v)), 133 | 0x7e: lambda v: "interrupt_enable CSR at %012x" % (csr_address(v)), 134 | 0x7f: lambda v: "interrupt_handlr CSR at %012x" % (csr_address(v)), 135 | } 136 | 137 | iidc104_protocol_entries = { 138 | 0x40: lambda v: "command_regs_base at %012x" % (csr_address(v)), 139 | 0x81: lambda v: "vendor name leaf", 140 | 0x82: lambda v: "model name leaf", 141 | } 142 | 143 | iidc131_protocol_entries = { 144 | 0x38: lambda v: "unit sub sw version v1.3%d" % (v >> 4), 145 | 0x39: lambda v: "(reserved)", 146 | 0x3a: lambda v: "(reserved)", 147 | 0x3b: lambda v: "(reserved)", 148 | 0x3c: lambda v: "vendor_unique_info_0", 149 | 0x3d: lambda v: "vendor_unique_info_1", 150 | 0x3e: lambda v: "vendor_unique_info_2", 151 | 0x3f: lambda v: "vendor_unique_info_3", 152 | 0x40: lambda v: "command_regs_base at %012x" % (csr_address(v)), 153 | 0x81: lambda v: "vendor name leaf", 154 | 0x82: lambda v: "model name leaf", 155 | } 156 | 157 | iidc2_100_protocol_entries = { 158 | 0x38: lambda v: "unit sub sw version v%d.%d.%d" % ( 159 | v >> 16, v >> 8 & 0xff, v & 0xff), 160 | 0x40: lambda v: "IIDC2Entry at %012x" % (csr_address(v)), 161 | 0x81: lambda v: "vendor name leaf", 162 | 0x82: lambda v: "model name leaf", 163 | } 164 | 165 | isight_audio_protocol_entries = { 166 | 0x40: lambda v: "register file at %012x" % (csr_address(v)), 167 | } 168 | 169 | isight_iris_protocol_entries = { 170 | 0x40: lambda v: "Iris Status Address register at %012x" % (csr_address(v)), 171 | } 172 | 173 | sbp3_protocol_entries = { 174 | 0x14: lambda v: "logical unit number: %sordered %d, %s" \ 175 | "type %s, lun %04x" % ( 176 | ("", "extended_status 1, ")[v >> 23 & 1], v >> 22 & 1, 177 | ("", "isoch 1, ")[v >> 21 & 1], { 178 | 0x00: "Disk", 0x01: "Tape", 0x02: "Printer", 0x03: "Processor", 179 | 0x04: "WORM", 0x05: "CD/DVD", 0x06: "Scanner", 0x07: "MOD", 180 | 0x08: "Changer", 0x09: "Comm", 0x0a: "Prepress", 0x0b: "Prepress", 181 | 0x0c: "RAID", 0x0d: "Enclosure", 0x0e: "RBC", 0x0f: "OCRW", 182 | 0x10: "Bridge", 0x11: "OSD", 0x12: "ADC-2", 183 | 0x1e: "w.k.LUN", 0x1f: "unnown", 184 | }.get(v >> 16 & 0x1f, "%02x?" % (v >> 16 & 0x1f)), v & 0xffff), 185 | 0x21: lambda v: "revision %d%s" % (v, 186 | (" = SBP-2", " = SBP-3")[v] if v < 2 else ""), 187 | 0x32: lambda v: "/ SBP-3 plug control register: %sPCR, " \ 188 | "plug_index %d" % (("i", "o")[v >> 5 & 1], v & 0x1f), 189 | 0x38: lambda v: "command set spec id" + (": " + 190 | protocols[v][0] if v in protocols else ""), 191 | 0x39: lambda v: "command set" + { 192 | 0x0104d8: ": SCSI Primary Commands 2 and related standards", 193 | 0x010001: ": AV/C", 194 | }.get(v, ""), 195 | 0x3a: lambda v: "unit char.: %smgt_ORB_timeout %gs, ORB_size " \ 196 | "%d quadlets" % (("", "distrib. data 1, ")[v >> 16 & 1], 197 | (v >> 8 & 0xff) * .5, v & 0xff), 198 | 0x3b: lambda v: "command set revision", 199 | 0x3c: lambda v: "firmware revision %06x" % (v), 200 | 0x3d: lambda v: "reconnect timeout: max_reconnect_hold %ds" % ((v & 0xffff) + 1), 201 | 0x3e: lambda v: "/ SBP-3 fast start: max_payload " + ( 202 | "%d bytes" % (v >> 8 << 2) if v & 0xff00 203 | else "per max_rec") + (", offset %d" % (v & 0xff)), 204 | 0x54: lambda v: "management agent CSR at %012x" % (csr_address(v)), 205 | 0x8d: lambda v: "unit unique id", 206 | 0xd4: lambda v: "logical unit directory", 207 | } 208 | 209 | protocols = { 210 | # standardized 211 | 0x00005e: ("IANA", { 212 | 0x000001: ("IPv4 over 1394 (RFC 2734)", {}), 213 | 0x000002: ("IPv6 over 1394 (RFC 3146)", {}), 214 | }), 215 | 0x00609e: ("INCITS", { 216 | 0x010483: ("SBP-2", sbp3_protocol_entries), 217 | 0x0105bb: ("AV/C over SBP-3", sbp3_protocol_entries), 218 | }), 219 | 0x00a02d: ("1394 TA", { 220 | 0x010001: ("AV/C", {}), 221 | 0x010002: ("CAL", {}), 222 | 0x010004: ("EHS", {}), 223 | 0x010008: ("HAVi", {}), 224 | 0x014000: ("Vender Unique", {}), 225 | 0x014001: ("Vender Unique and AV/C", {}), 226 | 0x000100: ("IIDC 1.04", iidc104_protocol_entries), 227 | 0x000101: ("IIDC 1.20", iidc104_protocol_entries), 228 | 0x000102: ("IIDC 1.30", iidc131_protocol_entries), 229 | 0x000110: ("IIDC2", iidc2_100_protocol_entries), 230 | 0x0A6BE2: ("DPP 1.0", dpp111_protocol_entries), 231 | 0x4B661F: ("IICP 1.0", iicp_protocol_entries), 232 | }), 233 | # vendor-defined 234 | 0x000595: ("Alesis Corporation", { 235 | 0x000001: ("audio", {}), 236 | }), 237 | 0x000a27: ("Apple Computer, Inc.", { 238 | 0x000010: ("iSight audio unit", isight_audio_protocol_entries), 239 | 0x000011: ("iSight factory unit", {}), 240 | 0x000012: ("iSight iris unit", isight_iris_protocol_entries), 241 | }), 242 | 0x00d04b: ("La Cie Group S.A.", { 243 | 0x484944: ("HID", {}), 244 | }), 245 | } 246 | 247 | def proto_of(spec, ver): 248 | return protocols[spec][1][ver] \ 249 | if spec in protocols and ver in protocols[spec][1] \ 250 | else (None, {}) 251 | 252 | def oui_of(ouidb, dummy, v): 253 | return ": " + protocols[v][0] if v in protocols \ 254 | else ": " + ouidb[v] if v in ouidb \ 255 | else "" 256 | 257 | ieee1212_key_ids2 = { 258 | 0x03: oui_of, 259 | 0x0c: lambda ouidb, spec, v: " per IEEE 1394" if v & 0x83c0 == 0x83c0 \ 260 | else "", 261 | 0x12: oui_of, 262 | 0x13: lambda ouidb, spec, v: ": " + protocols[spec][1][v][0] \ 263 | if spec in protocols and v in protocols[spec][1] \ 264 | else "", 265 | 0x1c: oui_of, 266 | } 267 | 268 | ieee1212_key_ids = { 269 | 0x01: "descriptor", 270 | 0x02: "bus dependent info", 271 | 0x03: "vendor", 272 | 0x04: "hardware version", 273 | 0x07: "module", 274 | 0x0c: "node capabilities", 275 | 0x0d: "eui-64", 276 | 0x11: "unit", 277 | 0x12: "specifier id", 278 | 0x13: "version", 279 | 0x14: "dependent info", 280 | 0x15: "unit location", 281 | 0x17: "model", 282 | 0x18: "instance", 283 | 0x19: "keyword", 284 | 0x1a: "feature", 285 | 0x1b: "extended rom", 286 | 0x1c: "extended key specifier id", 287 | 0x1d: "extended key", 288 | 0x1e: "extended data", 289 | 0x1f: "modifiable descriptor", 290 | 0x20: "directory id", 291 | 0x21: "revision", 292 | } 293 | 294 | ieee1212_types = { 295 | 0: "immediate", 296 | 1: "csr offset", 297 | 2: "leaf", 298 | 3: "directory", 299 | } 300 | 301 | def write_directory(f, ouidb, rom, blocks, o, i, end, context): 302 | dummy, dummy, dummy, spec, ver = context 303 | proto = proto_of(spec, ver) 304 | while i <= end: 305 | r = rom[i] 306 | t = r >> 30 307 | k = r >> 24 & 0x3f 308 | v = r & 0xffffff 309 | if t == 0: 310 | if k == 0x12: 311 | spec = v 312 | proto = proto_of(spec, ver) 313 | elif k == 0x13: 314 | ver = v 315 | proto = proto_of(spec, ver) 316 | headline = proto[0] + " " + proto[1][k](v) if k in proto[1] \ 317 | else ieee1212_key_ids[k] + \ 318 | ieee1212_key_ids2[k](ouidb, spec, v) if k in ieee1212_key_ids2 \ 319 | else ieee1212_key_ids[k] if k in ieee1212_key_ids \ 320 | else "(immediate value)" 321 | elif t == 1: 322 | headline = proto[0] + " " + proto[1][r >> 24](v) if r >> 24 in proto[1] \ 323 | else "CSR at %012x" % (csr_address(v)) 324 | else: 325 | headline = proto[0] + " " + proto[1][r >> 24](v) + " at %x" % \ 326 | (o + 4 * v) if r >> 24 in proto[1] \ 327 | else "%s%s at %x" % \ 328 | (ieee1212_key_ids[k] + " " if k in ieee1212_key_ids 329 | else "", ieee1212_types[t], o + 4 * v) 330 | blocks[i + v] = (headline, t, k, spec, ver) 331 | f.write("%x %08x %s%s\n" % (o, r, "--> " if t else "", headline)) 332 | i += 1 333 | o += 4 334 | return i 335 | 336 | def write_eui64_hi(f, ouidb, rom, o, i): 337 | hi = rom[i] 338 | f.write("%x %08x company_id %06x | %s\n" % 339 | (o, hi, hi >> 8, ouidb.get(hi >> 8, ""))) 340 | 341 | def write_eui64_lo(f, ouidb, rom, o, i): 342 | hi = rom[i - 1] 343 | lo = rom[i] 344 | f.write("%x %08x device_id %02x%08x | EUI-64 %08x%08x\n" % 345 | (o, lo, hi & 0xff, lo, hi, lo)) 346 | 347 | def write_leaf(f, ouidb, rom, o, i, end, context): 348 | dummy, dummy, key_id, spec, ver = context 349 | descriptor_type_and_spec = None 350 | is_minimal_ascii = None 351 | j = 0 352 | while i <= end: 353 | r = rom[i] 354 | if spec == 0x00a02d and ver in (0x000100, 0x000101, 0x000102) and \ 355 | key_id in (0x01, 0x02) and j < 2: # IIDC vendor or model name 356 | is_minimal_ascii = j and not r and not rom[i - 1]; 357 | f.write("%x %08x\n" % (o, r)) 358 | elif key_id == 0x01 and j == 0: # descriptor leaf, general header 359 | descriptor_type_and_spec = r 360 | if descriptor_type_and_spec == 0x00000000: 361 | f.write("%x %08x textual descriptor\n" % (o, r)) 362 | elif descriptor_type_and_spec == 0x01000000: 363 | f.write("%x %08x icon descriptor\n" % (o, r)) 364 | else: 365 | f.write("%x %08x descriptor_type %02x, specifier_ID %x\n" % 366 | (o, r, r >> 24, r & 0xffffff)) 367 | elif key_id == 0x1f: # modifiable descriptor 368 | if j == 0: 369 | f.write("%x %08x max_descriptor_size %d, " 370 | "descriptor_address_hi %d\n" % 371 | (o, r, r >> 16, r & 0xffff)) 372 | elif j == 1: 373 | f.write("%x %08x descriptor_address_lo %d\n" % (o, r, r)) 374 | elif (key_id == 0x07 or # primary node unique ID leaf 375 | key_id == 0x0d) and j < 2: # eui-64 leaf 376 | (write_eui64_hi, write_eui64_lo)[j](f, ouidb, rom, o, i) 377 | elif key_id == 0x19: # keyword leaf 378 | f.write("%x %08x %s\n" % (o, r, u32_to_string(r))) 379 | elif descriptor_type_and_spec == 0 and j == 1: # textual descriptor 380 | is_minimal_ascii = r >> 16 == 0 381 | if is_minimal_ascii: 382 | f.write("%x %08x minimal ASCII\n" % (o, r)) 383 | else: 384 | f.write("%x %08x width %d, character_set %d, language %s\n" % 385 | (o, r, r >> 28, r >> 16 & 0xfff, language(r))) 386 | elif is_minimal_ascii: 387 | f.write("%x %08x %s\n" % (o, r, u32_to_string(r))) 388 | else: 389 | f.write("%x %08x\n" % (o, r)) 390 | i += 1 391 | j += 1 392 | o += 4 393 | return i 394 | 395 | def write_block(f, ouidb, rom, blocks, i, context): 396 | headline, t, k, spec, ver = context 397 | r = rom[i] 398 | o = 0x400 + i * 4 399 | l = r >> 16 400 | c = r & 0xffff 401 | crc = crc16(rom, i + 1, l) 402 | f.write(" %s\n" 403 | " -----------------------------------------------------------------\n" 404 | "%x %08x %s_length %d, crc %d%s\n" % 405 | (headline, o, r, ieee1212_types[t], l, c, 406 | " (should be %d)" % (crc) if crc <> c else "")) 407 | end = min(i + l, 255) 408 | i += 1 409 | o += 4 410 | if t == 3: 411 | i = write_directory(f, ouidb, rom, blocks, o, i, end, context) 412 | else: 413 | i = write_leaf(f, ouidb, rom, o, i, end, context) 414 | return i 415 | 416 | def write_bus_info_block(f, ouidb, rom, blocks): 417 | f.write(" ROM header and bus information block\n" 418 | " -----------------------------------------------------------------\n") 419 | r = rom[0] 420 | bib_len = r >> 24 421 | crc_len = r >> 16 & 0xff 422 | c = r & 0xffff 423 | crc = crc16(rom, 1, crc_len) 424 | f.write("400 %08x bus_info_length %d, crc_length %d, crc %d%s\n" % 425 | (r, bib_len, crc_len, c, 426 | " (should be %d)" % (crc) if crc <> c else "")) 427 | r = rom[1] 428 | f.write("404 %08x bus_name %s\n" % (r, u32_to_string(r))) 429 | if r == 0x31333934: 430 | r = rom[2] 431 | gen = r >> 4 & 0xf 432 | if gen: 433 | f.write("408 %08x irmc %d, cmc %d, isc %d, bmc %d, pmc %d, " 434 | "cyc_clk_acc %d,\n max_rec %d (%d), " 435 | "max_rom %d, gen %d, spd %d (S%d00)\n" % 436 | (r, r >> 31, r >> 30 & 1, r >> 29 & 1, r >> 28 & 1, r >> 27 & 1, 437 | r >> 16 & 0xff, r >> 12 & 0xf, 2 << (r >> 12 & 0xf), 438 | r >> 8 & 3, gen, r & 7, 1 << (r & 7))) 439 | else: 440 | f.write("408 %08x irmc %d, cmc %d, isc %d, bmc %d, " 441 | "cyc_clk_acc %d, max_rec %d (%d)\n" % 442 | (r, r >> 31, r >> 30 & 1, r >> 29 & 1, r >> 28 & 1, 443 | r >> 16 & 0xff, r >> 12 & 0xf, 2 << (r >> 12 & 0xf))) 444 | else: 445 | f.write("408 %08x bus-dependent information\n" % (rom[2])) 446 | write_eui64_hi(f, ouidb, rom, 0x40c, 3) 447 | write_eui64_lo(f, ouidb, rom, 0x410, 4) 448 | i = 5 449 | while i <= bib_len: 450 | f.write("%x %08x bus-dependent information\n" % 451 | (0x400 + i * 4, rom[i])) 452 | i += 1 453 | blocks[i] = ("root directory", 3, None, None, None) 454 | return i 455 | 456 | def write_config_rom(f, ouidb, rom): 457 | blocks = {} 458 | i = write_bus_info_block(f, ouidb, rom, blocks) 459 | f.write("\n") 460 | need_linefeed = False 461 | while i < 256: 462 | if i in blocks: 463 | if need_linefeed: 464 | f.write("\n") 465 | i = write_block(f, ouidb, rom, blocks, i, blocks[i]) 466 | f.write("\n") 467 | need_linefeed = False 468 | elif rom[i]: 469 | f.write("%x %08x (unreferenced data)\n" % 470 | (0x400 + i * 4, rom[i])) 471 | need_linefeed = True 472 | i += 1 473 | else: 474 | if need_linefeed: 475 | f.write("\n") 476 | need_linefeed = False 477 | i += 1 478 | if need_linefeed: 479 | f.write("\n") 480 | 481 | def read_le32_data(s, l, rom): 482 | for i in xrange(0, l / 4): 483 | j = i * 4 484 | rom[i] = (ord(s[j ]) ) + \ 485 | (ord(s[j + 1]) << 8) + \ 486 | (ord(s[j + 2]) << 16) + \ 487 | (ord(s[j + 3]) << 24) 488 | 489 | def read_be32_data(s, l, rom): 490 | for i in xrange(0, l / 4): 491 | j = i * 4 492 | rom[i] = (ord(s[j ]) << 24) + \ 493 | (ord(s[j + 1]) << 16) + \ 494 | (ord(s[j + 2]) << 8) + \ 495 | (ord(s[j + 3]) ) 496 | 497 | def read_firecontrol_output(s, rom): 498 | i = -1 499 | for line in s.splitlines(): 500 | # fixme: should check that node IDs stay the same and no resets happened 501 | if line[0:18] == "reading from node ": 502 | j = int(line[-20:-8], 16) 503 | if j < 0xfffff0000400 or j >= 0xfffff0000800: 504 | i = -1 505 | continue 506 | i = (j - 0xfffff0000400) / 4 507 | if i == 0: 508 | rom[:] = [0] * 256 509 | continue 510 | if len(line) == 11 and \ 511 | line[2:3] == " " and line[5:6] == " " and line[8:9] == " " and \ 512 | i > -1: 513 | b1 = int(line[0:2], 16) 514 | b2 = int(line[3:5], 16) 515 | b3 = int(line[6:8], 16) 516 | b4 = int(line[9:11], 16) 517 | rom[i] = (b1 << 24) + (b2 << 16) + (b3 << 8) + b4 518 | 519 | def read_log_data(s, rom): 520 | i = -1 521 | for line in s.splitlines(): 522 | # fixme: - should take card, node IDs and transaction labels into account 523 | # - response event may be logged before request event 524 | if line.find("firewire_ohci") >= 0 and \ 525 | line.find(": AT spd ") >= 0 and \ 526 | line.find(", ack_pending , QR req, fffff0000") >= 0: 527 | j = int(line[-12:], 16) 528 | if j < 0xfffff0000400 or j >= 0xfffff0000800: 529 | i = -1 530 | continue 531 | i = (j - 0xfffff0000400) / 4 532 | if i == 0: 533 | rom[:] = [0] * 256 534 | continue 535 | if line.find("firewire_ohci") >= 0 and \ 536 | line.find(": AR spd ") >= 0 and \ 537 | line.find(", ack_complete, QR resp = ") >= 0 and \ 538 | i > -1: 539 | rom[i] = int(line[-8:], 16) 540 | 541 | def build_config_rom(f): 542 | rom = [0] * 256 543 | s = f.read() 544 | l = len(s) 545 | may_be_binary = l > 20 and l <= 1024 and l & 3 == 0 546 | if may_be_binary and s[4:8] == "4931": 547 | read_le32_data(s, l, rom) 548 | elif may_be_binary and s[4:8] == "1394": 549 | read_be32_data(s, l, rom) 550 | elif l > 20 and s[0:12] == "firecontrol ": 551 | read_firecontrol_output(s, rom) 552 | else: 553 | read_log_data(s, rom) 554 | return rom 555 | 556 | def build_oui24_db(paths): 557 | for p in paths: 558 | try: 559 | return dict(map(lambda l: (int(l[:6], 16), l[7:-1]), file(p, "r"))) 560 | except (IOError, ValueError): 561 | continue 562 | # fallback: specifiers from the protocols dictionary 563 | return dict(map(lambda proto: (proto[0], proto[1][0]), protocols.iteritems())) 564 | 565 | def main(): 566 | import sys 567 | rom = build_config_rom(sys.stdin) 568 | if not rom[0] >> 24: 569 | return "Nothing read." 570 | ouidb = build_oui24_db(ouidb_search_paths) 571 | write_config_rom(sys.stdout, ouidb, rom) 572 | 573 | if __name__ == "__main__": 574 | import sys 575 | sys.exit(main()) 576 | -------------------------------------------------------------------------------- /src/firewire-request.c: -------------------------------------------------------------------------------- 1 | /* 2 | * firewire-request.c - send requests to FireWire devices 3 | * 4 | * Copyright 2010-2011 Clemens Ladisch 5 | * 6 | * licensed under the terms of version 2 of the GNU General Public License 7 | */ 8 | 9 | #define _GNU_SOURCE 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define FCP_COMMAND_ADDR 0xfffff0000b00uLL 29 | #define FCP_RESPONSE_ADDR 0xfffff0000d00uLL 30 | 31 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof *(a)) 32 | 33 | #define ptr_to_u64(p) ((uintptr_t)(p)) 34 | 35 | typedef __u8 u8; 36 | typedef __u32 u32; 37 | typedef __u64 u64; 38 | 39 | typedef void (*command_func)(void); 40 | 41 | struct data { 42 | unsigned int length; 43 | u8 *data; 44 | }; 45 | 46 | static bool verbose; 47 | static const char *device_name; 48 | static u64 address; 49 | static unsigned int register_length; 50 | static unsigned int read_length; 51 | static struct data data; 52 | static struct data data2; 53 | static int fd; 54 | static u32 card_index; 55 | static u32 node_id; 56 | static u32 generation; 57 | 58 | static void open_device(void) 59 | { 60 | struct fw_cdev_get_info get_info; 61 | struct fw_cdev_event_bus_reset bus_reset; 62 | 63 | fd = open(device_name, O_RDWR); 64 | if (fd == -1) { 65 | perror(device_name); 66 | exit(EXIT_FAILURE); 67 | } 68 | 69 | #ifdef HAVE_CDEV_4 70 | get_info.version = 4; 71 | #else 72 | get_info.version = 3; 73 | #endif 74 | get_info.rom_length = 0; 75 | get_info.rom = 0; 76 | get_info.bus_reset = ptr_to_u64(&bus_reset); 77 | get_info.bus_reset_closure = 0; 78 | if (ioctl(fd, FW_CDEV_IOC_GET_INFO, &get_info) < 0) { 79 | perror("GET_INFO ioctl failed"); 80 | exit(EXIT_FAILURE); 81 | } 82 | card_index = get_info.card; 83 | node_id = bus_reset.node_id; 84 | generation = bus_reset.generation; 85 | } 86 | 87 | static struct fw_cdev_event_response *wait_for_response(void) 88 | { 89 | static u8 buf[sizeof(struct fw_cdev_event_response) + 16384]; 90 | int r; 91 | struct fw_cdev_event_common *event; 92 | 93 | for (;;) { 94 | r = read(fd, buf, sizeof buf); 95 | if (r < sizeof(struct fw_cdev_event_common)) { 96 | fputs("short read\n", stderr); 97 | exit(EXIT_FAILURE); 98 | } 99 | event = (void *)buf; 100 | if (event->type == FW_CDEV_EVENT_RESPONSE) 101 | return (struct fw_cdev_event_response *)buf; 102 | } 103 | } 104 | 105 | static void print_rcode(u32 rcode) 106 | { 107 | const char *s; 108 | 109 | switch (rcode) { 110 | case RCODE_CONFLICT_ERROR: s = "conflict error"; break; 111 | case RCODE_DATA_ERROR: s = "data error"; break; 112 | case RCODE_TYPE_ERROR: s = "type error"; break; 113 | case RCODE_ADDRESS_ERROR: s = "address error"; break; 114 | case RCODE_SEND_ERROR: s = "send error"; break; 115 | case RCODE_CANCELLED: s = "error: cancelled"; break; 116 | case RCODE_BUSY: s = "error: busy"; break; 117 | case RCODE_GENERATION: s = "error: bus reset"; break; 118 | case RCODE_NO_ACK: s = "error: no ack"; break; 119 | default: s = "unknown error"; break; 120 | } 121 | fprintf(stderr, "%s\n", s); 122 | } 123 | 124 | static void print_data(const char *prefix, const void *p, unsigned int length, bool allow_value) 125 | { 126 | const u8 *data = p; 127 | unsigned int line, col; 128 | 129 | if (allow_value) { 130 | const u32 *data = p; 131 | if (length == 4) { 132 | printf("%s%08x\n", prefix, __be32_to_cpu(*data)); 133 | return; 134 | } 135 | if (length == 8) { 136 | printf("%s%08x%08x\n", prefix, __be32_to_cpu(data[0]), __be32_to_cpu(data[1])); 137 | return; 138 | } 139 | } 140 | 141 | for (line = 0; line < ((length + 15) & ~15); line += 16) { 142 | printf("%s%03x:", prefix, line); 143 | for (col = line; col < line + 16 && col < length; ++col) 144 | printf(" %02x", data[col]); 145 | printf("%*s", 1 + (line + 16 - col) * 3, ""); 146 | for (col = line; col < line + 16 && col < length; ++col) 147 | if (data[col] >= 32 && data[col] < 127) 148 | putchar(data[col]); 149 | else 150 | putchar('.'); 151 | putchar('\n'); 152 | } 153 | } 154 | 155 | static void do_read(void) 156 | { 157 | struct fw_cdev_send_request send_request; 158 | struct fw_cdev_event_response *response; 159 | 160 | if (read_length == 4 && !(address & 3)) 161 | send_request.tcode = TCODE_READ_QUADLET_REQUEST; 162 | else 163 | send_request.tcode = TCODE_READ_BLOCK_REQUEST; 164 | send_request.length = read_length; 165 | send_request.offset = address; 166 | send_request.closure = 0; 167 | send_request.data = 0; 168 | send_request.generation = generation; 169 | if (ioctl(fd, FW_CDEV_IOC_SEND_REQUEST, &send_request) < 0) { 170 | perror("SEND_REQUEST ioctl failed"); 171 | exit(EXIT_FAILURE); 172 | } 173 | response = wait_for_response(); 174 | if (response->rcode != RCODE_COMPLETE) 175 | print_rcode(response->rcode); 176 | else 177 | print_data("result: ", response->data, response->length, response->length == read_length); 178 | } 179 | 180 | static void do_write_request(int request) 181 | { 182 | struct fw_cdev_send_request send_request; 183 | struct fw_cdev_event_response *response; 184 | 185 | if (data.length == 4 && !(address & 3)) 186 | send_request.tcode = TCODE_WRITE_QUADLET_REQUEST; 187 | else 188 | send_request.tcode = TCODE_WRITE_BLOCK_REQUEST; 189 | send_request.length = data.length; 190 | send_request.offset = address; 191 | send_request.closure = 0; 192 | send_request.data = ptr_to_u64(data.data); 193 | send_request.generation = generation; 194 | if (ioctl(fd, request, &send_request) < 0) { 195 | perror("SEND_REQUEST ioctl failed"); 196 | exit(EXIT_FAILURE); 197 | } 198 | response = wait_for_response(); 199 | if (response->rcode != RCODE_COMPLETE) 200 | print_rcode(response->rcode); 201 | } 202 | 203 | static void do_write(void) 204 | { 205 | do_write_request(FW_CDEV_IOC_SEND_REQUEST); 206 | } 207 | 208 | static void do_broadcast(void) 209 | { 210 | do_write_request(FW_CDEV_IOC_SEND_BROADCAST_REQUEST); 211 | } 212 | 213 | static void do_lock_request(u32 tcode) 214 | { 215 | bool has_data2; 216 | u8 *buf; 217 | struct fw_cdev_send_request send_request; 218 | struct fw_cdev_event_response *response; 219 | 220 | has_data2 = tcode != TCODE_LOCK_FETCH_ADD && tcode != TCODE_LOCK_LITTLE_ADD; 221 | if ((data.length != 4 && data.length != 8) || 222 | (has_data2 && (data2.length != 4 && data2.length != 8))) { 223 | fputs("data size must be 32 or 64 bits\n", stderr); 224 | exit(EXIT_FAILURE); 225 | } 226 | if (has_data2 && data.length != data2.length) { 227 | fputs("both data blocks must have the same size\n", stderr); 228 | exit(EXIT_FAILURE); 229 | } 230 | if (has_data2) { 231 | buf = malloc(data.length * 2); 232 | if (!buf) { 233 | fputs("out of memory\n", stderr); 234 | exit(EXIT_FAILURE); 235 | } 236 | memcpy(buf, data.data, data.length); 237 | memcpy(buf + data.length, data2.data, data2.length); 238 | } else 239 | buf = data.data; 240 | send_request.tcode = tcode;; 241 | send_request.length = has_data2 ? data.length * 2 : data.length; 242 | send_request.offset = address; 243 | send_request.closure = 0; 244 | send_request.data = ptr_to_u64(buf); 245 | send_request.generation = generation; 246 | if (ioctl(fd, FW_CDEV_IOC_SEND_REQUEST, &send_request) < 0) { 247 | perror("SEND_REQUEST ioctl failed"); 248 | exit(EXIT_FAILURE); 249 | } 250 | response = wait_for_response(); 251 | if (response->rcode != RCODE_COMPLETE) 252 | print_rcode(response->rcode); 253 | else 254 | print_data("old: ", response->data, response->length, true); 255 | } 256 | 257 | static void do_mask_swap(void) 258 | { 259 | do_lock_request(TCODE_LOCK_MASK_SWAP); 260 | } 261 | 262 | static void do_compare_swap(void) 263 | { 264 | do_lock_request(TCODE_LOCK_COMPARE_SWAP); 265 | } 266 | 267 | static void do_add_big(void) 268 | { 269 | do_lock_request(TCODE_LOCK_FETCH_ADD); 270 | } 271 | 272 | static void do_add_little(void) 273 | { 274 | do_lock_request(TCODE_LOCK_LITTLE_ADD); 275 | } 276 | 277 | static void do_bounded_add(void) 278 | { 279 | do_lock_request(TCODE_LOCK_BOUNDED_ADD); 280 | } 281 | 282 | static void do_wrap_add(void) 283 | { 284 | do_lock_request(TCODE_LOCK_WRAP_ADD); 285 | } 286 | 287 | static void send_response(u32 handle, u32 rcode) 288 | { 289 | struct fw_cdev_send_response send_response; 290 | 291 | send_response.rcode = rcode; 292 | send_response.length = 0; 293 | send_response.data = 0; 294 | send_response.handle = handle; 295 | if (ioctl(fd, FW_CDEV_IOC_SEND_RESPONSE, &send_response) < 0) { 296 | perror("SEND_RESPONSE ioctl failed"); 297 | exit(EXIT_FAILURE); 298 | } 299 | } 300 | 301 | static void do_fcp(void) 302 | { 303 | static u8 buf[sizeof(struct fw_cdev_event_response) + 0x200]; 304 | struct fw_cdev_allocate allocate; 305 | struct fw_cdev_send_request send_request; 306 | bool ack_received, response_received; 307 | struct pollfd pfd; 308 | int ready, r; 309 | struct fw_cdev_event_common *event; 310 | 311 | allocate.offset = FCP_RESPONSE_ADDR; 312 | allocate.closure = 0; 313 | allocate.length = 0x200; 314 | #ifdef HAVE_CDEV_4 315 | allocate.region_end = allocate.offset + allocate.length; 316 | #endif 317 | if (ioctl(fd, FW_CDEV_IOC_ALLOCATE, &allocate) < 0) { 318 | perror("ALLOCATE ioctl failed"); 319 | exit(EXIT_FAILURE); 320 | } 321 | 322 | send_request.tcode = data.length == 4 ? TCODE_WRITE_QUADLET_REQUEST : TCODE_WRITE_BLOCK_REQUEST; 323 | send_request.length = data.length; 324 | send_request.offset = FCP_COMMAND_ADDR; 325 | send_request.closure = 0; 326 | send_request.data = ptr_to_u64(data.data); 327 | send_request.generation = generation; 328 | if (ioctl(fd, FW_CDEV_IOC_SEND_REQUEST, &send_request) < 0) { 329 | perror("SEND_REQUEST ioctl failed"); 330 | exit(EXIT_FAILURE); 331 | } 332 | 333 | ack_received = false; 334 | response_received = false; 335 | pfd.fd = fd; 336 | pfd.events = POLLIN; 337 | while (!ack_received || !response_received) { 338 | /* XXX what's a practical timeout for FCP responses? */ 339 | ready = poll(&pfd, 1, 2345); 340 | if (ready < 0) { 341 | perror("poll failed"); 342 | exit(EXIT_FAILURE); 343 | } 344 | if (!ready) { 345 | fputs("timeout\n", stderr); 346 | exit(EXIT_FAILURE); 347 | } 348 | r = read(fd, buf, sizeof buf); 349 | if (r < sizeof(struct fw_cdev_event_common)) { 350 | fputs("short read\n", stderr); 351 | exit(EXIT_FAILURE); 352 | } 353 | event = (void *)buf; 354 | if (event->type == FW_CDEV_EVENT_BUS_RESET) { 355 | fputs("bus reset\n", stderr); 356 | exit(EXIT_FAILURE); 357 | } 358 | if (event->type == FW_CDEV_EVENT_RESPONSE) { 359 | struct fw_cdev_event_response *response = (void *)buf; 360 | if (response->rcode != RCODE_COMPLETE) { 361 | print_rcode(response->rcode); 362 | return; 363 | } 364 | ack_received = true; 365 | #ifdef HAVE_CDEV_4 366 | } else if (event->type == FW_CDEV_EVENT_REQUEST2) { 367 | struct fw_cdev_event_request2 *request = (void *)buf; 368 | send_response(request->handle, RCODE_COMPLETE); 369 | if (request->card == card_index && 370 | (request->source_node_id & 0x3f) == (node_id & 0x3f) && 371 | (request->tcode == TCODE_WRITE_QUADLET_REQUEST || 372 | request->tcode == TCODE_WRITE_BLOCK_REQUEST) && 373 | request->offset == FCP_RESPONSE_ADDR && 374 | request->generation == generation) { 375 | print_data("response: ", request->data, request->length, false); 376 | response_received = true; 377 | } 378 | #endif 379 | } else if (event->type == FW_CDEV_EVENT_REQUEST) { 380 | struct fw_cdev_event_request *request = (void *)buf; 381 | send_response(request->handle, RCODE_COMPLETE); 382 | if ((request->tcode == TCODE_WRITE_QUADLET_REQUEST || 383 | request->tcode == TCODE_WRITE_BLOCK_REQUEST) && 384 | request->offset == FCP_RESPONSE_ADDR) { 385 | print_data("response: ", request->data, request->length, false); 386 | response_received = true; 387 | } 388 | } 389 | } 390 | } 391 | 392 | static void do_bus_reset(u32 type) 393 | { 394 | struct fw_cdev_initiate_bus_reset reset; 395 | 396 | reset.type = type; 397 | if (ioctl(fd, FW_CDEV_IOC_INITIATE_BUS_RESET, &reset) < 0) { 398 | perror("INITIATE_BUS_RESET ioctl failed"); 399 | exit(EXIT_FAILURE); 400 | } 401 | } 402 | 403 | static void do_reset(void) 404 | { 405 | do_bus_reset(FW_CDEV_SHORT_RESET); 406 | } 407 | 408 | static void do_long_reset(void) 409 | { 410 | do_bus_reset(FW_CDEV_LONG_RESET); 411 | } 412 | 413 | static const struct command { 414 | const char *name; 415 | command_func function; 416 | bool has_addr; 417 | bool has_length; 418 | bool has_data; 419 | bool has_data2; 420 | } commands[] = { 421 | { "read", do_read, .has_addr = true, .has_length = true }, 422 | { "write", do_write, .has_addr = true, .has_data = true }, 423 | { "broadcast", do_broadcast, .has_addr = true, .has_data = true }, 424 | { "mask_swap", do_mask_swap, .has_addr = true, .has_data = true, .has_data2 = true }, 425 | { "compare_swap", do_compare_swap, .has_addr = true, .has_data = true, .has_data2 = true }, 426 | { "add", do_add_big, .has_addr = true, .has_data = true }, 427 | { "add_big", do_add_big, .has_addr = true, .has_data = true }, 428 | { "add_little", do_add_little, .has_addr = true, .has_data = true }, 429 | { "bounded_add", do_bounded_add, .has_addr = true, .has_data = true, .has_data2 = true }, 430 | { "bounded_add_big", do_bounded_add, .has_addr = true, .has_data = true, .has_data2 = true }, 431 | { "wrap_add", do_wrap_add, .has_addr = true, .has_data = true, .has_data2 = true }, 432 | { "wrap_add_big", do_wrap_add, .has_addr = true, .has_data = true, .has_data2 = true }, 433 | { "fcp", do_fcp, .has_data = true }, 434 | { "reset", do_reset }, 435 | { "long_reset", do_long_reset }, 436 | }; 437 | 438 | static const struct register_name { 439 | u64 address; 440 | unsigned int size; 441 | const char *name; 442 | bool hide; 443 | #define HIDDEN true 444 | } register_names[] = { 445 | { 0xfffff0000000uLL, 4, "state_clear" }, 446 | { 0xfffff0000004uLL, 4, "state_set" }, 447 | { 0xfffff0000008uLL, 4, "node_ids" }, 448 | { 0xfffff000000cuLL, 4, "reset_start" }, 449 | #if 0 /* reserved in IEEE-1394 */ 450 | { 0xfffff0000010uLL, 4, "indirect_address", HIDDEN }, 451 | { 0xfffff0000014uLL, 4, "indirect_data", HIDDEN }, 452 | #endif 453 | { 0xfffff0000018uLL, 8, "split_timeout" }, 454 | { 0xfffff0000018uLL, 4, "split_timeout_hi" }, 455 | { 0xfffff000001cuLL, 4, "split_timeout_lo" }, 456 | { 0xfffff0000020uLL, 8, "argument", HIDDEN }, 457 | { 0xfffff0000020uLL, 4, "argument_hi", HIDDEN }, 458 | { 0xfffff0000024uLL, 4, "argument_lo", HIDDEN }, 459 | { 0xfffff0000028uLL, 4, "test_start", HIDDEN }, 460 | { 0xfffff000002cuLL, 4, "test_status", HIDDEN }, 461 | #if 0 /* reserved in IEEE-1394 */ 462 | { 0xfffff0000030uLL, 8, "units_base", HIDDEN }, 463 | { 0xfffff0000030uLL, 4, "units_base_hi", HIDDEN }, 464 | { 0xfffff0000034uLL, 4, "units_base_lo", HIDDEN }, 465 | { 0xfffff0000038uLL, 8, "units_bound", HIDDEN }, 466 | { 0xfffff0000038uLL, 4, "units_bound_hi", HIDDEN }, 467 | { 0xfffff000003cuLL, 4, "units_bound_lo", HIDDEN }, 468 | { 0xfffff0000040uLL, 8, "memory_base", HIDDEN }, 469 | { 0xfffff0000040uLL, 4, "memory_base_hi", HIDDEN }, 470 | { 0xfffff0000044uLL, 4, "memory_base_lo", HIDDEN }, 471 | { 0xfffff0000048uLL, 8, "memory_bound", HIDDEN }, 472 | { 0xfffff0000048uLL, 4, "memory_bound_hi", HIDDEN }, 473 | { 0xfffff000004cuLL, 4, "memory_bound_lo", HIDDEN }, 474 | #endif 475 | { 0xfffff0000050uLL, 4, "interrupt_target", HIDDEN }, 476 | { 0xfffff0000054uLL, 4, "interrupt_mask", HIDDEN }, 477 | { 0xfffff0000058uLL, 8, "clock_value", HIDDEN }, 478 | { 0xfffff0000058uLL, 4, "clock_value_hi", HIDDEN }, 479 | { 0xfffff000005cuLL, 4, "clock_value_mid", HIDDEN }, 480 | { 0xfffff0000060uLL, 8, "clock_tick_period", HIDDEN }, 481 | { 0xfffff0000060uLL, 4, "clock_tick_period_mid", HIDDEN }, 482 | { 0xfffff0000064uLL, 4, "clock_tick_period_lo", HIDDEN }, 483 | { 0xfffff0000068uLL, 8, "clock_strobe_arrived", HIDDEN }, 484 | { 0xfffff0000068uLL, 4, "clock_strobe_arrived_hi", HIDDEN }, 485 | { 0xfffff000006cuLL, 4, "clock_strobe_arrived_mid", HIDDEN }, 486 | { 0xfffff0000070uLL, 4, "clock_info0", HIDDEN }, 487 | { 0xfffff0000074uLL, 4, "clock_info1", HIDDEN }, 488 | { 0xfffff0000078uLL, 4, "clock_info2", HIDDEN }, 489 | { 0xfffff000007cuLL, 4, "clock_info3", HIDDEN }, 490 | { 0xfffff0000080uLL, 64, "message_request" }, 491 | { 0xfffff00000c0uLL, 64, "message_response" }, 492 | { 0xfffff0000180uLL, 128, "error_log_buffer", HIDDEN }, 493 | { 0xfffff0000200uLL, 4, "cycle_time" }, 494 | { 0xfffff0000204uLL, 4, "bus_time" }, 495 | { 0xfffff0000208uLL, 4, "power_fail_imminent", HIDDEN }, 496 | { 0xfffff000020cuLL, 4, "power_source", HIDDEN }, 497 | { 0xfffff0000210uLL, 4, "busy_timeout" }, 498 | { 0xfffff0000214uLL, 4, "quarantine", HIDDEN }, 499 | { 0xfffff0000218uLL, 4, "priority_budget" }, 500 | { 0xfffff000021cuLL, 4, "bus_manager_id" }, 501 | { 0xfffff0000220uLL, 4, "bandwidth_available" }, 502 | { 0xfffff0000224uLL, 8, "channels_available" }, 503 | { 0xfffff0000224uLL, 4, "channels_available_hi" }, 504 | { 0xfffff0000228uLL, 4, "channels_available_lo" }, 505 | { 0xfffff000022cuLL, 4, "maint_control", HIDDEN }, 506 | { 0xfffff0000230uLL, 4, "maint_utility" }, 507 | { 0xfffff0000234uLL, 4, "broadcast_channel" }, 508 | { 0xfffff0000400uLL, 0x400, "config_rom" }, 509 | { 0xfffff0000900uLL, 4, "output_master_plug" }, 510 | { 0xfffff0000904uLL, 4, "output_plug0" }, 511 | { 0xfffff0000908uLL, 4, "output_plug1", HIDDEN }, 512 | { 0xfffff000090cuLL, 4, "output_plug2", HIDDEN }, 513 | { 0xfffff0000910uLL, 4, "output_plug3", HIDDEN }, 514 | { 0xfffff0000914uLL, 4, "output_plug4", HIDDEN }, 515 | { 0xfffff0000918uLL, 4, "output_plug5", HIDDEN }, 516 | { 0xfffff000091cuLL, 4, "output_plug6", HIDDEN }, 517 | { 0xfffff0000920uLL, 4, "output_plug7", HIDDEN }, 518 | { 0xfffff0000924uLL, 4, "output_plug8", HIDDEN }, 519 | { 0xfffff0000928uLL, 4, "output_plug9", HIDDEN }, 520 | { 0xfffff000092cuLL, 4, "output_plug10", HIDDEN }, 521 | { 0xfffff0000930uLL, 4, "output_plug11", HIDDEN }, 522 | { 0xfffff0000934uLL, 4, "output_plug12", HIDDEN }, 523 | { 0xfffff0000938uLL, 4, "output_plug13", HIDDEN }, 524 | { 0xfffff000093cuLL, 4, "output_plug14", HIDDEN }, 525 | { 0xfffff0000940uLL, 4, "output_plug15", HIDDEN }, 526 | { 0xfffff0000944uLL, 4, "output_plug16", HIDDEN }, 527 | { 0xfffff0000948uLL, 4, "output_plug17", HIDDEN }, 528 | { 0xfffff000094cuLL, 4, "output_plug18", HIDDEN }, 529 | { 0xfffff0000950uLL, 4, "output_plug19", HIDDEN }, 530 | { 0xfffff0000954uLL, 4, "output_plug20", HIDDEN }, 531 | { 0xfffff0000958uLL, 4, "output_plug21", HIDDEN }, 532 | { 0xfffff000095cuLL, 4, "output_plug22", HIDDEN }, 533 | { 0xfffff0000960uLL, 4, "output_plug23", HIDDEN }, 534 | { 0xfffff0000964uLL, 4, "output_plug24", HIDDEN }, 535 | { 0xfffff0000968uLL, 4, "output_plug25", HIDDEN }, 536 | { 0xfffff000096cuLL, 4, "output_plug26", HIDDEN }, 537 | { 0xfffff0000970uLL, 4, "output_plug27", HIDDEN }, 538 | { 0xfffff0000974uLL, 4, "output_plug28", HIDDEN }, 539 | { 0xfffff0000978uLL, 4, "output_plug29", HIDDEN }, 540 | { 0xfffff000097cuLL, 4, "output_plug30" }, 541 | { 0xfffff0000980uLL, 4, "input_master_plug" }, 542 | { 0xfffff0000984uLL, 4, "input_plug0" }, 543 | { 0xfffff0000988uLL, 4, "input_plug1", HIDDEN }, 544 | { 0xfffff000098cuLL, 4, "input_plug2", HIDDEN }, 545 | { 0xfffff0000990uLL, 4, "input_plug3", HIDDEN }, 546 | { 0xfffff0000994uLL, 4, "input_plug4", HIDDEN }, 547 | { 0xfffff0000998uLL, 4, "input_plug5", HIDDEN }, 548 | { 0xfffff000099cuLL, 4, "input_plug6", HIDDEN }, 549 | { 0xfffff00009a0uLL, 4, "input_plug7", HIDDEN }, 550 | { 0xfffff00009a4uLL, 4, "input_plug8", HIDDEN }, 551 | { 0xfffff00009a8uLL, 4, "input_plug9", HIDDEN }, 552 | { 0xfffff00009acuLL, 4, "input_plug10", HIDDEN }, 553 | { 0xfffff00009b0uLL, 4, "input_plug11", HIDDEN }, 554 | { 0xfffff00009b4uLL, 4, "input_plug12", HIDDEN }, 555 | { 0xfffff00009b8uLL, 4, "input_plug13", HIDDEN }, 556 | { 0xfffff00009bcuLL, 4, "input_plug14", HIDDEN }, 557 | { 0xfffff00009c0uLL, 4, "input_plug15", HIDDEN }, 558 | { 0xfffff00009c4uLL, 4, "input_plug16", HIDDEN }, 559 | { 0xfffff00009c8uLL, 4, "input_plug17", HIDDEN }, 560 | { 0xfffff00009ccuLL, 4, "input_plug18", HIDDEN }, 561 | { 0xfffff00009d0uLL, 4, "input_plug19", HIDDEN }, 562 | { 0xfffff00009d4uLL, 4, "input_plug20", HIDDEN }, 563 | { 0xfffff00009d8uLL, 4, "input_plug21", HIDDEN }, 564 | { 0xfffff00009dcuLL, 4, "input_plug22", HIDDEN }, 565 | { 0xfffff00009e0uLL, 4, "input_plug23", HIDDEN }, 566 | { 0xfffff00009e4uLL, 4, "input_plug24", HIDDEN }, 567 | { 0xfffff00009e8uLL, 4, "input_plug25", HIDDEN }, 568 | { 0xfffff00009ecuLL, 4, "input_plug26", HIDDEN }, 569 | { 0xfffff00009f0uLL, 4, "input_plug27", HIDDEN }, 570 | { 0xfffff00009f4uLL, 4, "input_plug28", HIDDEN }, 571 | { 0xfffff00009f8uLL, 4, "input_plug29", HIDDEN }, 572 | { 0xfffff00009fcuLL, 4, "input_plug30" }, 573 | { 0xfffff0000b00uLL, 0x200, "fcp_command" }, 574 | { 0xfffff0000d00uLL, 0x200, "fcp_response" }, 575 | { 0xfffff0001000uLL, 0x400, "topology_map" }, 576 | { 0xfffff0001c00uLL, 0x200, "virtual_id_map", HIDDEN }, 577 | { 0xfffff0001e00uLL, 0x100, "route_map", HIDDEN }, 578 | { 0xfffff0001f00uLL, 8, "clan_eui_64", HIDDEN }, 579 | { 0xfffff0001f08uLL, 4, "clan_info", HIDDEN }, 580 | { 0xfffff0002000uLL, 0x1000, "speed_map", HIDDEN }, 581 | }; 582 | 583 | static void parse_address(const char *s) 584 | { 585 | char *endptr; 586 | unsigned int i; 587 | 588 | address = strtoull(s, &endptr, 16); 589 | if (*endptr != '\0') { 590 | for (i = 0; i < ARRAY_SIZE(register_names); ++i) 591 | if (!strcasecmp(s, register_names[i].name)) { 592 | address = register_names[i].address; 593 | register_length = register_names[i].size; 594 | return; 595 | } 596 | fprintf(stderr, "invalid address: `%s'\n", s); 597 | exit(EXIT_FAILURE); 598 | } 599 | } 600 | 601 | static void parse_read_length(const char *s) 602 | { 603 | char *endptr; 604 | unsigned long int l; 605 | 606 | l = strtoul(s, &endptr, 16); 607 | if (*endptr != '\0') { 608 | fprintf(stderr, "invalid length: `%s'\n", s); 609 | exit(EXIT_FAILURE); 610 | } 611 | read_length = l; 612 | } 613 | 614 | static void parse_data(const char *s, struct data *data) 615 | { 616 | unsigned int len; 617 | bool high_nibble = true; 618 | u8 val; 619 | 620 | while (isspace(*s)) 621 | ++s; 622 | if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) 623 | s += 2; 624 | 625 | len = strlen(s); 626 | data->data = malloc(len / 2 + 1); 627 | if (!data->data) { 628 | fputs("out of memory\n", stderr); 629 | exit(EXIT_FAILURE); 630 | } 631 | 632 | for (len = 0; *s != '\0'; ++s) { 633 | if (isspace(*s) || *s == '_') 634 | continue; 635 | if ('0' <= *s && *s <= '9') 636 | val = *s - '0'; 637 | else if ('A' <= *s && *s <= 'F') 638 | val = *s - 'A' + 10; 639 | else if ('a' <= *s && *s <= 'f') 640 | val = *s - 'a' + 10; 641 | else { 642 | fprintf(stderr, "invalid character in data: `%c'\n", *s); 643 | exit(EXIT_FAILURE); 644 | } 645 | if (high_nibble) { 646 | data->data[len] = val << 4; 647 | high_nibble = false; 648 | } else { 649 | data->data[len++] |= val; 650 | high_nibble = true; 651 | } 652 | } 653 | if (!high_nibble) { 654 | fputs("data ends in a half byte\n", stderr); 655 | exit(EXIT_FAILURE); 656 | } 657 | if (register_length && register_length <= 8 && register_length != len) { 658 | fprintf(stderr, "data for this register must have %u bits\n", register_length * 8); 659 | exit(EXIT_FAILURE); 660 | } 661 | data->length = len; 662 | } 663 | 664 | static void help(void) 665 | { 666 | fputs("firewire-request read []\n" 667 | "firewire-request write \n" 668 | "firewire-request []\n" 669 | "firewire-request broadcast \n" 670 | "firewire-request fcp \n" 671 | "firewire-request reset|long_reset\n" 672 | "\n" 673 | " is device node (/dev/fwX)\n" 674 | " is address in hex or register name\n" 675 | " is byte length in hex, default from register or 4\n" 676 | " is data bytes in hex (spaces must be quoted)\n" 677 | " is mask_swap|compare_swap|add_big|add_little|bounded_add|wrap_add\n" 678 | "\n" 679 | "Options:\n" 680 | " -D,--dump-register-names show known register names and exit\n" 681 | " -v,--verbose more information\n" 682 | " -h,--help show this message and exit\n" 683 | " -V,--version show version number and exit\n" 684 | "\n" 685 | "Report bugs to <" PACKAGE_BUGREPORT ">.\n" 686 | PACKAGE_NAME " home page: <" PACKAGE_URL ">.\n", 687 | stderr); 688 | } 689 | 690 | static void help_registers(void) 691 | { 692 | unsigned int i; 693 | 694 | puts("address length name"); 695 | for (i = 0; i < ARRAY_SIZE(register_names); ++i) { 696 | bool hi_lo; 697 | if (register_names[i].hide && !verbose) 698 | continue; 699 | hi_lo = !verbose && i < ARRAY_SIZE(register_names) - 2 && 700 | register_names[i].address == register_names[i + 1].address; 701 | printf("%012Lx %4x %s%s\n", register_names[i].address, 702 | register_names[i].size, register_names[i].name, 703 | hi_lo ? "[_hi|_lo]" : ""); 704 | if (hi_lo) 705 | i += 2; 706 | } 707 | } 708 | 709 | static command_func parse_parameters(int argc, char *argv[]) 710 | { 711 | static const char short_options[] = "DvhV"; 712 | static const struct option long_options[] = { 713 | { "dump-register-names", 0, NULL, 'D' }, 714 | { "verbose", 0, NULL, 'v' }, 715 | { "help", 0, NULL, 'h' }, 716 | { "version", 0, NULL, 'V' }, 717 | {} 718 | }; 719 | int c; 720 | bool show_regs = false, show_help = false, show_version = false; 721 | const struct command *command; 722 | 723 | while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { 724 | switch (c) { 725 | case 'D': 726 | show_regs = true; 727 | break; 728 | case 'v': 729 | verbose = true; 730 | break; 731 | case 'h': 732 | show_help = true; 733 | break; 734 | case 'V': 735 | show_version = true; 736 | break; 737 | default: 738 | syntax_error: 739 | help(); 740 | exit(EXIT_FAILURE); 741 | } 742 | } 743 | 744 | if (show_regs) 745 | help_registers(); 746 | if (show_help) 747 | help(); 748 | if (show_version) 749 | puts("firewire-request version " PACKAGE_VERSION); 750 | if (show_regs || show_help || show_version) 751 | exit(EXIT_SUCCESS); 752 | 753 | if (optind >= argc) 754 | goto syntax_error; 755 | device_name = argv[optind++]; 756 | 757 | if (optind >= argc) 758 | goto syntax_error; 759 | for (c = 0; c < ARRAY_SIZE(commands); ++c) 760 | if (!strcasecmp(argv[optind], commands[c].name)) { 761 | command = &commands[c]; 762 | goto command_found; 763 | } 764 | fprintf(stderr, "unknown command: `%s'\n", argv[optind]); 765 | goto syntax_error; 766 | command_found: 767 | ++optind; 768 | 769 | if (command->has_addr) { 770 | if (optind >= argc) 771 | goto syntax_error; 772 | parse_address(argv[optind++]); 773 | } 774 | 775 | if (command->has_length) { 776 | if (optind < argc) 777 | parse_read_length(argv[optind++]); 778 | else if (register_length) 779 | read_length = register_length; 780 | else 781 | read_length = 4; 782 | } 783 | 784 | if (command->has_data) { 785 | if (optind >= argc) 786 | goto syntax_error; 787 | parse_data(argv[optind++], &data); 788 | } 789 | 790 | if (command->has_data2) { 791 | if (optind >= argc) 792 | goto syntax_error; 793 | parse_data(argv[optind++], &data2); 794 | } 795 | 796 | if (optind < argc) { 797 | fprintf(stderr, "superfluous parameter: `%s'\n", argv[optind]); 798 | goto syntax_error; 799 | } 800 | 801 | return command->function; 802 | } 803 | 804 | int main(int argc, char *argv[]) 805 | { 806 | command_func fn; 807 | 808 | fn = parse_parameters(argc, argv); 809 | open_device(); 810 | fn(); 811 | close(fd); 812 | return 0; 813 | } 814 | --------------------------------------------------------------------------------