/* $NetBSD: fwcontrol.c,v 1.18 2025/02/27 21:09:23 rillig Exp $ */
/*
* Copyright (C) 2002
* Hidetoshi Shimokawa. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
*
* This product includes software developed by Hidetoshi Shimokawa.
*
* 4. Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
//__FBSDID("$FreeBSD: src/usr.sbin/fwcontrol/fwcontrol.c,v 1.23 2006/10/26 22:33:38 imp Exp $");
__RCSID("$NetBSD: fwcontrol.c,v 1.18 2025/02/27 21:09:23 rillig Exp $");
qld = (uint32_t *)&asyreq->pkt;
if (!readmode)
asyreq->pkt.mode.wreqq.data = htonl(data);
if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
err(EX_IOERR, "%s: ioctl", __func__);
res = qld[3];
free(asyreq);
if (readmode)
return ntohl(res);
else
return 0;
}
/*
* Send a PHY Config Packet
* ieee 1394a-2005 4.3.4.3
*
* Message ID Root ID R T Gap Count
* 00(2 bits) (6 bits) 1 1 (6 bits)
*
* if "R" is set, then Root ID will be the next
* root node upon the next bus reset.
* if "T" is set, then Gap Count will be the
* value that all nodes use for their Gap Count
* if "R" and "T" are not set, then this message
* is either ignored or interpreted as an extended
* PHY config Packet as per 1394a-2005 4.3.4.4
*/
static void
send_phy_config(int fd, int root_node, int gap_count)
{
struct fw_asyreq *asyreq;
static int
get_crom(int fd, int node, void *crom_buf, int len)
{
struct fw_crom_buf buf;
int i, error;
struct fw_devlstreq *data;
data = malloc(sizeof(*data));
if (data == NULL)
err(EX_SOFTWARE, "%s: data malloc", __func__);
get_dev(fd, data);
for (i = 0; i < data->info_len; i++)
if (data->dev[i].dst == node && data->dev[i].eui.lo != 0)
break;
if (i == data->info_len)
errx(EX_SOFTWARE, "%s: no such node %d", __func__, node);
else
buf.eui = data->dev[i].eui;
free(data);
buf = malloc(RECV_NUM_PACKET * RECV_PACKET_SZ);
if (buf == NULL)
err(EX_SOFTWARE, "%s: buf malloc", __func__);
/*
* fwdev.c seems to return EIO on error and
* the return value of the last uiomove
* on success. For now, checking that the
* return is not less than zero should be
* sufficient. fwdev.c::fw_read() should
* return the total length read, not the value
* of the last uiomove().
*/
len = read(fd, buf, RECV_NUM_PACKET * RECV_PACKET_SZ);
if (len < 0)
err(EX_IOERR, "%s: error reading from device", __func__);
ptr = (uint32_t *) buf;
ciph = (struct ciphdr *)(ptr + 1);
switch (ciph->fmt) {
case CIP_FMT_DVCR:
fprintf(stderr, "Detected DV format on input.\n");
retfn = dvrecv;
break;
case CIP_FMT_MPEG:
fprintf(stderr, "Detected MPEG TS format on input.\n");
retfn = mpegtsrecv;
break;
default:
errx(EXIT_FAILURE,
"%s: Unsupported format for receiving: fmt=0x%x", __func__,
ciph->fmt);
}
free(buf);
return retfn;
}
int
main(int argc, char **argv)
{
#define MAX_BOARDS 10
uint32_t crom_buf[1024/4];
uint32_t crom_buf_hex[1024/4];
char devbase[64];
const char *device_string = "/dev/fw";
int fd = -1, ch, len=1024;
int32_t current_board = 0;
/*
* If !command_set, then -u will display the nodes for the board.
* This emulates the previous behavior when -u is passed by itself
*/
bool command_set = false;
bool open_needed = false;
long tmp;
struct fw_eui64 eui;
struct eui64 target;
fwmethod *recvfn = NULL;
if (argc < 2) {
for (current_board = 0; current_board < MAX_BOARDS; current_board++) {
snprintf(devbase, sizeof(devbase), "%s%d.0", device_string, current_board);
if (open_dev(&fd, devbase) < 0) {
if (current_board == 0)
usage();
return EIO;
}
list_dev(fd);
close(fd);
fd = -1;
}
}
/*
* Parse all command line options, then execute requested operations.
*/
while ((ch = getopt(argc, argv, "b:c:d:f:g:l:M:m:o:pR:rS:s:tu:")) != -1) {
switch (ch) {
case 'b':
priority_budget = strtol(optarg, NULL, 0);
if (priority_budget < 0 || priority_budget > INT32_MAX)
errx(EX_USAGE,
"%s: priority_budget out of range: %s",
__func__, optarg);
command_set = true;
open_needed = true;
display_board_only = false;
break;
case 'c':
crom_string = strdup(optarg);
if (crom_string == NULL)
err(EX_SOFTWARE, "%s: crom_string malloc",
__func__);
if (strtol(crom_string, NULL, 0) < 0 ||
strtol(crom_string, NULL, 0) > MAX_BOARDS)
errx(EX_USAGE, "%s: Invalid value for node",
__func__);
display_crom = 1;
open_needed = true;
command_set = true;
display_board_only = false;
break;
case 'd':
crom_string_hex = strdup(optarg);
if (crom_string_hex == NULL)
err(EX_SOFTWARE, "%s: crom_string_hex malloc",
__func__);
display_crom_hex = 1;
open_needed = true;
command_set = true;
display_board_only = false;
break;
case 'f':
#define MAX_PHY_CONFIG 0x3f
set_root_node = strtol(optarg, NULL, 0);
if (set_root_node < 0 || set_root_node > MAX_PHY_CONFIG)
errx(EX_USAGE, "%s: set_root_node out of range",
__func__);
open_needed = true;
command_set = true;
display_board_only = false;
break;
case 'g':
set_gap_count = strtol(optarg, NULL, 0);
if (set_gap_count < 0 || set_gap_count > MAX_PHY_CONFIG)
errx(EX_USAGE, "%s: set_gap_count out of range",
__func__);
open_needed = true;
command_set = true;
display_board_only = false;
break;
case 'l':
load_crom_from_file = 1;
load_crom(optarg, crom_buf);
command_set = true;
display_board_only = false;
break;
case 'm':
set_fwmem_target = 1;
open_needed = 0;
command_set = true;
display_board_only = false;
if (eui64_hostton(optarg, &target) != 0 &&
eui64_aton(optarg, &target) != 0)
errx(EX_USAGE, "%s: invalid target: %s",
__func__, optarg);
break;
case 'o':
send_link_on = str2node(fd, optarg);
if (send_link_on < 0 || send_link_on > MAX_PHY_CONFIG)
errx(EX_USAGE, "%s: node out of range: %s",
__func__, optarg);
open_needed = true;
command_set = true;
display_board_only = false;
break;
case 'p':
dump_phy_reg = 1;
open_needed = true;
command_set = true;
display_board_only = false;
break;
case 'r':
send_bus_reset = 1;
open_needed = true;
command_set = true;
display_board_only = false;
break;
case 's':
send_reset_start = str2node(fd, optarg);
if (send_reset_start < 0 ||
send_reset_start > MAX_PHY_CONFIG)
errx(EX_USAGE, "%s: node out of range: %s",
__func__, optarg);
open_needed = true;
command_set = true;
display_board_only = false;
break;
case 't':
dump_topology = 1;
open_needed = true;
command_set = true;
display_board_only = false;
break;
case 'u':
if (!command_set)
display_board_only = true;
current_board = strtol(optarg, NULL, 0);
open_needed = true;
break;
case 'M':
switch (optarg[0]) {
case 'm':
recvfn = mpegtsrecv;
break;
case 'd':
recvfn = dvrecv;
break;
default:
errx(EX_USAGE, "%s: unrecognized method: %s",
__func__, optarg);
}
command_set = true;
display_board_only = false;
break;
case 'R':
recv_data = strdup(optarg);
if (recv_data == NULL)
err(EX_SOFTWARE, "%s: recv_data malloc",
__func__);
open_needed = false;
command_set = true;
display_board_only = false;
break;
case 'S':
send_data = strdup(optarg);
if (send_data == NULL)
err(EX_SOFTWARE, "%s: send_data malloc",
__func__);
open_needed = true;
command_set = true;
display_board_only = false;
break;
default:
usage();
}
}
/*
* Catch the error case when the user
* executes the command with non ''-''
* delimited arguments.
*/
if (!command_set && !display_board_only)
usage();
/*
* If -u <bus_number> is passed, execute
* command for that card only.
*
* If -u <bus_number> is not passed, execute
* command for card 0 only.
*
*/
if (open_needed) {
snprintf(devbase, sizeof(devbase),
"%s%d.0", device_string, current_board);
if (open_dev(&fd, devbase) < 0) {
err(EX_IOERR,
"%s: Error opening firewire controller #%d %s",
__func__, current_board, devbase);
}
}
/*
* display the nodes on this board "-u" only
*/
if (display_board_only)
list_dev(fd);
/*
* dump_phy_reg "-p"
*/
if (dump_phy_reg)
dump_phy_registers(fd);
/*
* send a BUS_RESET Event "-r"
*/
if (send_bus_reset)
if (ioctl(fd, FW_IBUSRST, &tmp) < 0)
err(EX_IOERR, "%s: Ioctl of bus reset failed for %s",
__func__, devbase);
/*
* Print out the CROM for this node "-c"
*/
if (display_crom) {
tmp = str2node(fd, crom_string);
get_crom(fd, tmp, crom_buf, len);
show_crom(crom_buf);
free(crom_string);
}
/*
* Hex Dump the CROM for this node "-d"
*/
if (display_crom_hex) {
tmp = str2node(fd, crom_string_hex);
get_crom(fd, tmp, crom_buf_hex, len);
dump_crom(crom_buf_hex);
free(crom_string_hex);
}
/*
* Set Priority Budget to value for this node "-b"
*/
if (priority_budget >= 0)
set_pri_req(fd, priority_budget);
/*
* Explicitly set the root node of this bus to value "-f"
*/
if (set_root_node >= 0)
send_phy_config(fd, set_root_node, -1);
/*
* Set the gap count for this card/bus "-g"
*/
if (set_gap_count >= 0)
send_phy_config(fd, -1, set_gap_count);
/*
* Load a CROM from a file "-l"
*/
if (load_crom_from_file)
show_crom(crom_buf);
/*
* Set the fwmem target for a node to argument "-m"
*/
if (set_fwmem_target) {
uint32_t hi, lo;
memcpy(&hi, &target.octet[0], sizeof(hi));
memcpy(&lo, &target.octet[4], sizeof(lo));
eui.hi = ntohl(hi);
eui.lo = ntohl(lo);
sysctl_set_int("hw.fwmem.eui64_hi", eui.hi);
sysctl_set_int("hw.fwmem.eui64_lo", eui.lo);
}
/*
* Send a link on to this board/bus "-o"
*/
if (send_link_on >= 0)
link_on(fd, send_link_on);
/*
* Send a reset start to this board/bus "-s"
*/
if (send_reset_start >= 0)
reset_start(fd, send_reset_start);
/*
* Dump the node topology for this board/bus "-t"
*/
if (dump_topology)
show_topology_map(fd);
/*
* Receive data file from node "-R"
*/
#define TAG (1<<6)
#define CHANNEL 63
if (recv_data != NULL){
if (recvfn == NULL) { /* guess... */
recvfn = detect_recv_fn(fd, TAG | CHANNEL);
close(fd);
fd = -1;
}
snprintf(devbase, sizeof(devbase), "%s%d.0",
device_string, current_board);
if (open_dev(&fd, devbase) < 0)
err(EX_IOERR, "%s: Error opening firewire "
"controller #%d %s in recv_data",
__func__, current_board, devbase);
(*recvfn)(fd, recv_data, TAG | CHANNEL, -1);
free(recv_data);
}
/*
* Send data file to node "-S"
*/
if (send_data != NULL){
dvsend(fd, send_data, TAG | CHANNEL, -1);
free(send_data);
}