📄 comedi_fops.c
字号:
async=dev->write_subdev->async; }else{ async=dev->read_subdev->async; } if(async==NULL){ return -EINVAL; }#if LINUX_VERSION_CODE < 0x020300 if(vma->vm_offset != 0){ DPRINTK("comedi: mmap() offset must be 0.\n"); return -EINVAL; }#else if(vma->vm_pgoff != 0){ DPRINTK("comedi: mmap() offset must be 0.\n"); return -EINVAL; }#endif size = vma->vm_end - vma->vm_start; if(size>async->prealloc_bufsz) return -EFAULT; if(size&(~PAGE_MASK)) return -EFAULT; n_pages = size >> PAGE_SHIFT; for(i=0;i<n_pages;i++){ if(remap_page_range(start, __pa(async->buf_page_list[i]), PAGE_SIZE, PAGE_SHARED)){ return -EAGAIN; } start += PAGE_SIZE; } vma->vm_ops = &comedi_vm_ops;#if LINUX_VERSION_CODE < 0x020300 (void *)vma->vm_pte = async;#else vma->vm_private_data = async;#endif async->mmap_count++; return 0;}#endif#if LINUX_VERSION_CODE >= 0x020100static unsigned int comedi_poll_v22(struct file *file, poll_table * wait){ comedi_device *dev; comedi_subdevice *s; comedi_async *async; unsigned int mask; dev=comedi_get_device_by_minor(MINOR(RDEV_OF_FILE(file))); if(!dev->attached) { DPRINTK("no driver configured on comedi%i\n", dev->minor); return -ENODEV; } poll_wait(file, &dev->read_wait, wait); poll_wait(file, &dev->write_wait, wait); mask = 0; if(dev->read_subdev && dev->read_subdev->async){ s = dev->read_subdev; async = s->async; if(!s->busy || comedi_buf_read_n_available(async)>0 || !(s->subdev_flags&SDF_RUNNING)){ mask |= POLLIN | POLLRDNORM; } } if(dev->write_subdev && dev->write_subdev->async){ s = dev->write_subdev; async = s->async; if(!s->busy || !(s->subdev_flags&SDF_RUNNING) || comedi_buf_write_n_available(async)>0){ mask |= POLLOUT | POLLWRNORM; } } return mask;}#endifstatic ssize_t comedi_write_v22(struct file *file,const char *buf,size_t nbytes,loff_t *offset){ comedi_device *dev; comedi_subdevice *s; comedi_async *async; int n,m,count=0,retval=0; DECLARE_WAITQUEUE(wait,current); dev=comedi_get_device_by_minor(MINOR(RDEV_OF_FILE(file))); if(!dev->attached) { DPRINTK("no driver configured on comedi%i\n", dev->minor); return -ENODEV; } if(dev->write_subdev == NULL)return -EIO; s = dev->write_subdev; async = s->async; if(!nbytes)return 0; if(!s->busy) return 0; if(s->busy != file) return -EACCES; add_wait_queue(&dev->write_wait,&wait); while(nbytes>0 && !retval){ current->state=TASK_INTERRUPTIBLE; n=nbytes; m = n; if(async->buf_write_ptr + m > async->prealloc_bufsz){ m = async->prealloc_bufsz - async->buf_write_ptr; } m = comedi_buf_write_alloc(async, m); if(m < n) n = m; if(n==0){ if(file->f_flags&O_NONBLOCK){ retval=-EAGAIN; break; } if(signal_pending(current)){ retval=-ERESTARTSYS; break; } if(!(s->subdev_flags&SDF_RUNNING)){ if(s->runflags & SRF_ERROR){ retval = -EPIPE; }else{ retval = 0; } do_become_nonbusy(dev,s); break; } schedule(); continue; } m = copy_from_user(async->prealloc_buf + async->buf_write_ptr, buf, n); if(m){ n -= m; retval = -EFAULT; } comedi_buf_munge(dev, s, n); comedi_buf_write_free(async, n); count+=n; nbytes-=n; buf+=n; break; /* makes device work like a pipe */ } current->state=TASK_RUNNING; remove_wait_queue(&dev->write_wait,&wait); return (count ? count : retval);}static ssize_t comedi_read_v22(struct file * file,char *buf,size_t nbytes,loff_t *offset){ comedi_device *dev; comedi_subdevice *s; comedi_async *async; int n,m,count=0,retval=0; DECLARE_WAITQUEUE(wait,current); dev=comedi_get_device_by_minor(MINOR(RDEV_OF_FILE(file))); if(!dev->attached) { DPRINTK("no driver configured on comedi%i\n", dev->minor); return -ENODEV; } s = dev->read_subdev; if(s == NULL)return -EIO; async = s->async; if(!nbytes)return 0; if(!s->busy) return 0; if(s->busy != file) return -EACCES; add_wait_queue(&dev->read_wait,&wait); while(nbytes>0 && !retval){ current->state=TASK_INTERRUPTIBLE; n=nbytes; m = comedi_buf_read_n_available(async);//printk("%d available\n",m); if(async->buf_read_ptr + m > async->prealloc_bufsz){ m = async->prealloc_bufsz - async->buf_read_ptr; }//printk("%d contiguous\n",m); if(m<n)n=m; if(n==0){ if(!(s->subdev_flags&SDF_RUNNING)){ do_become_nonbusy(dev,s); if(s->runflags & SRF_ERROR){ retval = -EPIPE; }else{ retval = 0; } break; } if(file->f_flags&O_NONBLOCK){ retval=-EAGAIN; break; } if(signal_pending(current)){ retval=-ERESTARTSYS; break; } schedule(); continue; } m = copy_to_user(buf, async->prealloc_buf + async->buf_read_ptr, n); if(m){ n -= m; retval = -EFAULT; } comedi_buf_read_free(async, n); count+=n; nbytes-=n; buf+=n; break; /* makes device work like a pipe */ } if(!(s->subdev_flags&SDF_RUNNING) && !(s->runflags & SRF_ERROR) && async->buf_read_count - async->buf_write_count == 0) { do_become_nonbusy(dev,s); } current->state=TASK_RUNNING; remove_wait_queue(&dev->read_wait,&wait); return (count ? count : retval);}/* This function restores a subdevice to an idle state. */void do_become_nonbusy(comedi_device *dev,comedi_subdevice *s){ comedi_async *async = s->async; /* we do this because it's useful for the non-standard cases */ s->subdev_flags &= ~SDF_RUNNING;#ifdef CONFIG_COMEDI_RT if(s->runflags&SRF_RT){ comedi_switch_to_non_rt(dev); s->runflags &= ~SRF_RT; }#endif if(async){ init_async_buf( async ); }else{ printk("BUG: (?) do_become_nonbusy called with async=0\n"); } s->busy=NULL;}/* no chance that these will change soon */#define SEEK_SET 0#define SEEK_CUR 1#define SEEK_END 2static loff_t comedi_lseek_v22(struct file *file,loff_t offset,int origin){ comedi_device *dev; loff_t new_offset; dev=comedi_get_device_by_minor(MINOR(RDEV_OF_FILE(file))); switch(origin){ case SEEK_SET: new_offset = offset; break; case SEEK_CUR: new_offset = file->f_pos + offset; break; case SEEK_END: new_offset = dev->n_subdevices + offset; break; default: return -EINVAL; } if(new_offset<0 || new_offset >= dev->n_subdevices) return -EINVAL; return file->f_pos=new_offset;}static int comedi_fop_open(struct inode *inode,struct file *file){ kdev_t minor=MINOR(inode->i_rdev); comedi_device *dev; char mod[32]; if(minor>=COMEDI_NDEVICES)return -ENODEV; dev=comedi_get_device_by_minor(minor); /* This is slightly hacky, but we want module autoloading * to work for root. * case: user opens device, attached -> ok * case: user opens device, unattached, in_request_module=0 -> autoload * case: user opens device, unattached, in_request_module=1 -> fail * case: root opens device, attached -> ok * case: root opens device, unattached, in_request_module=1 -> ok * (typically called from modprobe) * case: root opens device, unattached, in_request_module=0 -> autoload * * The last could be changed to "-> ok", which would deny root * autoloading. */ if(dev->attached) goto ok; if(!suser() && dev->in_request_module) return -ENODEV; if(suser() && dev->in_request_module) goto ok; dev->in_request_module=1; sprintf(mod,"char-major-%i-%i",COMEDI_MAJOR,minor);#ifdef CONFIG_KMOD request_module(mod);#endif dev->in_request_module=0; if(dev->attached || suser()) goto ok; return -ENODEV;ok: MOD_INC_USE_COUNT; if(dev->attached && dev->driver->module){ __MOD_INC_USE_COUNT(dev->driver->module); } if(dev->attached && dev->use_count==0 && dev->open){ dev->open(dev); } dev->use_count++; return 0;}static int comedi_close_v22(struct inode *inode,struct file *file){ comedi_device *dev=comedi_get_device_by_minor(MINOR(inode->i_rdev)); comedi_subdevice *s = NULL; int i; for(i=0;i<dev->n_subdevices;i++){ s=dev->subdevices+i; if(s->busy==file){ do_cancel(dev,s); } if(s->lock==file){ s->lock=NULL; } } if(dev->attached && dev->use_count==1 && dev->close){ dev->close(dev); } MOD_DEC_USE_COUNT; if(dev->attached && dev->driver->module){ __MOD_DEC_USE_COUNT(dev->driver->module); } dev->use_count--;#if LINUX_VERSION_CODE >= 0x020100 if(file->f_flags & FASYNC){ comedi_fasync(-1,file,0); }#endif return 0;}#if LINUX_VERSION_CODE >= 0x020100static int comedi_fasync (int fd, struct file *file, int on){ comedi_device *dev=comedi_get_device_by_minor(MINOR(RDEV_OF_FILE(file))); return fasync_helper(fd,file,on,&dev->async_queue);}#endif/* kernel compatibility*/#if LINUX_VERSION_CODE < 0x020100static int comedi_write_v20(struct inode *inode,struct file *file,const char *buf,int nbytes){ return comedi_write_v22(file,buf,nbytes,NULL);}static int comedi_read_v20(struct inode *inode,struct file *file,char *buf,int nbytes){ return comedi_read_v22(file,buf,nbytes,NULL);}static int comedi_lseek_v20(struct inode * inode,struct file *file,off_t offset,int origin){ return comedi_lseek_v22(file,offset,origin);}static void comedi_close_v20(struct inode *inode,struct file *file){ comedi_close_v22(inode,file);}#define comedi_ioctl_v20 comedi_ioctl#define comedi_open_v20 comedi_fop_openstatic struct file_operations comedi_fops={ lseek : comedi_lseek_v20, ioctl : comedi_ioctl_v20, open : comedi_open_v20, release : comedi_close_v20, read : comedi_read_v20, write : comedi_write_v20,};#endif#if LINUX_VERSION_CODE >= 0x020200#define comedi_ioctl_v22 comedi_ioctl#define comedi_open_v22 comedi_fop_openstatic struct file_operations comedi_fops={#if LINUX_VERSION_CODE >= 0x020400 owner : THIS_MODULE,#endif llseek : comedi_lseek_v22, ioctl : comedi_ioctl_v22, open : comedi_open_v22, release : comedi_close_v22, read : comedi_read_v22, write : comedi_write_v22, mmap : comedi_mmap_v22, poll : comedi_poll_v22, fasync : comedi_fasync,};#endifstatic int __init comedi_init(void){ int i; printk("comedi: version " COMEDI_RELEASE " - David Schleef <ds@schleef.org>\n"); if(devfs_register_chrdev(COMEDI_MAJOR,"comedi",&comedi_fops)){ printk("comedi: unable to get major %d\n",COMEDI_MAJOR); return -EIO; } comedi_devices=(comedi_device *)kmalloc(sizeof(comedi_device)*COMEDI_NDEVICES,GFP_KERNEL); if(!comedi_devices) return -ENOMEM; memset(comedi_devices,0,sizeof(comedi_device)*COMEDI_NDEVICES); for(i=0;i<COMEDI_NDEVICES;i++){ comedi_devices[i].minor=i; spin_lock_init(&(comedi_devices[i].spinlock)); } /* XXX requires /proc interface */ comedi_proc_init(); for(i=0;i<COMEDI_NDEVICES;i++){ char name[20]; sprintf(name, "comedi%d", i); devfs_register(NULL, name, DEVFS_FL_DEFAULT, COMEDI_MAJOR, i, 0666 | S_IFCHR, &comedi_fops, NULL); } comedi_rt_init(); return 0;}static void __exit comedi_cleanup(void){ int i; if(MOD_IN_USE) printk("comedi: module in use -- remove delayed\n"); for(i=0;i<4;i++){ char name[20]; sprintf(name, "comedi%d", i); devfs_unregister(devfs_find_handle(NULL, name, COMEDI_MAJOR, i, DEVFS_SPECIAL_CHR, 0)); } devfs_unregister_chrdev(COMEDI_MAJOR,"comedi"); comedi_proc_cleanup(); for(i=0;i<COMEDI_NDEVICES;i++){ comedi_device *dev; dev=comedi_get_device_by_minor(i); if(dev->attached) comedi_device_detach(dev); } kfree(comedi_devices); comedi_rt_cleanup();}module_init(comedi_init);module_exit(comedi_cleanup);void comedi_error(const comedi_device *dev,const char *s){ rt_printk("comedi%d: %s: %s\n",dev->minor,dev->driver->driver_name,s);}void comedi_event(comedi_device *dev,comedi_subdevice *s, unsigned int mask){ comedi_async *async = s->async; mask = s->async->events; s->async->events = 0; mask |= COMEDI_CB_BLOCK; //DPRINTK("comedi_event %x\n",mask); if( (s->subdev_flags & SDF_RUNNING) == 0) return; if(mask&(COMEDI_CB_EOA|COMEDI_CB_ERROR|COMEDI_CB_OVERFLOW)){ s->subdev_flags &= ~SDF_RUNNING; } /* remember if an error event has occured, so an error * can be returned the next time the user does a read() */ if(mask & (COMEDI_CB_ERROR|COMEDI_CB_OVERFLOW)){ s->runflags |= SRF_ERROR; } if(async->cb_mask&mask){ if(s->runflags&SRF_USER){ if(dev->rt){#ifdef CONFIG_COMEDI_RT // pend wake up if(s==dev->read_subdev) comedi_rt_pend_wakeup(&dev->read_wait); if(s==dev->write_subdev) comedi_rt_pend_wakeup(&dev->write_wait);#else printk("BUG: comedi_event() code unreachable\n");#endif }else{ if(s==dev->read_subdev){ wake_up_interruptible(&dev->read_wait); KILL_FASYNC(dev->async_queue, SIGIO, POLL_IN); } if(s==dev->write_subdev){ wake_up_interruptible(&dev->write_wait); KILL_FASYNC(dev->async_queue, SIGIO, POLL_OUT); } } }else{ if(async->cb_func)async->cb_func(mask,async->cb_arg); /* XXX bug here. If subdevice A is rt, and * subdevice B tries to callback to a normal * linux kernel function, it will be at the * wrong priority. Since this isn't very * common, I'm not going to worry about it. */ } }}static void init_async_buf( comedi_async *async ){ async->buf_write_alloc_count = 0; async->buf_write_count = 0; async->buf_read_count = 0; async->buf_write_ptr = 0; async->buf_read_ptr = 0; async->cur_chan = 0; async->scan_progress = 0; async->munge_chan = 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -