* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * NOTES:
+ *
+ * ioctl return codes:
+ * EFAULT is only for invalid address for the argp
+ * EINVAL for out of range values
+ * EBUSY when trying to use an already used resource
+ * ESRCH when trying to free/stop a not used resource
+ * EAGAIN for resource allocation failure that could perhaps succeed later
+ * ENOTTY for unsupported ioctl request
+ *
*/
-
-/* jds -- add private data to file to keep track of iso contexts associated
- with each open -- so release won't kill all iso transfers */
-
-/* Damien Douxchamps: Fix failure when the number of DMA pages per frame is
- one */
-
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/vmalloc.h>
#include <linux/timex.h>
#include <linux/mm.h>
-#include <linux/ioctl32.h>
#include <linux/compat.h>
#include <linux/cdev.h>
#define ISO_CHANNELS 64
-#ifndef virt_to_page
-#define virt_to_page(x) MAP_NR(x)
-#endif
-
-#ifndef vmalloc_32
-#define vmalloc_32(x) vmalloc(x)
-#endif
-
struct it_dma_prg {
struct dma_cmd begin;
quadlet_t data[4];
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 ! */
#define PRINT(level, card, fmt, args...) \
printk(level "video1394_%d: " fmt "\n" , card , ## args)
-void wakeup_dma_ir_ctx(unsigned long l);
-void wakeup_dma_it_ctx(unsigned long l);
+static void wakeup_dma_ir_ctx(unsigned long l);
+static void wakeup_dma_it_ctx(unsigned long l);
static struct hpsb_highlevel video1394_highlevel;
kfree(d->prg_reg);
}
- if (d->ir_prg)
- kfree(d->ir_prg);
-
- if (d->it_prg)
- kfree(d->it_prg);
-
- if (d->buffer_status)
- kfree(d->buffer_status);
- if (d->buffer_time)
- kfree(d->buffer_time);
- if (d->last_used_cmd)
- kfree(d->last_used_cmd);
- if (d->next_buffer)
- kfree(d->next_buffer);
-
+ 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);
list_del(&d->link);
-
kfree(d);
return 0;
struct dma_iso_ctx *d;
int i;
- d = kmalloc(sizeof(struct dma_iso_ctx), GFP_KERNEL);
- if (d == NULL) {
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d) {
PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma_iso_ctx");
return NULL;
}
- memset(d, 0, sizeof *d);
-
d->ohci = ohci;
d->type = type;
d->channel = channel;
/* 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);
}
d->ctx = d->iso_tasklet.context;
- d->prg_reg = kmalloc(d->num_desc * sizeof(struct dma_prog_region),
- GFP_KERNEL);
- if (d->prg_reg == NULL) {
+ d->prg_reg = kmalloc(d->num_desc * sizeof(*d->prg_reg), GFP_KERNEL);
+ if (!d->prg_reg) {
PRINT(KERN_ERR, ohci->host->id, "Failed to allocate ir prg regs");
free_dma_iso_ctx(d);
return NULL;
d->cmdPtr = OHCI1394_IsoRcvCommandPtr+32*d->ctx;
d->ctxMatch = OHCI1394_IsoRcvContextMatch+32*d->ctx;
- d->ir_prg = kmalloc(d->num_desc * sizeof(struct dma_cmd *),
+ d->ir_prg = kzalloc(d->num_desc * sizeof(*d->ir_prg),
GFP_KERNEL);
- if (d->ir_prg == NULL) {
+ if (!d->ir_prg) {
PRINT(KERN_ERR, ohci->host->id, "Failed to allocate dma ir prg");
free_dma_iso_ctx(d);
return NULL;
}
- memset(d->ir_prg, 0, d->num_desc * sizeof(struct dma_cmd *));
d->nb_cmd = d->buf_size / PAGE_SIZE + 1;
d->left_size = (d->frame_size % PAGE_SIZE) ?
d->ctrlClear = OHCI1394_IsoXmitContextControlClear+16*d->ctx;
d->cmdPtr = OHCI1394_IsoXmitCommandPtr+16*d->ctx;
- d->it_prg = kmalloc(d->num_desc * sizeof(struct it_dma_prg *),
+ d->it_prg = kzalloc(d->num_desc * sizeof(*d->it_prg),
GFP_KERNEL);
- if (d->it_prg == NULL) {
+ if (!d->it_prg) {
PRINT(KERN_ERR, ohci->host->id,
"Failed to allocate dma it prg");
free_dma_iso_ctx(d);
return NULL;
}
- memset(d->it_prg, 0, d->num_desc*sizeof(struct it_dma_prg *));
d->packet_size = packet_size;
}
}
- d->buffer_status = 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),
- GFP_KERNEL);
- d->next_buffer = kmalloc(d->num_desc * sizeof(int),
- GFP_KERNEL);
-
- if (d->buffer_status == NULL) {
- PRINT(KERN_ERR, ohci->host->id, "Failed to allocate buffer_status");
+ d->buffer_status =
+ kzalloc(d->num_desc * sizeof(*d->buffer_status), GFP_KERNEL);
+ d->buffer_prg_assignment =
+ kzalloc(d->num_desc * sizeof(*d->buffer_prg_assignment), GFP_KERNEL);
+ d->buffer_time =
+ kzalloc(d->num_desc * sizeof(*d->buffer_time), GFP_KERNEL);
+ d->last_used_cmd =
+ kzalloc(d->num_desc * sizeof(*d->last_used_cmd), GFP_KERNEL);
+ d->next_buffer =
+ kzalloc(d->num_desc * sizeof(*d->next_buffer), GFP_KERNEL);
+
+ if (!d->buffer_status || !d->buffer_prg_assignment || !d->buffer_time ||
+ !d->last_used_cmd || !d->next_buffer) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "Failed to allocate dma_iso_ctx member");
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);
- return NULL;
- }
- if (d->last_used_cmd == NULL) {
- PRINT(KERN_ERR, ohci->host->id, "Failed to allocate last_used_cmd");
- free_dma_iso_ctx(d);
- return NULL;
- }
- if (d->next_buffer == NULL) {
- PRINT(KERN_ERR, ohci->host->id, "Failed to allocate next_buffer");
- free_dma_iso_ctx(d);
- return NULL;
- }
- memset(d->buffer_status, 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));
spin_lock_init(&d->lock);
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;
}
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 */
return NULL;
}
-void wakeup_dma_ir_ctx(unsigned long l)
+static void wakeup_dma_ir_ctx(unsigned long l)
{
struct dma_iso_ctx *d = (struct dma_iso_ctx *) l;
int i;
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;
- do_gettimeofday(&d->buffer_time[i]);
+ d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY;
+ do_gettimeofday(&d->buffer_time[d->buffer_prg_assignment[i]]);
}
}
#endif
}
-void wakeup_dma_it_ctx(unsigned long l)
+static void wakeup_dma_it_ctx(unsigned long l)
{
struct dma_iso_ctx *d = (struct dma_iso_ctx *) l;
struct ti_ohci *ohci = d->ohci;
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;
}
}
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++) {
reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1<<d->ctx);
}
-static int video1394_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static inline unsigned video1394_buffer_state(struct dma_iso_ctx *d,
+ unsigned int buffer)
+{
+ unsigned long flags;
+ unsigned int ret;
+ spin_lock_irqsave(&d->lock, flags);
+ ret = d->buffer_status[buffer];
+ spin_unlock_irqrestore(&d->lock, flags);
+ return ret;
+}
+
+static int __video1394_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
{
struct file_ctx *ctx = (struct file_ctx *)file->private_data;
struct ti_ohci *ohci = ctx->ohci;
unsigned long flags;
+ void __user *argp = (void __user *)arg;
switch(cmd)
{
struct dma_iso_ctx *d;
int i;
- if (copy_from_user(&v, (void *)arg, sizeof(v)))
+ if (copy_from_user(&v, argp, sizeof(v)))
return -EFAULT;
/* if channel < 0, find lowest available one */
if (v.channel < 0) {
mask = (u64)0x1;
- for (i=0; i<ISO_CHANNELS; i++) {
+ for (i=0; ; i++) {
+ if (i == ISO_CHANNELS) {
+ PRINT(KERN_ERR, ohci->host->id,
+ "No free channel found");
+ return -EAGAIN;
+ }
if (!(ohci->ISO_channel_usage & mask)) {
v.channel = i;
PRINT(KERN_INFO, ohci->host->id, "Found free channel %d", i);
}
mask = mask << 1;
}
- }
-
- if (v.channel<0 || v.channel>(ISO_CHANNELS-1)) {
+ } else if (v.channel >= ISO_CHANNELS) {
PRINT(KERN_ERR, ohci->host->id,
"Iso channel %d out of bounds", v.channel);
- return -EFAULT;
+ return -EINVAL;
+ } else {
+ mask = (u64)0x1<<v.channel;
}
- mask = (u64)0x1<<v.channel;
- printk("mask: %08X%08X usage: %08X%08X\n",
- (u32)(mask>>32),(u32)(mask&0xffffffff),
- (u32)(ohci->ISO_channel_usage>>32),
- (u32)(ohci->ISO_channel_usage&0xffffffff));
+ PRINT(KERN_INFO, ohci->host->id, "mask: %08X%08X usage: %08X%08X\n",
+ (u32)(mask>>32),(u32)(mask&0xffffffff),
+ (u32)(ohci->ISO_channel_usage>>32),
+ (u32)(ohci->ISO_channel_usage&0xffffffff));
if (ohci->ISO_channel_usage & mask) {
PRINT(KERN_ERR, ohci->host->id,
"Channel %d is already taken", v.channel);
- return -EFAULT;
+ return -EBUSY;
}
- ohci->ISO_channel_usage |= mask;
if (v.buf_size == 0 || v.buf_size > VIDEO1394_MAX_SIZE) {
PRINT(KERN_ERR, ohci->host->id,
"Invalid %d length buffer requested",v.buf_size);
- return -EFAULT;
+ return -EINVAL;
}
if (v.nb_buffers == 0 || v.nb_buffers > VIDEO1394_MAX_SIZE) {
PRINT(KERN_ERR, ohci->host->id,
"Invalid %d buffers requested",v.nb_buffers);
- return -EFAULT;
+ return -EINVAL;
}
if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) {
PRINT(KERN_ERR, ohci->host->id,
"%d buffers of size %d bytes is too big",
v.nb_buffers, v.buf_size);
- return -EFAULT;
+ return -EINVAL;
}
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) {
PRINT(KERN_ERR, ohci->host->id,
"Couldn't allocate ir context");
- return -EFAULT;
+ return -EAGAIN;
}
initialize_dma_ir_ctx(d, v.sync_tag, v.flags);
}
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) {
PRINT(KERN_ERR, ohci->host->id,
"Couldn't allocate it context");
- return -EFAULT;
+ return -EAGAIN;
}
initialize_dma_it_ctx(d, v.sync_tag,
v.syt_offset, v.flags);
v.channel);
}
- if (copy_to_user((void *)arg, &v, sizeof(v)))
+ if (copy_to_user(argp, &v, sizeof(v))) {
+ /* FIXME : free allocated dma resources */
return -EFAULT;
+ }
+
+ ohci->ISO_channel_usage |= mask;
return 0;
}
u64 mask;
struct dma_iso_ctx *d;
- if (copy_from_user(&channel, (void *)arg, sizeof(int)))
+ if (copy_from_user(&channel, argp, sizeof(int)))
return -EFAULT;
- if (channel<0 || channel>(ISO_CHANNELS-1)) {
+ if (channel < 0 || channel >= ISO_CHANNELS) {
PRINT(KERN_ERR, ohci->host->id,
"Iso channel %d out of bound", channel);
- return -EFAULT;
+ return -EINVAL;
}
mask = (u64)0x1<<channel;
if (!(ohci->ISO_channel_usage & mask)) {
PRINT(KERN_ERR, ohci->host->id,
"Channel %d is not being used", channel);
- return -EFAULT;
+ return -ESRCH;
}
/* Mark this channel as unused */
else
d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, channel);
- if (d == NULL) return -EFAULT;
+ if (d == NULL) return -ESRCH;
PRINT(KERN_INFO, ohci->host->id, "Iso context %d "
"stop talking on channel %d", d->ctx, channel);
free_dma_iso_ctx(d);
{
struct video1394_wait v;
struct dma_iso_ctx *d;
+ int next_prg;
- if (copy_from_user(&v, (void *)arg, sizeof(v)))
+ if (copy_from_user(&v, argp, sizeof(v)))
return -EFAULT;
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 -EFAULT;
+ return -EINVAL;
}
spin_lock_irqsave(&d->lock,flags);
PRINT(KERN_ERR, ohci->host->id,
"Buffer %d is already used",v.buffer);
spin_unlock_irqrestore(&d->lock,flags);
- return -EFAULT;
+ return -EBUSY;
}
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;
/* 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);
{
struct video1394_wait v;
struct dma_iso_ctx *d;
- int i;
+ int i = 0;
- if (copy_from_user(&v, (void *)arg, sizeof(v)))
+ if (copy_from_user(&v, argp, sizeof(v)))
return -EFAULT;
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 -EFAULT;
+ return -EINVAL;
}
/*
return -EINTR;
}
-#if 1
- while (d->buffer_status[v.buffer]!=
- VIDEO1394_BUFFER_READY) {
- spin_unlock_irqrestore(&d->lock, flags);
- interruptible_sleep_on(&d->waitq);
- spin_lock_irqsave(&d->lock, flags);
- if (signal_pending(current)) {
- spin_unlock_irqrestore(&d->lock,flags);
- return -EINTR;
- }
- }
-#else
- if (wait_event_interruptible(d->waitq,
- d->buffer_status[v.buffer]
- == VIDEO1394_BUFFER_READY)
- == -ERESTARTSYS)
- return -EINTR;
-#endif
+ spin_unlock_irqrestore(&d->lock, flags);
+ wait_event_interruptible(d->waitq,
+ video1394_buffer_state(d, v.buffer) ==
+ VIDEO1394_BUFFER_READY);
+ if (signal_pending(current))
+ return -EINTR;
+ spin_lock_irqsave(&d->lock, flags);
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
break;
default:
PRINT(KERN_ERR, ohci->host->id,
"Buffer %d is not queued",v.buffer);
spin_unlock_irqrestore(&d->lock, flags);
- return -EFAULT;
+ return -ESRCH;
}
/* set time of buffer */
v.filltime = d->buffer_time[v.buffer];
-// printk("Buffer %d time %d\n", v.buffer, (d->buffer_time[v.buffer]).tv_usec);
/*
* 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);
v.buffer=i;
- if (copy_to_user((void *)arg, &v, sizeof(v)))
+ if (copy_to_user(argp, &v, sizeof(v)))
return -EFAULT;
return 0;
case VIDEO1394_IOC_TALK_QUEUE_BUFFER:
{
struct video1394_wait v;
- struct video1394_queue_variable qv;
+ unsigned int *psizes = NULL;
struct dma_iso_ctx *d;
+ int next_prg;
- qv.packet_sizes = NULL;
-
- if (copy_from_user(&v, (void *)arg, sizeof(v)))
+ if (copy_from_user(&v, argp, sizeof(v)))
return -EFAULT;
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 -EFAULT;
+ return -EINVAL;
}
if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) {
- unsigned int *psizes;
- int buf_size = d->nb_cmd * sizeof(unsigned int);
+ int buf_size = d->nb_cmd * sizeof(*psizes);
+ struct video1394_queue_variable __user *p = argp;
+ unsigned int __user *qv;
- if (copy_from_user(&qv, (void *)arg, sizeof(qv)))
+ if (get_user(qv, &p->packet_sizes))
return -EFAULT;
psizes = kmalloc(buf_size, GFP_KERNEL);
if (!psizes)
return -ENOMEM;
- if (copy_from_user(psizes, qv.packet_sizes, buf_size)) {
+ if (copy_from_user(psizes, qv, buf_size)) {
kfree(psizes);
return -EFAULT;
}
-
- qv.packet_sizes = psizes;
}
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);
spin_unlock_irqrestore(&d->lock,flags);
- if (qv.packet_sizes)
- kfree(qv.packet_sizes);
- return -EFAULT;
+ kfree(psizes);
+ return -EBUSY;
}
if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) {
initialize_dma_it_prg_var_packet_queue(
- d, v.buffer, qv.packet_sizes,
- ohci);
+ d, next_prg, psizes, ohci);
}
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
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;
/* 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);
}
}
- if (qv.packet_sizes)
- kfree(qv.packet_sizes);
-
+ kfree(psizes);
return 0;
}
struct video1394_wait v;
struct dma_iso_ctx *d;
- if (copy_from_user(&v, (void *)arg, sizeof(v)))
+ if (copy_from_user(&v, argp, sizeof(v)))
return -EFAULT;
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 -EFAULT;
+ return -EINVAL;
}
switch(d->buffer_status[v.buffer]) {
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
return 0;
case VIDEO1394_BUFFER_QUEUED:
-#if 1
- while (d->buffer_status[v.buffer]!=
- VIDEO1394_BUFFER_READY) {
- interruptible_sleep_on(&d->waitq);
- if (signal_pending(current)) return -EINTR;
- }
-#else
- if (wait_event_interruptible(d->waitq,
- d->buffer_status[v.buffer]
- == VIDEO1394_BUFFER_READY)
- == -ERESTARTSYS)
+ wait_event_interruptible(d->waitq,
+ (d->buffer_status[v.buffer] == VIDEO1394_BUFFER_READY));
+ if (signal_pending(current))
return -EINTR;
-#endif
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
return 0;
default:
PRINT(KERN_ERR, ohci->host->id,
"Buffer %d is not queued",v.buffer);
- return -EFAULT;
+ return -ESRCH;
}
}
default:
- return -EINVAL;
+ return -ENOTTY;
}
}
+static long video1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int err;
+ lock_kernel();
+ err = __video1394_ioctl(file, cmd, arg);
+ unlock_kernel();
+ return err;
+}
+
/*
* This maps the vmalloced and reserved buffer to user space.
*
* FIXME:
* - PAGE_READONLY should suffice!?
- * - remap_page_range is kind of inefficient for page by page remapping.
+ * - remap_pfn_range is kind of inefficient for page by page remapping.
* But e.g. pte_alloc() does not work in modules ... :-(
*/
-int video1394_mmap(struct file *file, struct vm_area_struct *vma)
+static int video1394_mmap(struct file *file, struct vm_area_struct *vma)
{
struct file_ctx *ctx = (struct file_ctx *)file->private_data;
int res = -EINVAL;
if (ohci == NULL)
return -EIO;
- ctx = kmalloc(sizeof(struct file_ctx), GFP_KERNEL);
- if (ctx == NULL) {
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
PRINT(KERN_ERR, ohci->host->id, "Cannot malloc file_ctx");
return -ENOMEM;
}
- memset(ctx, 0, sizeof(struct file_ctx));
ctx->ohci = ohci;
INIT_LIST_HEAD(&ctx->context_list);
ctx->current_ctx = NULL;
return 0;
}
+#ifdef CONFIG_COMPAT
+static long video1394_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg);
+#endif
+
static struct cdev video1394_cdev;
static struct file_operations video1394_fops=
{
.owner = THIS_MODULE,
- .ioctl = video1394_ioctl,
+ .unlocked_ioctl = video1394_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = video1394_compat_ioctl,
+#endif
.mmap = video1394_mmap,
.open = video1394_open,
.release = video1394_release
.specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
.version = CAMERA_SW_VERSION_ENTRY & 0xffffff
},
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
+ .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ .version = (CAMERA_SW_VERSION_ENTRY + 1) & 0xffffff
+ },
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION,
+ .specifier_id = CAMERA_UNIT_SPEC_ID_ENTRY & 0xffffff,
+ .version = (CAMERA_SW_VERSION_ENTRY + 2) & 0xffffff
+ },
{ }
};
hpsb_set_hostinfo_key(&video1394_highlevel, host, ohci->host->id);
minor = IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id;
+ class_device_create(hpsb_protocol_class, NULL, MKDEV(
+ IEEE1394_MAJOR, minor),
+ NULL, "%s-%d", VIDEO1394_DRIVER_NAME, ohci->host->id);
devfs_mk_cdev(MKDEV(IEEE1394_MAJOR, minor),
S_IFCHR | S_IRUSR | S_IWUSR,
"%s/%d", VIDEO1394_DRIVER_NAME, ohci->host->id);
{
struct ti_ohci *ohci = hpsb_get_hostinfo(&video1394_highlevel, host);
- if (ohci)
+ if (ohci) {
+ class_device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR,
+ IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id));
devfs_remove("%s/%d", VIDEO1394_DRIVER_NAME, ohci->host->id);
-
+ }
+
return;
}
struct compat_timeval filltime;
};
-static int video1394_wr_wait32(unsigned int fd, unsigned int cmd, unsigned long arg,
- struct file *file)
+static int video1394_wr_wait32(struct file *file, unsigned int cmd, unsigned long arg)
{
+ struct video1394_wait32 __user *argp = (void __user *)arg;
struct video1394_wait32 wait32;
struct video1394_wait wait;
mm_segment_t old_fs;
int ret;
- if (file->f_op->ioctl != video1394_ioctl)
- return -EFAULT;
-
- if (copy_from_user(&wait32, (void *)arg, sizeof(wait32)))
+ if (copy_from_user(&wait32, argp, sizeof(wait32)))
return -EFAULT;
wait.channel = wait32.channel;
old_fs = get_fs();
set_fs(KERNEL_DS);
if (cmd == VIDEO1394_IOC32_LISTEN_WAIT_BUFFER)
- ret = video1394_ioctl(file->f_dentry->d_inode, file,
+ ret = video1394_ioctl(file,
VIDEO1394_IOC_LISTEN_WAIT_BUFFER,
(unsigned long) &wait);
else
- ret = video1394_ioctl(file->f_dentry->d_inode, file,
+ ret = video1394_ioctl(file,
VIDEO1394_IOC_LISTEN_POLL_BUFFER,
(unsigned long) &wait);
set_fs(old_fs);
wait32.filltime.tv_sec = (int)wait.filltime.tv_sec;
wait32.filltime.tv_usec = (int)wait.filltime.tv_usec;
- if (copy_to_user((struct video1394_wait32 *)arg, &wait32, sizeof(wait32)))
+ if (copy_to_user(argp, &wait32, sizeof(wait32)))
ret = -EFAULT;
}
return ret;
}
-static int video1394_w_wait32(unsigned int fd, unsigned int cmd, unsigned long arg,
- struct file *file)
+static int video1394_w_wait32(struct file *file, unsigned int cmd, unsigned long arg)
{
struct video1394_wait32 wait32;
struct video1394_wait wait;
mm_segment_t old_fs;
int ret;
- if (file->f_op->ioctl != video1394_ioctl)
- return -EFAULT;
-
- if (copy_from_user(&wait32, (void *)arg, sizeof(wait32)))
+ if (copy_from_user(&wait32, (void __user *)arg, sizeof(wait32)))
return -EFAULT;
wait.channel = wait32.channel;
old_fs = get_fs();
set_fs(KERNEL_DS);
if (cmd == VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER)
- ret = video1394_ioctl(file->f_dentry->d_inode, file,
+ ret = video1394_ioctl(file,
VIDEO1394_IOC_LISTEN_QUEUE_BUFFER,
(unsigned long) &wait);
else
- ret = video1394_ioctl(file->f_dentry->d_inode, file,
+ ret = video1394_ioctl(file,
VIDEO1394_IOC_TALK_WAIT_BUFFER,
(unsigned long) &wait);
set_fs(old_fs);
return ret;
}
-static int video1394_queue_buf32(unsigned int fd, unsigned int cmd, unsigned long arg,
- struct file *file)
+static int video1394_queue_buf32(struct file *file, unsigned int cmd, unsigned long arg)
{
- if (file->f_op->ioctl != video1394_ioctl)
- return -EFAULT;
+ return -EFAULT; /* ??? was there before. */
- return -EFAULT;
-
- return video1394_ioctl(file->f_dentry->d_inode, file,
+ return video1394_ioctl(file,
VIDEO1394_IOC_TALK_QUEUE_BUFFER, arg);
}
+static long video1394_compat_ioctl(struct file *f, unsigned cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case VIDEO1394_IOC_LISTEN_CHANNEL:
+ case VIDEO1394_IOC_UNLISTEN_CHANNEL:
+ case VIDEO1394_IOC_TALK_CHANNEL:
+ case VIDEO1394_IOC_UNTALK_CHANNEL:
+ return video1394_ioctl(f, cmd, arg);
+
+ case VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER:
+ return video1394_w_wait32(f, cmd, arg);
+ case VIDEO1394_IOC32_LISTEN_WAIT_BUFFER:
+ return video1394_wr_wait32(f, cmd, arg);
+ case VIDEO1394_IOC_TALK_QUEUE_BUFFER:
+ return video1394_queue_buf32(f, cmd, arg);
+ case VIDEO1394_IOC32_TALK_WAIT_BUFFER:
+ return video1394_w_wait32(f, cmd, arg);
+ case VIDEO1394_IOC32_LISTEN_POLL_BUFFER:
+ return video1394_wr_wait32(f, cmd, arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
#endif /* CONFIG_COMPAT */
static void __exit video1394_exit_module (void)
{
-#ifdef CONFIG_COMPAT
- int ret;
-
- ret = unregister_ioctl32_conversion(VIDEO1394_IOC_LISTEN_CHANNEL);
- ret |= unregister_ioctl32_conversion(VIDEO1394_IOC_UNLISTEN_CHANNEL);
- ret |= unregister_ioctl32_conversion(VIDEO1394_IOC_TALK_CHANNEL);
- ret |= unregister_ioctl32_conversion(VIDEO1394_IOC_UNTALK_CHANNEL);
- ret |= unregister_ioctl32_conversion(VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER);
- ret |= unregister_ioctl32_conversion(VIDEO1394_IOC32_LISTEN_WAIT_BUFFER);
- ret |= unregister_ioctl32_conversion(VIDEO1394_IOC_TALK_QUEUE_BUFFER);
- ret |= unregister_ioctl32_conversion(VIDEO1394_IOC32_TALK_WAIT_BUFFER);
- ret |= unregister_ioctl32_conversion(VIDEO1394_IOC32_LISTEN_POLL_BUFFER);
- if (ret)
- PRINT_G(KERN_CRIT, "Error unregistering ioctl32 translations");
-#endif
-
hpsb_unregister_protocol(&video1394_driver);
hpsb_unregister_highlevel(&video1394_highlevel);
return ret;
}
-#ifdef CONFIG_COMPAT
- {
- /* First the compatible ones */
- ret = register_ioctl32_conversion(VIDEO1394_IOC_LISTEN_CHANNEL, NULL);
- ret |= register_ioctl32_conversion(VIDEO1394_IOC_UNLISTEN_CHANNEL, NULL);
- ret |= register_ioctl32_conversion(VIDEO1394_IOC_TALK_CHANNEL, NULL);
- ret |= register_ioctl32_conversion(VIDEO1394_IOC_UNTALK_CHANNEL, NULL);
-
- /* These need translation */
- ret |= register_ioctl32_conversion(VIDEO1394_IOC32_LISTEN_QUEUE_BUFFER,
- video1394_w_wait32);
- ret |= register_ioctl32_conversion(VIDEO1394_IOC32_LISTEN_WAIT_BUFFER,
- video1394_wr_wait32);
- ret |= register_ioctl32_conversion(VIDEO1394_IOC_TALK_QUEUE_BUFFER,
- video1394_queue_buf32);
- ret |= register_ioctl32_conversion(VIDEO1394_IOC32_TALK_WAIT_BUFFER,
- video1394_w_wait32);
- ret |= register_ioctl32_conversion(VIDEO1394_IOC32_LISTEN_POLL_BUFFER,
- video1394_wr_wait32);
- if (ret)
- PRINT_G(KERN_INFO, "Error registering ioctl32 translations");
- }
-#endif
-
PRINT_G(KERN_INFO, "Installed " VIDEO1394_DRIVER_NAME " module");
return 0;
}
module_init(video1394_init_module);
module_exit(video1394_exit_module);
-MODULE_ALIAS_CHARDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_VIDEO1394 * 16);