📄 dv1394.c
字号:
want_interrupt = ((i % (MAX_PACKETS/2)) == 0 || i == (MAX_PACKETS-1));
fill_input_last( &(block->u.in.il), want_interrupt, 512, data_dma);
/* link descriptors */
last_branch_address = f->frame_end_branch;
if (last_branch_address != NULL)
*(last_branch_address) = cpu_to_le32(block_dma | 1); /* set Z=1 */
f->frame_end_branch = &(block->u.in.il.q[2]);
}
} /* next j */
spin_unlock_irqrestore(&video->spinlock, irq_flags);
}
/*** MANAGEMENT FUNCTIONS **************************************************/
static int do_dv1394_init(struct video_card *video, struct dv1394_init *init)
{
unsigned long flags, new_buf_size;
int i;
u64 chan_mask;
int retval = -EINVAL;
debug_printk("dv1394: initialising %d\n", video->id);
if (init->api_version != DV1394_API_VERSION)
return -EINVAL;
/* first sanitize all the parameters */
if ( (init->n_frames < 2) || (init->n_frames > DV1394_MAX_FRAMES) )
return -EINVAL;
if ( (init->format != DV1394_NTSC) && (init->format != DV1394_PAL) )
return -EINVAL;
if ( (init->syt_offset == 0) || (init->syt_offset > 50) )
/* default SYT offset is 3 cycles */
init->syt_offset = 3;
if ( (init->channel > 63) || (init->channel < 0) )
init->channel = 63;
chan_mask = (u64)1 << init->channel;
/* calculate what size DMA buffer is needed */
if (init->format == DV1394_NTSC)
new_buf_size = DV1394_NTSC_FRAME_SIZE * init->n_frames;
else
new_buf_size = DV1394_PAL_FRAME_SIZE * init->n_frames;
/* round up to PAGE_SIZE */
if (new_buf_size % PAGE_SIZE) new_buf_size += PAGE_SIZE - (new_buf_size % PAGE_SIZE);
/* don't allow the user to allocate the DMA buffer more than once */
if (video->dv_buf.kvirt && video->dv_buf_size != new_buf_size) {
printk("dv1394: re-sizing the DMA buffer is not allowed\n");
return -EINVAL;
}
/* shutdown the card if it's currently active */
/* (the card should not be reset if the parameters are screwy) */
do_dv1394_shutdown(video, 0);
/* try to claim the ISO channel */
spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
if (video->ohci->ISO_channel_usage & chan_mask) {
spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
retval = -EBUSY;
goto err;
}
video->ohci->ISO_channel_usage |= chan_mask;
spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
video->channel = init->channel;
/* initialize misc. fields of video */
video->n_frames = init->n_frames;
video->pal_or_ntsc = init->format;
video->cip_accum = 0;
video->continuity_counter = 0;
video->active_frame = -1;
video->first_clear_frame = 0;
video->n_clear_frames = video->n_frames;
video->dropped_frames = 0;
video->write_off = 0;
video->first_run = 1;
video->current_packet = -1;
video->first_frame = 0;
if (video->pal_or_ntsc == DV1394_NTSC) {
video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_NTSC;
video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_NTSC;
video->frame_size = DV1394_NTSC_FRAME_SIZE;
} else {
video->cip_n = init->cip_n != 0 ? init->cip_n : CIP_N_PAL;
video->cip_d = init->cip_d != 0 ? init->cip_d : CIP_D_PAL;
video->frame_size = DV1394_PAL_FRAME_SIZE;
}
video->syt_offset = init->syt_offset;
/* find and claim DMA contexts on the OHCI card */
if (video->ohci_it_ctx == -1) {
ohci1394_init_iso_tasklet(&video->it_tasklet, OHCI_ISO_TRANSMIT,
it_tasklet_func, (unsigned long) video);
if (ohci1394_register_iso_tasklet(video->ohci, &video->it_tasklet) < 0) {
printk(KERN_ERR "dv1394: could not find an available IT DMA context\n");
retval = -EBUSY;
goto err;
}
video->ohci_it_ctx = video->it_tasklet.context;
debug_printk("dv1394: claimed IT DMA context %d\n", video->ohci_it_ctx);
}
if (video->ohci_ir_ctx == -1) {
ohci1394_init_iso_tasklet(&video->ir_tasklet, OHCI_ISO_RECEIVE,
ir_tasklet_func, (unsigned long) video);
if (ohci1394_register_iso_tasklet(video->ohci, &video->ir_tasklet) < 0) {
printk(KERN_ERR "dv1394: could not find an available IR DMA context\n");
retval = -EBUSY;
goto err;
}
video->ohci_ir_ctx = video->ir_tasklet.context;
debug_printk("dv1394: claimed IR DMA context %d\n", video->ohci_ir_ctx);
}
/* allocate struct frames */
for (i = 0; i < init->n_frames; i++) {
video->frames[i] = frame_new(i, video);
if (!video->frames[i]) {
printk(KERN_ERR "dv1394: Cannot allocate frame structs\n");
retval = -ENOMEM;
goto err;
}
}
if (!video->dv_buf.kvirt) {
/* allocate the ringbuffer */
retval = dma_region_alloc(&video->dv_buf, new_buf_size, video->ohci->dev, PCI_DMA_TODEVICE);
if (retval)
goto err;
video->dv_buf_size = new_buf_size;
debug_printk("dv1394: Allocated %d frame buffers, total %u pages (%u DMA pages), %lu bytes\n",
video->n_frames, video->dv_buf.n_pages,
video->dv_buf.n_dma_pages, video->dv_buf_size);
}
/* set up the frame->data pointers */
for (i = 0; i < video->n_frames; i++)
video->frames[i]->data = (unsigned long) video->dv_buf.kvirt + i * video->frame_size;
if (!video->packet_buf.kvirt) {
/* allocate packet buffer */
video->packet_buf_size = sizeof(struct packet) * video->n_frames * MAX_PACKETS;
if (video->packet_buf_size % PAGE_SIZE)
video->packet_buf_size += PAGE_SIZE - (video->packet_buf_size % PAGE_SIZE);
retval = dma_region_alloc(&video->packet_buf, video->packet_buf_size,
video->ohci->dev, PCI_DMA_FROMDEVICE);
if (retval)
goto err;
debug_printk("dv1394: Allocated %d packets in buffer, total %u pages (%u DMA pages), %lu bytes\n",
video->n_frames*MAX_PACKETS, video->packet_buf.n_pages,
video->packet_buf.n_dma_pages, video->packet_buf_size);
}
/* set up register offsets for IT context */
/* IT DMA context registers are spaced 16 bytes apart */
video->ohci_IsoXmitContextControlSet = OHCI1394_IsoXmitContextControlSet+16*video->ohci_it_ctx;
video->ohci_IsoXmitContextControlClear = OHCI1394_IsoXmitContextControlClear+16*video->ohci_it_ctx;
video->ohci_IsoXmitCommandPtr = OHCI1394_IsoXmitCommandPtr+16*video->ohci_it_ctx;
/* enable interrupts for IT context */
reg_write(video->ohci, OHCI1394_IsoXmitIntMaskSet, (1 << video->ohci_it_ctx));
debug_printk("dv1394: interrupts enabled for IT context %d\n", video->ohci_it_ctx);
/* set up register offsets for IR context */
/* IR DMA context registers are spaced 32 bytes apart */
video->ohci_IsoRcvContextControlSet = OHCI1394_IsoRcvContextControlSet+32*video->ohci_ir_ctx;
video->ohci_IsoRcvContextControlClear = OHCI1394_IsoRcvContextControlClear+32*video->ohci_ir_ctx;
video->ohci_IsoRcvCommandPtr = OHCI1394_IsoRcvCommandPtr+32*video->ohci_ir_ctx;
video->ohci_IsoRcvContextMatch = OHCI1394_IsoRcvContextMatch+32*video->ohci_ir_ctx;
/* enable interrupts for IR context */
reg_write(video->ohci, OHCI1394_IsoRecvIntMaskSet, (1 << video->ohci_ir_ctx) );
debug_printk("dv1394: interrupts enabled for IR context %d\n", video->ohci_ir_ctx);
return 0;
err:
do_dv1394_shutdown(video, 1);
return retval;
}
/* if the user doesn't bother to call ioctl(INIT) before starting
mmap() or read()/write(), just give him some default values */
static int do_dv1394_init_default(struct video_card *video)
{
struct dv1394_init init;
init.api_version = DV1394_API_VERSION;
init.n_frames = DV1394_MAX_FRAMES / 4;
init.channel = video->channel;
init.format = video->pal_or_ntsc;
init.cip_n = video->cip_n;
init.cip_d = video->cip_d;
init.syt_offset = video->syt_offset;
return do_dv1394_init(video, &init);
}
/* do NOT call from interrupt context */
static void stop_dma(struct video_card *video)
{
unsigned long flags;
int i;
/* no interrupts */
spin_lock_irqsave(&video->spinlock, flags);
video->dma_running = 0;
if ( (video->ohci_it_ctx == -1) && (video->ohci_ir_ctx == -1) )
goto out;
/* stop DMA if in progress */
if ( (video->active_frame != -1) ||
(reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) ||
(reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) {
/* clear the .run bits */
reg_write(video->ohci, video->ohci_IsoXmitContextControlClear, (1 << 15));
reg_write(video->ohci, video->ohci_IsoRcvContextControlClear, (1 << 15));
flush_pci_write(video->ohci);
video->active_frame = -1;
video->first_run = 1;
/* wait until DMA really stops */
i = 0;
while (i < 1000) {
/* wait 0.1 millisecond */
udelay(100);
if ( (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) ||
(reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) {
/* still active */
debug_printk("dv1394: stop_dma: DMA not stopped yet\n" );
mb();
} else {
debug_printk("dv1394: stop_dma: DMA stopped safely after %d ms\n", i/10);
break;
}
i++;
}
if (i == 1000) {
printk(KERN_ERR "dv1394: stop_dma: DMA still going after %d ms!\n", i/10);
}
}
else
debug_printk("dv1394: stop_dma: already stopped.\n");
out:
spin_unlock_irqrestore(&video->spinlock, flags);
}
static void do_dv1394_shutdown(struct video_card *video, int free_dv_buf)
{
int i;
debug_printk("dv1394: shutdown...\n");
/* stop DMA if in progress */
stop_dma(video);
/* release the DMA contexts */
if (video->ohci_it_ctx != -1) {
video->ohci_IsoXmitContextControlSet = 0;
video->ohci_IsoXmitContextControlClear = 0;
video->ohci_IsoXmitCommandPtr = 0;
/* disable interrupts for IT context */
reg_write(video->ohci, OHCI1394_IsoXmitIntMaskClear, (1 << video->ohci_it_ctx));
/* remove tasklet */
ohci1394_unregister_iso_tasklet(video->ohci, &video->it_tasklet);
debug_printk("dv1394: IT context %d released\n", video->ohci_it_ctx);
video->ohci_it_ctx = -1;
}
if (video->ohci_ir_ctx != -1) {
video->ohci_IsoRcvContextControlSet = 0;
video->ohci_IsoRcvContextControlClear = 0;
video->ohci_IsoRcvCommandPtr = 0;
video->ohci_IsoRcvContextMatch = 0;
/* disable interrupts for IR context */
reg_write(video->ohci, OHCI1394_IsoRecvIntMaskClear, (1 << video->ohci_ir_ctx));
/* remove tasklet */
ohci1394_unregister_iso_tasklet(video->ohci, &video->ir_tasklet);
debug_printk("dv1394: IR context %d released\n", video->ohci_ir_ctx);
video->ohci_ir_ctx = -1;
}
/* release the ISO channel */
if (video->channel != -1) {
u64 chan_mask;
unsigned long flags;
chan_mask = (u64)1 << video->channel;
spin_lock_irqsave(&video->ohci->IR_channel_lock, flags);
video->ohci->ISO_channel_usage &= ~(chan_mask);
spin_unlock_irqrestore(&video->ohci->IR_channel_lock, flags);
video->channel = -1;
}
/* free the frame structs */
for (i = 0; i < DV1394_MAX_FRAMES; i++) {
if (video->frames[i])
frame_delete(video->frames[i]);
video->frames[i] = NULL;
}
video->n_frames = 0;
/* we can't free the DMA buffer unless it is guaranteed that
no more user-space mappings exist */
if (free_dv_buf) {
dma_region_free(&video->dv_buf);
video->dv_buf_size = 0;
}
/* free packet buffer */
dma_region_free(&video->packet_buf);
video->packet_buf_size = 0;
debug_printk("dv1394: shutdown OK\n");
}
/*
**********************************
*** MMAP() THEORY OF OPERATION ***
**********************************
The ringbuffer cannot be re-allocated or freed while
a user program maintains a mapping of it. (note that a mapping
can persist even after the device fd is closed!)
So, only let the user process allocate the DMA buffer once.
To resize or deallocate it, you must close the device file
and open it again.
Previously Dan M. hacked out a scheme that allowed the DMA
buffer to change by forcefully unmapping it from the user's
address space. It was prone to error because it's very hard to
track all the places the buffer could have been mapped (we
would have had to walk the vma list of every process in the
system to be sure we found all the mappings!). Instead, we
force the user to choose one buffer size and stick with
it. This small sacrifice is worth the huge reduction in
error-prone code in dv1394.
*/
static int dv1394_mmap(struct file *file, struct vm_area_struct *vma)
{
struct video_card *video = file_to_video_card(file);
int retval = -EINVAL;
/* serialize mmap */
mutex_lock(&video->mtx);
if ( ! video_card_initialized(video) ) {
retval = do_dv1394_init_default(video);
if (retval)
goto out;
}
retval = dma_region_mmap(&video->dv_buf, file, vma);
out:
mutex_unlock(&video->mtx);
return retval;
}
/*** DEVICE FILE INTERFACE *************************************************/
/* no need to serialize, multiple threads OK */
static unsigned int dv1394_poll(struct file *file, struct poll_table_struct *wait)
{
struct video_card *video = file_to_video_card(file);
unsigned int mask = 0;
unsigned long flags;
poll_wait(file, &video->waitq, wait);
spin_lock_irqsave(&video->spinlock, flags);
if ( video->n_frames == 0 ) {
} else if ( video->active_frame == -1 ) {
/* nothing going on */
mask |= POLLOUT;
} else {
/* any clear/ready buffers? */
if (video->n_clear_frames >0)
mask |= POLLOUT | POLLIN;
}
spin_unlock_irqrestore(&video->spinlock, flags);
return mask;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -