Fix FCP and ARM source node ID on firewire-core

The firewire-core (juju) backend of libraw1394 installs address range
mappings on the default ioctl fd, i.e. a file that represents a random
device on the chosen port.  It receives incoming requests from any
sender node via this address range mapping.  Due to a kernel ABI
limitation, the sender node ID is not known though.  So far libraw1394
simply assumed the node ID of the device that provided the default
ioctl fd.  This only works if there is only one accessible fd on the
entire bus.

This limitation caused for example libffado to fail to work with
another AV/C or IIDC device attached to the bus, because node IDs of
FCP requests and FCP responses did not match since the latter were
wrong.  FCP clients which did not check sender node IDs were seemingly
not affected by this bug.  The bug is fixed by a kernel ABI extension
in Linux 2.6.36.  This libraw1394 change implements libraw1394's
counterpart to this ABI extension.

Hence this libraw1394 fix requires
  - kernel-headers 2.6.36 or later at build time of libraw1394
  - kernel 2.6.36 or later at runtime.
Otherwise, libraw1394 simply degrades to the faulty previous behaviour.

Side note:  The change of IMPLEMENTED_CDEV_ABI_VERSION to 4 requires
that we fill in struct fw_cdev_allocate.region_end which was added in
the ABI v4.

Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
This commit is contained in:
Stefan Richter 2010-08-05 11:50:32 +02:00
parent 3fc1f00be3
commit d3ace3dfb4
1 changed files with 75 additions and 42 deletions

111
src/fw.c
View File

@ -28,7 +28,11 @@
/* /*
* ABI version history is documented in linux/firewire-cdev.h. * ABI version history is documented in linux/firewire-cdev.h.
*/ */
#ifdef FW_CDEV_EVENT_REQUEST2
#define IMPLEMENTED_CDEV_ABI_VERSION 4
#else
#define IMPLEMENTED_CDEV_ABI_VERSION 2 #define IMPLEMENTED_CDEV_ABI_VERSION 2
#endif
int int
fw_errcode_to_errno(raw1394_errcode_t errcode) fw_errcode_to_errno(raw1394_errcode_t errcode)
@ -215,17 +219,20 @@ handle_lost_device(fw_handle_t handle, int i)
struct address_closure { struct address_closure {
int (*callback)(raw1394handle_t handle, struct address_closure *ac, int (*callback)(raw1394handle_t handle, struct address_closure *ac,
struct fw_cdev_event_request *request, int i); int tcode, unsigned long long offset,
int source_node_id, unsigned kernel_handle,
size_t length, void *data);
}; };
static int static int
handle_fcp_request(raw1394handle_t handle, struct address_closure *ac, handle_fcp_request(raw1394handle_t handle, struct address_closure *ac,
struct fw_cdev_event_request *request, int i) int tcode, unsigned long long offset, int source_node_id,
unsigned kernel_handle, size_t length, void *data)
{ {
struct fw_cdev_send_response response; struct fw_cdev_send_response response;
int is_response; int is_response;
response.handle = request->handle; response.handle = kernel_handle;
response.rcode = RCODE_COMPLETE; response.rcode = RCODE_COMPLETE;
response.length = 0; response.length = 0;
response.data = 0; response.data = 0;
@ -233,23 +240,20 @@ handle_fcp_request(raw1394handle_t handle, struct address_closure *ac,
if (handle->mode.fw->fcp_handler == NULL) if (handle->mode.fw->fcp_handler == NULL)
response.rcode = RCODE_ADDRESS_ERROR; response.rcode = RCODE_ADDRESS_ERROR;
if (request->tcode >= TCODE_WRITE_RESPONSE) if (tcode >= TCODE_WRITE_RESPONSE)
response.rcode = RCODE_CONFLICT_ERROR; response.rcode = RCODE_CONFLICT_ERROR;
if (ioctl(handle->mode.fw->devices[i].fd, if (ioctl(handle->mode.fw->ioctl_fd,
FW_CDEV_IOC_SEND_RESPONSE, &response) < 0) FW_CDEV_IOC_SEND_RESPONSE, &response) < 0)
return -1; return -1;
if (response.rcode != RCODE_COMPLETE) if (response.rcode != RCODE_COMPLETE)
return 0; return 0;
is_response = request->offset >= CSR_REGISTER_BASE + CSR_FCP_RESPONSE; is_response = offset >= CSR_REGISTER_BASE + CSR_FCP_RESPONSE;
return handle->mode.fw->fcp_handler(handle, return handle->mode.fw->fcp_handler(handle, source_node_id,
handle->mode.fw->devices[i].node_id, is_response, length, data);
is_response,
request->length,
(unsigned char *) request->data);
} }
static int static int
@ -307,7 +311,22 @@ handle_device_event(raw1394handle_t handle,
case FW_CDEV_EVENT_REQUEST: case FW_CDEV_EVENT_REQUEST:
ac = u64_to_ptr(u->request.closure); ac = u64_to_ptr(u->request.closure);
return ac->callback(handle, ac, &u->request, i); return ac->callback(handle, ac, u->request.tcode,
u->request.offset,
/* wild guess, but can't do better */
fwhandle->devices[i].node_id,
u->request.handle,
u->request.length, u->request.data);
#ifdef FW_CDEV_EVENT_REQUEST2 /* added in kernel 2.6.36 */
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.handle,
u->request2.length, u->request2.data);
#endif
#ifdef FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED /* added in kernel 2.6.30 */ #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_ALLOCATED:
@ -695,7 +714,7 @@ struct request_response_block {
}; };
struct allocation { struct allocation {
struct address_closure closure; struct address_closure closure; /* must be first member */
struct allocation *next; struct allocation *next;
__u32 handle; __u32 handle;
byte_t *buffer; byte_t *buffer;
@ -710,7 +729,8 @@ struct allocation {
static int static int
handle_arm_request(raw1394handle_t handle, struct address_closure *ac, handle_arm_request(raw1394handle_t handle, struct address_closure *ac,
struct fw_cdev_event_request *request, int i) int tcode, unsigned long long offset, int source_node_id,
unsigned kernel_handle, size_t length, void *data)
{ {
fw_handle_t fwhandle = handle->mode.fw; fw_handle_t fwhandle = handle->mode.fw;
struct allocation *allocation = (struct allocation *) ac; struct allocation *allocation = (struct allocation *) ac;
@ -718,19 +738,19 @@ handle_arm_request(raw1394handle_t handle, struct address_closure *ac,
struct fw_cdev_send_response response; struct fw_cdev_send_response response;
arm_options_t type; arm_options_t type;
size_t in_length; size_t in_length;
int offset; int pos;
offset = request->offset - allocation->offset; pos = offset - allocation->offset;
response.handle = request->handle; response.handle = kernel_handle;
switch (request->tcode) { switch (tcode) {
case TCODE_WRITE_QUADLET_REQUEST: case TCODE_WRITE_QUADLET_REQUEST:
case TCODE_WRITE_BLOCK_REQUEST: case TCODE_WRITE_BLOCK_REQUEST:
printf("got write request, offset=0x%012llx, length=%d\n", printf("got write request, offset=0x%012llx, length=%d\n",
request->offset, request->length); offset, (int)length);
type = RAW1394_ARM_WRITE; type = RAW1394_ARM_WRITE;
in_length = request->length; in_length = length;
response.rcode = RCODE_COMPLETE; response.rcode = RCODE_COMPLETE;
response.length = 0; response.length = 0;
response.data = 0; response.data = 0;
@ -739,18 +759,29 @@ handle_arm_request(raw1394handle_t handle, struct address_closure *ac,
case TCODE_READ_QUADLET_REQUEST: case TCODE_READ_QUADLET_REQUEST:
case TCODE_READ_BLOCK_REQUEST: case TCODE_READ_BLOCK_REQUEST:
printf("got read request, offset=0x%012llx, length=%d\n", printf("got read request, offset=0x%012llx, length=%d\n",
request->offset, request->length); offset, (int)length);
type = RAW1394_ARM_READ; type = RAW1394_ARM_READ;
in_length = 0; in_length = 0;
response.rcode = RCODE_COMPLETE; response.rcode = RCODE_COMPLETE;
response.length = request->length; response.length = length;
response.data = ptr_to_u64(allocation->data + offset); response.data = ptr_to_u64(allocation->data + pos);
break; break;
case TCODE_LOCK_REQUEST: 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; type = RAW1394_ARM_LOCK;
in_length = request->length; in_length = length;
response.length = 4; response.length = 4;
break; break;
@ -764,17 +795,16 @@ handle_arm_request(raw1394handle_t handle, struct address_closure *ac,
response.rcode = RCODE_TYPE_ERROR; response.rcode = RCODE_TYPE_ERROR;
response.length = 0; response.length = 0;
response.data = 0; response.data = 0;
if (ioctl(fwhandle->devices[i].fd, if (ioctl(fwhandle->ioctl_fd,
FW_CDEV_IOC_SEND_RESPONSE, &response) < 0) FW_CDEV_IOC_SEND_RESPONSE, &response) < 0)
return -1; return -1;
} else if (!(allocation->client_transactions & type)) { } else if (!(allocation->client_transactions & type)) {
if (type == RAW1394_ARM_WRITE) if (type == RAW1394_ARM_WRITE)
memcpy(allocation->data + offset, memcpy(allocation->data + pos, data, length);
request->data, request->length);
else if (type == RAW1394_ARM_LOCK) else if (type == RAW1394_ARM_LOCK)
/* FIXME: do lock ops here */; /* FIXME: do lock ops here */;
if (ioctl(fwhandle->devices[i].fd, if (ioctl(fwhandle->ioctl_fd,
FW_CDEV_IOC_SEND_RESPONSE, &response) < 0) FW_CDEV_IOC_SEND_RESPONSE, &response) < 0)
return -1; return -1;
} }
@ -792,30 +822,28 @@ handle_arm_request(raw1394handle_t handle, struct address_closure *ac,
rrb->request_response.response = &rrb->response; rrb->request_response.response = &rrb->response;
rrb->request.destination_nodeid = fwhandle->reset.local_node_id; rrb->request.destination_nodeid = fwhandle->reset.local_node_id;
rrb->request.source_nodeid = fwhandle->devices[i].node_id; rrb->request.source_nodeid = source_node_id;
rrb->request.destination_offset = request->offset; rrb->request.destination_offset = offset;
rrb->request.tlabel = 0; rrb->request.tlabel = 0;
if (request->tcode < 0x10) { if (tcode < 0x10) {
rrb->request.tcode = request->tcode; rrb->request.tcode = tcode;
rrb->request.extended_transaction_code = 0; rrb->request.extended_transaction_code = 0;
} else { } else {
rrb->request.tcode = TCODE_LOCK_REQUEST; rrb->request.tcode = TCODE_LOCK_REQUEST;
rrb->request.extended_transaction_code = request->tcode - 0x10; rrb->request.extended_transaction_code = tcode - 0x10;
} }
rrb->request.generation = fwhandle->reset.generation; rrb->request.generation = fwhandle->reset.generation;
rrb->request.buffer_length = in_length; rrb->request.buffer_length = in_length;
rrb->request.buffer = rrb->data; rrb->request.buffer = rrb->data;
memcpy(rrb->request.buffer, request->data, in_length); memcpy(rrb->request.buffer, data, in_length);
rrb->response.response_code = response.rcode; rrb->response.response_code = response.rcode;
rrb->response.buffer_length = response.length; rrb->response.buffer_length = response.length;
rrb->response.buffer = rrb->data + in_length; rrb->response.buffer = rrb->data + in_length;
memcpy(rrb->response.buffer, memcpy(rrb->response.buffer, allocation->data + pos, response.length);
allocation->data + offset, response.length);
return fwhandle->arm_tag_handler(handle, allocation->tag, type, return fwhandle->arm_tag_handler(handle, allocation->tag, type,
request->length, length, &rrb->request_response);
&rrb->request_response);
} }
int int
@ -849,7 +877,9 @@ fw_arm_register(fw_handle_t handle, nodeaddr_t start,
request.offset = start; request.offset = start;
request.length = length; request.length = length;
request.closure = ptr_to_u64(&allocation->closure); request.closure = ptr_to_u64(&allocation->closure);
#if IMPLEMENTED_CDEV_ABI_VERSION >= 4
request.region_end = start + length;
#endif
retval = ioctl(handle->ioctl_fd, FW_CDEV_IOC_ALLOCATE, &request); retval = ioctl(handle->ioctl_fd, FW_CDEV_IOC_ALLOCATE, &request);
if (retval < 0) { if (retval < 0) {
free(allocation); free(allocation);
@ -1343,6 +1373,9 @@ fw_start_fcp_listen(fw_handle_t handle)
request.offset = CSR_REGISTER_BASE + CSR_FCP_COMMAND; request.offset = CSR_REGISTER_BASE + CSR_FCP_COMMAND;
request.length = CSR_FCP_END - CSR_FCP_COMMAND; request.length = CSR_FCP_END - CSR_FCP_COMMAND;
request.closure = ptr_to_u64(closure); request.closure = ptr_to_u64(closure);
#if IMPLEMENTED_CDEV_ABI_VERSION >= 4
request.region_end = CSR_REGISTER_BASE + CSR_FCP_END;
#endif
if (ioctl(handle->ioctl_fd, FW_CDEV_IOC_ALLOCATE, &request) < 0) if (ioctl(handle->ioctl_fd, FW_CDEV_IOC_ALLOCATE, &request) < 0)
return -1; return -1;