diff --git a/Makefile.am b/Makefile.am index 04ed38a..21df527 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,7 @@ # process this file with automake to create a Makefile.in -SUBDIRS = src tools doc debian +SUBDIRS = $(LIB_SUBDIR) tools doc debian +DIST_SUBDIRS = src juju pkgconfigdir = @libdir@/pkgconfig pkgconfig_DATA = libraw1394.pc diff --git a/configure.ac b/configure.ac index fe23ca8..7e5dd66 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,27 @@ AC_SUBST(lt_major) AC_SUBST(lt_revision) AC_SUBST(lt_age) +AC_ARG_WITH(juju-dir,[ --with-juju-dir= Path to juju include files]) +if ! test -z "$with_juju_dir" ; then + JUJU_DIR="$with_juju_dir" + LIB_SUBDIR=juju + AC_SUBST(JUJU_DIR) +else + LIB_SUBDIR=src +fi +AC_SUBST(LIB_SUBDIR) + +AC_ARG_WITH(fw-device-prefix, + [ --with-fw-device-prefix= Prefix of firewire device file names (default "fw").], + [FW_DEVICE_PREFIX="\"$withval\""], [FW_DEVICE_PREFIX="\"fw\""]) +AC_ARG_WITH(fw-device-dir, + [ --with-fw-device-dir= Directory to watch for firewire device files (default "/dev").], + [FW_DEVICE_DIR="\"$withval\""], [FW_DEVICE_DIR="\"/dev\""]) + +AC_DEFINE_UNQUOTED(FW_DEVICE_PREFIX, $FW_DEVICE_PREFIX, + [Prefix of firewire device file names.]) +AC_DEFINE_UNQUOTED(FW_DEVICE_DIR, $FW_DEVICE_DIR, + [Directory to watch for firewire device files.]) #CFLAGS=${CFLAGS:-"-Wall"} AC_OUTPUT([ @@ -31,6 +52,7 @@ Makefile libraw1394.pc libraw1394.spec src/Makefile +juju/Makefile tools/Makefile doc/Makefile doc/testlibraw.1 diff --git a/juju/Makefile.am b/juju/Makefile.am new file mode 100644 index 0000000..6fd6a5e --- /dev/null +++ b/juju/Makefile.am @@ -0,0 +1,8 @@ +lib_LTLIBRARIES = libraw1394.la + +INCLUDES = -I$(JUJU_DIR) +libraw1394_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@ + +libraw1394_la_SOURCES = raw1394.c raw1394-iso.c juju.h + +pkginclude_HEADERS = ../src/raw1394.h ../src/csr.h ../src/ieee1394.h diff --git a/juju/juju.h b/juju/juju.h new file mode 100644 index 0000000..5299cbf --- /dev/null +++ b/juju/juju.h @@ -0,0 +1,139 @@ +/* -*- c-basic-offset: 8 -*- + * + * juju.h -- Internal header file for raw1394 emulation + * + * Copyright (C) 2007 Kristian Hoegsberg + * + * 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 __juju_h +#define __juju_h + +#include +#include +#include +#include "../src/raw1394.h" +#include "../src/csr.h" +#include "config.h" + +#define ACK_COMPLETE 1 + +#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 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 raw1394_handle { + struct port ports[MAX_PORTS]; + int port_count; + int err; + int generation; + void *user_data; + 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; + 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_index; + int packet_phase; + int buf_packets; + int max_packet_size; + 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, *head, *tail; + + struct fw_cdev_queue_iso queue_iso; + struct fw_cdev_iso_packet *packets; + } iso; + + char buffer[BUFFER_SIZE]; +}; + +#endif diff --git a/juju/raw1394-iso.c b/juju/raw1394-iso.c new file mode 100644 index 0000000..6427d39 --- /dev/null +++ b/juju/raw1394-iso.c @@ -0,0 +1,385 @@ +/* -*- c-basic-offset: 8 -*- + * + * raw1394-iso.c -- Emulation of the raw1394 rawiso API on the juju stack + * + * Copyright (C) 2007 Kristian Hoegsberg + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "juju.h" + +static int +refill_xmit_buffer(raw1394handle_t handle, struct fw_cdev_queue_iso *queue_iso) +{ + int i; + struct fw_cdev_iso_packet *p = handle->iso.packets; + enum raw1394_iso_disposition d; + unsigned int len, dropped; + unsigned char tag, sy, *data, *buffer; + int cycle; + + buffer = handle->iso.buffer + + handle->iso.packet_index * handle->iso.max_packet_size; + data = buffer; + + for (i = 0; i < handle->iso.irq_interval; i++) { + cycle = -1; + dropped = 0; + d = handle->iso.xmit_handler(handle, data, + &len, &tag, &sy, cycle, dropped); + /* FIXME: handle the different dispositions. */ + + p->payload_length = len; + p->interrupt = handle->iso.packet_phase == 0; + p->skip = 0; + p->tag = tag; + p->sy = sy; + p->header_length = 0; + + data += handle->iso.max_packet_size; + handle->iso.packet_index++; + if (handle->iso.packet_index == handle->iso.buf_packets) { + handle->iso.packet_index = 0; + break; + } + + handle->iso.packet_phase++; + if (handle->iso.packet_phase == handle->iso.irq_interval) + handle->iso.packet_phase = 0; + + } + + queue_iso->packets = ptr_to_u64(handle->iso.packets); + queue_iso->size = + handle->iso.irq_interval * sizeof handle->iso.packets[0]; + queue_iso->data = ptr_to_u64(buffer); + + return 0; +} + +static int +flush_xmit_packets(raw1394handle_t handle, int limit) +{ + struct fw_cdev_queue_iso queue_iso; + int len; + + while (handle->iso.packet_index + handle->iso.irq_interval <= limit) { + if (handle->iso.queue_iso.size == 0) + refill_xmit_buffer(handle, &queue_iso); + len = ioctl(handle->iso.fd, + FW_CDEV_IOC_QUEUE_ISO, &queue_iso); + if (len < 0) + return -1; + if (handle->iso.queue_iso.size > 0) + break; + } + + return 0; +} + +int raw1394_iso_xmit_start(raw1394handle_t handle, int start_on_cycle, + int prebuffer_packets) +{ + struct fw_cdev_start_iso start_iso; + int retval; + + if (prebuffer_packets == -1) + prebuffer_packets = handle->iso.irq_interval; + + flush_xmit_packets(handle, prebuffer_packets); + + start_iso.cycle = start_on_cycle; + + retval = ioctl(handle->iso.fd, FW_CDEV_IOC_START_ISO, &start_iso); + if (retval < 0) + return retval; + + return flush_xmit_packets(handle, handle->iso.buf_packets); +} + +static int +handle_recv_packets(raw1394handle_t handle, + struct fw_cdev_event_iso_interrupt *interrupt) +{ + enum raw1394_iso_disposition d; + quadlet_t header, *p, *end; + unsigned int len, cycle, dropped; + unsigned char channel, tag, sy; + unsigned char *data; + + p = interrupt->header; + end = (void *) interrupt->header + interrupt->header_length; + cycle = interrupt->cycle; + data = NULL; + + while (p < end) { + header = be32_to_cpu(*p++); + len = header >> 8; + channel = header >> 8; + tag = header >> 8; + sy = header >> 8; + + d = handle->iso.recv_handler(handle, data, len, channel, + tag, sy, cycle, dropped); + cycle++; + } + + return 0; +} + +int raw1394_iso_recv_start(raw1394handle_t handle, int start_on_cycle, + int tag_mask, int sync) +{ + struct fw_cdev_start_iso start_iso; + + 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; + + 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) +{ + struct fw_cdev_event_iso_interrupt *interrupt; + int len; + + len = read(handle->iso.fd, handle->buffer, sizeof handle->buffer); + if (len < 0) + return -1; + + interrupt = (struct fw_cdev_event_iso_interrupt *) handle->buffer; + if (interrupt->type != FW_CDEV_EVENT_BUS_RESET) + return 0; + + switch (handle->iso.type) { + case FW_CDEV_ISO_CONTEXT_TRANSMIT: + handle->iso.packet_index -= handle->iso.irq_interval; + return flush_xmit_packets(handle, handle->iso.buf_packets); + case FW_CDEV_ISO_CONTEXT_RECEIVE: + return handle_recv_packets(handle, interrupt); + default: + /* Doesn't happen. */ + return -1; + } +} + +int raw1394_iso_xmit_write(raw1394handle_t handle, unsigned char *data, + unsigned int len, unsigned char tag, + unsigned char sy) +{ + struct fw_cdev_iso_packet packet; + + packet.payload_length = len; + packet.interrupt = handle->iso.packet_phase == 0; + packet.skip = 0; + packet.tag = tag; + packet.sy = sy; + packet.header_length = 0; + + handle->iso.packet_phase++; + if (handle->iso.packet_phase == handle->iso.irq_interval) + handle->iso.packet_phase = 0; + + /* FIXME: circular buffer goo. */ + + memcpy(handle->iso.head, data, len); + handle->iso.head += len; + + return -1; +} + +int raw1394_iso_xmit_sync(raw1394handle_t handle) +{ + /* FIXME: queue a skip packet and wait for that interrupt. */ + + return 0; +} + +int raw1394_iso_recv_flush(raw1394handle_t handle) +{ + /* FIXME: huh, we'll need kernel support here... */ + + return 0; +} + +int raw1394_iso_xmit_init(raw1394handle_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) +{ + struct fw_cdev_create_iso_context create; + struct epoll_event ep; + int retval; + + if (handle->iso.fd != -1) { + errno = EBUSY; + return -1; + } + + handle->iso.type = FW_CDEV_ISO_CONTEXT_TRANSMIT; + handle->iso.irq_interval = irq_interval; + handle->iso.xmit_handler = handler; + handle->iso.buf_packets = buf_packets; + handle->iso.max_packet_size = max_packet_size; + handle->iso.packet_index = 0; + handle->iso.packet_phase = 0; + handle->iso.queue_iso.size = 0; + handle->iso.packets = + malloc(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); + 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); + return -1; + } + + create.type = FW_CDEV_ISO_CONTEXT_TRANSMIT; + create.channel = channel; + create.speed = speed; + + retval = ioctl(handle->iso.fd, + FW_CDEV_IOC_CREATE_ISO_CONTEXT, &create); + if (retval < 0) { + close(handle->iso.fd); + free(handle->iso.packets); + return retval; + } + + handle->iso.buffer = + mmap(NULL, buf_packets * max_packet_size, + PROT_READ | PROT_WRITE, MAP_SHARED, handle->iso.fd, 0); + + if (handle->iso.buffer == MAP_FAILED) { + close(handle->iso.fd); + free(handle->iso.packets); + return -1; + } + + return 0; +} + +int raw1394_iso_recv_init(raw1394handle_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) +{ + struct fw_cdev_create_iso_context create; + + if (handle->iso.fd != -1) { + errno = EBUSY; + return -1; + } + + /* FIXME: Do we need this? When would you ever want this...? */ + if (mode == RAW1394_DMA_PACKET_PER_BUFFER) + return -1; + + handle->iso.buffer = + mmap(NULL, buf_packets * max_packet_size, + PROT_READ, MAP_SHARED, handle->iso.fd, 0); + + if (handle->iso.buffer == MAP_FAILED) + return -1; + + create.type = FW_CDEV_ISO_CONTEXT_RECEIVE; + create.channel = channel; + create.speed = 0; + create.header_size = 0; /* Never strip any headers. */ + + handle->iso.type = FW_CDEV_ISO_CONTEXT_RECEIVE; + handle->iso.irq_interval = irq_interval; + handle->iso.recv_handler = handler; + + return ioctl(handle->iso.fd, + FW_CDEV_IOC_CREATE_ISO_CONTEXT, &create); +} + +int raw1394_iso_multichannel_recv_init(raw1394handle_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 raw1394_iso_recv_listen_channel(raw1394handle_t handle, + unsigned char channel) +{ + /* FIXME: multichannel */ + errno = ENOSYS; + return -1; +} + +int raw1394_iso_recv_unlisten_channel(raw1394handle_t handle, + unsigned char channel) +{ + /* FIXME: multichannel */ + errno = ENOSYS; + return -1; +} + +int raw1394_iso_recv_set_channel_mask(raw1394handle_t handle, u_int64_t mask) +{ + /* FIXME: multichannel */ + errno = ENOSYS; + return -1; +} + +void raw1394_iso_stop(raw1394handle_t handle) +{ + ioctl(handle->iso.fd, FW_CDEV_IOC_STOP_ISO); +} + +void raw1394_iso_shutdown(raw1394handle_t handle) +{ + munmap(handle->iso.buffer, + handle->iso.buf_packets * handle->iso.max_packet_size); + close(handle->iso.fd); + free(handle->iso.packets); +} diff --git a/juju/raw1394.c b/juju/raw1394.c new file mode 100644 index 0000000..6bfd7e1 --- /dev/null +++ b/juju/raw1394.c @@ -0,0 +1,1437 @@ +/* -*- c-basic-offset: 8 -*- + * + * raw1394.c -- Emulation of the raw1394 API on the juju stack + * + * Copyright (C) 2007 Kristian Hoegsberg + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "juju.h" + +raw1394_errcode_t +raw1394_get_errcode(raw1394handle_t handle) +{ + return handle->err; +} + +int +raw1394_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 +juju_to_raw1394_errcode(int rcode) +{ + /* Best effort matching juju extended rcodes to raw1394 err + * code. Since the raw1394 errcode decoding are macros we try + * to convert the juju 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 juju 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(struct raw1394_handle *handle, unsigned int gen) +{ + raw1394_update_generation(handle, gen); + + return 0; +} + +static int +scan_devices(raw1394handle_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->pipe_fds[0], &value, sizeof value) < 0) + return -1; + + return value; +} + +static int +handle_lost_device(raw1394handle_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.serial = request->serial; + response.rcode = RCODE_COMPLETE; + response.length = 0; + response.data = 0; + + if (handle->fcp_handler == NULL) + response.rcode = RCODE_ADDRESS_ERROR; + + if (request->tcode >= TCODE_WRITE_RESPONSE) + response.rcode = RCODE_CONFLICT_ERROR; + + if (ioctl(handle->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->fcp_handler(handle, + handle->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) +{ + 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 - handle->devices; + if (events == EPOLLHUP) + return handle_lost_device(handle, i); + + len = read(handle->devices[i].fd, + handle->buffer, sizeof handle->buffer); + if (len < 0) + return -1; + + u = (void *) handle->buffer; + switch (u->common.type) { + case FW_CDEV_EVENT_BUS_RESET: + /* Clear old entry, unless it's been overwritten. */ + phy_id = handle->devices[i].node_id & 0x3f; + if (handle->nodes[phy_id] == i) + handle->nodes[phy_id] = -1; + handle->nodes[u->bus_reset.node_id & 0x3f] = i; + handle->devices[i].node_id = u->bus_reset.node_id; + handle->devices[i].generation = u->bus_reset.generation; + + if (u->bus_reset.node_id != u->bus_reset.local_node_id) + return 0; + + memcpy(&handle->reset, &u->bus_reset, sizeof handle->reset); + return handle->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 = juju_to_raw1394_errcode(u->response.rcode); + + return handle->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) +{ + 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 *) handle->buffer; + len = read(handle->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 (handle->devices[i].node_id == -1) + break; + if (i == MAX_DEVICES) { + close(fd); + return -1; + } + + 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; + } + + return 0; +} + +int raw1394_loop_iterate(raw1394handle_t handle) +{ + int i, count, retval = 0; + struct epoll_closure *closure; + struct epoll_event ep[32]; + + count = epoll_wait(handle->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->epoll_fd, ep, ARRAY_LENGTH(ep), 0); + + return retval; +} + +raw1394handle_t raw1394_new_handle(void) +{ + raw1394handle_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 raw1394_destroy_handle(raw1394handle_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; +} + +raw1394handle_t raw1394_new_handle_on_port(int port) +{ + raw1394handle_t handle; + + handle = raw1394_new_handle(); + if (handle == NULL) + return NULL; + + if (raw1394_set_port(handle, port) < 0) + return NULL; + + return handle; +} + +int raw1394_busreset_notify (raw1394handle_t handle, int off_on_switch) +{ + handle->notify_bus_reset = off_on_switch; + + return 0; +} + +int raw1394_get_fd(raw1394handle_t handle) +{ + return handle->epoll_fd; +} + +void raw1394_set_userdata(raw1394handle_t handle, void *data) +{ + handle->user_data = data; +} + +void *raw1394_get_userdata(raw1394handle_t handle) +{ + return handle->user_data; +} + +nodeid_t raw1394_get_local_id(raw1394handle_t handle) +{ + return handle->reset.local_node_id; +} + +nodeid_t raw1394_get_irm_id(raw1394handle_t handle) +{ + return handle->reset.irm_node_id; +} + +int raw1394_get_nodecount(raw1394handle_t handle) +{ + return (handle->reset.root_node_id & 0x3f) + 1; +} + +int raw1394_get_port_info(raw1394handle_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 raw1394_set_port(raw1394handle_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 raw1394_reset_bus(raw1394handle_t handle) +{ + return raw1394_reset_bus_new(handle, RAW1394_LONG_RESET); +} + +int raw1394_reset_bus_new(raw1394handle_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); +} + +bus_reset_handler_t raw1394_set_bus_reset_handler(raw1394handle_t handle, + bus_reset_handler_t new_h) +{ + bus_reset_handler_t old_h = handle->bus_reset_handler; + + handle->bus_reset_handler = new_h; + + return old_h; +} + +unsigned int raw1394_get_generation(raw1394handle_t handle) +{ + return handle->generation; +} + +void raw1394_update_generation(raw1394handle_t handle, unsigned int generation) +{ + handle->generation = generation; +} + +tag_handler_t +raw1394_set_tag_handler(raw1394handle_t handle, tag_handler_t new_h) +{ + tag_handler_t old_h = handle->tag_handler; + + handle->tag_handler = new_h; + + return old_h; +} + +arm_tag_handler_t +raw1394_set_arm_tag_handler(raw1394handle_t handle, arm_tag_handler_t new_h) +{ + arm_tag_handler_t old_h = handle->arm_tag_handler; + + handle->arm_tag_handler = new_h; + + return old_h; +} + +fcp_handler_t +raw1394_set_fcp_handler(raw1394handle_t handle, fcp_handler_t new_h) +{ + fcp_handler_t old_h = handle->fcp_handler; + + handle->fcp_handler = new_h; + + return old_h; +} + +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; + 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) +{ + 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.serial = request->serial; + + 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(handle->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(handle->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 = handle->reset.local_node_id; + rrb->request.source_nodeid = handle->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 = handle->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 handle->arm_tag_handler(handle, allocation->tag, type, + request->length, + &rrb->request_response); +} + +int +raw1394_arm_register(raw1394handle_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->next = handle->allocations; + handle->allocations = allocation; + + return 0; +} + +static struct allocation * +lookup_allocation(raw1394handle_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 +raw1394_arm_unregister(raw1394handle_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; + } + + free(allocation); + + request.offset = start; + + return ioctl(handle->local_fd, FW_CDEV_IOC_DEALLOCATE, &request); +} + +int +raw1394_arm_set_buf(raw1394handle_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 +raw1394_arm_get_buf(raw1394handle_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 +raw1394_echo_request(raw1394handle_t handle, quadlet_t data) +{ + return write(handle->pipe_fds[1], &data, sizeof data); +} + +int raw1394_wake_up(raw1394handle_t handle) +{ + return raw1394_echo_request(handle, 0); +} + +int raw1394_phy_packet_write (raw1394handle_t handle, quadlet_t data) +{ + errno = ENOSYS; + return -1; +} + +int +raw1394_start_phy_packet_write(raw1394handle_t handle, + quadlet_t data, unsigned long tag) +{ + errno = ENOSYS; + return -1; +} + +static int +send_request(raw1394handle_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 = raw1394_errcode_to_errno(handle->err); + return -1; + } + + i = handle->nodes[node & 0x3f]; + if (i == -1) { + handle->err = -RCODE_NO_ACK; + errno = raw1394_errcode_to_errno(handle->err); + return -1; + } + + if (handle->generation != handle->devices[i].generation) { + handle->err = -RCODE_GENERATION; + errno = raw1394_errcode_to_errno(handle->err); + return -1; + } + + closure = malloc(sizeof *closure); + if (closure == NULL) { + handle->err = -RCODE_SEND_ERROR; + errno = raw1394_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 +raw1394_start_read(raw1394handle_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 +raw1394_start_write(raw1394handle_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 sizeof buffer; + + 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 sizeof buffer; + + default: + errno = EINVAL; + return -1; + } +} + +int +raw1394_start_lock(raw1394handle_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 +raw1394_start_lock64(raw1394handle_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 +raw1394_start_async_stream(raw1394handle_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 +raw1394_start_async_send(raw1394handle_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) +{ + struct raw1394_reqhandle reqhandle; + struct sync_data sd = { 0, 0 }; + int err; + + reqhandle.callback = sync_callback; + reqhandle.data = &sd; + + err = send_request(handle, tcode, node, addr, + length, in, out, (unsigned long) &reqhandle); + + while (!sd.done) { + if (err < 0) + return err; + err = raw1394_loop_iterate(handle); + } + + handle->err = sd.err; + errno = raw1394_errcode_to_errno(sd.err); + + return (errno ? -1 : 0); +} + +int +raw1394_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 +raw1394_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 +raw1394_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 +raw1394_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 +raw1394_async_stream(raw1394handle_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 +raw1394_async_send(raw1394handle_t handle, + size_t length, size_t header_length, + unsigned int expect_response, + quadlet_t *data) +{ + /* FIXME: implement this? */ + return -1; +} + +int +raw1394_start_fcp_listen(raw1394handle_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; + + return 0; +} + +int +raw1394_stop_fcp_listen(raw1394handle_t handle) +{ + struct fw_cdev_deallocate request; + + request.offset = CSR_REGISTER_BASE + CSR_FCP_COMMAND; + + return ioctl(handle->local_fd, FW_CDEV_IOC_DEALLOCATE, &request); +} + +const char * +raw1394_get_libversion(void) +{ + return VERSION " (Juju)"; +} + +int +raw1394_update_config_rom(raw1394handle_t handle, const quadlet_t *new_rom, + size_t size, unsigned char rom_version) +{ + return -1; +} + +int +raw1394_get_config_rom(raw1394handle_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 +raw1394_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 +raw1394_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/tools/Makefile.am b/tools/Makefile.am index 29b250e..5be1b6f 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -2,4 +2,4 @@ MAINTAINERCLEANFILES = Makefile.in # testlibraw bin_PROGRAMS = testlibraw sendiso dumpiso -LDADD = ../src/libraw1394.la +LDADD = ../$(LIB_SUBDIR)/libraw1394.la diff --git a/tools/testlibraw.c b/tools/testlibraw.c index 5f73bd9..2f02a6d 100644 --- a/tools/testlibraw.c +++ b/tools/testlibraw.c @@ -1,4 +1,5 @@ -/* +/* -*- c-basic-offset: 8 -*- + * * libraw1394 - library for raw access to the 1394 bus with the Linux subsystem. * * Copyright (C) 1999,2000 Andreas Bombe @@ -13,12 +14,13 @@ #include #include #include +#include #include "../src/raw1394.h" #include "../src/csr.h" -#define TESTADDR (CSR_REGISTER_BASE + CSR_CYCLE_TIME) +#define TESTADDR (CSR_REGISTER_BASE + CSR_CONFIG_ROM) const char not_compatible[] = "\ This libraw1394 does not work with your version of Linux. You need a different\n\ @@ -45,12 +47,18 @@ int my_tag_handler(raw1394handle_t handle, unsigned long tag, return 0; } +static const unsigned char fcp_data[] = + { 0x1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; + int my_fcp_handler(raw1394handle_t handle, nodeid_t nodeid, int response, size_t length, unsigned char *data) { printf("got fcp %s from node %d of %d bytes:", (response ? "response" : "command"), nodeid & 0x3f, length); + if (memcmp(fcp_data, data, sizeof fcp_data) != 0) + printf("ERROR: fcp payload not correct\n"); + while (length) { printf(" %02x", *data); data++; @@ -62,6 +70,47 @@ int my_fcp_handler(raw1394handle_t handle, nodeid_t nodeid, int response, return 0; } +static void +test_fcp(raw1394handle_t handle) +{ + printf("\ntesting FCP monitoring on local node\n"); + raw1394_set_fcp_handler(handle, my_fcp_handler); + raw1394_start_fcp_listen(handle); + raw1394_write(handle, raw1394_get_local_id(handle), + CSR_REGISTER_BASE + CSR_FCP_COMMAND, sizeof(fcp_data), + (quadlet_t *)fcp_data); + raw1394_write(handle, raw1394_get_local_id(handle), + CSR_REGISTER_BASE + CSR_FCP_RESPONSE, sizeof(fcp_data), + (quadlet_t *)fcp_data); +} + +static void +read_topology_map(raw1394handle_t handle) +{ + quadlet_t map[70]; + nodeid_t local_id; + int node_count, self_id_count, i, retval; + + local_id = raw1394_get_local_id(handle) | 0xffc0; + + retval = raw1394_read(handle, local_id, + CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP, 12, &map[0]); + if (retval < 0) + perror("topology map: raw1394_read failed with error"); + + self_id_count = ntohl(map[2]) & 0xffff; + node_count = ntohl(map[2]) >> 16; + retval = raw1394_read(handle, local_id, + CSR_REGISTER_BASE + CSR_TOPOLOGY_MAP + 12, + self_id_count * sizeof map[0], &map[3]); + if (retval < 0) + perror("topology map: raw1394_read failed with error"); + + printf("topology map: %d nodes, %d self ids, generation %d\n", + node_count, self_id_count, ntohl(map[1])); + for (i = 0; i < self_id_count; i++) + printf(" 0x%08x\n", ntohl(map[3 + i])); +} int main(int argc, char **argv) { @@ -73,7 +122,6 @@ int main(int argc, char **argv) int retval; struct pollfd pfd; - unsigned char fcp_test[] = { 0x1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; quadlet_t rom[0x100]; size_t rom_size; unsigned char rom_version; @@ -150,17 +198,8 @@ int main(int argc, char **argv) } } - printf("\ntesting FCP monitoring on local node\n"); - raw1394_set_fcp_handler(handle, my_fcp_handler); - raw1394_start_fcp_listen(handle); - raw1394_write(handle, raw1394_get_local_id(handle), - CSR_REGISTER_BASE + CSR_FCP_COMMAND, sizeof(fcp_test), - (quadlet_t *)fcp_test); - raw1394_write(handle, raw1394_get_local_id(handle), - CSR_REGISTER_BASE + CSR_FCP_RESPONSE, sizeof(fcp_test), - (quadlet_t *)fcp_test); - - + test_fcp(handle); + read_topology_map(handle); printf("testing config rom stuff\n"); retval=raw1394_get_config_rom(handle, rom, 0x100, &rom_size, &rom_version); @@ -176,16 +215,19 @@ int main(int argc, char **argv) retval=raw1394_update_config_rom(handle, rom, rom_size, rom_version); printf("update_config_rom returned %d\n",retval); + printf("\nposting 0xdeadbeef as an echo request\n"); + raw1394_echo_request(handle, 0xdeadbeef); - - printf("\npolling for leftover messages\n"); + printf("polling for leftover messages\n"); pfd.fd = raw1394_get_fd(handle); pfd.events = POLLIN; pfd.revents = 0; while (1) { retval = poll(&pfd, 1, 10); if (retval < 1) break; - raw1394_loop_iterate(handle); + retval = raw1394_loop_iterate(handle); + if (retval != 0) + printf("raw1394_loop_iterate() returned 0x%08x\n", retval); } if (retval < 0) perror("poll failed");