📄 video1394.c
字号:
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; if (d->last_buffer>=0) d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = cpu_to_le32((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 */ reg_write(ohci, d->cmdPtr, virt_to_bus(&(d->ir_prg[v.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)) { PRINT(KERN_INFO, ohci->id, "Waking up iso dma ctx=%d", d->ctx); reg_write(ohci, d->ctrlSet, 0x1000); } } return 0; } case VIDEO1394_LISTEN_WAIT_BUFFER: case VIDEO1394_LISTEN_POLL_BUFFER: { struct video1394_wait v; struct dma_iso_ctx *d; int i; if(copy_from_user(&v, (void *)arg, sizeof(v))) return -EFAULT; d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel); if ((v.buffer<0) || (v.buffer>d->num_desc)) { PRINT(KERN_ERR, ohci->id, "Buffer %d out of range",v.buffer); return -EFAULT; } /* * 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_LISTEN_POLL_BUFFER) { /* for polling, return error code EINTR */ spin_unlock_irqrestore(&d->lock, flags); 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 d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; break; default: PRINT(KERN_ERR, ohci->id, "Buffer %d is not queued",v.buffer); spin_unlock_irqrestore(&d->lock, flags); return -EFAULT; } /* 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]== VIDEO1394_BUFFER_READY) { v.buffer=(v.buffer+1)%d->num_desc; i++; } spin_unlock_irqrestore(&d->lock, flags); v.buffer=i; if(copy_to_user((void *)arg, &v, sizeof(v))) return -EFAULT; return 0; } case VIDEO1394_TALK_QUEUE_BUFFER: { struct video1394_wait v; struct video1394_queue_variable qv; struct dma_iso_ctx *d; if(copy_from_user(&v, (void *)arg, sizeof(v))) return -EFAULT; d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel); if ((v.buffer<0) || (v.buffer>d->num_desc)) { PRINT(KERN_ERR, ohci->id, "Buffer %d out of range",v.buffer); return -EFAULT; } if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) { if (copy_from_user(&qv, (void *)arg, sizeof(qv))) return -EFAULT; if (!access_ok(VERIFY_READ, qv.packet_sizes, d->nb_cmd * sizeof(unsigned int))) { return -EFAULT; } } spin_lock_irqsave(&d->lock,flags); if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) { PRINT(KERN_ERR, ohci->id, "Buffer %d is already used",v.buffer); spin_unlock_irqrestore(&d->lock,flags); return -EFAULT; } if (d->flags & VIDEO1394_VARIABLE_PACKET_SIZE) { initialize_dma_it_prg_var_packet_queue( d, v.buffer, qv.packet_sizes, 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((virt_to_bus(&(d->it_prg[v.buffer][0].begin.control)) & 0xfffffff0) | 0x3); d->it_prg[d->last_buffer] [d->last_used_cmd[d->last_buffer] ].begin.branchAddress = cpu_to_le32((virt_to_bus(&(d->it_prg[v.buffer][0].begin.control)) & 0xfffffff0) | 0x3); d->next_buffer[d->last_buffer] = v.buffer; } 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; spin_unlock_irqrestore(&d->lock,flags); if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) { DBGMSG(ohci->id, "Starting iso transmit DMA ctx=%d", d->ctx); put_timestamp(ohci, d, d->last_buffer); /* Tell the controller where the first program is */ reg_write(ohci, d->cmdPtr, virt_to_bus(&(d->it_prg[v.buffer][0]))|0x3); /* Run IT context */ reg_write(ohci, d->ctrlSet, 0x8000); } else { /* Wake up dma context if necessary */ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { PRINT(KERN_INFO, ohci->id, "Waking up iso transmit dma ctx=%d", d->ctx); put_timestamp(ohci, d, d->last_buffer); reg_write(ohci, d->ctrlSet, 0x1000); } } return 0; } case VIDEO1394_TALK_WAIT_BUFFER: { struct video1394_wait v; struct dma_iso_ctx *d; if(copy_from_user(&v, (void *)arg, sizeof(v))) return -EFAULT; d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, v.channel); if ((v.buffer<0) || (v.buffer>d->num_desc)) { PRINT(KERN_ERR, ohci->id, "Buffer %d out of range",v.buffer); return -EFAULT; } switch(d->buffer_status[v.buffer]) { case VIDEO1394_BUFFER_READY: 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) return -EINTR;#endif d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; return 0; default: PRINT(KERN_ERR, ohci->id, "Buffer %d is not queued",v.buffer); return -EFAULT; } } default: return -EINVAL; }}/* * 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. * But e.g. pte_alloc() does not work in modules ... :-( */int video1394_mmap(struct file *file, struct vm_area_struct *vma){ struct file_ctx *ctx = (struct file_ctx *)file->private_data; struct video_card *video = ctx->video; struct ti_ohci *ohci = video->ohci; int res = -EINVAL; lock_kernel(); ohci = video->ohci; if (ctx->current_ctx == NULL) { PRINT(KERN_ERR, ohci->id, "Current iso context not set"); } else res = do_iso_mmap(ohci, ctx->current_ctx, vma); unlock_kernel(); return res;}static int video1394_open(struct inode *inode, struct file *file){ int i = ieee1394_file_to_instance(file); unsigned long flags; struct video_card *video = NULL; struct list_head *lh; struct file_ctx *ctx; spin_lock_irqsave(&video1394_cards_lock, flags); list_for_each(lh, &video1394_cards) { struct video_card *p = list_entry(lh, struct video_card, list); if (p->id == i) { video = p; break; } } spin_unlock_irqrestore(&video1394_cards_lock, flags); if (video == NULL) return -EIO; ctx = kmalloc(sizeof(struct file_ctx), GFP_KERNEL); if (ctx == NULL) { PRINT(KERN_ERR, video->ohci->id, "Cannot malloc file_ctx"); return -ENOMEM; } memset(ctx, 0, sizeof(struct file_ctx)); ctx->video = video; INIT_LIST_HEAD(&ctx->context_list); ctx->current_ctx = NULL; file->private_data = ctx; return 0;}static int video1394_release(struct inode *inode, struct file *file){ struct file_ctx *ctx = (struct file_ctx *)file->private_data; struct video_card *video = ctx->video; struct ti_ohci *ohci = video->ohci; struct list_head *lh, *next; u64 mask; lock_kernel(); list_for_each_safe(lh, next, &ctx->context_list) { struct dma_iso_ctx *d; d = list_entry(lh, struct dma_iso_ctx, link); mask = (u64) 1 << d->channel; if (!(ohci->ISO_channel_usage & mask)) PRINT(KERN_ERR, ohci->id, "On release: Channel %d " "is not being used", d->channel); else ohci->ISO_channel_usage &= ~mask; PRINT(KERN_INFO, ohci->id, "On release: Iso %s context " "%d stop listening on channel %d", d->type == OHCI_ISO_RECEIVE ? "receive" : "transmit", d->ctx, d->channel); free_dma_iso_ctx(d); } kfree(ctx); file->private_data = NULL; unlock_kernel(); return 0;}static struct file_operations video1394_fops={ .owner = THIS_MODULE, .ioctl = video1394_ioctl, .mmap = video1394_mmap, .open = video1394_open, .release = video1394_release};static int video1394_init(struct ti_ohci *ohci){ struct video_card *video; unsigned long flags; char name[16]; int minor; video = kmalloc(sizeof(struct video_card), GFP_KERNEL); if (video == NULL) { PRINT(KERN_ERR, ohci->id, "Cannot allocate video_card"); return -1; } memset(video, 0, sizeof(struct video_card)); spin_lock_irqsave(&video1394_cards_lock, flags); INIT_LIST_HEAD(&video->list); list_add_tail(&video->list, &video1394_cards); spin_unlock_irqrestore(&video1394_cards_lock, flags); video->id = ohci->id; video->ohci = ohci; sprintf(name, "%d", video->id); minor = IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + video->id; video->devfs = devfs_register(devfs_handle, name, DEVFS_FL_AUTO_OWNER, IEEE1394_MAJOR, minor, S_IFCHR | S_IRUSR | S_IWUSR, &video1394_fops, NULL); return 0;}/* Must be called under spinlock */static void remove_card(struct video_card *video){ devfs_unregister(video->devfs); list_del(&video->list); kfree(video);}static void video1394_remove_host (struct hpsb_host *host){ struct ti_ohci *ohci; unsigned long flags; struct list_head *lh, *next; struct video_card *p; /* We only work with the OHCI-1394 driver */ if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) return; ohci = (struct ti_ohci *)host->hostdata; spin_lock_irqsave(&video1394_cards_lock, flags); list_for_each_safe(lh, next, &video1394_cards) { p = list_entry(lh, struct video_card, list); if (p->ohci == ohci) { remove_card(p); break; } } spin_unlock_irqrestore(&video1394_cards_lock, flags); return;}static void video1394_add_host (struct hpsb_host *host){ struct ti_ohci *ohci; /* We only work with the OHCI-1394 driver */ if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) return; ohci = (struct ti_ohci *)host->hostdata; video1394_init(ohci); return;}static struct hpsb_highlevel_ops hl_ops = { .add_host = video1394_add_host, .remove_host = video1394_remove_host,};MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>");MODULE_DESCRIPTION("driver for digital video on OHCI board");MODULE_SUPPORTED_DEVICE(VIDEO1394_DRIVER_NAME);MODULE_LICENSE("GPL");static void __exit video1394_exit_module (void){ hpsb_unregister_highlevel (hl_handle); devfs_unregister(devfs_handle); ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394); PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module");}static int __init video1394_init_module (void){ if (ieee1394_register_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394, THIS_MODULE, &video1394_fops)) { PRINT_G(KERN_ERR, "video1394: unable to get minor device block"); return -EIO; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) devfs_handle = devfs_mk_dir(NULL, VIDEO1394_DRIVER_NAME, strlen(VIDEO1394_DRIVER_NAME), NULL);#else devfs_handle = devfs_mk_dir(NULL, VIDEO1394_DRIVER_NAME, NULL);#endif hl_handle = hpsb_register_highlevel (VIDEO1394_DRIVER_NAME, &hl_ops); if (hl_handle == NULL) { PRINT_G(KERN_ERR, "No more memory for driver\n"); devfs_unregister(devfs_handle); ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_VIDEO1394); return -ENOMEM; } PRINT_G(KERN_INFO, "Installed " VIDEO1394_DRIVER_NAME " module"); return 0;}module_init(video1394_init_module);module_exit(video1394_exit_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -