[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: Dan Dennedy <dan@dennedy.org>
Signed-off-by: Jody McIntyre <scjody@steamballoon.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Jody McIntyre 2005-05-16 21:54:04 -07:00 committed by Linus Torvalds
parent 2554bd2a68
commit 8d98c5cd41

View File

@ -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/config.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/list.h> #include <linux/list.h>
@ -112,6 +117,7 @@ struct dma_iso_ctx {
struct it_dma_prg **it_prg; struct it_dma_prg **it_prg;
unsigned int *buffer_status; unsigned int *buffer_status;
unsigned int *buffer_prg_assignment;
struct timeval *buffer_time; /* time when the buffer was received */ struct timeval *buffer_time; /* time when the buffer was received */
unsigned int *last_used_cmd; /* For ISO Transmit with unsigned int *last_used_cmd; /* For ISO Transmit with
variable sized packets only ! */ 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->ir_prg);
kfree(d->it_prg); kfree(d->it_prg);
kfree(d->buffer_status); kfree(d->buffer_status);
kfree(d->buffer_prg_assignment);
kfree(d->buffer_time); kfree(d->buffer_time);
kfree(d->last_used_cmd); kfree(d->last_used_cmd);
kfree(d->next_buffer); 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 */ /* Init the regions for easy cleanup */
dma_region_init(&d->dma); 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)) { PCI_DMA_BIDIRECTIONAL)) {
PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma buffer"); PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma buffer");
free_dma_iso_ctx(d); 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), d->buffer_status = kmalloc(d->num_desc * sizeof(unsigned int),
GFP_KERNEL); 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), d->buffer_time = kmalloc(d->num_desc * sizeof(struct timeval),
GFP_KERNEL); GFP_KERNEL);
d->last_used_cmd = kmalloc(d->num_desc * sizeof(unsigned int), 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); free_dma_iso_ctx(d);
return NULL; 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) { if (d->buffer_time == NULL) {
PRINT(KERN_ERR, ohci->host->id, "Failed to allocate buffer_time"); PRINT(KERN_ERR, ohci->host->id, "Failed to allocate buffer_time");
free_dma_iso_ctx(d); free_dma_iso_ctx(d);
@ -360,6 +374,7 @@ alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int num_desc,
return NULL; return NULL;
} }
memset(d->buffer_status, 0, d->num_desc * sizeof(unsigned int)); 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->buffer_time, 0, d->num_desc * sizeof(struct timeval));
memset(d->last_used_cmd, 0, d->num_desc * sizeof(unsigned int)); memset(d->last_used_cmd, 0, d->num_desc * sizeof(unsigned int));
memset(d->next_buffer, -1, d->num_desc * sizeof(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 " PRINT(KERN_INFO, ohci->host->id, "Iso %s DMA: %d buffers "
"of size %d allocated for a frame size %d, each with %d prgs", "of size %d allocated for a frame size %d, each with %d prgs",
(type == OHCI_ISO_RECEIVE) ? "receive" : "transmit", (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; 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); 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) 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_cmd *ir_prg = d->ir_prg[n];
struct dma_prog_region *ir_reg = &d->prg_reg[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; int i;
/* the first descriptor will read only 4 bytes */ /* 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++) { for (i = 0; i < d->num_desc; i++) {
if (d->ir_prg[i][d->nb_cmd-1].status & cpu_to_le32(0xFFFF0000)) { if (d->ir_prg[i][d->nb_cmd-1].status & cpu_to_le32(0xFFFF0000)) {
reset_ir_status(d, i); 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]); 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]; int next = d->next_buffer[i];
put_timestamp(ohci, d, next); put_timestamp(ohci, d, next);
d->it_prg[i][d->last_used_cmd[i]].end.status = 0; 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); 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) 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 it_dma_prg *it_prg = d->it_prg[n];
struct dma_prog_region *it_reg = &d->prg_reg[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; int i;
d->last_used_cmd[n] = d->nb_cmd - 1; d->last_used_cmd[n] = d->nb_cmd - 1;
for (i=0;i<d->nb_cmd;i++) { 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) { if (cmd == VIDEO1394_IOC_LISTEN_CHANNEL) {
d = alloc_dma_iso_ctx(ohci, OHCI_ISO_RECEIVE, 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); v.channel, 0);
if (d == NULL) { if (d == NULL) {
@ -807,7 +861,7 @@ static int __video1394_ioctl(struct file *file,
} }
else { else {
d = alloc_dma_iso_ctx(ohci, OHCI_ISO_TRANSMIT, 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); v.channel, v.packet_size);
if (d == NULL) { if (d == NULL) {
@ -879,6 +933,7 @@ static int __video1394_ioctl(struct file *file,
{ {
struct video1394_wait v; struct video1394_wait v;
struct dma_iso_ctx *d; struct dma_iso_ctx *d;
int next_prg;
if (copy_from_user(&v, argp, sizeof(v))) if (copy_from_user(&v, argp, sizeof(v)))
return -EFAULT; return -EFAULT;
@ -886,7 +941,7 @@ static int __video1394_ioctl(struct file *file,
d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel);
if (d == NULL) return -EFAULT; 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, PRINT(KERN_ERR, ohci->host->id,
"Buffer %d out of range",v.buffer); "Buffer %d out of range",v.buffer);
return -EINVAL; return -EINVAL;
@ -903,12 +958,14 @@ static int __video1394_ioctl(struct file *file,
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
next_prg = (d->last_buffer + 1) % d->num_desc;
if (d->last_buffer>=0) if (d->last_buffer>=0)
d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = 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); & 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; 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 */ /* Tell the controller where the first program is */
reg_write(ohci, d->cmdPtr, 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 */ /* Run IR context */
reg_write(ohci, d->ctrlSet, 0x8000); reg_write(ohci, d->ctrlSet, 0x8000);
@ -941,7 +998,7 @@ static int __video1394_ioctl(struct file *file,
{ {
struct video1394_wait v; struct video1394_wait v;
struct dma_iso_ctx *d; struct dma_iso_ctx *d;
int i; int i = 0;
if (copy_from_user(&v, argp, sizeof(v))) if (copy_from_user(&v, argp, sizeof(v)))
return -EFAULT; return -EFAULT;
@ -949,7 +1006,7 @@ static int __video1394_ioctl(struct file *file,
d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel);
if (d == NULL) return -EFAULT; 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, PRINT(KERN_ERR, ohci->host->id,
"Buffer %d out of range",v.buffer); "Buffer %d out of range",v.buffer);
return -EINVAL; return -EINVAL;
@ -995,9 +1052,9 @@ static int __video1394_ioctl(struct file *file,
* Look ahead to see how many more buffers have been received * Look ahead to see how many more buffers have been received
*/ */
i=0; 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) { VIDEO1394_BUFFER_READY) {
v.buffer=(v.buffer+1)%d->num_desc; v.buffer=(v.buffer+1)%(d->num_desc - 1);
i++; i++;
} }
spin_unlock_irqrestore(&d->lock, flags); spin_unlock_irqrestore(&d->lock, flags);
@ -1013,6 +1070,7 @@ static int __video1394_ioctl(struct file *file,
struct video1394_wait v; struct video1394_wait v;
unsigned int *psizes = NULL; unsigned int *psizes = NULL;
struct dma_iso_ctx *d; struct dma_iso_ctx *d;
int next_prg;
if (copy_from_user(&v, argp, sizeof(v))) if (copy_from_user(&v, argp, sizeof(v)))
return -EFAULT; return -EFAULT;
@ -1020,7 +1078,7 @@ static int __video1394_ioctl(struct file *file,
d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel); d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel);
if (d == NULL) return -EFAULT; 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, PRINT(KERN_ERR, ohci->host->id,
"Buffer %d out of range",v.buffer); "Buffer %d out of range",v.buffer);
return -EINVAL; return -EINVAL;
@ -1046,6 +1104,8 @@ static int __video1394_ioctl(struct file *file,
spin_lock_irqsave(&d->lock,flags); 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) { if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) {
PRINT(KERN_ERR, ohci->host->id, PRINT(KERN_ERR, ohci->host->id,
"Buffer %d is already used",v.buffer); "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) { if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) {
initialize_dma_it_prg_var_packet_queue( initialize_dma_it_prg_var_packet_queue(
d, v.buffer, psizes, d, next_prg, psizes, ohci);
ohci);
} }
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
@ -1065,16 +1124,17 @@ static int __video1394_ioctl(struct file *file,
if (d->last_buffer >= 0) { if (d->last_buffer >= 0) {
d->it_prg[d->last_buffer] d->it_prg[d->last_buffer]
[ d->last_used_cmd[d->last_buffer] ].end.branchAddress = [ 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); 0) & 0xfffffff0) | 0x3);
d->it_prg[d->last_buffer] d->it_prg[d->last_buffer]
[ d->last_used_cmd[d->last_buffer] ].begin.branchAddress = [ 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); 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->next_buffer[d->last_buffer] = -1;
d->it_prg[d->last_buffer][d->last_used_cmd[d->last_buffer]].end.branchAddress = 0; 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 */ /* Tell the controller where the first program is */
reg_write(ohci, d->cmdPtr, 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 */ /* Run IT context */
reg_write(ohci, d->ctrlSet, 0x8000); 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); d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel);
if (d == NULL) return -EFAULT; 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, PRINT(KERN_ERR, ohci->host->id,
"Buffer %d out of range",v.buffer); "Buffer %d out of range",v.buffer);
return -EINVAL; return -EINVAL;