aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/firewire/fw-ohci.c212
-rw-r--r--drivers/firewire/fw-transaction.c3
2 files changed, 132 insertions, 83 deletions
diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c
index 29285f209dc..ec47ae9a2dd 100644
--- a/drivers/firewire/fw-ohci.c
+++ b/drivers/firewire/fw-ohci.c
@@ -55,17 +55,20 @@ struct descriptor {
__le16 transfer_status;
} __attribute__((aligned(16)));
-struct ar_context {
- struct fw_ohci *ohci;
+struct ar_buffer {
struct descriptor descriptor;
- __le32 buffer[512];
- dma_addr_t descriptor_bus;
- dma_addr_t buffer_bus;
+ struct ar_buffer *next;
+ __le32 data[0];
+};
+struct ar_context {
+ struct fw_ohci *ohci;
+ struct ar_buffer *current_buffer;
+ struct ar_buffer *last_buffer;
+ void *pointer;
u32 command_ptr;
u32 control_set;
u32 control_clear;
-
struct tasklet_struct tasklet;
};
@@ -169,8 +172,7 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card)
#define OHCI_LOOP_COUNT 500
#define OHCI1394_PCI_HCI_Control 0x40
#define SELF_ID_BUF_SIZE 0x800
-
-#define MAX_STOP_CONTEXT_LOOPS 1000
+#define OHCI_TCODE_PHY_PACKET 0x0e
static char ohci_driver_name[] = KBUILD_MODNAME;
@@ -213,66 +215,97 @@ ohci_update_phy_reg(struct fw_card *card, int addr,
return 0;
}
-static void ar_context_run(struct ar_context *ctx)
+static int ar_context_add_page(struct ar_context *ctx)
{
- reg_write(ctx->ohci, ctx->command_ptr, ctx->descriptor_bus | 1);
- reg_write(ctx->ohci, ctx->control_set, CONTEXT_RUN);
+ struct device *dev = ctx->ohci->card.device;
+ struct ar_buffer *ab;
+ dma_addr_t ab_bus;
+ size_t offset;
+
+ ab = (struct ar_buffer *) __get_free_page(GFP_ATOMIC);
+ if (ab == NULL)
+ return -ENOMEM;
+
+ ab_bus = dma_map_single(dev, ab, PAGE_SIZE, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(ab_bus)) {
+ free_page((unsigned long) ab);
+ return -ENOMEM;
+ }
+
+ memset(&ab->descriptor, 0, sizeof ab->descriptor);
+ ab->descriptor.control = cpu_to_le16(descriptor_input_more |
+ descriptor_status |
+ descriptor_branch_always);
+ offset = offsetof(struct ar_buffer, data);
+ ab->descriptor.req_count = cpu_to_le16(PAGE_SIZE - offset);
+ ab->descriptor.data_address = cpu_to_le32(ab_bus + offset);
+ ab->descriptor.res_count = cpu_to_le16(PAGE_SIZE - offset);
+ ab->descriptor.branch_address = 0;
+
+ dma_sync_single_for_device(dev, ab_bus, PAGE_SIZE, DMA_BIDIRECTIONAL);
+
+ ctx->last_buffer->descriptor.branch_address = ab_bus | 1;
+ ctx->last_buffer->next = ab;
+ ctx->last_buffer = ab;
+
+ reg_write(ctx->ohci, ctx->control_set, CONTEXT_WAKE);
flush_writes(ctx->ohci);
+
+ return 0;
}
-static void ar_context_tasklet(unsigned long data)
+static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
{
- struct ar_context *ctx = (struct ar_context *)data;
struct fw_ohci *ohci = ctx->ohci;
struct fw_packet p;
u32 status, length, tcode;
- int i;
- /* FIXME: We stop and restart the ar context here, what if we
- * stop while a receive is in progress? Maybe we could just
- * loop the context back to itself and use it in buffer fill
- * mode as intended... */
- reg_write(ctx->ohci, ctx->control_clear, CONTEXT_RUN);
-
- /* FIXME: What to do about evt_* errors? */
- length = le16_to_cpu(ctx->descriptor.req_count) -
- le16_to_cpu(ctx->descriptor.res_count) - 4;
- status = le32_to_cpu(ctx->buffer[length / 4]);
-
- p.ack = ((status >> 16) & 0x1f) - 16;
- p.speed = (status >> 21) & 0x7;
- p.timestamp = status & 0xffff;
- p.generation = ohci->request_generation;
-
- p.header[0] = le32_to_cpu(ctx->buffer[0]);
- p.header[1] = le32_to_cpu(ctx->buffer[1]);
- p.header[2] = le32_to_cpu(ctx->buffer[2]);
+ p.header[0] = le32_to_cpu(buffer[0]);
+ p.header[1] = le32_to_cpu(buffer[1]);
+ p.header[2] = le32_to_cpu(buffer[2]);
tcode = (p.header[0] >> 4) & 0x0f;
switch (tcode) {
case TCODE_WRITE_QUADLET_REQUEST:
case TCODE_READ_QUADLET_RESPONSE:
- p.header[3] = ctx->buffer[3];
+ p.header[3] = (__force __u32) buffer[3];
p.header_length = 16;
+ p.payload_length = 0;
break;
- case TCODE_WRITE_BLOCK_REQUEST:
case TCODE_READ_BLOCK_REQUEST :
+ p.header[3] = le32_to_cpu(buffer[3]);
+ p.header_length = 16;
+ p.payload_length = 0;
+ break;
+
+ case TCODE_WRITE_BLOCK_REQUEST:
case TCODE_READ_BLOCK_RESPONSE:
case TCODE_LOCK_REQUEST:
case TCODE_LOCK_RESPONSE:
- p.header[3] = le32_to_cpu(ctx->buffer[3]);
+ p.header[3] = le32_to_cpu(buffer[3]);
p.header_length = 16;
+ p.payload_length = p.header[3] >> 16;
break;
case TCODE_WRITE_RESPONSE:
case TCODE_READ_QUADLET_REQUEST:
+ case OHCI_TCODE_PHY_PACKET:
p.header_length = 12;
+ p.payload_length = 0;
break;
}
- p.payload = (void *) ctx->buffer + p.header_length;
- p.payload_length = length - p.header_length;
+ p.payload = (void *) buffer + p.header_length;
+
+ /* FIXME: What to do about evt_* errors? */
+ length = (p.header_length + p.payload_length + 3) / 4;
+ status = le32_to_cpu(buffer[length]);
+
+ p.ack = ((status >> 16) & 0x1f) - 16;
+ p.speed = (status >> 21) & 0x7;
+ p.timestamp = status & 0xffff;
+ p.generation = ohci->request_generation;
/* The OHCI bus reset handler synthesizes a phy packet with
* the new generation number when a bus reset happens (see
@@ -283,69 +316,84 @@ static void ar_context_tasklet(unsigned long data)
* request. */
if (p.ack + 16 == 0x09)
- ohci->request_generation = (ctx->buffer[2] >> 16) & 0xff;
+ ohci->request_generation = (buffer[2] >> 16) & 0xff;
else if (ctx == &ohci->ar_request_ctx)
fw_core_handle_request(&ohci->card, &p);
else
fw_core_handle_response(&ohci->card, &p);
- ctx->descriptor.data_address = cpu_to_le32(ctx->buffer_bus);
- ctx->descriptor.req_count = cpu_to_le16(sizeof ctx->buffer);
- ctx->descriptor.res_count = cpu_to_le16(sizeof ctx->buffer);
-
- dma_sync_single_for_device(ohci->card.device, ctx->descriptor_bus,
- sizeof ctx->descriptor_bus, DMA_TO_DEVICE);
+ return buffer + length + 1;
+}
- /* Make sure the active bit is 0 before we reprogram the DMA. */
- for (i = 0; i < MAX_STOP_CONTEXT_LOOPS; i++)
- if (!(reg_read(ctx->ohci,
- ctx->control_clear) & CONTEXT_ACTIVE))
- break;
- if (i == MAX_STOP_CONTEXT_LOOPS)
- fw_error("Failed to stop ar context\n");
+static void ar_context_tasklet(unsigned long data)
+{
+ struct ar_context *ctx = (struct ar_context *)data;
+ struct fw_ohci *ohci = ctx->ohci;
+ struct ar_buffer *ab;
+ struct descriptor *d;
+ void *buffer, *end;
+
+ ab = ctx->current_buffer;
+ d = &ab->descriptor;
+
+ if (d->res_count == 0) {
+ size_t size, rest, offset;
+
+ /* This descriptor is finished and we may have a
+ * packet split across this and the next buffer. We
+ * reuse the page for reassembling the split packet. */
+
+ offset = offsetof(struct ar_buffer, data);
+ dma_unmap_single(ohci->card.device,
+ ab->descriptor.data_address - offset,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+
+ buffer = ab;
+ ab = ab->next;
+ d = &ab->descriptor;
+ size = buffer + PAGE_SIZE - ctx->pointer;
+ rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count);
+ memmove(buffer, ctx->pointer, size);
+ memcpy(buffer + size, ab->data, rest);
+ ctx->current_buffer = ab;
+ ctx->pointer = (void *) ab->data + rest;
+ end = buffer + size + rest;
+
+ while (buffer < end)
+ buffer = handle_ar_packet(ctx, buffer);
+
+ free_page((unsigned long)buffer);
+ ar_context_add_page(ctx);
+ } else {
+ buffer = ctx->pointer;
+ ctx->pointer = end =
+ (void *) ab + PAGE_SIZE - le16_to_cpu(d->res_count);
- ar_context_run(ctx);
+ while (buffer < end)
+ buffer = handle_ar_packet(ctx, buffer);
+ }
}
static int
ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, u32 control_set)
{
- ctx->descriptor_bus =
- dma_map_single(ohci->card.device, &ctx->descriptor,
- sizeof ctx->descriptor, DMA_TO_DEVICE);
- if (ctx->descriptor_bus == 0)
- return -ENOMEM;
-
- if (ctx->descriptor_bus & 0xf)
- fw_notify("descriptor not 16-byte aligned: 0x%08lx\n",
- (unsigned long)ctx->descriptor_bus);
-
- ctx->buffer_bus =
- dma_map_single(ohci->card.device, ctx->buffer,
- sizeof ctx->buffer, DMA_FROM_DEVICE);
-
- if (ctx->buffer_bus == 0) {
- dma_unmap_single(ohci->card.device, ctx->descriptor_bus,
- sizeof ctx->descriptor, DMA_TO_DEVICE);
- return -ENOMEM;
- }
-
- memset(&ctx->descriptor, 0, sizeof ctx->descriptor);
- ctx->descriptor.control = cpu_to_le16(descriptor_input_more |
- descriptor_status |
- descriptor_branch_always);
- ctx->descriptor.req_count = cpu_to_le16(sizeof ctx->buffer);
- ctx->descriptor.data_address = cpu_to_le32(ctx->buffer_bus);
- ctx->descriptor.res_count = cpu_to_le16(sizeof ctx->buffer);
+ struct ar_buffer ab;
ctx->control_set = control_set;
ctx->control_clear = control_set + 4;
ctx->command_ptr = control_set + 12;
ctx->ohci = ohci;
-
+ ctx->last_buffer = &ab;
tasklet_init(&ctx->tasklet, ar_context_tasklet, (unsigned long)ctx);
- ar_context_run(ctx);
+ ar_context_add_page(ctx);
+ ar_context_add_page(ctx);
+ ctx->current_buffer = ab.next;
+ ctx->pointer = ctx->current_buffer->data;
+
+ reg_write(ctx->ohci, ctx->command_ptr, ab.descriptor.branch_address);
+ reg_write(ctx->ohci, ctx->control_set, CONTEXT_RUN);
+ flush_writes(ctx->ohci);
return 0;
}
diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c
index 4a48e2d7694..fb3b77e1bb2 100644
--- a/drivers/firewire/fw-transaction.c
+++ b/drivers/firewire/fw-transaction.c
@@ -640,7 +640,8 @@ fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
spin_unlock_irqrestore(&card->lock, flags);
if (&t->link == &card->transaction_list) {
- fw_notify("Unsolicited response\n");
+ fw_notify("Unsolicited response (source %x, tlabel %x)\n",
+ source, tlabel);
return;
}