📄 video1394.c
字号:
return; } cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer); timeStamp = ((cycleTimer & 0x0fff) + d->syt_offset); /* 11059 = 450 us */ timeStamp = (timeStamp % 3072 + ((timeStamp / 3072) << 12) + (cycleTimer & 0xf000)) & 0xffff; buf[6] = timeStamp >> 8; buf[7] = timeStamp & 0xff; /* if first packet is empty packet, then put timestamp into the next full one too */ if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) { buf += d->packet_size; buf[6] = timeStamp >> 8; buf[7] = timeStamp & 0xff; } /* do the next buffer frame too in case of irq latency */ n = d->next_buffer[n]; if (n == -1) { return; } buf = d->dma.kvirt + n * d->buf_size; timeStamp += (d->last_used_cmd[n] << 12) & 0xffff; buf[6] = timeStamp >> 8; buf[7] = timeStamp & 0xff; /* if first packet is empty packet, then put timestamp into the next full one too */ if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) { buf += d->packet_size; buf[6] = timeStamp >> 8; buf[7] = timeStamp & 0xff; }#if 0 printk("curr: %d, next: %d, cycleTimer: %08x timeStamp: %08x\n", curr, n, cycleTimer, timeStamp);#endif}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 i; spin_lock(&d->lock); for (i = 0; i < d->num_desc; i++) { if (d->it_prg[i][d->last_used_cmd[i]].end.status & cpu_to_le32(0xFFFF0000)) { 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[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY; } } spin_unlock(&d->lock); if (waitqueue_active(&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){ 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; int i; d->last_used_cmd[n] = d->nb_cmd - 1; for (i=0;i<d->nb_cmd;i++) { it_prg[i].begin.control = cpu_to_le32(DMA_CTL_OUTPUT_MORE | DMA_CTL_IMMEDIATE | 8) ; it_prg[i].begin.address = 0; it_prg[i].begin.status = 0; it_prg[i].data[0] = cpu_to_le32( (IEEE1394_SPEED_100 << 16) | (/* tag */ 1 << 14) | (d->channel << 8) | (TCODE_ISO_DATA << 4)); if (i==0) it_prg[i].data[0] |= cpu_to_le32(sync_tag); it_prg[i].data[1] = cpu_to_le32(d->packet_size << 16); it_prg[i].data[2] = 0; it_prg[i].data[3] = 0; it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST | DMA_CTL_BRANCH); 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)); if (i<d->nb_cmd-1) { it_prg[i].end.control |= cpu_to_le32(d->packet_size); it_prg[i].begin.branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); it_prg[i].end.branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); } else { /* the last prg generates an interrupt */ it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE | DMA_CTL_IRQ | d->left_size); /* the last prg doesn't branch */ it_prg[i].begin.branchAddress = 0; it_prg[i].end.branchAddress = 0; } it_prg[i].end.status = 0; }}static void initialize_dma_it_prg_var_packet_queue( struct dma_iso_ctx *d, int n, unsigned int * packet_sizes, struct ti_ohci *ohci){ struct it_dma_prg *it_prg = d->it_prg[n]; struct dma_prog_region *it_reg = &d->prg_reg[n]; int i;#if 0 if (n != -1) { put_timestamp(ohci, d, n); }#endif d->last_used_cmd[n] = d->nb_cmd - 1; for (i = 0; i < d->nb_cmd; i++) { unsigned int size; if (packet_sizes[i] > d->packet_size) { size = d->packet_size; } else { size = packet_sizes[i]; } it_prg[i].data[1] = cpu_to_le32(size << 16); it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST | DMA_CTL_BRANCH); if (i < d->nb_cmd-1 && packet_sizes[i+1] != 0) { it_prg[i].end.control |= cpu_to_le32(size); it_prg[i].begin.branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); it_prg[i].end.branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) * sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3); } else { /* the last prg generates an interrupt */ it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE | DMA_CTL_IRQ | size); /* the last prg doesn't branch */ it_prg[i].begin.branchAddress = 0; it_prg[i].end.branchAddress = 0; d->last_used_cmd[n] = i; break; } }}static void initialize_dma_it_ctx(struct dma_iso_ctx *d, int sync_tag, unsigned int syt_offset, int flags){ struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; int i; d->flags = flags; d->syt_offset = (syt_offset == 0 ? 11000 : syt_offset); ohci1394_stop_context(ohci, d->ctrlClear, NULL); for (i=0;i<d->num_desc;i++) initialize_dma_it_prg(d, i, sync_tag); /* Set up isoRecvIntMask to generate interrupts */ reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1<<d->ctx);}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 long 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) { case VIDEO1394_IOC_LISTEN_CHANNEL: case VIDEO1394_IOC_TALK_CHANNEL: { struct video1394_mmap v; u64 mask; struct dma_iso_ctx *d; int i; 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++) { 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); break; } mask = mask << 1; } } else if (v.channel >= ISO_CHANNELS) { PRINT(KERN_ERR, ohci->host->id, "Iso channel %d out of bounds", v.channel); return -EINVAL; } else { mask = (u64)0x1<<v.channel; } DBGMSG(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 -EBUSY; } 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 -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 -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 -EINVAL; } if (cmd == VIDEO1394_IOC_LISTEN_CHANNEL) { d = alloc_dma_iso_ctx(ohci, OHCI_ISO_RECEIVE, 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 -EAGAIN; } initialize_dma_ir_ctx(d, v.sync_tag, v.flags); ctx->current_ctx = d; v.buf_size = d->buf_size; list_add_tail(&d->link, &ctx->context_list); DBGMSG(ohci->host->id, "iso context %d listen on channel %d", d->ctx, v.channel); } else { d = alloc_dma_iso_ctx(ohci, OHCI_ISO_TRANSMIT, 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 -EAGAIN; } initialize_dma_it_ctx(d, v.sync_tag, v.syt_offset, v.flags); ctx->current_ctx = d; v.buf_size = d->buf_size; list_add_tail(&d->link, &ctx->context_list); DBGMSG(ohci->host->id, "Iso context %d talk on channel %d", d->ctx, v.channel); } if (copy_to_user(argp, &v, sizeof(v))) { /* FIXME : free allocated dma resources */ return -EFAULT; } ohci->ISO_channel_usage |= mask; return 0; } case VIDEO1394_IOC_UNLISTEN_CHANNEL: case VIDEO1394_IOC_UNTALK_CHANNEL: { int channel; u64 mask; struct dma_iso_ctx *d; if (copy_from_user(&channel, argp, sizeof(int))) return -EFAULT; if (channel < 0 || channel >= ISO_CHANNELS) { PRINT(KERN_ERR, ohci->host->id, "Iso channel %d out of bound", channel); 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 -ESRCH; } /* Mark this channel as unused */ ohci->ISO_channel_usage &= ~mask; if (cmd == VIDEO1394_IOC_UNLISTEN_CHANNEL) d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, channel); else d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, channel); if (d == NULL) return -ESRCH; DBGMSG(ohci->host->id, "Iso context %d " "stop talking on channel %d", d->ctx, channel); free_dma_iso_ctx(d); return 0; } case VIDEO1394_IOC_LISTEN_QUEUE_BUFFER: { struct video1394_wait v; struct dma_iso_ctx *d; int next_prg; if (unlikely(copy_from_user(&v, argp, sizeof(v)))) return -EFAULT; d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); if (unlikely(d == NULL)) return -EFAULT; if (unlikely((v.buffer<0) || (v.buffer>=d->num_desc - 1))) { PRINT(KERN_ERR, ohci->host->id, "Buffer %d out of range",v.buffer); return -EINVAL; } spin_lock_irqsave(&d->lock,flags); if (unlikely(d->buffer_status[v.buffer]==VIDEO1394_BUFFER_QUEUED)) { PRINT(KERN_ERR, ohci->host->id, "Buffer %d is already used",v.buffer); spin_unlock_irqrestore(&d->lock,flags); 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[next_prg], 0) & 0xfffffff0) | 0x1); 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; spin_unlock_irqrestore(&d->lock,flags); if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) { DBGMSG(ohci->host->id, "Starting iso DMA ctx=%d",d->ctx); /* Tell the controller where the first program is */ reg_write(ohci, d->cmdPtr, dma_prog_region_offset_to_bus(&d->prg_reg[d->last_buffer], 0) | 0x1); /* Run IR context */ reg_write(ohci, d->ctrlSet, 0x8000); } else { /* Wake up dma context if necessary */ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { DBGMSG(ohci->host->id, "Waking up iso dma ctx=%d", d->ctx); reg_write(ohci, d->ctrlSet, 0x1000); } } return 0; } case VIDEO1394_IOC_LISTEN_WAIT_BUFFER: case VIDEO1394_IOC_LISTEN_POLL_BUFFER: { struct video1394_wait v; struct dma_iso_ctx *d; int i = 0; if (unlikely(copy_from_user(&v, argp, sizeof(v)))) return -EFAULT; d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); if (unlikely(d == NULL)) return -EFAULT; if (unlikely((v.buffer<0) || (v.buffer>d->num_desc - 1))) { PRINT(KERN_ERR, ohci->host->id, "Buffer %d out of range",v.buffer); return -EINVAL; } /* * I change the way it works so that it returns * the last received frame. */ spin_lock_irqsave(&d->lock, flags); switch(d->buffer_status[v.buffer]) { case VIDEO1394_BUFFER_READY: d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; break; case VIDEO1394_BUFFER_QUEUED: if (cmd == VIDEO1394_IOC_LISTEN_POLL_BUFFER) { /* for polling, return error code EINTR */ spin_unlock_irqrestore(&d->lock, flags); return -EINTR; } 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 -ESRCH; } /* set time of buffer */ v.filltime = d->buffer_time[v.buffer]; /* * Look ahead to see how many more buffers have been received */ i=0; while (d->buffer_status[(v.buffer+1)%(d->num_desc - 1)]== VIDEO1394_BUFFER_READY) { v.buffer=(v.buffer+1)%(d->num_desc - 1); i++; } spin_unlock_irqrestore(&d->lock, flags); v.buffer=i; if (unlikely(copy_to_user(argp, &v, sizeof(v)))) return -EFAULT; return 0; } case VIDEO1394_IOC_TALK_QUEUE_BUFFER: {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -