summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Dan Dennedy 2008-04-30 14:37:28 -0700
committerGravatar Dan Dennedy 2008-04-30 14:37:28 -0700
commitf9681ff59da0acca543ad5d15213c6253114f0ce (patch)
treeea1da14c7a5fba1c6c6f1fceff1fb76c25ef8551 /src
parentlibraw1394.sgml, raw1394.h: remove information about deprecated isochronous (diff)
Move the source code files in the juju directory into the src directory and
give them 'fw' names instead of 'juju.'
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am8
-rw-r--r--src/dispatch.c2
-rw-r--r--src/fw-iso.c529
-rw-r--r--src/fw.c1369
-rw-r--r--src/fw.h257
-rw-r--r--src/raw1394_private.h2
6 files changed, 2162 insertions, 5 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 56a0950..a62df10 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,6 +6,8 @@ lib_LTLIBRARIES = libraw1394.la
libraw1394_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@
+INCLUDES = -I$(FW_DIR)
+
libraw1394_la_SOURCES = \
main.c \
eventloop.c \
@@ -19,9 +21,9 @@ libraw1394_la_SOURCES = \
raw1394_private.h \
ieee1394-ioctl.h \
dispatch.c \
- ../juju/raw1394.c \
- ../juju/raw1394-iso.c \
- ../juju.h
+ fw.c \
+ fw-iso.c \
+ fw.h
# headers to be installed
diff --git a/src/dispatch.c b/src/dispatch.c
index cb68ef8..4fe805e 100644
--- a/src/dispatch.c
+++ b/src/dispatch.c
@@ -14,7 +14,7 @@
#include "csr.h"
#include "kernel-raw1394.h"
#include "raw1394_private.h"
-#include "../juju/juju.h"
+#include "fw.h"
int raw1394_errcode_to_errno(raw1394_errcode_t errcode)
{
diff --git a/src/fw-iso.c b/src/fw-iso.c
new file mode 100644
index 0000000..471d981
--- /dev/null
+++ b/src/fw-iso.c
@@ -0,0 +1,529 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * raw1394-iso.c -- Emulation of the raw1394 rawiso API on the firewire stack
+ *
+ * Copyright (C) 2007 Kristian Hoegsberg <krh@bitplanet.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+
+#include "fw.h"
+#include "raw1394_private.h"
+
+static int
+queue_packet(fw_handle_t handle,
+ unsigned int length, unsigned int header_length,
+ unsigned char tag, unsigned char sy)
+{
+ struct fw_cdev_queue_iso queue_iso;
+ struct fw_cdev_iso_packet *p;
+ int err;
+
+ p = &handle->iso.packets[handle->iso.packet_index];
+ p->control =
+ FW_CDEV_ISO_PAYLOAD_LENGTH(length) |
+ FW_CDEV_ISO_TAG(tag) |
+ FW_CDEV_ISO_SY(sy) |
+ FW_CDEV_ISO_HEADER_LENGTH(header_length);
+
+ if (handle->iso.packet_phase == handle->iso.irq_interval - 1)
+ p->control |= FW_CDEV_ISO_INTERRUPT;
+
+ handle->iso.head += length;
+ handle->iso.packet_count++;
+ handle->iso.packet_phase++;
+ handle->iso.packet_index++;
+
+ if (handle->iso.packet_phase == handle->iso.irq_interval)
+ handle->iso.packet_phase = 0;
+
+ if (handle->iso.head + handle->iso.max_packet_size > handle->iso.buffer_end)
+ handle->iso.head = handle->iso.buffer;
+
+ /* Queue the packets in the kernel if we filled up the packets
+ * array or wrapped the payload buffer. */
+ if (handle->iso.packet_index == handle->iso.irq_interval ||
+ handle->iso.head == handle->iso.buffer) {
+ queue_iso.packets = ptr_to_u64(handle->iso.packets);
+ queue_iso.size = handle->iso.packet_index * sizeof handle->iso.packets[0];
+ queue_iso.data = ptr_to_u64(handle->iso.first_payload);
+ queue_iso.handle = 0;
+ handle->iso.packet_index = 0;
+ handle->iso.first_payload = handle->iso.head;
+
+ err = ioctl(handle->iso.fd, FW_CDEV_IOC_QUEUE_ISO, &queue_iso);
+ if (err < 0)
+ return -1;
+ }
+}
+
+static int
+queue_xmit_packets(raw1394handle_t handle, int limit)
+{
+ fw_handle_t fwhandle = handle->mode.fw;
+ enum raw1394_iso_disposition d;
+ unsigned char tag, sy;
+ int len, cycle, dropped;
+
+ if (fwhandle->iso.xmit_handler == NULL)
+ return 0;
+
+ while (fwhandle->iso.packet_count < limit) {
+
+ d = fwhandle->iso.xmit_handler(handle, fwhandle->iso.head,
+ &len, &tag, &sy, cycle, dropped);
+
+ switch (d) {
+ case RAW1394_ISO_OK:
+ queue_packet(fwhandle, len, 0, tag, sy);
+ break;
+ case RAW1394_ISO_DEFER:
+ case RAW1394_ISO_AGAIN:
+ default:
+ return 0;
+ case RAW1394_ISO_ERROR:
+ return -1;
+ case RAW1394_ISO_STOP:
+ fw_iso_stop(fwhandle);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+int fw_iso_xmit_start(raw1394handle_t handle, int start_on_cycle,
+ int prebuffer_packets)
+{
+ fw_handle_t fwhandle = handle->mode.fw;
+ struct fw_cdev_start_iso start_iso;
+ int retval;
+
+ if (prebuffer_packets == -1)
+ prebuffer_packets = fwhandle->iso.irq_interval;
+
+ fwhandle->iso.prebuffer = prebuffer_packets;
+ fwhandle->iso.start_on_cycle = start_on_cycle;
+
+ queue_xmit_packets(handle, prebuffer_packets);
+
+ if (fwhandle->iso.prebuffer <= fwhandle->iso.packet_count) {
+ start_iso.cycle = start_on_cycle;
+ start_iso.handle = 0;
+
+ retval = ioctl(fwhandle->iso.fd,
+ FW_CDEV_IOC_START_ISO, &start_iso);
+ if (retval < 0)
+ return retval;
+ }
+
+ return queue_xmit_packets(handle, fwhandle->iso.buf_packets);
+}
+
+static int
+queue_recv_packets(fw_handle_t handle)
+{
+ while (handle->iso.packet_count <= handle->iso.buf_packets)
+ queue_packet(handle, handle->iso.max_packet_size, 4, 0, 0);
+
+ return 0;
+}
+
+static enum raw1394_iso_disposition
+flush_recv_packets(raw1394handle_t handle,
+ struct fw_cdev_event_iso_interrupt *interrupt)
+{
+ fw_handle_t fwhandle = handle->mode.fw;
+ enum raw1394_iso_disposition d;
+ quadlet_t header, *p, *end;
+ unsigned int len, cycle, dropped;
+ unsigned char channel, tag, sy;
+
+ p = interrupt->header;
+ end = (void *) interrupt->header + interrupt->header_length;
+ cycle = interrupt->cycle;
+ dropped = 0;
+ d = RAW1394_ISO_OK;
+
+ while (p < end) {
+ header = be32_to_cpu(*p++);
+ len = header >> 16;
+ tag = (header >> 14) & 0x3;
+ channel = (header >> 8) & 0x3f;
+ sy = header & 0x0f;
+
+ d = fwhandle->iso.recv_handler(handle, fwhandle->iso.tail, len,
+ channel, tag, sy, cycle, dropped);
+ if (d != RAW1394_ISO_OK)
+ /* FIXME: we need to save the headers so we
+ * can restart this loop. */
+ break;
+ cycle++;
+
+ fwhandle->iso.tail += fwhandle->iso.max_packet_size;
+ fwhandle->iso.packet_count--;
+
+ if (fwhandle->iso.tail + fwhandle->iso.max_packet_size > fwhandle->iso.buffer_end)
+ fwhandle->iso.tail = fwhandle->iso.buffer;
+ }
+
+ switch (d) {
+ case RAW1394_ISO_OK:
+ case RAW1394_ISO_DEFER:
+ default:
+ break;
+
+ case RAW1394_ISO_ERROR:
+ return -1;
+
+ case RAW1394_ISO_STOP:
+ fw_iso_stop(fwhandle);
+ return 0;
+ }
+
+ queue_recv_packets(fwhandle);
+
+ return 0;
+}
+
+int fw_iso_recv_start(fw_handle_t handle, int start_on_cycle,
+ int tag_mask, int sync)
+{
+ struct fw_cdev_start_iso start_iso;
+
+ queue_recv_packets(handle);
+
+ start_iso.cycle = start_on_cycle;
+ start_iso.tags =
+ tag_mask == -1 ? FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS : tag_mask;
+ /* sync is documented as 'not used' */
+ start_iso.sync = 0;
+ start_iso.handle = 0;
+
+ return ioctl(handle->iso.fd, FW_CDEV_IOC_START_ISO, &start_iso);
+}
+
+static int handle_iso_event(raw1394handle_t handle,
+ struct epoll_closure *closure, __uint32_t events)
+{
+ fw_handle_t fwhandle = handle->mode.fw;
+ struct fw_cdev_event_iso_interrupt *interrupt;
+ int len;
+
+ len = read(fwhandle->iso.fd, fwhandle->buffer, sizeof fwhandle->buffer);
+ if (len < 0)
+ return -1;
+
+ interrupt = (struct fw_cdev_event_iso_interrupt *) fwhandle->buffer;
+ if (interrupt->type != FW_CDEV_EVENT_ISO_INTERRUPT)
+ return 0;
+
+ switch (fwhandle->iso.type) {
+ case FW_CDEV_ISO_CONTEXT_TRANSMIT:
+ fwhandle->iso.packet_count -= fwhandle->iso.irq_interval;
+ return queue_xmit_packets(handle, fwhandle->iso.buf_packets);
+ case FW_CDEV_ISO_CONTEXT_RECEIVE:
+ return flush_recv_packets(handle, interrupt);
+ default:
+ /* Doesn't happen. */
+ return -1;
+ }
+}
+
+int fw_iso_xmit_write(raw1394handle_t handle, unsigned char *data,
+ unsigned int len, unsigned char tag,
+ unsigned char sy)
+{
+ fw_handle_t fwhandle = handle->mode.fw;
+ struct fw_cdev_queue_iso queue_iso;
+ struct fw_cdev_start_iso start_iso;
+ struct fw_cdev_iso_packet *p;
+
+ if (len > fwhandle->iso.max_packet_size) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Block until we have space for another packet. */
+ while (fwhandle->iso.packet_count + fwhandle->iso.irq_interval >
+ fwhandle->iso.buf_packets)
+ fw_loop_iterate(handle);
+
+ memcpy(fwhandle->iso.head, data, len);
+ if (queue_packet(fwhandle, len, 0, tag, sy) < 0)
+ return -1;
+
+ /* Start the streaming if it's not already running and if
+ * we've buffered up enough packets. */
+ if (fwhandle->iso.prebuffer > 0 &&
+ fwhandle->iso.packet_count >= fwhandle->iso.prebuffer) {
+ /* Set this to 0 to indicate that we're running. */
+ fwhandle->iso.prebuffer = 0;
+ start_iso.cycle = fwhandle->iso.start_on_cycle;
+ start_iso.handle = 0;
+
+ len = ioctl(fwhandle->iso.fd,
+ FW_CDEV_IOC_START_ISO, &start_iso);
+ if (len < 0)
+ return len;
+ }
+
+ return 0;
+}
+
+int fw_iso_xmit_sync(raw1394handle_t handle)
+{
+ fw_handle_t fwhandle = handle->mode.fw;
+ struct fw_cdev_iso_packet skip;
+ struct fw_cdev_queue_iso queue_iso;
+ int len;
+
+ skip.control = FW_CDEV_ISO_INTERRUPT | FW_CDEV_ISO_SKIP;
+ queue_iso.packets = ptr_to_u64(&skip);
+ queue_iso.size = sizeof skip;
+ queue_iso.data = 0;
+ queue_iso.handle = 0;
+
+ len = ioctl(fwhandle->iso.fd, FW_CDEV_IOC_QUEUE_ISO, &queue_iso);
+ if (len < 0)
+ return -1;
+
+ /* Now that we've queued the skip packet, we'll get an
+ * interrupt when the transmit buffer is flushed, so all we do
+ * here is wait. */
+ while (fwhandle->iso.packet_count > 0)
+ fw_loop_iterate(handle);
+
+ /* The iso mainloop thinks that interrutps indicate another
+ * irq_interval number of packets was sent, so the skip
+ * interrupt makes it go out of whack. We just reset it. */
+ fwhandle->iso.head = fwhandle->iso.buffer;
+ fwhandle->iso.tail = fwhandle->iso.buffer;
+ fwhandle->iso.first_payload = fwhandle->iso.buffer;
+ fwhandle->iso.packet_phase = 0;
+ fwhandle->iso.packet_count = 0;
+
+ return 0;
+}
+
+int fw_iso_recv_flush(fw_handle_t handle)
+{
+ /* FIXME: huh, we'll need kernel support here... */
+
+ return 0;
+}
+
+static unsigned int
+round_to_power_of_two(unsigned int value)
+{
+ unsigned int pot;
+
+ pot = 1;
+ while (pot < value)
+ pot <<= 1;
+
+ return pot;
+}
+
+static int
+iso_init(fw_handle_t handle, int type,
+ raw1394_iso_xmit_handler_t xmit_handler,
+ raw1394_iso_recv_handler_t recv_handler,
+ unsigned int buf_packets,
+ unsigned int max_packet_size,
+ unsigned char channel,
+ enum raw1394_iso_speed speed,
+ int irq_interval)
+{
+ struct fw_cdev_create_iso_context create;
+ struct epoll_event ep;
+ int retval, prot;
+
+ if (handle->iso.fd != -1) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ switch (type) {
+ case FW_CDEV_ISO_CONTEXT_TRANSMIT:
+ prot = PROT_READ | PROT_WRITE;
+ break;
+ case FW_CDEV_ISO_CONTEXT_RECEIVE:
+ prot = PROT_READ;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ handle->iso.type = type;
+ if (irq_interval < 0)
+ handle->iso.irq_interval = 256;
+ else
+ handle->iso.irq_interval = irq_interval;
+ handle->iso.xmit_handler = xmit_handler;
+ handle->iso.recv_handler = recv_handler;
+ handle->iso.buf_packets = buf_packets;
+ handle->iso.max_packet_size = round_to_power_of_two(max_packet_size);
+ handle->iso.packet_phase = 0;
+ handle->iso.packet_count = 0;
+ handle->iso.packets =
+ malloc(handle->iso.irq_interval * sizeof handle->iso.packets[0]);
+ if (handle->iso.packets == NULL)
+ return -1;
+
+ handle->iso.fd = open(handle->local_filename, O_RDWR);
+ if (handle->iso.fd < 0) {
+ free(handle->iso.packets);
+ handle->iso.packets = NULL;
+ return -1;
+ }
+
+ handle->iso.closure.func = handle_iso_event;
+ ep.events = EPOLLIN;
+ ep.data.ptr = &handle->iso.closure;
+ if (epoll_ctl(handle->epoll_fd, EPOLL_CTL_ADD,
+ handle->iso.fd, &ep) < 0) {
+ close(handle->iso.fd);
+ free(handle->iso.packets);
+ handle->iso.packets = NULL;
+ return -1;
+ }
+
+ create.type = type;
+ create.channel = channel;
+ create.speed = speed;
+ create.header_size = 4;
+
+ retval = ioctl(handle->iso.fd,
+ FW_CDEV_IOC_CREATE_ISO_CONTEXT, &create);
+ if (retval < 0) {
+ close(handle->iso.fd);
+ free(handle->iso.packets);
+ handle->iso.packets = NULL;
+ return retval;
+ }
+
+ handle->iso.buffer =
+ mmap(NULL, buf_packets * handle->iso.max_packet_size,
+ prot, MAP_SHARED, handle->iso.fd, 0);
+
+ if (handle->iso.buffer == MAP_FAILED) {
+ close(handle->iso.fd);
+ free(handle->iso.packets);
+ handle->iso.packets = NULL;
+ return -1;
+ }
+
+ handle->iso.buffer_end = handle->iso.buffer +
+ buf_packets * handle->iso.max_packet_size;
+ handle->iso.head = handle->iso.buffer;
+ handle->iso.tail = handle->iso.buffer;
+ handle->iso.first_payload = handle->iso.buffer;
+
+ return 0;
+}
+
+int fw_iso_xmit_init(fw_handle_t handle,
+ raw1394_iso_xmit_handler_t handler,
+ unsigned int buf_packets,
+ unsigned int max_packet_size,
+ unsigned char channel,
+ enum raw1394_iso_speed speed,
+ int irq_interval)
+{
+ return iso_init(handle, FW_CDEV_ISO_CONTEXT_TRANSMIT,
+ handler, NULL, buf_packets, max_packet_size,
+ channel, speed, irq_interval);
+}
+
+int fw_iso_recv_init(fw_handle_t handle,
+ raw1394_iso_recv_handler_t handler,
+ unsigned int buf_packets,
+ unsigned int max_packet_size,
+ unsigned char channel,
+ enum raw1394_iso_dma_recv_mode mode,
+ int irq_interval)
+{
+ return iso_init(handle, FW_CDEV_ISO_CONTEXT_RECEIVE,
+ NULL, handler, buf_packets, max_packet_size,
+ channel, 0, irq_interval);
+}
+
+int fw_iso_multichannel_recv_init(fw_handle_t handle,
+ raw1394_iso_recv_handler_t handler,
+ unsigned int buf_packets,
+ unsigned int max_packet_size,
+ int irq_interval)
+{
+ /* FIXME: gah */
+ errno = ENOSYS;
+ return -1;
+}
+
+int fw_iso_recv_listen_channel(fw_handle_t handle,
+ unsigned char channel)
+{
+ /* FIXME: multichannel */
+ errno = ENOSYS;
+ return -1;
+}
+
+int fw_iso_recv_unlisten_channel(fw_handle_t handle,
+ unsigned char channel)
+{
+ /* FIXME: multichannel */
+ errno = ENOSYS;
+ return -1;
+}
+
+int fw_iso_recv_set_channel_mask(fw_handle_t handle, u_int64_t mask)
+{
+ /* FIXME: multichannel */
+ errno = ENOSYS;
+ return -1;
+}
+
+void fw_iso_stop(fw_handle_t handle)
+{
+ struct fw_cdev_stop_iso stop_iso;
+
+ stop_iso.handle = 0;
+ ioctl(handle->iso.fd, FW_CDEV_IOC_STOP_ISO);
+
+ handle->iso.head = handle->iso.buffer;
+ handle->iso.tail = handle->iso.buffer;
+ handle->iso.first_payload = handle->iso.buffer;
+ handle->iso.packet_phase = 0;
+ handle->iso.packet_count = 0;
+}
+
+void fw_iso_shutdown(fw_handle_t handle)
+{
+ munmap(handle->iso.buffer,
+ handle->iso.buf_packets * handle->iso.max_packet_size);
+ close(handle->iso.fd);
+ free(handle->iso.packets);
+ handle->iso.packets = NULL;
+}
diff --git a/src/fw.c b/src/fw.c
new file mode 100644
index 0000000..1322fe2
--- /dev/null
+++ b/src/fw.c
@@ -0,0 +1,1369 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * raw1394.c -- Emulation of the raw1394 API on the fw stack
+ *
+ * Copyright (C) 2007 Kristian Hoegsberg <krh@bitplanet.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <sys/epoll.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <arpa/inet.h> /* for ntohl and htonl */
+
+#include "fw.h"
+#include "raw1394_private.h"
+
+int
+fw_errcode_to_errno(raw1394_errcode_t errcode)
+{
+ switch (errcode) {
+
+ case -RCODE_SEND_ERROR:
+ case -RCODE_CANCELLED:
+ case -RCODE_BUSY:
+ case -RCODE_GENERATION:
+ case -RCODE_NO_ACK:
+ return EAGAIN;
+
+ case raw1394_make_errcode(ACK_COMPLETE, RCODE_COMPLETE):
+ return 0;
+ case raw1394_make_errcode(ACK_COMPLETE, RCODE_CONFLICT_ERROR):
+ return EAGAIN;
+ case raw1394_make_errcode(ACK_COMPLETE, RCODE_DATA_ERROR):
+ return EREMOTEIO;
+ case raw1394_make_errcode(ACK_COMPLETE, RCODE_TYPE_ERROR):
+ return EPERM;
+ case raw1394_make_errcode(ACK_COMPLETE, RCODE_ADDRESS_ERROR):
+ return EINVAL;
+ default:
+ return EINVAL;
+ }
+}
+
+static int
+fw_to_raw1394_errcode(int rcode)
+{
+ /* Best effort matching fw extended rcodes to raw1394 err
+ * code. Since the raw1394 errcode decoding are macros we try
+ * to convert the fw rcodes to something that looks enough
+ * like the raw1394 errcodes that we retain ABI compatibility.
+ *
+ * Juju rcodes less than 0x10 are standard ieee1394 rcodes,
+ * which we map to a raw1394 errcode by or'ing in an
+ * ACK_COMPLETE ack code in the upper 16 bits. Errors
+ * internal to raw1394 are negative values, but fw encodes
+ * these errors as rcodes greater than or equal to 0x10. In
+ * this case, we just the negated value, which will look like
+ * an raw1394 internal error code. */
+
+ if (rcode < 0x10)
+ return raw1394_make_errcode(ACK_COMPLETE, rcode);
+ else
+ return -rcode;
+}
+
+static int
+default_tag_handler(raw1394handle_t handle,
+ unsigned long tag, raw1394_errcode_t err)
+{
+ struct raw1394_reqhandle *rh = (struct raw1394_reqhandle *) tag;
+
+ if (rh != NULL)
+ return rh->callback(handle, rh->data, err);
+
+ return -1;
+}
+
+static int
+default_arm_tag_handler(raw1394handle_t handle, unsigned long arm_tag,
+ byte_t type, unsigned int length, void *data)
+{
+ struct raw1394_arm_reqhandle *rh;
+
+ if (arm_tag == 0)
+ return -1;
+
+ rh = (struct raw1394_arm_reqhandle *) arm_tag;
+
+ return rh->arm_callback(handle, data, length, rh->pcontext, type);
+}
+
+static int
+default_bus_reset_handler(raw1394handle_t handle, unsigned int gen)
+{
+ handle->mode.fw->generation = gen;
+
+ return 0;
+}
+
+static int
+scan_devices(fw_handle_t handle)
+{
+ DIR *dir;
+ struct dirent *de;
+ char filename[32];
+ struct fw_cdev_get_info get_info;
+ struct fw_cdev_event_bus_reset reset;
+ int fd, err, i;
+ struct port *ports;
+
+ ports = handle->ports;
+ memset(ports, 0, sizeof handle->ports);
+ dir = opendir(FW_DEVICE_DIR);
+ if (dir == NULL)
+ return -1;
+
+ i = 0;
+ while (1) {
+ de = readdir(dir);
+ if (de == NULL)
+ break;
+
+ if (strncmp(de->d_name,
+ FW_DEVICE_PREFIX, strlen(FW_DEVICE_PREFIX)) != 0)
+ continue;
+
+ snprintf(filename, sizeof filename, FW_DEVICE_DIR "/%s", de->d_name);
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0)
+ continue;
+ get_info.version = FW_CDEV_VERSION;
+ get_info.rom = 0;
+ get_info.rom_length = 0;
+ get_info.bus_reset = ptr_to_u64(&reset);
+ err = ioctl(fd, FW_CDEV_IOC_GET_INFO, &get_info);
+ close(fd);
+
+ if (err < 0)
+ continue;
+
+ if (i < MAX_PORTS && reset.node_id == reset.local_node_id) {
+ strncpy(ports[i].device_file, filename,
+ sizeof ports[i].device_file);
+ ports[i].node_count = (reset.root_node_id & 0x3f) + 1;
+ ports[i].card = get_info.card;
+ i++;
+ }
+ }
+ closedir(dir);
+
+ handle->port_count = i;
+
+ return 0;
+}
+
+static int
+handle_echo_pipe(raw1394handle_t handle,
+ struct epoll_closure *ec, __uint32_t events)
+{
+ quadlet_t value;
+
+ if (read(handle->mode.fw->pipe_fds[0], &value, sizeof value) < 0)
+ return -1;
+
+ return value;
+}
+
+static int
+handle_lost_device(fw_handle_t handle, int i)
+{
+ int phy_id;
+
+ /* The device got unplugged, get rid of it. The fd is
+ * automatically dropped from the epoll context when we close it. */
+
+ close(handle->devices[i].fd);
+ phy_id = handle->devices[i].node_id & 0x3f;
+ if (handle->nodes[phy_id] == i)
+ handle->nodes[phy_id] = -1;
+ handle->devices[i].node_id = -1;
+
+ return 0;
+}
+
+struct address_closure {
+ int (*callback)(raw1394handle_t handle, struct address_closure *ac,
+ struct fw_cdev_event_request *request, int i);
+};
+
+static int
+handle_fcp_request(raw1394handle_t handle, struct address_closure *ac,
+ struct fw_cdev_event_request *request, int i)
+{
+ struct fw_cdev_send_response response;
+ int is_response;
+
+ response.handle = request->handle;
+ response.rcode = RCODE_COMPLETE;
+ response.length = 0;
+ response.data = 0;
+
+ if (handle->mode.fw->fcp_handler == NULL)
+ response.rcode = RCODE_ADDRESS_ERROR;
+
+ if (request->tcode >= TCODE_WRITE_RESPONSE)
+ response.rcode = RCODE_CONFLICT_ERROR;
+
+ if (ioctl(handle->mode.fw->devices[i].fd,
+ FW_CDEV_IOC_SEND_RESPONSE, &response) < 0)
+ return -1;
+
+ if (response.rcode != RCODE_COMPLETE)
+ return 0;
+
+ is_response = request->offset >= CSR_REGISTER_BASE + CSR_FCP_RESPONSE;
+
+ return handle->mode.fw->fcp_handler(handle,
+ handle->mode.fw->devices[i].node_id,
+ is_response,
+ request->length,
+ (unsigned char *) request->data);
+}
+
+static int
+handle_device_event(raw1394handle_t handle,
+ struct epoll_closure *ec, __uint32_t events)
+{
+ fw_handle_t fwhandle = handle->mode.fw;
+ union fw_cdev_event *u;
+ struct device *device = (struct device *) ec;
+ struct address_closure *ac;
+ struct request_closure *rc;
+ raw1394_errcode_t errcode;
+ int len, phy_id;
+ int i;
+
+ i = device - fwhandle->devices;
+ if (events == EPOLLHUP)
+ return handle_lost_device(fwhandle, i);
+
+ len = read(fwhandle->devices[i].fd,
+ fwhandle->buffer, sizeof fwhandle->buffer);
+ if (len < 0)
+ return -1;
+
+ u = (void *) fwhandle->buffer;
+ switch (u->common.type) {
+ case FW_CDEV_EVENT_BUS_RESET:
+ /* Clear old entry, unless it's been overwritten. */
+ phy_id = fwhandle->devices[i].node_id & 0x3f;
+ if (fwhandle->nodes[phy_id] == i)
+ fwhandle->nodes[phy_id] = -1;
+ fwhandle->nodes[u->bus_reset.node_id & 0x3f] = i;
+ fwhandle->devices[i].node_id = u->bus_reset.node_id;
+ fwhandle->devices[i].generation = u->bus_reset.generation;
+
+ if (u->bus_reset.node_id != u->bus_reset.local_node_id)
+ return 0;
+
+ memcpy(&fwhandle->reset, &u->bus_reset, sizeof fwhandle->reset);
+ return fwhandle->bus_reset_handler(handle,
+ u->bus_reset.generation);
+
+ case FW_CDEV_EVENT_RESPONSE:
+ rc = u64_to_ptr(u->response.closure);
+
+ if (rc->data != NULL)
+ memcpy(rc->data, u->response.data, rc->length);
+
+ errcode = fw_to_raw1394_errcode(u->response.rcode);
+
+ return fwhandle->tag_handler(handle, rc->tag, errcode);
+
+ case FW_CDEV_EVENT_REQUEST:
+ ac = u64_to_ptr(u->request.closure);
+ return ac->callback(handle, ac, &u->request, i);
+
+ default:
+ case FW_CDEV_EVENT_ISO_INTERRUPT:
+ /* Never happens. */
+ return -1;
+ }
+}
+
+static int
+handle_inotify(raw1394handle_t handle, struct epoll_closure *ec,
+ __uint32_t events)
+{
+ fw_handle_t fwhandle = handle->mode.fw;
+ struct inotify_event *event;
+ char filename[32];
+ struct fw_cdev_get_info info;
+ struct fw_cdev_event_bus_reset reset;
+ struct epoll_event ep;
+ int i, len, fd, phy_id;
+
+ event = (struct inotify_event *) fwhandle->buffer;
+ len = read(fwhandle->inotify_fd, event, BUFFER_SIZE);
+ if (!(event->mask & IN_CREATE))
+ return -1;
+ if (strncmp(event->name,
+ FW_DEVICE_PREFIX, strlen(FW_DEVICE_PREFIX)) != 0)
+ return 0;
+ snprintf(filename, sizeof filename, FW_DEVICE_DIR "/%s", event->name);
+ fd = open(filename, O_RDWR);
+ if (fd < 0) {
+ switch (errno) {
+ case ENOENT:
+ /* Huh, it disappeared before we could
+ * open it. */
+ return 0;
+ case EACCES:
+ /* We don't have permission to talk to
+ * this device, maybe it's a storage
+ * device. */
+ return 0;
+ default:
+ /* Anything else is bad news. */
+ return -1;
+ }
+ }
+
+ info.version = FW_CDEV_VERSION;
+ info.rom = 0;
+ info.rom_length = 0;
+ info.bus_reset = ptr_to_u64(&reset);
+ if (ioctl(fd, FW_CDEV_IOC_GET_INFO, &info) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ for (i = 0; i < MAX_DEVICES; i++)
+ if (fwhandle->devices[i].node_id == -1)
+ break;
+ if (i == MAX_DEVICES) {
+ close(fd);
+ return -1;
+ }
+
+ phy_id = reset.node_id & 0x3f;
+ fwhandle->nodes[phy_id] = i;
+ fwhandle->devices[i].node_id = reset.node_id;
+ fwhandle->devices[i].generation = reset.generation;
+ fwhandle->devices[i].fd = fd;
+ strncpy(fwhandle->devices[i].filename, filename,
+ sizeof fwhandle->devices[i].filename);
+ fwhandle->devices[i].closure.func = handle_device_event;
+ ep.events = EPOLLIN;
+ ep.data.ptr = &fwhandle->devices[i].closure;
+ if (epoll_ctl(fwhandle->epoll_fd, EPOLL_CTL_ADD, fd, &ep) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return 0;
+}
+
+int fw_loop_iterate(raw1394handle_t handle)
+{
+ int i, count, retval = 0;
+ struct epoll_closure *closure;
+ struct epoll_event ep[32];
+
+ count = epoll_wait(handle->mode.fw->epoll_fd, ep, ARRAY_LENGTH(ep), -1);
+ if (count < 0)
+ return -1;
+
+ for (i = 0; i < count; i++) {
+ closure = ep[i].data.ptr;
+ retval = closure->func(handle, closure, ep[i].events);
+ }
+
+ /* It looks like we have to add this work-around to get epoll
+ * to recompute the POLLIN status of the epoll_fd. */
+ epoll_wait(handle->mode.fw->epoll_fd, ep, ARRAY_LENGTH(ep), 0);
+
+ return retval;
+}
+
+fw_handle_t fw_new_handle(void)
+{
+ fw_handle_t handle;
+ struct epoll_event ep;
+ int i;
+
+ handle = malloc(sizeof *handle);
+
+ handle->tag_handler = default_tag_handler;
+ handle->arm_tag_handler = default_arm_tag_handler;
+ handle->allocations = NULL;
+
+ handle->notify_bus_reset = RAW1394_NOTIFY_ON;
+ handle->bus_reset_handler = default_bus_reset_handler;
+
+ handle->iso.fd = -1;
+
+ handle->epoll_fd = epoll_create(16);
+ if (handle->epoll_fd < 0)
+ goto out_handle;
+
+ if (pipe(handle->pipe_fds) < 0)
+ goto out_epoll;
+
+ handle->inotify_fd = inotify_init();
+ if (handle->inotify_fd < 0)
+ goto out_pipe;
+
+ handle->inotify_watch =
+ inotify_add_watch(handle->inotify_fd, FW_DEVICE_DIR, IN_CREATE);
+ if (handle->inotify_watch < 0)
+ goto out_inotify;
+
+ handle->pipe_closure.func = handle_echo_pipe;
+ ep.events = EPOLLIN;
+ ep.data.ptr = &handle->pipe_closure;
+ if (epoll_ctl(handle->epoll_fd, EPOLL_CTL_ADD,
+ handle->pipe_fds[0], &ep) < 0)
+ goto out_inotify;
+
+ handle->inotify_closure.func = handle_inotify;
+ ep.events = EPOLLIN;
+ ep.data.ptr = &handle->inotify_closure;
+ if (epoll_ctl(handle->epoll_fd, EPOLL_CTL_ADD,
+ handle->inotify_fd, &ep) < 0)
+ goto out_inotify;
+
+ for (i = 0; i < MAX_DEVICES; i++) {
+ handle->nodes[i] = -1;
+ handle->devices[i].node_id = -1;
+ }
+
+ scan_devices(handle);
+
+ return handle;
+
+ out_inotify:
+ close(handle->inotify_fd);
+ out_pipe:
+ close(handle->pipe_fds[0]);
+ close(handle->pipe_fds[1]);
+ out_epoll:
+ close(handle->epoll_fd);
+ out_handle:
+ free(handle);
+ return NULL;
+}
+
+void fw_destroy_handle(fw_handle_t handle)
+{
+ int i;
+
+ close(handle->inotify_fd);
+ close(handle->pipe_fds[0]);
+ close(handle->pipe_fds[1]);
+
+ for (i = 0; i < MAX_DEVICES; i++) {
+ if (handle->devices[i].node_id == -1)
+ continue;
+
+ close(handle->devices[i].fd);
+ }
+
+ close(handle->epoll_fd);
+
+ free(handle);
+
+ return;
+}
+
+fw_handle_t fw_new_handle_on_port(int port)
+{
+ fw_handle_t handle;
+
+ handle = fw_new_handle();
+ if (handle == NULL)
+ return NULL;
+
+ if (fw_set_port(handle, port) < 0)
+ return NULL;
+
+ return handle;
+}
+
+int fw_busreset_notify (fw_handle_t handle, int off_on_switch)
+{
+ handle->notify_bus_reset = off_on_switch;
+
+ return 0;
+}
+
+int fw_get_fd(fw_handle_t handle)
+{
+ return handle->epoll_fd;
+}
+
+nodeid_t fw_get_local_id(fw_handle_t handle)
+{
+ return handle->reset.local_node_id;
+}
+
+nodeid_t fw_get_irm_id(fw_handle_t handle)
+{
+ return handle->reset.irm_node_id;
+}
+
+int fw_get_nodecount(fw_handle_t handle)
+{
+ return (handle->reset.root_node_id & 0x3f) + 1;
+}
+
+int fw_get_port_info(fw_handle_t handle,
+ struct raw1394_portinfo *pinf,
+ int maxports)
+{
+ int i;
+
+ if (maxports >= handle->port_count)
+ maxports = handle->port_count;
+
+ for (i = 0; i < maxports; i++) {
+ pinf[i].nodes = handle->ports[i].node_count;
+ strncpy(pinf[i].name, handle->ports[i].device_file,
+ sizeof pinf[i].name);
+ }
+
+ return handle->port_count;
+}
+
+int fw_set_port(fw_handle_t handle, int port)
+{
+ struct fw_cdev_get_info get_info;
+ struct fw_cdev_event_bus_reset reset;
+ struct epoll_event ep;
+ struct dirent *de;
+ char filename[32];
+ DIR *dir;
+ int i, fd, phy_id;
+
+ if (port >= handle->port_count) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ dir = opendir("/dev");
+ if (dir == NULL)
+ return -1;
+
+ for (i = 0; i < MAX_DEVICES; ) {
+ de = readdir(dir);
+ if (de == NULL)
+ break;
+
+ if (strncmp(de->d_name, "fw", 2) != 0)
+ continue;
+
+ snprintf(filename, sizeof filename, "/dev/%s", de->d_name);
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0)
+ continue;
+
+ get_info.version = FW_CDEV_VERSION;
+ get_info.rom = 0;
+ get_info.rom_length = 0;
+ get_info.bus_reset = ptr_to_u64(&reset);
+ if (ioctl(fd, FW_CDEV_IOC_GET_INFO, &get_info) < 0) {
+ close(fd);
+ continue;
+ }
+
+ if (get_info.card != handle->ports[port].card) {
+ close(fd);
+ continue;
+ }
+
+ phy_id = reset.node_id & 0x3f;
+ handle->nodes[phy_id] = i;
+ handle->devices[i].node_id = reset.node_id;
+ handle->devices[i].generation = reset.generation;
+ handle->devices[i].fd = fd;
+ strncpy(handle->devices[i].filename, filename,
+ sizeof handle->devices[i].filename);
+
+ handle->devices[i].closure.func = handle_device_event;
+ ep.events = EPOLLIN;
+ ep.data.ptr = &handle->devices[i].closure;
+ if (epoll_ctl(handle->epoll_fd, EPOLL_CTL_ADD, fd, &ep) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ handle->generation = reset.generation;
+ if (reset.node_id == reset.local_node_id) {
+ memcpy(&handle->reset, &reset, sizeof handle->reset);
+ handle->local_fd = fd;
+ strncpy(handle->local_filename, filename,
+ sizeof handle->local_filename);
+ }
+
+ i++;
+ }
+
+ return 0;
+}
+
+int fw_reset_bus_new(fw_handle_t handle, int type)
+{
+ struct fw_cdev_initiate_bus_reset initiate;
+
+ switch (type) {
+ case RAW1394_LONG_RESET:
+ initiate.type = FW_CDEV_LONG_RESET;
+ break;
+ case RAW1394_SHORT_RESET:
+ initiate.type = FW_CDEV_SHORT_RESET;
+ break;
+ }
+
+ return ioctl(handle->local_fd,
+ FW_CDEV_IOC_INITIATE_BUS_RESET, &initiate);
+}
+
+struct request_response_block {
+ struct raw1394_arm_request_response request_response;
+ struct raw1394_arm_request request;
+ struct raw1394_arm_response response;
+ unsigned char data[0];
+};
+
+struct allocation {
+ struct address_closure closure;
+ struct allocation *next;
+ __u32 handle;
+ byte_t *buffer;
+ octlet_t tag;
+ arm_options_t access_rights;
+ arm_options_t notification_options;
+ arm_options_t client_transactions;
+ nodeaddr_t offset;
+ size_t length;
+ unsigned char data[0];
+};
+
+static int
+handle_arm_request(raw1394handle_t handle, struct address_closure *ac,
+ struct fw_cdev_event_request *request, int i)
+{
+ fw_handle_t fwhandle = handle->mode.fw;
+ struct allocation *allocation = (struct allocation *) ac;
+ struct request_response_block *rrb;
+ struct fw_cdev_send_response response;
+ arm_options_t type;
+ size_t in_length;
+ int offset;
+
+ offset = request->offset - allocation->offset;
+ response.handle = request->handle;
+
+ switch (request->tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ case TCODE_WRITE_BLOCK_REQUEST:
+ printf("got write request, offset=0x%012llx, length=%d\n",
+ request->offset, request->length);
+
+ type = RAW1394_ARM_WRITE;
+ in_length = request->length;
+ response.rcode = RCODE_COMPLETE;
+ response.length = 0;
+ response.data = 0;
+ break;
+
+ case TCODE_READ_QUADLET_REQUEST:
+ case TCODE_READ_BLOCK_REQUEST:
+ printf("got read request, offset=0x%012llx, length=%d\n",
+ request->offset, request->length);
+
+ type = RAW1394_ARM_READ;
+ in_length = 0;
+ response.rcode = RCODE_COMPLETE;
+ response.length = request->length;
+ response.data = ptr_to_u64(allocation->data + offset);
+ break;
+
+ case TCODE_LOCK_REQUEST:
+ type = RAW1394_ARM_LOCK;
+ in_length = request->length;
+ response.length = 4;
+ break;
+
+ default:
+ in_length = 0;
+ type = 0;
+ break;
+ }
+
+ if (!(allocation->access_rights & type)) {
+ response.rcode = RCODE_TYPE_ERROR;
+ response.length = 0;
+ response.data = 0;
+ if (ioctl(fwhandle->devices[i].fd,
+ FW_CDEV_IOC_SEND_RESPONSE, &response) < 0)
+ return -1;
+ } else if (!(allocation->client_transactions & type)) {
+ if (type == RAW1394_ARM_WRITE)
+ memcpy(allocation->data + offset,
+ request->data, request->length);
+ else if (type == RAW1394_ARM_LOCK)
+ /* FIXME: do lock ops here */;
+
+ if (ioctl(fwhandle->devices[i].fd,
+ FW_CDEV_IOC_SEND_RESPONSE, &response) < 0)
+ return -1;
+ }
+
+ if (!(allocation->notification_options & type))
+ return 0;
+
+ rrb = malloc(sizeof *rrb + in_length + response.length);
+
+ rrb->request_response.request = &rrb->request;
+ rrb->request_response.response = &rrb->response;
+
+ rrb->request.destination_nodeid = fwhandle->reset.local_node_id;
+ rrb->request.source_nodeid = fwhandle->devices[i].node_id;
+ rrb->request.destination_offset = request->offset;
+ rrb->request.tlabel = 0;
+ if (request->tcode < 0x10) {
+ rrb->request.tcode = request->tcode;
+ rrb->request.extended_transaction_code = 0;
+ } else {
+ rrb->request.tcode = TCODE_LOCK_REQUEST;
+ rrb->request.extended_transaction_code = request->tcode - 0x10;
+ }
+ rrb->request.generation = fwhandle->reset.generation;
+ rrb->request.buffer_length = in_length;
+ memcpy(rrb->request.buffer, request->data, in_length);
+
+ rrb->response.response_code = response.rcode;
+ rrb->response.buffer_length = response.length;
+ memcpy(rrb->response.buffer,
+ allocation->data + offset, response.length);
+
+ return fwhandle->arm_tag_handler(handle, allocation->tag, type,
+ request->length,
+ &rrb->request_response);
+}
+
+int
+fw_arm_register(fw_handle_t handle, nodeaddr_t start,
+ size_t length, byte_t *initial_value,
+ octlet_t arm_tag, arm_options_t access_rights,
+ arm_options_t notification_options,
+ arm_options_t client_transactions)
+{
+ struct fw_cdev_allocate request;
+ struct allocation *allocation;
+ int retval;
+
+ allocation = malloc(sizeof *allocation + length);
+ if (allocation == NULL)
+ return -1;
+
+ allocation->closure.callback = handle_arm_request;
+ allocation->buffer = initial_value;
+ allocation->tag = arm_tag;
+ allocation->access_rights = access_rights;
+ allocation->notification_options = notification_options;
+ allocation->client_transactions = client_transactions;
+ allocation->offset = start;
+ allocation->length = length;
+ if (initial_value != NULL)
+ memcpy(allocation->data, initial_value, length);
+
+ request.offset = start;
+ request.length = length;
+ request.closure = ptr_to_u64(&allocation->closure);
+
+ retval = ioctl(handle->local_fd, FW_CDEV_IOC_ALLOCATE, &request);
+ if (retval < 0) {
+ free(allocation);
+ return -1;
+ }
+
+ allocation->handle = request.handle;
+ allocation->next = handle->allocations;
+ handle->allocations = allocation;
+
+ return 0;
+}
+
+static struct allocation *
+lookup_allocation(fw_handle_t handle, nodeaddr_t start, int delete)
+{
+ struct allocation *a, **prev;
+
+ prev = &handle->allocations;
+ for (a = handle->allocations; a != NULL; a = a->next) {
+ if (a->offset <= start && start < a->offset + a->length)
+ break;
+ prev = &a->next;
+ }
+
+ if (a != NULL && delete)
+ *prev = a->next;
+
+ return a;
+}
+
+int
+fw_arm_unregister(fw_handle_t handle, nodeaddr_t start)
+{
+ struct fw_cdev_deallocate request;
+ struct allocation *allocation;
+
+ allocation = lookup_allocation(handle, start, 1);
+ if (allocation == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ request.handle = allocation->handle;
+ free(allocation);
+
+ return ioctl(handle->local_fd, FW_CDEV_IOC_DEALLOCATE, &request);
+}
+
+int
+fw_arm_set_buf(fw_handle_t handle, nodeaddr_t start,
+ size_t length, void *buf)
+{
+ struct allocation *allocation;
+
+ allocation = lookup_allocation(handle, start, 0);
+ if (allocation == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ memcpy(allocation->data + allocation->offset - start, buf, length);
+
+ return 0;
+}
+
+int
+fw_arm_get_buf(fw_handle_t handle, nodeaddr_t start,
+ size_t length, void *buf)
+{
+ struct allocation *allocation;
+
+ allocation = lookup_allocation(handle, start, 0);
+ if (allocation == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ memcpy(buf, allocation->data + allocation->offset - start, length);
+
+ return 0;
+}
+
+int
+fw_echo_request(fw_handle_t handle, quadlet_t data)
+{
+ return write(handle->pipe_fds[1], &data, sizeof data);
+}
+
+int fw_wake_up(fw_handle_t handle)
+{
+ return fw_echo_request(handle, 0);
+}
+
+int fw_phy_packet_write (fw_handle_t handle, quadlet_t data)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int
+fw_start_phy_packet_write(fw_handle_t handle,
+ quadlet_t data, unsigned long tag)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int
+send_request(fw_handle_t handle, int tcode,
+ nodeid_t node, nodeaddr_t addr,
+ size_t length, void *in, void *out, unsigned long tag)
+{
+ struct fw_cdev_send_request *request;
+ struct request_closure *closure;
+ int i;
+
+ if (node > handle->reset.root_node_id) {
+ handle->err = -RCODE_NO_ACK;
+ errno = fw_errcode_to_errno(handle->err);
+ return -1;
+ }
+
+ i = handle->nodes[node & 0x3f];
+ if (i == -1) {
+ handle->err = -RCODE_NO_ACK;
+ errno = fw_errcode_to_errno(handle->err);
+ return -1;
+ }
+
+ if (handle->generation != handle->devices[i].generation) {
+ handle->err = -RCODE_GENERATION;
+ errno = fw_errcode_to_errno(handle->err);
+ return -1;
+ }
+
+ closure = malloc(sizeof *closure);
+ if (closure == NULL) {
+ handle->err = -RCODE_SEND_ERROR;
+ errno = fw_errcode_to_errno(handle->err);
+ return -1;
+ }
+
+ closure->data = out;
+ closure->length = length;
+ closure->tag = tag;
+
+ request = (struct fw_cdev_send_request *) handle->buffer;
+ request->tcode = tcode;
+ request->generation = handle->generation;
+ request->offset = addr;
+ request->length = length;
+ request->closure = ptr_to_u64(closure);
+ request->data = ptr_to_u64(in);
+
+ return ioctl(handle->devices[i].fd, FW_CDEV_IOC_SEND_REQUEST, request);
+}
+
+int
+fw_start_read(fw_handle_t handle, nodeid_t node, nodeaddr_t addr,
+ size_t length, quadlet_t *buffer, unsigned long tag)
+{
+ int tcode;
+
+ if (length == 4)
+ tcode = TCODE_READ_QUADLET_REQUEST;
+ else
+ tcode = TCODE_READ_BLOCK_REQUEST;
+
+ return send_request(handle, tcode,
+ node, addr, length, NULL, buffer, tag);
+}
+
+int
+fw_start_write(fw_handle_t handle, nodeid_t node, nodeaddr_t addr,
+ size_t length, quadlet_t *data, unsigned long tag)
+{
+ int tcode;
+
+ if (length == 4)
+ tcode = TCODE_WRITE_QUADLET_REQUEST;
+ else
+ tcode = TCODE_WRITE_BLOCK_REQUEST;
+
+ return send_request(handle, tcode,
+ node, addr, length, data, NULL, tag);
+}
+
+static int
+setup_lock(int extcode, quadlet_t data, quadlet_t arg, quadlet_t *buffer)
+{
+ switch (extcode) {
+ case RAW1394_EXTCODE_FETCH_ADD:
+ case RAW1394_EXTCODE_LITTLE_ADD:
+ buffer[0] = data;
+ return sizeof buffer[0];
+
+ case RAW1394_EXTCODE_MASK_SWAP:
+ case RAW1394_EXTCODE_COMPARE_SWAP:
+ case RAW1394_EXTCODE_BOUNDED_ADD:
+ case RAW1394_EXTCODE_WRAP_ADD:
+ buffer[0] = arg;
+ buffer[1] = data;
+ return 2 * sizeof buffer[0];
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+static int
+setup_lock64(int extcode, octlet_t data, octlet_t arg, octlet_t *buffer)
+{
+ switch (extcode) {
+ case RAW1394_EXTCODE_FETCH_ADD:
+ case RAW1394_EXTCODE_LITTLE_ADD:
+ buffer[0] = data;
+ return sizeof buffer[0];
+
+ case RAW1394_EXTCODE_MASK_SWAP:
+ case RAW1394_EXTCODE_COMPARE_SWAP:
+ case RAW1394_EXTCODE_BOUNDED_ADD:
+ case RAW1394_EXTCODE_WRAP_ADD:
+ buffer[0] = arg;
+ buffer[1] = data;
+ return 2 * sizeof buffer[0];
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+int
+fw_start_lock(fw_handle_t handle, nodeid_t node, nodeaddr_t addr,
+ unsigned int extcode, quadlet_t data, quadlet_t arg,
+ quadlet_t *result, unsigned long tag)
+{
+ quadlet_t buffer[2];
+ int length;
+
+ length = setup_lock(extcode, data, arg, buffer);
+ if (length < 0)
+ return length;
+
+ return send_request(handle, 16 + extcode,
+ node, addr, length, buffer, result, tag);
+}
+
+int
+fw_start_lock64(fw_handle_t handle, nodeid_t node, nodeaddr_t addr,
+ unsigned int extcode, octlet_t data, octlet_t arg,
+ octlet_t *result, unsigned long tag)
+{
+ octlet_t buffer[2];
+ int length;
+
+ length = setup_lock64(extcode, data, arg, buffer);
+ if (length < 0)
+ return length;
+
+ return send_request(handle, 16 + extcode,
+ node, addr, length, buffer, result, tag);
+}
+
+int
+fw_start_async_stream(fw_handle_t handle, unsigned int channel,
+ unsigned int tag, unsigned int sy,
+ unsigned int speed, size_t length, quadlet_t *data,
+ unsigned long rawtag)
+{
+ /* FIXME: implement this? */
+ return -1;
+}
+
+
+int
+fw_start_async_send(fw_handle_t handle,
+ size_t length, size_t header_length,
+ unsigned int expect_response,
+ quadlet_t *data, unsigned long rawtag)
+{
+ /* FIXME: implement this? */
+ return -1;
+}
+
+struct sync_data {
+ raw1394_errcode_t err;
+ int done;
+};
+
+static int
+sync_callback(raw1394handle_t handle, void *data, raw1394_errcode_t err)
+{
+ struct sync_data *sd = data;
+
+ sd->err = err;
+ sd->done = 1;
+
+ return 0;
+}
+
+static int
+send_request_sync(raw1394handle_t handle, int tcode,
+ nodeid_t node, nodeaddr_t addr,
+ size_t length, void *in, void *out)
+{
+ fw_handle_t fwhandle = handle->mode.fw;
+ struct raw1394_reqhandle reqhandle;
+ struct sync_data sd = { 0, 0 };
+ int err;
+
+ reqhandle.callback = sync_callback;
+ reqhandle.data = &sd;
+
+ err = send_request(fwhandle, tcode, node, addr,
+ length, in, out, (unsigned long) &reqhandle);
+
+ while (!sd.done) {
+ if (err < 0)
+ return err;
+ err = fw_loop_iterate(handle);
+ }
+
+ fwhandle->err = sd.err;
+ errno = fw_errcode_to_errno(sd.err);
+
+ return (errno ? -1 : 0);
+}
+
+int
+fw_read(raw1394handle_t handle, nodeid_t node, nodeaddr_t addr,
+ size_t length, quadlet_t *buffer)
+{
+ int tcode;
+
+ if (length == 4)
+ tcode = TCODE_READ_QUADLET_REQUEST;
+ else
+ tcode = TCODE_READ_BLOCK_REQUEST;
+
+ return send_request_sync(handle, tcode,
+ node, addr, length, NULL, buffer);
+}
+
+int
+fw_write(raw1394handle_t handle, nodeid_t node, nodeaddr_t addr,
+ size_t length, quadlet_t *data)
+{
+ int tcode;
+
+ if (length == 4)
+ tcode = TCODE_WRITE_QUADLET_REQUEST;
+ else
+ tcode = TCODE_WRITE_BLOCK_REQUEST;
+
+ return send_request_sync(handle, tcode,
+ node, addr, length, data, NULL);
+}
+
+int
+fw_lock(raw1394handle_t handle, nodeid_t node, nodeaddr_t addr,
+ unsigned int extcode, quadlet_t data, quadlet_t arg,
+ quadlet_t *result)
+{
+ quadlet_t buffer[2];
+ size_t length;
+
+ length = setup_lock(extcode, data, arg, buffer);
+ if (length < 0)
+ return length;
+
+ return send_request_sync(handle, 16 + extcode, node, addr,
+ length, buffer, result);
+}
+
+int
+fw_lock64(raw1394handle_t handle, nodeid_t node, nodeaddr_t addr,
+ unsigned int extcode, octlet_t data, octlet_t arg,
+ octlet_t *result)
+{
+ octlet_t buffer[2];
+ size_t length;
+
+ length = setup_lock64(extcode, data, arg, buffer);
+ if (length < 0)
+ return length;
+
+ return send_request_sync(handle, 16 + extcode, node, addr,
+ length, buffer, result);
+}
+
+int
+fw_async_stream(fw_handle_t handle, unsigned int channel,
+ unsigned int tag, unsigned int sy, unsigned int speed,
+ size_t length, quadlet_t *data)
+{
+ /* FIXME: implement this? */
+ return -1;
+}
+
+int
+fw_async_send(fw_handle_t handle,
+ size_t length, size_t header_length,
+ unsigned int expect_response,
+ quadlet_t *data)
+{
+ /* FIXME: implement this? */
+ return -1;
+}
+
+int
+fw_start_fcp_listen(fw_handle_t handle)
+{
+ struct fw_cdev_allocate request;
+ struct address_closure *closure;
+
+ closure = malloc(sizeof *closure);
+ if (closure == NULL)
+ return -1;
+
+ closure->callback = handle_fcp_request;
+
+ request.offset = CSR_REGISTER_BASE + CSR_FCP_COMMAND;
+ request.length = CSR_FCP_END - CSR_FCP_COMMAND;
+ request.closure = ptr_to_u64(closure);
+ if (ioctl(handle->local_fd, FW_CDEV_IOC_ALLOCATE, &request) < 0)
+ return -1;
+
+ handle->fcp_allocation_handle = request.handle;
+
+ return 0;
+}
+
+int
+fw_stop_fcp_listen(fw_handle_t handle)
+{
+ struct fw_cdev_deallocate request;
+
+ request.handle = handle->fcp_allocation_handle;
+
+ return ioctl(handle->local_fd, FW_CDEV_IOC_DEALLOCATE, &request);
+}
+
+int
+fw_update_config_rom(fw_handle_t handle, const quadlet_t *new_rom,
+ size_t size, unsigned char rom_version)
+{
+ return -1;
+}
+
+int
+fw_get_config_rom(fw_handle_t handle, quadlet_t *buffer,
+ size_t buffersize, size_t *rom_size,
+ unsigned char *rom_version)
+{
+ struct fw_cdev_get_info get_info;
+ int err;
+
+ get_info.version = FW_CDEV_VERSION;
+ get_info.rom = ptr_to_u64(buffer);
+ get_info.rom_length = buffersize;
+ get_info.bus_reset = 0;
+
+ err = ioctl(handle->local_fd, FW_CDEV_IOC_GET_INFO, &get_info);
+ if (err)
+ return err;
+
+ *rom_size = get_info.rom_length;
+ *rom_version = 0;
+
+ return 0;
+}
+
+#define MAXIMUM_BANDWIDTH 4915
+
+int
+fw_bandwidth_modify (raw1394handle_t handle,
+ unsigned int bandwidth,
+ enum raw1394_modify_mode mode)
+{
+ quadlet_t buffer, compare, swap;
+ nodeaddr_t addr;
+ int result;
+
+ if (bandwidth == 0)
+ return 0;
+
+ addr = CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE;
+ /* Read current bandwidth usage from IRM. */
+ result = raw1394_read (handle, raw1394_get_irm_id (handle), addr,
+ sizeof buffer, &buffer);
+ if (result < 0)
+ return -1;
+
+ compare = ntohl (buffer);
+ switch (mode) {
+ case RAW1394_MODIFY_ALLOC:
+ swap = compare - bandwidth;
+ if (swap < 0)
+ return -1;
+ break;
+
+ case RAW1394_MODIFY_FREE:
+ swap = compare + bandwidth;
+ if (swap > MAXIMUM_BANDWIDTH)
+ swap = MAXIMUM_BANDWIDTH;
+ break;
+
+ default:
+ return -1;
+ }
+
+ result = raw1394_lock(handle, raw1394_get_irm_id (handle), addr,
+ RAW1394_EXTCODE_COMPARE_SWAP,
+ htonl(swap), htonl(compare), &buffer);
+ if (result < 0 || ntohl(buffer) != compare)
+ return -1;
+
+ return 0;
+}
+
+int
+fw_channel_modify (raw1394handle_t handle,
+ unsigned int channel,
+ enum raw1394_modify_mode mode)
+{
+ quadlet_t buffer, compare, swap, bit;
+ nodeaddr_t addr;
+ int result;
+
+ if (channel >= 64)
+ return -1;
+ addr = CSR_REGISTER_BASE +
+ CSR_CHANNELS_AVAILABLE_HI + 4 * (channel / 32);
+ /* Read currently available channels from IRM. */
+ result = raw1394_read(handle, raw1394_get_irm_id (handle), addr,
+ sizeof buffer, &buffer);
+ if (result < 0)
+ return -1;
+
+ /* IEEE numbers bits from MSB (0) to LSB (31). */
+ bit = 1 << (31 - (channel & 31));
+ compare = ntohl(buffer);
+ switch (mode) {
+ case RAW1394_MODIFY_ALLOC:
+ if ((compare & bit) == 0)
+ return -1;
+ swap = buffer & ~bit;
+ break;
+
+ case RAW1394_MODIFY_FREE:
+ if ((buffer & bit) != 0)
+ return -1;
+ swap = buffer | bit;
+ break;
+
+ default:
+ return -1;
+ }
+
+ result = raw1394_lock (handle, raw1394_get_irm_id (handle), addr,
+ RAW1394_EXTCODE_COMPARE_SWAP,
+ htonl(swap), htonl(compare), &buffer);
+
+ if (result < 0 || ntohl(buffer) != compare)
+ return -1;
+
+ return 0;
+}
diff --git a/src/fw.h b/src/fw.h
new file mode 100644
index 0000000..e3196de
--- /dev/null
+++ b/src/fw.h
@@ -0,0 +1,257 @@
+/* -*- c-basic-offset: 8 -*-
+ *
+ * fw.h -- Internal header file for firewire raw1394 emulation
+ *
+ * Copyright (C) 2007 Kristian Hoegsberg <krh@bitplanet.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBRAW1394_FW_H
+#define LIBRAW1394_FW_H
+
+#include <stdlib.h>
+#include <byteswap.h>
+#include <linux/firewire-cdev.h>
+#include "raw1394.h"
+#include "csr.h"
+#include "config.h"
+
+#define ptr_to_u64(p) ((__u64)(unsigned long)(p))
+#define u64_to_ptr(p) ((void *)(unsigned long)(p))
+
+static inline __u32
+be32_to_cpu(__u32 q)
+{
+ union { char c[4]; __u32 q; } u = { { 1, 0, 0, 0 } };
+
+ return u.q == 1 ? bswap_32(q) : q;
+}
+
+static inline __u32
+cpu_to_be32(__u32 q)
+{
+ return be32_to_cpu(q);
+}
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
+
+#define BUFFER_SIZE (16 * 1024)
+
+#define MAX_PORTS 16
+
+struct fw_handle;
+
+struct epoll_closure {
+ int (*func)(raw1394handle_t handle,
+ struct epoll_closure *closure, __uint32_t events);
+};
+
+struct port {
+ char device_file[32];
+ char *name;
+ int node_count;
+ int card;
+};
+
+#define MAX_DEVICES 63
+#define FILENAME_SIZE 16
+
+struct device {
+ struct epoll_closure closure;
+ int fd;
+ int node_id;
+ int generation;
+ char filename[FILENAME_SIZE];
+};
+
+struct request_closure {
+ void *data;
+ size_t length;
+ unsigned long tag;
+ struct raw1394_reqhandle reqhandle;
+};
+
+struct allocation;
+
+struct fw_handle {
+ struct port ports[MAX_PORTS];
+ int port_count;
+ int err;
+ int generation;
+ void *userdata;
+ int notify_bus_reset;
+
+ bus_reset_handler_t bus_reset_handler;
+ tag_handler_t tag_handler;
+ arm_tag_handler_t arm_tag_handler;
+ fcp_handler_t fcp_handler;
+ __u32 fcp_allocation_handle;
+ struct allocation *allocations;
+
+ int epoll_fd;
+ int inotify_fd;
+ int inotify_watch;
+ int pipe_fds[2];
+
+ struct epoll_closure pipe_closure;
+ struct epoll_closure inotify_closure;
+
+ struct device devices[MAX_DEVICES];
+ int nodes[MAX_DEVICES];
+ int local_fd;
+ char local_filename[FILENAME_SIZE];
+
+ struct fw_cdev_event_bus_reset reset;
+
+ struct {
+ struct epoll_closure closure;
+ int fd;
+ int type;
+ int irq_interval;
+ int packet_phase;
+ int packet_count;
+ int packet_index;
+ int buf_packets;
+ int max_packet_size;
+ int prebuffer;
+ int start_on_cycle;
+ enum raw1394_iso_dma_recv_mode recv_mode;
+ raw1394_iso_xmit_handler_t xmit_handler;
+ raw1394_iso_recv_handler_t recv_handler;
+ unsigned char *buffer, *buffer_end, *head;
+ unsigned char *tail, *first_payload;
+
+ struct fw_cdev_iso_packet *packets;
+ } iso;
+
+ char buffer[BUFFER_SIZE];
+};
+typedef struct fw_handle *fw_handle_t;
+
+int fw_loop_iterate(raw1394handle_t handle);
+fw_handle_t fw_new_handle(void);
+void fw_destroy_handle(fw_handle_t handle);
+fw_handle_t fw_new_handle_on_port(int port);
+int fw_busreset_notify (fw_handle_t handle, int off_on_switch);
+int fw_get_fd(fw_handle_t handle);
+nodeid_t fw_get_local_id(fw_handle_t handle);
+nodeid_t fw_get_irm_id(fw_handle_t handle);
+int fw_get_nodecount(fw_handle_t handle);
+int fw_get_port_info(fw_handle_t handle, struct raw1394_portinfo *pinf,
+ int maxports);
+int fw_set_port(fw_handle_t handle, int port);
+int fw_reset_bus_new(fw_handle_t handle, int type);
+int fw_arm_register(fw_handle_t handle, nodeaddr_t start,
+ size_t length, byte_t *initial_value,
+ octlet_t arm_tag, arm_options_t access_rights,
+ arm_options_t notification_options,
+ arm_options_t client_transactions);
+int fw_arm_unregister(fw_handle_t handle, nodeaddr_t start);
+int fw_arm_set_buf(fw_handle_t handle, nodeaddr_t start,
+ size_t length, void *buf);
+int fw_arm_get_buf(fw_handle_t handle, nodeaddr_t start,
+ size_t length, void *buf);
+int fw_echo_request(fw_handle_t handle, quadlet_t data);
+int fw_wake_up(fw_handle_t handle);
+int fw_phy_packet_write (fw_handle_t handle, quadlet_t data);
+int fw_start_phy_packet_write(fw_handle_t handle,
+ quadlet_t data, unsigned long tag);
+int fw_start_read(fw_handle_t handle, nodeid_t node, nodeaddr_t addr,
+ size_t length, quadlet_t *buffer, unsigned long tag);
+int fw_start_write(fw_handle_t handle, nodeid_t node, nodeaddr_t addr,
+ size_t length, quadlet_t *data, unsigned long tag);
+int fw_start_lock(fw_handle_t handle, nodeid_t node, nodeaddr_t addr,
+ unsigned int extcode, quadlet_t data, quadlet_t arg,
+ quadlet_t *result, unsigned long tag);
+int fw_start_lock64(fw_handle_t handle, nodeid_t node, nodeaddr_t addr,
+ unsigned int extcode, octlet_t data, octlet_t arg,
+ octlet_t *result, unsigned long tag);
+int fw_start_async_stream(fw_handle_t handle, unsigned int channel,
+ unsigned int tag, unsigned int sy,
+ unsigned int speed, size_t length, quadlet_t *data,
+ unsigned long rawtag);
+int fw_start_async_send(fw_handle_t handle,
+ size_t length, size_t header_length,
+ unsigned int expect_response,
+ quadlet_t *data, unsigned long rawtag);
+int fw_read(raw1394handle_t handle, nodeid_t node, nodeaddr_t addr,
+ size_t length, quadlet_t *buffer);
+int fw_write(raw1394handle_t handle, nodeid_t node, nodeaddr_t addr,
+ size_t length, quadlet_t *data);
+int fw_lock(raw1394handle_t handle, nodeid_t node, nodeaddr_t addr,
+ unsigned int extcode, quadlet_t data, quadlet_t arg,
+ quadlet_t *result);
+int fw_lock64(raw1394handle_t handle, nodeid_t node, nodeaddr_t addr,
+ unsigned int extcode, octlet_t data, octlet_t arg,
+ octlet_t *result);
+int fw_async_stream(fw_handle_t handle, unsigned int channel,
+ unsigned int tag, unsigned int sy, unsigned int speed,
+ size_t length, quadlet_t *data);
+int fw_async_send(fw_handle_t handle,
+ size_t length, size_t header_length,
+ unsigned int expect_response,
+ quadlet_t *data);
+int fw_start_fcp_listen(fw_handle_t handle);
+int fw_stop_fcp_listen(fw_handle_t handle);
+int fw_update_config_rom(fw_handle_t handle, const quadlet_t *new_rom,
+ size_t size, unsigned char rom_version);
+int fw_get_config_rom(fw_handle_t handle, quadlet_t *buffer,
+ size_t buffersize, size_t *rom_size,
+ unsigned char *rom_version);
+int fw_bandwidth_modify (raw1394handle_t handle,
+ unsigned int bandwidth,
+ enum raw1394_modify_mode mode);
+int fw_channel_modify (raw1394handle_t handle,
+ unsigned int channel,
+ enum raw1394_modify_mode mode);
+
+int fw_iso_xmit_start(raw1394handle_t handle, int start_on_cycle,
+ int prebuffer_packets);
+int fw_iso_recv_start(fw_handle_t handle, int start_on_cycle,
+ int tag_mask, int sync);
+int fw_iso_xmit_write(raw1394handle_t handle, unsigned char *data,
+ unsigned int len, unsigned char tag,
+ unsigned char sy);
+int fw_iso_xmit_sync(raw1394handle_t handle);
+int fw_iso_recv_flush(fw_handle_t handle);
+int fw_iso_xmit_init(fw_handle_t handle,
+ raw1394_iso_xmit_handler_t handler,
+ unsigned int buf_packets,
+ unsigned int max_packet_size,
+ unsigned char channel,
+ enum raw1394_iso_speed speed,
+ int irq_interval);
+int fw_iso_recv_init(fw_handle_t handle,
+ raw1394_iso_recv_handler_t handler,
+ unsigned int buf_packets,
+ unsigned int max_packet_size,
+ unsigned char channel,
+ enum raw1394_iso_dma_recv_mode mode,
+ int irq_interval);
+int fw_iso_multichannel_recv_init(fw_handle_t handle,
+ raw1394_iso_recv_handler_t handler,
+ unsigned int buf_packets,
+ unsigned int max_packet_size,
+ int irq_interval);
+int fw_iso_recv_listen_channel(fw_handle_t handle,
+ unsigned char channel);
+int fw_iso_recv_unlisten_channel(fw_handle_t handle,
+ unsigned char channel);
+int fw_iso_recv_set_channel_mask(fw_handle_t handle, u_int64_t mask);
+void fw_iso_stop(fw_handle_t handle);
+void fw_iso_shutdown(fw_handle_t handle);
+
+#endif
diff --git a/src/raw1394_private.h b/src/raw1394_private.h
index 3f50740..0c8677d 100644
--- a/src/raw1394_private.h
+++ b/src/raw1394_private.h
@@ -3,7 +3,7 @@
#include "raw1394.h"
#include "csr.h"
-#include "../juju/juju.h"
+#include "fw.h"
#include "kernel-raw1394.h"
#define HBUF_SIZE 8192