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

Commit 8d98c5cd authored by Jody McIntyre's avatar Jody McIntyre Committed by Linus Torvalds
Browse files

[PATCH] ieee1394: single buffer fixes to video1394



Apply and fixup patch from Markus Tavenrath <speedygoo@speedygoo.de> for
video1394 to allow only a single buffer on receive and two buffers on
transmit.  Tested with libdc1394 and dvconnect (libdv).

Signed-off-by: default avatarDan Dennedy <dan@dennedy.org>
Signed-off-by: default avatarJody McIntyre <scjody@steamballoon.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 2554bd2a
Loading
Loading
Loading
Loading
+85 −25
Original line number Diff line number Diff line
@@ -35,6 +35,11 @@
 *
 */

/* Markus Tavenrath <speedygoo@speedygoo.de> :
   - fixed checks for valid buffer-numbers in video1394_icotl
   - changed the ways the dma prg's are used, now it's possible to use
     even a single dma buffer
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -112,6 +117,7 @@ struct dma_iso_ctx {
	struct it_dma_prg **it_prg;

	unsigned int *buffer_status;
	unsigned int *buffer_prg_assignment;
        struct timeval *buffer_time; /* time when the buffer was received */
	unsigned int *last_used_cmd; /* For ISO Transmit with
					variable sized packets only ! */
@@ -183,6 +189,7 @@ static int free_dma_iso_ctx(struct dma_iso_ctx *d)
	kfree(d->ir_prg);
	kfree(d->it_prg);
	kfree(d->buffer_status);
	kfree(d->buffer_prg_assignment);
	kfree(d->buffer_time);
	kfree(d->last_used_cmd);
	kfree(d->next_buffer);
@@ -220,7 +227,7 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
	/* Init the regions for easy cleanup */
	dma_region_init(&d->dma);

	if (dma_region_alloc(&d->dma, d->num_desc * d->buf_size, ohci->dev,
	if (dma_region_alloc(&d->dma, (d->num_desc - 1) * d->buf_size, ohci->dev,
			     PCI_DMA_BIDIRECTIONAL)) {
		PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma buffer");
		free_dma_iso_ctx(d);
@@ -332,6 +339,8 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,

	d->buffer_status = kmalloc(d->num_desc * sizeof(unsigned int),
				   GFP_KERNEL);
	d->buffer_prg_assignment = kmalloc(d->num_desc * sizeof(unsigned int),
				   GFP_KERNEL);
	d->buffer_time = kmalloc(d->num_desc * sizeof(struct timeval),
				   GFP_KERNEL);
	d->last_used_cmd = kmalloc(d->num_desc * sizeof(unsigned int),
@@ -344,6 +353,11 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
		free_dma_iso_ctx(d);
		return NULL;
	}
	if (d->buffer_prg_assignment == NULL) {
		PRINT(KERN_ERR, ohci->host->id, "Failed to allocate buffer_prg_assignment");
		free_dma_iso_ctx(d);
		return NULL;
	}
	if (d->buffer_time == NULL) {
		PRINT(KERN_ERR, ohci->host->id, "Failed to allocate buffer_time");
		free_dma_iso_ctx(d);
@@ -360,6 +374,7 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
		return NULL;
	}
	memset(d->buffer_status, 0, d->num_desc * sizeof(unsigned int));
	memset(d->buffer_prg_assignment, 0, d->num_desc * sizeof(unsigned int));
	memset(d->buffer_time, 0, d->num_desc * sizeof(struct timeval));
	memset(d->last_used_cmd, 0, d->num_desc * sizeof(unsigned int));
	memset(d->next_buffer, -1, d->num_desc * sizeof(int));
@@ -369,7 +384,7 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
	PRINT(KERN_INFO, ohci->host->id, "Iso %s DMA: %d buffers "
	      "of size %d allocated for a frame size %d, each with %d prgs",
	      (type == OHCI_ISO_RECEIVE) ? "receive" : "transmit",
	      d->num_desc, d->buf_size, d->frame_size, d->nb_cmd);
	      d->num_desc - 1, d->buf_size, d->frame_size, d->nb_cmd);

	return d;
}
@@ -384,11 +399,36 @@ static void reset_ir_status(struct dma_iso_ctx *d, int n)
	d->ir_prg[n][i].status = cpu_to_le32(d->left_size);
}

static void reprogram_dma_ir_prg(struct dma_iso_ctx *d, int n, int buffer, int flags)
{
	struct dma_cmd *ir_prg = d->ir_prg[n];
	unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size;
	int i;

	d->buffer_prg_assignment[n] = buffer;

	ir_prg[0].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, buf -
  	                        (unsigned long)d->dma.kvirt));
	ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
				(buf + 4) - (unsigned long)d->dma.kvirt));

	for (i=2;i<d->nb_cmd-1;i++) {
		ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
						(buf+(i-1)*PAGE_SIZE) -
						(unsigned long)d->dma.kvirt));
	}

	ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE |
				  DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size);
	ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma,
				  (buf+(i-1)*PAGE_SIZE) - (unsigned long)d->dma.kvirt));
}

