📄 video1394.c
字号:
/* set the sync flag */ if (flags & VIDEO1394_SYNC_FRAMES) ir_prg[0].control |= 0x00030000; ir_prg[0].address = kvirt_to_bus(buf); ir_prg[0].branchAddress = (virt_to_bus(&(ir_prg[1].control)) & 0xfffffff0) | 0x1; /* the second descriptor will read PAGE_SIZE-4 bytes */ ir_prg[1].control = (0x280C << 16) | (PAGE_SIZE-4); ir_prg[1].address = kvirt_to_bus(buf+4); ir_prg[1].branchAddress = (virt_to_bus(&(ir_prg[2].control)) & 0xfffffff0) | 0x1; for (i=2;i<d->nb_cmd-1;i++) { ir_prg[i].control = (0x280C << 16) | PAGE_SIZE; ir_prg[i].address = kvirt_to_bus(buf+(i-1)*PAGE_SIZE); ir_prg[i].branchAddress = (virt_to_bus(&(ir_prg[i+1].control)) & 0xfffffff0) | 0x1; } /* the last descriptor will generate an interrupt */ ir_prg[i].control = (0x283C << 16) | d->left_size; ir_prg[i].address = kvirt_to_bus(buf+(i-1)*PAGE_SIZE);} static void initialize_dma_ir_ctx(struct dma_iso_ctx *d, int tag, int flags){ struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; int i; ohci1394_stop_context(ohci, d->ctrlClear, NULL); for (i=0;i<d->num_desc;i++) { initialize_dma_ir_prg(d, i, flags); reset_ir_status(d, i); } /* reset the ctrl register */ reg_write(ohci, d->ctrlClear, 0xf0000000); /* Set bufferFill */ reg_write(ohci, d->ctrlSet, 0x80000000); /* Set isoch header */ if (flags & VIDEO1394_INCLUDE_ISO_HEADERS) reg_write(ohci, d->ctrlSet, 0x40000000); /* Set the context match register to match on all tags, sync for sync tag, and listen to d->channel */ reg_write(ohci, d->ctxMatch, 0xf0000000|((tag&0xf)<<8)|d->channel); /* Set up isoRecvIntMask to generate interrupts */ reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1<<d->ctx);}/* find which context is listening to this channel */int ir_ctx_listening(struct video_card *video, int channel){ int i; struct ti_ohci *ohci = video->ohci; for (i=0;i<ohci->nb_iso_rcv_ctx-1;i++) if (video->ir_context[i]) { if (video->ir_context[i]->channel==channel) return i; } PRINT(KERN_ERR, ohci->id, "no iso context is listening to channel %d", channel); return -1;}int it_ctx_talking(struct video_card *video, int channel){ int i; struct ti_ohci *ohci = video->ohci; for (i=0;i<ohci->nb_iso_xmit_ctx;i++) if (video->it_context[i]) { if (video->it_context[i]->channel==channel) return i; } PRINT(KERN_ERR, ohci->id, "no iso context is talking to channel %d", channel); return -1;}int wakeup_dma_ir_ctx(struct ti_ohci *ohci, struct dma_iso_ctx *d) { int i; if (d==NULL) { PRINT(KERN_ERR, ohci->id, "Iso receive event received but " "context not allocated"); return -EFAULT; } spin_lock(&d->lock); for (i=0;i<d->num_desc;i++) { if (d->ir_prg[i][d->nb_cmd-1].status & 0xFFFF0000) { reset_ir_status(d, i); d->buffer_status[i] = VIDEO1394_BUFFER_READY; } } spin_unlock(&d->lock); if (waitqueue_active(&d->waitq)) wake_up_interruptible(&d->waitq); return 0;}int wakeup_dma_it_ctx(struct ti_ohci *ohci, struct dma_iso_ctx *d) { int i; if (d==NULL) { PRINT(KERN_ERR, ohci->id, "Iso transmit event received but " "context not allocated"); return -EFAULT; } spin_lock(&d->lock); for (i=0;i<d->num_desc;i++) { if (d->it_prg[i][d->nb_cmd-1].end.status & 0xFFFF0000) { d->it_prg[i][d->nb_cmd-1].end.status = 0; d->buffer_status[i] = VIDEO1394_BUFFER_READY; } } spin_unlock(&d->lock); if (waitqueue_active(&d->waitq)) wake_up_interruptible(&d->waitq); return 0;}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]; unsigned long buf = (unsigned long)d->buf+n*d->buf_size; int i; for (i=0;i<d->nb_cmd;i++) { it_prg[i].begin.control = OUTPUT_MORE_IMMEDIATE | 8 ; it_prg[i].begin.address = 0; it_prg[i].begin.status = 0; /* FIXME: what is the tag value + speed selection */ it_prg[i].data[0] = (DMA_SPEED_400<<16) | (d->channel<<8) | 0xa0; if (i==0) it_prg[i].data[0] |= sync_tag; it_prg[i].data[1] = d->packet_size << 16; it_prg[i].data[2] = 0; it_prg[i].data[3] = 0; it_prg[i].end.control = 0x100c0000; it_prg[i].end.address = kvirt_to_bus(buf+i*d->packet_size); if (i<d->nb_cmd-1) { it_prg[i].end.control |= d->packet_size; it_prg[i].begin.branchAddress = (virt_to_bus(&(it_prg[i+1].begin.control)) & 0xfffffff0) | 0x3; it_prg[i].end.branchAddress = (virt_to_bus(&(it_prg[i+1].begin.control)) & 0xfffffff0) | 0x3; } else { /* the last prg generates an interrupt */ it_prg[i].end.control |= 0x08300000 | 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;#if 0 printk("%d:%d: %08x-%08x ctrl %08x brch %08x d0 %08x d1 %08x\n",n,i, virt_to_bus(&(it_prg[i].begin.control)), virt_to_bus(&(it_prg[i].end.control)), it_prg[i].end.control, it_prg[i].end.branchAddress, it_prg[i].data[0], it_prg[i].data[1]);#endif }}static void initialize_dma_it_ctx(struct dma_iso_ctx *d, int sync_tag){ struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; int i; 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 int do_iso_mmap(struct ti_ohci *ohci, struct dma_iso_ctx *d, const char *adr, unsigned long size){ unsigned long start=(unsigned long) adr; unsigned long page,pos; if (size>d->num_desc * d->buf_size) { PRINT(KERN_ERR, ohci->id, "iso context %d buf size is different from mmap size", d->ctx); return -EINVAL; } if (!d->buf) { PRINT(KERN_ERR, ohci->id, "iso context %d is not allocated", d->ctx); return -EINVAL; } pos=(unsigned long) d->buf; while (size > 0) { page = kvirt_to_pa(pos); if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; start+=PAGE_SIZE; pos+=PAGE_SIZE; size-=PAGE_SIZE; } return 0;}static int video1394_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct video_card *video = &video_cards[MINOR(inode->i_rdev)]; struct ti_ohci *ohci= video->ohci; unsigned long flags; switch(cmd) { case VIDEO1394_LISTEN_CHANNEL: case VIDEO1394_TALK_CHANNEL: { struct video1394_mmap v; u64 mask; int i; if(copy_from_user(&v, (void *)arg, sizeof(v))) return -EFAULT; if (v.channel<0 || v.channel>(ISO_CHANNELS-1)) { PRINT(KERN_ERR, ohci->id, "iso channel %d out of bound", v.channel); return -EFAULT; } 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)); if (ohci->ISO_channel_usage & mask) { PRINT(KERN_ERR, ohci->id, "channel %d is already taken", v.channel); return -EFAULT; } ohci->ISO_channel_usage |= mask; if (v.buf_size<=0) { PRINT(KERN_ERR, ohci->id, "Invalid %d length buffer requested",v.buf_size); return -EFAULT; } if (v.nb_buffers<=0) { PRINT(KERN_ERR, ohci->id, "Invalid %d buffers requested",v.nb_buffers); return -EFAULT; } if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) { PRINT(KERN_ERR, ohci->id, "%d buffers of size %d bytes is too big", v.nb_buffers, v.buf_size); return -EFAULT; } if (cmd == VIDEO1394_LISTEN_CHANNEL) { /* find a free iso receive context */ for (i=0;i<ohci->nb_iso_rcv_ctx-1;i++) if (video->ir_context[i]==NULL) break; if (i==(ohci->nb_iso_rcv_ctx-1)) { PRINT(KERN_ERR, ohci->id, "no iso context available"); return -EFAULT; } video->ir_context[i] = alloc_dma_iso_ctx(ohci, ISO_RECEIVE, i+1, v.nb_buffers, v.buf_size, v.channel, 0); if (video->ir_context[i] == NULL) { PRINT(KERN_ERR, ohci->id, "Couldn't allocate ir context"); return -EFAULT; } initialize_dma_ir_ctx(video->ir_context[i], v.sync_tag, v.flags); video->current_ctx = video->ir_context[i]; v.buf_size = video->ir_context[i]->buf_size; PRINT(KERN_INFO, ohci->id, "iso context %d listen on channel %d", i+1, v.channel); } else { /* find a free iso transmit context */ for (i=0;i<ohci->nb_iso_xmit_ctx;i++) if (video->it_context[i]==NULL) break; if (i==ohci->nb_iso_xmit_ctx) { PRINT(KERN_ERR, ohci->id, "no iso context available"); return -EFAULT; } video->it_context[i] = alloc_dma_iso_ctx(ohci, ISO_TRANSMIT, i, v.nb_buffers, v.buf_size, v.channel, v.packet_size); if (video->it_context[i] == NULL) { PRINT(KERN_ERR, ohci->id, "Couldn't allocate it context"); return -EFAULT; } initialize_dma_it_ctx(video->it_context[i], v.sync_tag); video->current_ctx = video->it_context[i]; v.buf_size = video->it_context[i]->buf_size; PRINT(KERN_INFO, ohci->id, "iso context %d talk on channel %d", i, v.channel); } if(copy_to_user((void *)arg, &v, sizeof(v))) return -EFAULT; return 0; } case VIDEO1394_UNLISTEN_CHANNEL: case VIDEO1394_UNTALK_CHANNEL: { int channel; u64 mask; int i; if(copy_from_user(&channel, (void *)arg, sizeof(int))) return -EFAULT; if (channel<0 || channel>(ISO_CHANNELS-1)) { PRINT(KERN_ERR, ohci->id, "iso channel %d out of bound", channel); return -EFAULT; } mask = (u64)0x1<<channel; if (!(ohci->ISO_channel_usage & mask)) { PRINT(KERN_ERR, ohci->id, "channel %d is not being used", channel); return -EFAULT; } ohci->ISO_channel_usage &= ~mask; if (cmd == VIDEO1394_UNLISTEN_CHANNEL) { i = ir_ctx_listening(video, channel); if (i<0) return -EFAULT; free_dma_iso_ctx(&video->ir_context[i]); PRINT(KERN_INFO, ohci->id, "iso context %d stop listening on channel %d", i+1, channel); } else { i = it_ctx_talking(video, channel); if (i<0) return -EFAULT; free_dma_iso_ctx(&video->it_context[i]); PRINT(KERN_INFO, ohci->id, "iso context %d stop talking on channel %d", i, channel); } return 0; } case VIDEO1394_LISTEN_QUEUE_BUFFER: { struct video1394_wait v; struct dma_iso_ctx *d; int i; if(copy_from_user(&v, (void *)arg, sizeof(v))) return -EFAULT; i = ir_ctx_listening(video, v.channel); if (i<0) return -EFAULT; d = video->ir_context[i]; if ((v.buffer<0) || (v.buffer>d->num_desc)) { PRINT(KERN_ERR, ohci->id, "buffer %d out of range",v.buffer); return -EFAULT; } spin_lock_irqsave(&d->lock,flags); if (d->buffer_status[v.buffer]==VIDEO1394_BUFFER_QUEUED) { PRINT(KERN_ERR, ohci->id, "buffer %d is already used",v.buffer); spin_unlock_irqrestore(&d->lock,flags); return -EFAULT; } d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; if (d->last_buffer>=0) d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = (virt_to_bus(&(d->ir_prg[v.buffer][0].control)) & 0xfffffff0) | 0x1; d->last_buffer = v.buffer; 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->id, "Starting iso DMA ctx=%d",d->ctx); /* Tell the controller where the first program is */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -