Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5ea13950 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6:
  firewire: OHCI 1.0 Isochronous Receive support
parents 437105f2 a186b4a6
Loading
Loading
Loading
Loading
+155 −20
Original line number Diff line number Diff line
@@ -437,6 +437,21 @@ static void ar_context_run(struct ar_context *ctx)
	flush_writes(ctx->ohci);
}

static struct descriptor *
find_branch_descriptor(struct descriptor *d, int z)
{
	int b, key;

	b   = (le16_to_cpu(d->control) & DESCRIPTOR_BRANCH_ALWAYS) >> 2;
	key = (le16_to_cpu(d->control) & DESCRIPTOR_KEY_IMMEDIATE) >> 8;

	/* figure out which descriptor the branch address goes in */
	if (z == 2 && (b == 3 || key == 2))
		return d;
	else
		return d + z - 1;
}

static void context_tasklet(unsigned long data)
{
	struct context *ctx = (struct context *) data;
@@ -455,7 +470,7 @@ static void context_tasklet(unsigned long data)
		address = le32_to_cpu(last->branch_address);
		z = address & 0xf;
		d = ctx->buffer + (address - ctx->buffer_bus) / sizeof(*d);
		last = (z == 2) ? d : d + z - 1;
		last = find_branch_descriptor(d, z);

		if (!ctx->callback(ctx, d, last))
			break;
@@ -566,7 +581,7 @@ static void context_append(struct context *ctx,

	ctx->head_descriptor = d + z + extra;
	ctx->prev_descriptor->branch_address = cpu_to_le32(d_bus | z);
	ctx->prev_descriptor = z == 2 ? d : d + z - 1;
	ctx->prev_descriptor = find_branch_descriptor(d, z);

	dma_sync_single_for_device(ctx->ohci->card.device, ctx->buffer_bus,
				   ctx->buffer_size, DMA_TO_DEVICE);
@@ -1431,6 +1446,57 @@ static int handle_ir_dualbuffer_packet(struct context *context,
	return 1;
}

static int handle_ir_packet_per_buffer(struct context *context,
				       struct descriptor *d,
				       struct descriptor *last)
{
	struct iso_context *ctx =
		container_of(context, struct iso_context, context);
	struct descriptor *pd = d + 1;
	__le32 *ir_header;
	size_t header_length;
	void *p, *end;
	int i, z;

	if (pd->res_count == pd->req_count)
		/* Descriptor(s) not done yet, stop iteration */
		return 0;

	header_length = le16_to_cpu(d->req_count);

	i   = ctx->header_length;
	z   = le32_to_cpu(pd->branch_address) & 0xf;
	p   = d + z;
	end = p + header_length;

	while (p < end && i + ctx->base.header_size <= PAGE_SIZE) {
		/*
		 * The iso header is byteswapped to little endian by
		 * the controller, but the remaining header quadlets
		 * are big endian.  We want to present all the headers
		 * as big endian, so we have to swap the first quadlet.
		 */
		*(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
		memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4);
		i += ctx->base.header_size;
		p += ctx->base.header_size + 4;
	}

	ctx->header_length = i;

	if (le16_to_cpu(pd->control) & DESCRIPTOR_IRQ_ALWAYS) {
		ir_header = (__le32 *) (d + z);
		ctx->base.callback(&ctx->base,
				   le32_to_cpu(ir_header[0]) & 0xffff,
				   ctx->header_length, ctx->header,
				   ctx->base.callback_data);
		ctx->header_length = 0;
	}


	return 1;
}

static int handle_it_packet(struct context *context,
			    struct descriptor *d,
			    struct descriptor *last)
@@ -1466,14 +1532,12 @@ ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size)
	} else {
		mask = &ohci->ir_context_mask;
		list = ohci->ir_context_list;
		if (ohci->version >= OHCI_VERSION_1_1)
			callback = handle_ir_dualbuffer_packet;
		else
			callback = handle_ir_packet_per_buffer;
	}

	/* FIXME: We need a fallback for pre 1.1 OHCI. */
	if (callback == handle_ir_dualbuffer_packet &&
	    ohci->version < OHCI_VERSION_1_1)
		return ERR_PTR(-ENOSYS);

	spin_lock_irqsave(&ohci->lock, flags);
	index = ffs(*mask) - 1;
	if (index >= 0)