static void initialize_dma_ir_prg(struct dma_iso_ctx *d, int n, int flags)
{
	struct dma_cmd *ir_prg = d->ir_prg[n];
	struct dma_prog_region *ir_reg = &d->prg_reg[n];
	unsigned long buf = (unsigned long)d->dma.kvirt + n * d->buf_size;
	unsigned long buf = (unsigned long)d->dma.kvirt;
	int i;

	/* the first descriptor will read only 4 bytes */
@@ -498,7 +538,7 @@ static void wakeup_dma_ir_ctx(unsigned long l)
	for (i = 0; i < d->num_desc; i++) {
		if (d->ir_prg[i][d->nb_cmd-1].status & cpu_to_le32(0xFFFF0000)) {
			reset_ir_status(d, i);
			d->buffer_status[i] = VIDEO1394_BUFFER_READY;
			d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY;
			do_gettimeofday(&d->buffer_time[i]);
		}
	}
@@ -575,7 +615,7 @@ static void wakeup_dma_it_ctx(unsigned long l)
			int next = d->next_buffer[i];
			put_timestamp(ohci, d, next);
			d->it_prg[i][d->last_used_cmd[i]].end.status = 0;
			d->buffer_status[i] = VIDEO1394_BUFFER_READY;
			d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY;
		}
	}

@@ -585,11 +625,25 @@ static void wakeup_dma_it_ctx(unsigned long l)
		wake_up_interruptible(&d->waitq);
}

static void reprogram_dma_it_prg(struct dma_iso_ctx  *d, int n, int buffer)
{
	struct it_dma_prg *it_prg = d->it_prg[n];
	unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size;
	int i;

	d->buffer_prg_assignment[n] = buffer;
	for (i=0;i<d->nb_cmd;i++) {
	  it_prg[i].end.address =
		cpu_to_le32(dma_region_offset_to_bus(&d->dma,
			(buf+i*d->packet_size) - (unsigned long)d->dma.kvirt));
	}
}

