/* -*- c-basic-offset: 8 -*- * * raw1394.c -- Emulation of the raw1394 API on the fw stack * * Copyright (C) 2007 Kristian Hoegsberg * * 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 #include #include #include #include #include #include #include #include #include #include /* for ntohl and htonl */ #include "fw.h" #include "raw1394_private.h" int fw_errcode_to_errno(raw1394_errcode_t errcode) { switch (errcode) { case -RCODE_SEND_ERROR: case -RCODE_CANCELLED: case -RCODE_BUSY: case -RCODE_GENERATION: case -RCODE_NO_ACK: return EAGAIN; case raw1394_make_errcode(ACK_COMPLETE, RCODE_COMPLETE): return 0; case raw1394_make_errcode(ACK_COMPLETE, RCODE_CONFLICT_ERROR): return EAGAIN; case raw1394_make_errcode(ACK_COMPLETE, RCODE_DATA_ERROR): return EREMOTEIO; case raw1394_make_errcode(ACK_COMPLETE, RCODE_TYPE_ERROR): return EPERM; case raw1394_make_errcode(ACK_COMPLETE, RCODE_ADDRESS_ERROR): return EINVAL; default: return EINVAL; } } static int fw_to_raw1394_errcode(int rcode) { /* Best effort matching fw extended rcodes to raw1394 err * code. Since the raw1394 errcode decoding are macros we try * to convert the fw rcodes to something that looks enough * like the raw1394 errcodes that we retain ABI compatibility. * * Juju rcodes less than 0x10 are standard ieee1394 rcodes, * which we map to a raw1394 errcode by or'ing in an * ACK_COMPLETE ack code in the upper 16 bits. Errors * internal to raw1394 are negative values, but fw encodes * these errors as rcodes greater than or equal to 0x10. In * this case, we just the negated value, which will look like * an raw1394 internal error code. */ if (rcode < 0x10) return raw1394_make_errcode(ACK_COMPLETE, rcode); else return -rcode; } static int default_tag_handler(raw1394handle_t handle, unsigned long tag, raw1394_errcode_t err) { struct raw1394_reqhandle *rh = (struct raw1394_reqhandle *) tag; if (rh != NULL) return rh->callback(handle, rh->data, err); return -1; } static int default_arm_tag_handler(raw1394handle_t handle, unsigned long arm_tag, byte_t type, unsigned int length, void *data) { struct raw1394_arm_reqhandle *rh; if (arm_tag == 0) return -1; rh = (struct raw1394_arm_reqhandle *) arm_tag; return rh->arm_callback(handle, data, length, rh->pcontext, type); } static int default_bus_reset_handler(raw1394handle_t handle, unsigned int gen) { handle->mode.fw->generation = gen; return 0; } static int scan_devices(fw_handle_t handle) { DIR *dir; struct dirent *de; char filename[32]; struct fw_cdev_get_info get_info; struct fw_cdev_event_bus_reset reset; int fd, err, i, 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 (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; memset(&get_info, 0, sizeof(get_info)); memset(&reset, 0, sizeof(reset)); 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; 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, struct fw_cdev_event_request *request, int i); }; static int handle_fcp_request(raw1394handle_t handle, struct address_closure *ac, struct fw_cdev_event_request *request, int i) { struct fw_cdev_send_response response; int is_response; response.handle = request->handle; response.rcode = RCODE_COMPLETE; response.length = 0; response.data = 0; if (handle->mode.fw->fcp_handler == NULL) response.rcode = RCODE_ADDRESS_ERROR; if (request->tcode >= TCODE_WRITE_RESPONSE) response.rcode = RCODE_CONFLICT_ERROR; if (ioctl(handle->mode.fw->devices[i].fd, FW_CDEV_IOC_SEND_RESPONSE, &response) < 0) return -1; if (response.rcode != RCODE_COMPLETE) return 0; is_response = request->offset >= CSR_REGISTER_BASE + CSR_FCP_RESPONSE; return handle->mode.fw->fcp_handler(handle, handle->mode.fw->devices[i].node_id, is_response, request->length, (unsigned char *) request->data); } static int handle_device_event(raw1394handle_t handle, struct epoll_closure *ec, __uint32_t events) { fw_handle_t fwhandle = handle->mode.fw; union fw_cdev_event *u; struct device *device = (struct device *) ec; struct address_closure *ac; struct request_closure *rc; 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); if (rc->data != NULL) memcpy(rc->data, u->response.data, rc->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, i); #ifdef FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED /* added in kernel 2.6.30 */ 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; #endif default: case FW_CDEV_EVENT_ISO_INTERRUPT: /* Never happens. */ return -1; } } static int handle_inotify(raw1394handle_t handle, struct epoll_closure *ec, __uint32_t events) { fw_handle_t fwhandle = handle->mode.fw; struct inotify_event *event; char filename[32]; struct fw_cdev_get_info info; struct fw_cdev_event_bus_reset reset; struct epoll_event ep; int i, len, fd, phy_id, fname_str_sz; event = (struct inotify_event *) fwhandle->buffer; len = read(fwhandle->inotify_fd, event, BUFFER_SIZE); if (!(event->mask & IN_CREATE)) return -1; if (strncmp(event->name, FW_DEVICE_PREFIX, strlen(FW_DEVICE_PREFIX)) != 0) return 0; snprintf(filename, sizeof filename, FW_DEVICE_DIR "/%s", event->name); fd = open(filename, O_RDWR); if (fd < 0) { switch (errno) { case ENOENT: /* Huh, it disappeared before we could * open it. */ return 0; case EACCES: /* We don't have permission to talk to * this device, maybe it's a storage * device. */ return 0; default: /* Anything else is bad news. */ return -1; } } info.version = FW_CDEV_VERSION; info.rom = 0; info.rom_length = 0; info.bus_reset = ptr_to_u64(&reset); if (ioctl(fd, FW_CDEV_IOC_GET_INFO, &info) < 0) { close(fd); return -1; } for (i = 0; i < MAX_DEVICES; i++) if (fwhandle->devices[i].node_id == -1) break; if (i == MAX_DEVICES) { close(fd); return -1; } phy_id = reset.node_id & 0x3f; fwhandle->nodes[phy_id] = i; fwhandle->devices[i].node_id = reset.node_id; fwhandle->devices[i].generation = reset.generation; fwhandle->devices[i].fd = fd; 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); return -1; } return 0; } int fw_loop_iterate(raw1394handle_t handle) { int i, count, retval = 0; struct epoll_closure *closure; struct epoll_event ep[32]; count = epoll_wait(handle->mode.fw->epoll_fd, ep, ARRAY_LENGTH(ep), -1); if (count < 0) return -1; for (i = 0; i < count; i++) { closure = ep[i].data.ptr; retval = closure->func(handle, closure, ep[i].events); } /* It looks like we have to add this work-around to get epoll * to recompute the POLLIN status of the epoll_fd. */ epoll_wait(handle->mode.fw->epoll_fd, ep, ARRAY_LENGTH(ep), 0); return retval; } fw_handle_t fw_new_handle(void) { fw_handle_t handle; struct epoll_event ep; int i; memset(&ep, 0, sizeof(ep)); handle = malloc(sizeof *handle); 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); } 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_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 = 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; 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->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; struct allocation *next; __u32 handle; byte_t *buffer; octlet_t tag; arm_options_t access_rights; arm_options_t notification_options; arm_options_t client_transactions; nodeaddr_t offset; size_t length; unsigned char data[0]; }; static int handle_arm_request(raw1394handle_t handle, struct address_closure *ac, struct fw_cdev_event_request *request, int i) { fw_handle_t fwhandle = handle->mode.fw; struct allocation *allocation = (struct allocation *) ac; struct request_response_block *rrb; struct fw_cdev_send_response response; arm_options_t type; size_t in_length; int offset; offset = request->offset - allocation->offset; response.handle = request->handle; switch (request->tcode) { case TCODE_WRITE_QUADLET_REQUEST: case TCODE_WRITE_BLOCK_REQUEST: printf("got write request, offset=0x%012llx, length=%d\n", request->offset, request->length); type = RAW1394_ARM_WRITE; in_length = request->length; response.rcode = RCODE_COMPLETE; response.length = 0; response.data = 0; break; case TCODE_READ_QUADLET_REQUEST: case TCODE_READ_BLOCK_REQUEST: printf("got read request, offset=0x%012llx, length=%d\n", request->offset, request->length); type = RAW1394_ARM_READ; in_length = 0; response.rcode = RCODE_COMPLETE; response.length = request->length; response.data = ptr_to_u64(allocation->data + offset); break; case TCODE_LOCK_REQUEST: type = RAW1394_ARM_LOCK; in_length = request->length; response.length = 4; break; default: in_length = 0; type = 0; break; } if (!(allocation->access_rights & type)) { response.rcode = RCODE_TYPE_ERROR; response.length = 0; response.data = 0; if (ioctl(fwhandle->devices[i].fd, FW_CDEV_IOC_SEND_RESPONSE, &response) < 0) return -1; } else if (!(allocation->client_transactions & type)) { if (type == RAW1394_ARM_WRITE) memcpy(allocation->data + offset, request->data, request->length); else if (type == RAW1394_ARM_LOCK) /* FIXME: do lock ops here */; if (ioctl(fwhandle->devices[i].fd, FW_CDEV_IOC_SEND_RESPONSE, &response) < 0) return -1; } if (!(allocation->notification_options & type)) return 0; rrb = malloc(sizeof *rrb + in_length + response.length); rrb->request_response.request = &rrb->request; rrb->request_response.response = &rrb->response; rrb->request.destination_nodeid = fwhandle->reset.local_node_id; rrb->request.source_nodeid = fwhandle->devices[i].node_id; rrb->request.destination_offset = request->offset; rrb->request.tlabel = 0; if (request->tcode < 0x10) { rrb->request.tcode = request->tcode; rrb->request.extended_transaction_code = 0; } else { rrb->request.tcode = TCODE_LOCK_REQUEST; rrb->request.extended_transaction_code = request->tcode - 0x10; } rrb->request.generation = fwhandle->reset.generation; rrb->request.buffer_length = in_length; rrb->request.buffer = rrb->data; memcpy(rrb->request.buffer, request->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 + offset, response.length); return fwhandle->arm_tag_handler(handle, allocation->tag, type, request->length, &rrb->request_response); } int fw_arm_register(fw_handle_t handle, nodeaddr_t start, size_t length, byte_t *initial_value, octlet_t arm_tag, arm_options_t access_rights, arm_options_t notification_options, arm_options_t client_transactions) { struct fw_cdev_allocate request; struct allocation *allocation; int retval; allocation = malloc(sizeof *allocation + length); if (allocation == NULL) return -1; allocation->closure.callback = handle_arm_request; allocation->buffer = initial_value; allocation->tag = arm_tag; allocation->access_rights = access_rights; allocation->notification_options = notification_options; allocation->client_transactions = client_transactions; allocation->offset = start; allocation->length = length; if (initial_value != NULL) memcpy(allocation->data, initial_value, length); request.offset = start; request.length = length; request.closure = ptr_to_u64(&allocation->closure); retval = ioctl(handle->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); } int fw_phy_packet_write (fw_handle_t handle, quadlet_t data) { errno = ENOSYS; return -1; } int fw_start_phy_packet_write(fw_handle_t handle, quadlet_t data, unsigned long tag) { errno = ENOSYS; return -1; } static int send_request(fw_handle_t handle, int tcode, nodeid_t node, nodeaddr_t addr, size_t 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; #ifdef FW_CDEV_IOC_SEND_STREAM_PACKET /* added in kernel 2.6.30 */ if (tcode == TCODE_STREAM_DATA) { ioctl_nr = FW_CDEV_IOC_SEND_STREAM_PACKET; fd = handle->ioctl_fd; } #else if (tcode == TCODE_STREAM_DATA) { errno = ENOSYS; return -1; } #endif #ifdef FW_CDEV_IOC_SEND_BROADCAST_REQUEST /* added in kernel 2.6.30 */ if (node == 0xffff) { ioctl_nr = FW_CDEV_IOC_SEND_BROADCAST_REQUEST; fd = handle->ioctl_fd; } #endif 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->length = out_length; 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; #ifdef FW_CDEV_IOC_SEND_STREAM_PACKET 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; } #endif 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_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); } int fw_read(raw1394handle_t handle, nodeid_t node, nodeaddr_t addr, size_t length, quadlet_t *buffer) { int tcode; if (length == 4) tcode = TCODE_READ_QUADLET_REQUEST; else tcode = TCODE_READ_BLOCK_REQUEST; return send_request_sync(handle, tcode, node, addr, 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_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) 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); 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_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 = FW_CDEV_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; } #ifdef FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE /* added in kernel 2.6.30 */ 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; 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; } static inline int abi_v2_available(raw1394handle_t handle) { return handle->mode.fw->abi_version >= 2; } #else static inline int iso_resource_modify(raw1394handle_t handle, unsigned int bandwidth, int channel, enum raw1394_modify_mode mode) { return -1; } static inline int abi_v2_available(raw1394handle_t handle) { return 0; } #endif /* defined(FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE) */ int fw_bandwidth_modify(raw1394handle_t handle, unsigned int bandwidth, enum raw1394_modify_mode mode) { if (abi_v2_available(handle)) 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 (abi_v2_available(handle)) return iso_resource_modify(handle, 0, channel, mode); else return ieee1394_channel_modify(handle, channel, mode); }