@@ -1532,7 +1596,9 @@ static int ohci_start_iso(struct fw_iso_context *base,
		context_run(&ctx->context, match);
	} else {
		index = ctx - ohci->ir_context_list;
		control = IR_CONTEXT_DUAL_BUFFER_MODE | IR_CONTEXT_ISOCH_HEADER;
		control = IR_CONTEXT_ISOCH_HEADER;
		if (ohci->version >= OHCI_VERSION_1_1)
			control |= IR_CONTEXT_DUAL_BUFFER_MODE;
		match = (tags << 28) | (sync << 8) | ctx->base.channel;
		if (cycle >= 0) {
			match |= (cycle & 0x07fff) << 12;
@@ -1738,7 +1804,6 @@ ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
	offset   = payload & ~PAGE_MASK;
	rest     = p->payload_length;

	/* FIXME: OHCI 1.0 doesn't support dual buffer receive */
	/* FIXME: make packet-per-buffer/dual-buffer a context option */
	while (rest > 0) {
		d = context_get_descriptors(&ctx->context,
@@ -1776,6 +1841,81 @@ ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
	return 0;
}

static int
ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base,
					 struct fw_iso_packet *packet,
					 struct fw_iso_buffer *buffer,
					 unsigned long payload)
{
	struct iso_context *ctx = container_of(base, struct iso_context, base);
	struct descriptor *d = NULL, *pd = NULL;
	struct fw_iso_packet *p;
	dma_addr_t d_bus, page_bus;
	u32 z, header_z, rest;
	int i, page, offset, packet_count, header_size;

	if (packet->skip) {
		d = context_get_descriptors(&ctx->context, 1, &d_bus);
		if (d == NULL)
			return -ENOMEM;

		d->control = cpu_to_le16(DESCRIPTOR_STATUS |
					 DESCRIPTOR_INPUT_LAST |
					 DESCRIPTOR_BRANCH_ALWAYS |
					 DESCRIPTOR_WAIT);
		context_append(&ctx->context, d, 1, 0);
	}

	/* one descriptor for header, one for payload */
	/* FIXME: handle cases where we need multiple desc. for payload */
	z = 2;
	p = packet;

	/*
	 * The OHCI controller puts the status word in the
	 * buffer too, so we need 4 extra bytes per packet.
	 */
	packet_count = p->header_length / ctx->base.header_size;
	header_size  = packet_count * (ctx->base.header_size + 4);

	/* Get header size in number of descriptors. */
	header_z = DIV_ROUND_UP(header_size, sizeof(*d));
	page     = payload >> PAGE_SHIFT;
	offset   = payload & ~PAGE_MASK;
	rest     = p->payload_length;

	for (i = 0; i < packet_count; i++) {
		/* d points to the header descriptor */
		d = context_get_descriptors(&ctx->context,
					    z + header_z, &d_bus);
		if (d == NULL)
			return -ENOMEM;

		d->control      = cpu_to_le16(DESCRIPTOR_INPUT_MORE);
		d->req_count    = cpu_to_le16(header_size);
		d->res_count    = d->req_count;
		d->data_address = cpu_to_le32(d_bus + (z * sizeof(*d)));

		/* pd points to the payload descriptor */
		pd = d + 1;
		pd->control = cpu_to_le16(DESCRIPTOR_STATUS |
					  DESCRIPTOR_INPUT_LAST |
					  DESCRIPTOR_BRANCH_ALWAYS);
		if (p->interrupt)
			pd->control |= cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS);

		pd->req_count = cpu_to_le16(rest);
		pd->res_count = pd->req_count;

		page_bus = page_private(buffer->pages[page]);
		pd->data_address = cpu_to_le32(page_bus + offset);

		context_append(&ctx->context, d, z, header_z);
	}

	return 0;
}

static int
ohci_queue_iso(struct fw_iso_context *base,
	       struct fw_iso_packet *packet,
@@ -1790,8 +1930,9 @@ ohci_queue_iso(struct fw_iso_context *base,
		return ohci_queue_iso_receive_dualbuffer(base, packet,
							 buffer, payload);
	else
		/* FIXME: Implement fallback for OHCI 1.0 controllers. */
		return -ENOSYS;
		return ohci_queue_iso_receive_packet_per_buffer(base, packet,
								buffer,
								payload);
}

static const struct fw_card_driver ohci_driver = {
@@ -1911,12 +2052,6 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
	ohci->version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff;
	fw_notify("Added fw-ohci device %s, OHCI version %x.%x\n",
		  dev->dev.bus_id, ohci->version >> 16, ohci->version & 0xff);
	if (ohci->version < OHCI_VERSION_1_1) {
		fw_notify("    Isochronous I/O is not yet implemented for "
			  "OHCI 1.0 chips.\n");
		fw_notify("    Cameras, audio devices etc. won't work on "
			  "this controller with this driver version.\n");
	}
	return 0;

 fail_self_id: