📄 dv1394.c
字号:
}/*** 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; if(init->api_version != DV1394_API_VERSION) goto err; /* first sanitize all the parameters */ if( (init->n_frames < 2) || (init->n_frames > DV1394_MAX_FRAMES) ) goto err; if( (init->format != DV1394_NTSC) && (init->format != DV1394_PAL) ) goto err; 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->user_buf) && (video->user_buf_size != new_buf_size) ) { goto err; } /* shutdown the card if it's currently active */ /* (the card should not be reset if the parameters are screwy) */ if( video_card_initialized(video) ) 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 */ /* XXX this should be the last step of initialization, since the interrupt handler uses ohci_i*_ctx to indicate whether or not it is safe to touch frames. I'm not making this change quite yet, since it would be better to clean up the init/shutdown process first.*/ 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_ctx; } else { 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_ctx; } else { 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_frames; } } if(video->user_buf == NULL) { unsigned int i; /* allocate the ringbuffer */ video->user_buf = rvmalloc(new_buf_size); if(!video->user_buf) { printk(KERN_ERR "dv1394: Cannot allocate frame buffers\n"); goto err_frames; } video->user_buf_size = new_buf_size; /* allocate the sglist to hold the DMA addresses */ video->user_dma.n_pages = video->user_buf_size / PAGE_SIZE; video->user_dma.sglist = kmalloc(video->user_dma.n_pages * sizeof(struct scatterlist), GFP_KERNEL); if(!video->user_dma.sglist) { printk(KERN_ERR "dv1394: Cannot allocate sglist for user buffer\n"); goto err_user_buf; } /* initialize all fields of all sglist entries to zero (new requirement due to PCI changes in 2.4.13) */ memset(video->user_dma.sglist, 0, video->user_dma.n_pages * sizeof(struct scatterlist)); /* fill the sglist with the kernel addresses of pages in the non-contiguous buffer */ for(i = 0; i < video->user_dma.n_pages; i++) { unsigned long va = (unsigned long) video->user_buf + i * PAGE_SIZE; video->user_dma.sglist[i].page = vmalloc_to_page((void *)va); video->user_dma.sglist[i].length = PAGE_SIZE; } /* map the buffer in the IOMMU */ /* the user_data buffer only allows DMA *to* the card for transmission; incoming DV data comes through the packet_buffer first, and then is copied to user_data */ video->user_dma.n_dma_pages = pci_map_sg(video->ohci->dev, &video->user_dma.sglist[0], video->user_dma.n_pages, PCI_DMA_TODEVICE); if(video->user_dma.n_dma_pages == 0) { printk(KERN_ERR "dv1394: Error mapping user buffer to the IOMMU\n"); goto err_user_buf; } debug_printk("dv1394: Allocated %d frame buffers, total %u pages (%u DMA pages), %lu bytes\n", video->n_frames, video->user_dma.n_pages, video->user_dma.n_dma_pages, video->user_buf_size); } /* set up the frame->data pointers */ for(i = 0; i < video->n_frames; i++) video->frames[i]->data = (unsigned long) video->user_buf + i * video->frame_size; /* allocate packet buffers */ video->packet_buffer_size = sizeof(struct packet) * MAX_PACKET_BUFFER; if (video->packet_buffer_size % PAGE_SIZE) video->packet_buffer_size += PAGE_SIZE - (video->packet_buffer_size % PAGE_SIZE); video->packet_buffer = kmalloc(video->packet_buffer_size, GFP_KERNEL); if(!video->packet_buffer) { printk(KERN_ERR "dv1394: Cannot allocate packet buffers"); retval = -ENOMEM; goto err_user_buf; } /* map the packet buffer into the IOMMU */ video->packet_buffer_dma = pci_map_single(video->ohci->dev, video->packet_buffer, video->packet_buffer_size, PCI_DMA_FROMDEVICE); if(!video->packet_buffer_dma) { printk(KERN_ERR "dv1394: Cannot map packet buffer to IOMMU"); kfree(video->packet_buffer); video->packet_buffer = NULL; retval = -ENOMEM; goto err_user_buf; } debug_printk("dv1394: Allocated %d packet buffers for receive, total %lu bytes\n", MAX_PACKET_BUFFER, video->packet_buffer_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_user_buf: if(video->user_buf) { if(video->user_dma.sglist) { if(video->user_dma.n_dma_pages > 0) { /* unmap it from the IOMMU */ pci_unmap_sg(video->ohci->dev, video->user_dma.sglist, video->user_dma.n_pages, PCI_DMA_TODEVICE); video->user_dma.n_dma_pages = 0; } kfree(video->user_dma.sglist); video->user_dma.sglist = NULL; video->user_dma.n_pages = 0; } rvfree(video->user_buf, video->user_buf_size); video->user_buf = NULL; video->user_buf_size = 0; } err_frames: for(i = 0; i < DV1394_MAX_FRAMES; i++) { if(video->frames[i]) frame_delete(video->frames[i]); } video->n_frames = 0; err_ctx: if(video->ohci_it_ctx != -1) { ohci1394_unregister_iso_tasklet(video->ohci, &video->it_tasklet); video->ohci_it_ctx = -1; } if(video->ohci_ir_ctx != -1) { ohci1394_unregister_iso_tasklet(video->ohci, &video->ir_tasklet); video->ohci_ir_ctx = -1; } 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); err: 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 = 2; /* the following are now set via proc_fs or devfs */ 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); /* 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 */ 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); } } spin_unlock_irqrestore(&video->spinlock, flags);}static int do_dv1394_shutdown(struct video_card *video, int free_user_buf){ int i; unsigned long flags; debug_printk("dv1394: shutdown...\n"); /* stop DMA if in progress */ stop_dma(video); spin_lock_irqsave(&video->spinlock, flags); /* 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)); clear_bit(video->ohci_it_ctx, &video->ohci->it_ctx_usage); 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)); clear_bit(video->ohci_ir_ctx, &video->ohci->ir_ctx_usage); debug_printk("dv1394: IR context %d released\n", video->ohci_ir_ctx); video->ohci_ir_ctx = -1; } spin_unlock_irqrestore(&video->spinlock, flags); /* 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_user_buf && video->user_buf) { if(video->user_dma.sglist) { if(video->user_dma.n_dma_pages > 0) { /* unmap it from the IOMMU */ pci_unmap_sg(video->ohci->dev, video->user_dma.sglist, video->user_dma.n_pages, PCI_DMA_TODEVICE); video->user_dma.n_dma_pages = 0; } kfree(video->user_dma.sglist); video->user_dma.sglist = NULL; video->user_dma.n_pages = 0; } rvfree(video->user_buf, video->user_buf_size); video->user_buf = NULL; video->user_buf_size = 0; } if (video->packet_buffer) { pci_unmap_single(video->ohci->dev, video->packet_buffer_dma, video->packet_buffer_size,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -