| /* -*- c-basic-offset: 8 -*- |
| * |
| * raw1394.c -- Emulation of the raw1394 API on the fw stack |
| * |
| * Copyright (C) 2007 Kristian Hoegsberg <krh@bitplanet.net> |
| * |
| * This library is licensed under the GNU Lesser General Public License (LGPL), |
| * version 2.1 or later. See the file COPYING.LIB in the distribution for |
| * details. |
| */ |
| |
| #include <ctype.h> |
| #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 <time.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" |
| |
| /* |
| * ABI version history is documented in firewire-cdev.h. |
| */ |
| #define IMPLEMENTED_CDEV_ABI_VERSION 4 |
| |
| 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 |
| is_fw_device_name(char *name) |
| { |
| return strncmp(name, FW_DEVICE_PREFIX, strlen(FW_DEVICE_PREFIX)) == 0 |
| && isdigit(name[strlen(FW_DEVICE_PREFIX)]); |
| } |
| |
| 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, j, fname_str_sz; |
| struct port *ports; |
| |
| ports = handle->ports; |
| memset(ports, 0, sizeof handle->ports); |
| for (i = 0; i < MAX_PORTS; i++) |
| ports[i].card = -1; |
| |
| dir = opendir(FW_DEVICE_DIR); |
| if (dir == NULL) |
| return -1; |
| |
| for (i = 0; i < MAX_PORTS; ) { |
| de = readdir(dir); |
| if (de == NULL) |
| break; |
| |
| if (!is_fw_device_name(de->d_name)) |
| continue; |
| |
| snprintf(filename, sizeof filename, FW_DEVICE_DIR "/%s", de->d_name); |
| |
| fd = open(filename, O_RDWR); |
| if (fd < 0) |
| continue; |
| memset(&get_info, 0, sizeof(get_info)); |
| memset(&reset, 0, sizeof(reset)); |
| get_info.version = IMPLEMENTED_CDEV_ABI_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; |
| |
| for (j = 0; j < i; j++) |
| if (ports[j].card == get_info.card) |
| break; |
| if (j == i) { |
| fname_str_sz = sizeof(ports[i].device_file) - 1; |
| strncpy(ports[i].device_file, filename, fname_str_sz); |
| ports[i].device_file[fname_str_sz] = '\0'; |
| 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, |
| int tcode, unsigned long long offset, |
| int source_node_id, int card, unsigned kernel_handle, |
| size_t length, void *data); |
| }; |
| |
| static int |
| handle_fcp_request(raw1394handle_t handle, struct address_closure *ac, |
| int tcode, unsigned long long offset, int source_node_id, |
| int card, unsigned kernel_handle, size_t length, void *data) |
| { |
| struct fw_cdev_send_response response; |
| int is_response; |
| |
| response.handle = kernel_handle; |
| response.rcode = RCODE_COMPLETE; |
| response.length = 0; |
| response.data = 0; |
| |
| if (handle->mode.fw->fcp_handler == NULL) |
| response.rcode = RCODE_ADDRESS_ERROR; |
| |
| if (tcode >= TCODE_WRITE_RESPONSE) |
| response.rcode = RCODE_CONFLICT_ERROR; |
| |
| if (ioctl(handle->mode.fw->ioctl_fd, |
| FW_CDEV_IOC_SEND_RESPONSE, &response) < 0) |
| return -1; |
| |
| if (card != handle->mode.fw->card) |
| return 0; |
| |
| if (response.rcode != RCODE_COMPLETE) |
| return 0; |
| |
| is_response = offset >= CSR_REGISTER_BASE + CSR_FCP_RESPONSE; |
| |
| return handle->mode.fw->fcp_handler(handle, source_node_id, |
| is_response, length, 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; |
| unsigned long tag; |
| 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); |
| |
| /* Kernel ensures that u->response.length does not overflow. */ |
| if (rc->data != NULL && u->response.rcode == RCODE_COMPLETE) |
| memcpy(rc->data, u->response.data, u->response.length); |
| |
| errcode = fw_to_raw1394_errcode(u->response.rcode); |
| tag = rc->tag; |
| free(rc); |
| |
| return fwhandle->tag_handler(handle, tag, errcode); |
| |
| case FW_CDEV_EVENT_REQUEST: |
| ac = u64_to_ptr(u->request.closure); |
| return ac->callback(handle, ac, u->request.tcode, |
| u->request.offset, |
| /* wild guess, but can't do better */ |
| fwhandle->devices[i].node_id, |
| fwhandle->card, |
| u->request.handle, |
| u->request.length, u->request.data); |
| |
| case FW_CDEV_EVENT_REQUEST2: |
| ac = u64_to_ptr(u->request.closure); |
| return ac->callback(handle, ac, u->request2.tcode, |
| u->request2.offset, |
| u->request2.source_node_id, |
| u->request2.card, |
| u->request2.handle, |
| u->request2.length, u->request2.data); |
| |
| case FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED: |
| case FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED: |
| memcpy(u64_to_ptr(u->iso_resource.closure), u, |
| sizeof u->iso_resource); |
| return 0; |
| |
| case FW_CDEV_EVENT_PHY_PACKET_SENT: |
| rc = u64_to_ptr(u->phy_packet.closure); |
| errcode = fw_to_raw1394_errcode(u->phy_packet.rcode); |
| tag = rc->tag; |
| free(rc); |
| |
| return fwhandle->tag_handler(handle, tag, errcode); |
| |
| default: |
| case FW_CDEV_EVENT_ISO_INTERRUPT: |
| /* Never happens. */ |
| return -1; |
| } |
| } |
| |
| static int |
| process_inotify_event(fw_handle_t fwhandle, 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, fd, phy_id, fname_str_sz; |
| |
| if (!(event->mask & IN_CREATE)) |
| return 0; |
| if (!is_fw_device_name(event->name)) |
| 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 = IMPLEMENTED_CDEV_ABI_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; |
| fname_str_sz = sizeof(fwhandle->devices[i].filename) - 1; |
| strncpy(fwhandle->devices[i].filename, filename, fname_str_sz); |
| fwhandle->devices[i].filename[fname_str_sz] = '\0'; |
| 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); |
| fwhandle->devices[i].fd = -1; |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| 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; |
| ssize_t len; |
| int retval = 0; |
| |
| event = (struct inotify_event *) fwhandle->buffer; |
| len = read(fwhandle->inotify_fd, event, BUFFER_SIZE); |
| |
| while (len >= sizeof(struct inotify_event)) { |
| retval |= process_inotify_event(fwhandle, event); |
| len -= sizeof(struct inotify_event) + event->len; |
| event = (struct inotify_event *) ((char *)event + |
| (sizeof(struct inotify_event) + event->len)); |
| } |
| |
| return retval; |
| } |
| |
| 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); |
| if (handle == NULL) { |
| errno = ENOMEM; |
| return NULL; |
| } |
| |
| memset(&ep, 0, sizeof(ep)); |
| memset(handle, 0, 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); |
| } |
| fw_iso_shutdown(handle); |
| 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) { |
| fw_destroy_handle(handle); |
| 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_speed(fw_handle_t handle, nodeid_t node) |
| { |
| int i; |
| |
| if ((node & ~0x3f) != 0xffc0) { |
| errno = ENOSYS; |
| return -1; |
| } |
| |
| if (node > handle->reset.root_node_id) { |
| errno = fw_errcode_to_errno(-RCODE_NO_ACK); |
| return -1; |
| } |
| |
| i = handle->nodes[node & 0x3f]; |
| if (i == -1) { |
| errno = fw_errcode_to_errno(-RCODE_NO_ACK); |
| return -1; |
| } |
| |
| if (handle->generation != handle->devices[i].generation) { |
| errno = fw_errcode_to_errno(-RCODE_GENERATION); |
| return -1; |
| } |
| |
| return ioctl(handle->devices[i].fd, FW_CDEV_IOC_GET_SPEED); |
| } |
| |
| int fw_get_port_info(fw_handle_t handle, |
| struct raw1394_portinfo *pinf, |
| int maxports) |
| { |
| int i, port_name_sz; |
| |
| if (maxports >= handle->port_count) |
| maxports = handle->port_count; |
| |
| for (i = 0; i < maxports; i++) { |
| pinf[i].nodes = handle->ports[i].node_count; |
| port_name_sz = sizeof(pinf[i].name) - 1; |
| strncpy(pinf[i].name, handle->ports[i].device_file, |
| port_name_sz); |
| pinf[i].name[port_name_sz] = '\0'; |
| } |
| |
| 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, fname_str_sz; |
| |
| 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; |
| |
| memset(&get_info, 0, sizeof(get_info)); |
| memset(&reset, 0, sizeof(reset)); |
| get_info.version = IMPLEMENTED_CDEV_ABI_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; |
| fname_str_sz = sizeof(handle->devices[i].filename) -1; |
| strncpy(handle->devices[i].filename, filename, fname_str_sz); |
| handle->devices[i].filename[fname_str_sz] = '\0'; |
| |
| handle->devices[i].closure.func = handle_device_event; |
| memset(&ep, 0, sizeof(ep)); |
| ep.events = EPOLLIN; |
| ep.data.ptr = &handle->devices[i].closure; |
| if (epoll_ctl(handle->epoll_fd, EPOLL_CTL_ADD, fd, &ep) < 0) { |
| close(fd); |
| closedir(dir); |
| return -1; |
| } |
| |
| if (handle->iso.filename == NULL) { |
| memcpy(&handle->reset, &reset, sizeof handle->reset); |
| handle->iso.filename = handle->devices[i].filename; |
| handle->ioctl_fd = fd; |
| } |
| |
| if (reset.node_id == reset.local_node_id) |
| handle->local_device = &handle->devices[i]; |
| |
| handle->card = get_info.card; |
| handle->generation = reset.generation; |
| handle->abi_version = get_info.version; |
| |
| i++; |
| } |
| |
| closedir(dir); |
| |
| if (i == 0) { |
| errno = ENODEV; |
| return -1; |
| } |
| |
| 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->ioctl_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; /* must be first member */ |
| 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, |
| int tcode, unsigned long long offset, int source_node_id, |
| int card, unsigned kernel_handle, size_t length, void *data) |
| { |
| 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 pos, retval; |
| |
| pos = offset - allocation->offset; |
| response.handle = kernel_handle; |
| |
| switch (tcode) { |
| case TCODE_WRITE_QUADLET_REQUEST: |
| case TCODE_WRITE_BLOCK_REQUEST: |
| type = RAW1394_ARM_WRITE; |
| in_length = length; |
| response.rcode = RCODE_COMPLETE; |
| response.length = 0; |
| response.data = 0; |
| break; |
| |
| case TCODE_READ_QUADLET_REQUEST: |
| case TCODE_READ_BLOCK_REQUEST: |
| type = RAW1394_ARM_READ; |
| in_length = 0; |
| response.rcode = RCODE_COMPLETE; |
| response.length = length; |
| response.data = ptr_to_u64(allocation->data + pos); |
| break; |
| |
| case TCODE_LOCK_REQUEST: |
| /* |
| * TCODE_LOCK_REQUEST is generated by ABI v3 and older, cannot |
| * be handled. Fall through for now. FIXME. |
| */ |
| case TCODE_LOCK_MASK_SWAP: |
| case TCODE_LOCK_COMPARE_SWAP: |
| case TCODE_LOCK_FETCH_ADD: |
| case TCODE_LOCK_LITTLE_ADD: |
| case TCODE_LOCK_BOUNDED_ADD: |
| case TCODE_LOCK_WRAP_ADD: |
| case TCODE_LOCK_VENDOR_DEPENDENT: |
| type = RAW1394_ARM_LOCK; |
| in_length = 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->ioctl_fd, |
| FW_CDEV_IOC_SEND_RESPONSE, &response) < 0) |
| return -1; |
| } else if (!(allocation->client_transactions & type)) { |
| if (type == RAW1394_ARM_WRITE) |
| memcpy(allocation->data + pos, data, length); |
| else if (type == RAW1394_ARM_LOCK) |
| /* FIXME: do lock ops here */; |
| |
| if (ioctl(fwhandle->ioctl_fd, |
| FW_CDEV_IOC_SEND_RESPONSE, &response) < 0) |
| return -1; |
| } |
| |
| /* |
| * libraw1394 clients do not expect requests from nodes on |
| * a card other than the one set by raw1394_set_port(). |
| */ |
| if (card != fwhandle->card) |
| return 0; |
| |
| if (!(allocation->notification_options & type)) |
| return 0; |
| |
| rrb = malloc(sizeof *rrb + in_length + response.length); |
| if (rrb == NULL) { |
| errno = ENOMEM; |
| return -1; |
| } |
| |
| 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 = source_node_id; |
| rrb->request.destination_offset = offset; |
| rrb->request.tlabel = 0; |
| if (tcode < 0x10) { |
| rrb->request.tcode = tcode; |
| rrb->request.extended_transaction_code = 0; |
| } else { |
| rrb->request.tcode = TCODE_LOCK_REQUEST; |
| rrb->request.extended_transaction_code = tcode - 0x10; |
| } |
| rrb->request.generation = fwhandle->reset.generation; |
| rrb->request.buffer_length = in_length; |
| rrb->request.buffer = rrb->data; |
| memcpy(rrb->request.buffer, data, in_length); |
| |
| rrb->response.response_code = response.rcode; |
| rrb->response.buffer_length = response.length; |
| rrb->response.buffer = rrb->data + in_length; |
| memcpy(rrb->response.buffer, allocation->data + pos, response.length); |
| |
| retval = fwhandle->arm_tag_handler(handle, allocation->tag, type, |
| length, &rrb->request_response); |
| free(rrb); |
| return retval; |
| } |
| |
| 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) { |
| errno = ENOMEM; |
| 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); |
| request.region_end = start + length; |
| |
| retval = ioctl(handle->ioctl_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->ioctl_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); |
| } |
| |
| static int |
| send_request(fw_handle_t handle, int tcode, |
| nodeid_t node, nodeaddr_t addr, |
| size_t in_length, void *in, size_t out_length, void *out, |
| unsigned long tag) |
| { |
| struct fw_cdev_send_request *request; |
| struct request_closure *closure; |
| int ioctl_nr = FW_CDEV_IOC_SEND_REQUEST; |
| int fd, i, retval; |
| |
| if (tcode == TCODE_STREAM_DATA) { |
| ioctl_nr = FW_CDEV_IOC_SEND_STREAM_PACKET; |
| fd = handle->ioctl_fd; |
| } |
| |
| if (node == 0xffff) { |
| ioctl_nr = FW_CDEV_IOC_SEND_BROADCAST_REQUEST; |
| fd = handle->ioctl_fd; |
| } |
| |
| if (ioctl_nr != FW_CDEV_IOC_SEND_REQUEST) |
| goto node_id_ok; |
| |
| 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; |
| } |
| fd = handle->devices[i].fd; |
| |
| node_id_ok: |
| |
| 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->tag = tag; |
| |
| request = (struct fw_cdev_send_request *) handle->buffer; |
| request->tcode = tcode; |
| request->length = in_length > out_length ? in_length : out_length; |
| request->offset = addr; |
| request->closure = ptr_to_u64(closure); |
| request->data = ptr_to_u64(in); |
| request->generation = handle->generation; |
| |
| if (tcode == TCODE_STREAM_DATA) { |
| struct fw_cdev_send_stream_packet *p |
| = (struct fw_cdev_send_stream_packet *) request; |
| |
| p->length = in_length; |
| p->tag = (addr >> 14) & 0x3; |
| p->channel = (addr >> 8) & 0x3f; |
| p->sy = addr & 0xf; |
| p->closure = ptr_to_u64(closure); |
| p->data = ptr_to_u64(in); |
| p->generation = handle->generation; |
| p->speed = (addr >> 4) & 0x7; |
| } |
| |
| retval = ioctl(fd, ioctl_nr, request); |
| if (retval < 0) |
| free(closure); |
| |
| return retval; |
| } |
| |
| 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, 0, NULL, length, 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, 0, 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, |
| sizeof *result, 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, |
| sizeof *result, 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) |
| { |
| nodeaddr_t addr = (tag & 0x3) << 14 | |
| (channel & 0x3f) << 8 | |
| (speed & 0x7) << 4 | |
| (sy & 0xf); |
| |
| return send_request(handle, TCODE_STREAM_DATA, |
| 0, addr, length, data, 0, NULL, rawtag); |
| } |
| |
| int |
| fw_start_phy_packet_write(fw_handle_t handle, quadlet_t data, unsigned long tag) |
| { |
| struct fw_cdev_send_phy_packet send_phy_packet; |
| struct request_closure *closure; |
| int retval; |
| |
| if (handle->local_device == NULL) { |
| handle->err = -EPERM; |
| errno = EPERM; |
| 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 = NULL; |
| closure->tag = tag; |
| |
| send_phy_packet.closure = ptr_to_u64(closure); |
| send_phy_packet.data[0] = be32_to_cpu(data); |
| send_phy_packet.data[1] = ~be32_to_cpu(data); |
| send_phy_packet.generation = handle->local_device->generation; |
| retval = ioctl(handle->local_device->fd, FW_CDEV_IOC_SEND_PHY_PACKET, |
| &send_phy_packet); |
| if (retval < 0) |
| free(closure); |
| |
| return retval; |
| } |
| |
| 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? */ |
| errno = ENOSYS; |
| 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 in_length, void *in, size_t out_length, 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, |
| in_length, in, out_length, 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); |
| } |
| |
| static int |
| send_request_sync(raw1394handle_t handle, int tcode, |
| nodeid_t node, nodeaddr_t addr, |
| size_t in_length, void *in, size_t out_length, void *out) |
| { |
| static const struct timespec delay = { |
| .tv_sec = 0, |
| .tv_nsec = 10 * 1000 * 1000, |
| }; |
| fw_handle_t fwhandle = handle->mode.fw; |
| int i, ret; |
| |
| /* |
| * Retry after ack-busy, but give the responder some breathing room. |
| * 10 ms delay between retries is proper for ancient camcorders. |
| */ |
| for (i = 1; ; i++) { |
| ret = _send_request_sync(handle, tcode, node, addr, |
| in_length, in, out_length, out); |
| if (ret == 0 || i == 10 || fwhandle->err != -RCODE_BUSY) |
| break; |
| nanosleep(&delay, NULL); |
| } |
| |
| return ret; |
| } |
| |
| static int |
| read_config_rom(fw_handle_t handle, nodeid_t node, nodeaddr_t addr, |
| size_t length, quadlet_t *buffer) |
| { |
| struct fw_cdev_get_info get_info; |
| quadlet_t rom[256]; |
| int offset, fd, i, err; |
| |
| 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; |
| } |
| fd = handle->devices[i].fd; |
| |
| memset(&get_info, 0, sizeof(get_info)); |
| memset(&rom, 0, sizeof(rom)); |
| get_info.version = IMPLEMENTED_CDEV_ABI_VERSION; |
| get_info.rom = ptr_to_u64(rom); |
| get_info.rom_length = sizeof(rom); |
| get_info.bus_reset = 0; |
| |
| err = ioctl(fd, FW_CDEV_IOC_GET_INFO, &get_info); |
| if (err) |
| return err; |
| |
| offset = (addr - CSR_REGISTER_BASE - CSR_CONFIG_ROM) / 4; |
| for (i = 0; i < length / 4; i++) |
| buffer[i] = cpu_to_be32(rom[offset + i]); |
| |
| return 0; |
| } |
| |
| int |
| fw_read(raw1394handle_t handle, nodeid_t node, nodeaddr_t addr, |
| size_t length, quadlet_t *buffer) |
| { |
| int tcode; |
| |
| if (addr >= CSR_REGISTER_BASE + CSR_CONFIG_ROM && |
| addr + length <= CSR_REGISTER_BASE + CSR_CONFIG_ROM_END && |
| !(addr & 3) && length > 0 && !(length & 3)) |
| return read_config_rom(handle->mode.fw, |
| node, addr, length, buffer); |
| |
| if (length == 4) |
| tcode = TCODE_READ_QUADLET_REQUEST; |
| else |
| tcode = TCODE_READ_BLOCK_REQUEST; |
| |
| return send_request_sync(handle, tcode, |
| node, addr, 0, NULL, length, 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, 0, 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]; |
| ssize_t length; |
| |
| length = setup_lock(extcode, data, arg, buffer); |
| if (length < 0) |
| return length; |
| |
| return send_request_sync(handle, 16 + extcode, node, addr, |
| (size_t) length, buffer, |
| sizeof *result, 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]; |
| ssize_t length; |
| |
| length = setup_lock64(extcode, data, arg, buffer); |
| if (length < 0) |
| return length; |
| |
| return send_request_sync(handle, 16 + extcode, node, addr, |
| (size_t) length, buffer, |
| sizeof *result, result); |
| } |
| |
| int |
| fw_async_stream(raw1394handle_t handle, unsigned int channel, |
| unsigned int tag, unsigned int sy, unsigned int speed, |
| size_t length, quadlet_t *data) |
| { |
| nodeaddr_t addr = (tag & 0x3) << 14 | |
| (channel & 0x3f) << 8 | |
| (speed & 0x7) << 4 | |
| (sy & 0xf); |
| |
| return send_request_sync(handle, TCODE_STREAM_DATA, |
| 0, addr, length, data, 0, NULL); |
| } |
| |
| int |
| fw_phy_packet_write(raw1394handle_t handle, quadlet_t data) |
| { |
| 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 = fw_start_phy_packet_write(fwhandle, data, |
| (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_async_send(fw_handle_t handle, |
| size_t length, size_t header_length, |
| unsigned int expect_response, |
| quadlet_t *data) |
| { |
| /* FIXME: implement this? */ |
| errno = ENOSYS; |
| 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) { |
| errno = ENOMEM; |
| return -1; |
| } |
| |
| closure->callback = handle_fcp_request; |
| |
| memset(&request, 0, sizeof(request)); |
| request.offset = CSR_REGISTER_BASE + CSR_FCP_COMMAND; |
| request.length = CSR_FCP_END - CSR_FCP_COMMAND; |
| request.closure = ptr_to_u64(closure); |
| request.region_end = CSR_REGISTER_BASE + CSR_FCP_END; |
| |
| if (ioctl(handle->ioctl_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->ioctl_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) |
| { |
| errno = ENOSYS; |
| return -1; |
| } |
| |
| int |
| fw_add_config_rom_descriptor(fw_handle_t handle, u_int32_t *token, |
| quadlet_t immediate_key, quadlet_t key, |
| const quadlet_t *data, size_t size) |
| { |
| struct fw_cdev_add_descriptor request; |
| int err; |
| |
| request.immediate = immediate_key; |
| request.key = key; |
| request.data = ptr_to_u64(data); |
| request.length = size / 4; |
| request.handle = 0; |
| |
| err = ioctl(handle->local_device->fd, FW_CDEV_IOC_ADD_DESCRIPTOR, |
| &request); |
| if (err) |
| return err; |
| |
| if (token) |
| *token = request.handle; |
| |
| return 0; |
| } |
| |
| int |
| fw_remove_config_rom_descriptor(fw_handle_t handle, u_int32_t token) |
| { |
| struct fw_cdev_remove_descriptor request = {.handle = token}; |
| |
| return ioctl(handle->local_device->fd, FW_CDEV_IOC_REMOVE_DESCRIPTOR, |
| &request); |
| } |
| |
| 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; |
| |
| if (handle->local_device == NULL) { |
| errno = EPERM; |
| return -1; |
| } |
| |
| memset(&get_info, 0, sizeof(get_info)); |
| get_info.version = IMPLEMENTED_CDEV_ABI_VERSION; |
| get_info.rom = ptr_to_u64(buffer); |
| get_info.rom_length = buffersize; |
| get_info.bus_reset = 0; |
| |
| err = ioctl(handle->local_device->fd, FW_CDEV_IOC_GET_INFO, &get_info); |
| if (err) |
| return err; |
| |
| *rom_size = get_info.rom_length; |
| *rom_version = 0; |
| |
| return 0; |
| } |
| |
| static int |
| iso_resource_modify(raw1394handle_t handle, unsigned int bandwidth, |
| int channel, enum raw1394_modify_mode mode) |
| { |
| fw_handle_t fwhandle = handle->mode.fw; |
| struct fw_cdev_allocate_iso_resource resource; |
| struct fw_cdev_event_iso_resource event; |
| int ioctl_nr; |
| int err; |
| |
| if (channel > 63) { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| event.closure = 0; |
| event.channel = -1; |
| event.bandwidth = 0; |
| |
| resource.closure = ptr_to_u64(&event); |
| resource.channels = channel >= 0 ? 1ULL << channel : 0; |
| resource.bandwidth = bandwidth; |
| resource.handle = 0; /* unused */ |
| |
| ioctl_nr = mode == RAW1394_MODIFY_ALLOC ? |
| FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE : |
| FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE; |
| |
| err = ioctl(fwhandle->ioctl_fd, ioctl_nr, &resource); |
| |
| while (err >= 0 && event.closure != resource.closure) |
| err = fw_loop_iterate(handle); |
| |
| if (err < 0) |
| return err; |
| |
| if ((channel >= 0 && event.channel < 0) || |
| (bandwidth > 0 && event.bandwidth == 0)) { |
| errno = EIO; |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int |
| fw_bandwidth_modify(raw1394handle_t handle, unsigned int bandwidth, |
| enum raw1394_modify_mode mode) |
| { |
| if (handle->mode.fw->abi_version >= 2) |
| return iso_resource_modify(handle, bandwidth, -1, mode); |
| else |
| return ieee1394_bandwidth_modify(handle, bandwidth, mode); |
| } |
| |
| int |
| fw_channel_modify(raw1394handle_t handle, unsigned int channel, |
| enum raw1394_modify_mode mode) |
| { |
| if (handle->mode.fw->abi_version >= 2) |
| return iso_resource_modify(handle, 0, channel, mode); |
| else |
| return ieee1394_channel_modify(handle, channel, mode); |
| } |