static void initialize_dma_it_prg(struct dma_iso_ctx *d, int n, int sync_tag)
{
	struct it_dma_prg *it_prg = d->it_prg[n];
	struct dma_prog_region *it_reg = &d->prg_reg[n];
	unsigned long buf = (unsigned long)d->dma.kvirt + n * d->buf_size;
	unsigned long buf = (unsigned long)d->dma.kvirt;
	int i;
	d->last_used_cmd[n] = d->nb_cmd - 1;
	for (i=0;i<d->nb_cmd;i++) {
@@ -786,7 +840,7 @@ static int __video1394_ioctl(struct file *file,

		if (cmd == VIDEO1394_IOC_LISTEN_CHANNEL) {
			d = alloc_dma_iso_ctx(ohci, OHCI_ISO_RECEIVE,
					      v.nb_buffers, v.buf_size,
					      v.nb_buffers + 1, v.buf_size,
					      v.channel, 0);

			if (d == NULL) {
@@ -807,7 +861,7 @@ static int __video1394_ioctl(struct file *file,
		}
		else {
			d = alloc_dma_iso_ctx(ohci, OHCI_ISO_TRANSMIT,
					      v.nb_buffers, v.buf_size,
					      v.nb_buffers + 1, v.buf_size,
					      v.channel, v.packet_size);

			if (d == NULL) {
@@ -879,6 +933,7 @@ static int __video1394_ioctl(struct file *file,
	{
		struct video1394_wait v;
		struct dma_iso_ctx *d;
		int next_prg;

		if (copy_from_user(&v, argp, sizeof(v)))
			return -EFAULT;
@@ -886,7 +941,7 @@ static int __video1394_ioctl(struct file *file,
		d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel);
		if (d == NULL) return -EFAULT;

		if ((v.buffer<0) || (v.buffer>d->num_desc)) {
		if ((v.buffer<0) || (v.buffer>=d->num_desc - 1)) {
			PRINT(KERN_ERR, ohci->host->id,
			      "Buffer %d out of range",v.buffer);
			return -EINVAL;
@@ -903,12 +958,14 @@ static int __video1394_ioctl(struct file *file,

		d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;

		next_prg = (d->last_buffer + 1) % d->num_desc;
		if (d->last_buffer>=0)
			d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress =
				cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer], 0)
				cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0)
					& 0xfffffff0) | 0x1);

		d->last_buffer = v.buffer;
		d->last_buffer = next_prg;
		reprogram_dma_ir_prg(d, d->last_buffer, v.buffer, d->flags);

		d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = 0;

@@ -920,7 +977,7 @@ static int __video1394_ioctl(struct file *file,

			/* Tell the controller where the first program is */
			reg_write(ohci, d->cmdPtr,
				dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer], 0) | 0x1);
				  dma_prog_region_offset_to_bus(&d->prg_reg[d->last_buffer], 0) | 0x1);

			/* Run IR context */
			reg_write(ohci, d->ctrlSet, 0x8000);
@@ -941,7 +998,7 @@ static int __video1394_ioctl(struct file *file,
	{
		struct video1394_wait v;
		struct dma_iso_ctx *d;
		int i;
		int i = 0;

		if (copy_from_user(&v, argp, sizeof(v)))
			return -EFAULT;
@@ -949,7 +1006,7 @@ static int __video1394_ioctl(struct file *file,
		d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel);
		if (d == NULL) return -EFAULT;

		if ((v.buffer<0) || (v.buffer>d->num_desc)) {
		if ((v.buffer<0) || (v.buffer>d->num_desc - 1)) {
			PRINT(KERN_ERR, ohci->host->id,
			      "Buffer %d out of range",v.buffer);
			return -EINVAL;
@@ -995,9 +1052,9 @@ static int __video1394_ioctl(struct file *file,
		 * Look ahead to see how many more buffers have been received
		 */
		i=0;
		while (d->buffer_status[(v.buffer+1)%d->num_desc]==
		while (d->buffer_status[(v.buffer+1)%(d->num_desc - 1)]==
		       VIDEO1394_BUFFER_READY) {
			v.buffer=(v.buffer+1)%d->num_desc;
			v.buffer=(v.buffer+1)%(d->num_desc - 1);
			i++;
		}
		spin_unlock_irqrestore(&d->lock, flags);
@@ -1013,6 +1070,7 @@ static int __video1394_ioctl(struct file *file,
		struct video1394_wait v;
		unsigned int *psizes = NULL;
		struct dma_iso_ctx *d;
		int next_prg;

		if (copy_from_user(&v, argp, sizeof(v)))
			return -EFAULT;
@@ -1020,7 +1078,7 @@ static int __video1394_ioctl(struct file *file,
		d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel);
		if (d == NULL) return -EFAULT;

		if ((v.buffer<0) || (v.buffer>d->num_desc)) {
		if ((v.buffer<0) || (v.buffer>=d->num_desc - 1)) {
			PRINT(KERN_ERR, ohci->host->id,
			      "Buffer %d out of range",v.buffer);
			return -EINVAL;
@@ -1046,6 +1104,8 @@ static int __video1394_ioctl(struct file *file,

		spin_lock_irqsave(&d->lock,flags);

		// last_buffer is last_prg
		next_prg = (d->last_buffer + 1) % d->num_desc;
		if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) {
			PRINT(KERN_ERR, ohci->host->id,
			      "Buffer %d is already used",v.buffer);
@@ -1056,8 +1116,7 @@ static int __video1394_ioctl(struct file *file,

		if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) {
			initialize_dma_it_prg_var_packet_queue(
				d, v.buffer, psizes,
				ohci);
				d, next_prg, psizes, ohci);
		}

		d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
@@ -1065,16 +1124,17 @@ static int __video1394_ioctl(struct file *file,
		if (d->last_buffer >= 0) {
			d->it_prg[d->last_buffer]
				[ d->last_used_cmd[d->last_buffer] ].end.branchAddress =
					cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer],
					cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg],
						0) & 0xfffffff0) | 0x3);

			d->it_prg[d->last_buffer]
				[ d->last_used_cmd[d->last_buffer] ].begin.branchAddress =
					cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer],
					cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg],
						0) & 0xfffffff0) | 0x3);
			d->next_buffer[d->last_buffer] = v.buffer;
			d->next_buffer[d->last_buffer] = (v.buffer + 1) % (d->num_desc - 1);
		}
		d->last_buffer = v.buffer;
		d->last_buffer = next_prg;
		reprogram_dma_it_prg(d, d->last_buffer, v.buffer);
		d->next_buffer[d->last_buffer] = -1;

		d->it_prg[d->last_buffer][d->last_used_cmd[d->last_buffer]].end.branchAddress = 0;
@@ -1089,7 +1149,7 @@ static int __video1394_ioctl(struct file *file,

			/* Tell the controller where the first program is */
			reg_write(ohci, d->cmdPtr,
				dma_prog_region_offset_to_bus(&d->prg_reg[v.buffer], 0) | 0x3);
				dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0) | 0x3);

			/* Run IT context */
			reg_write(ohci, d->ctrlSet, 0x8000);
@@ -1120,7 +1180,7 @@ static int __video1394_ioctl(struct file *file,
		d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel);
		if (d == NULL) return -EFAULT;

		if ((v.buffer<0) || (v.buffer>d->num_desc)) {
		if ((v.buffer<0) || (v.buffer>=d->num_desc-1)) {
			PRINT(KERN_ERR, ohci->host->id,
			      "Buffer %d out of range",v.buffer);
			return -EINVAL;