MODULE_DESCRIPTION("Comedi core module");
MODULE_LICENSE("GPL");
+#define DEFAULT_BUF_MAXSIZE_KB 64
+#define DEFAULT_BUF_SIZE_KB 64
+
#ifdef CONFIG_COMEDI_DEBUG
int comedi_debug;
EXPORT_SYMBOL(comedi_debug);
-module_param(comedi_debug, int, 0644);
+module_param(comedi_debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(comedi_debug,
+ "enable comedi core and driver debugging if non-zero (default 0)"
+ );
#endif
int comedi_autoconfig = 1;
-module_param(comedi_autoconfig, bool, 0444);
+module_param(comedi_autoconfig, bool, S_IRUGO);
+MODULE_PARM_DESC(comedi_autoconfig,
+ "enable drivers to auto-configure comedi devices (default 1)");
static int comedi_num_legacy_minors;
-module_param(comedi_num_legacy_minors, int, 0444);
+module_param(comedi_num_legacy_minors, int, S_IRUGO);
+MODULE_PARM_DESC(comedi_num_legacy_minors,
+ "number of comedi minor devices to reserve for non-auto-configured devices (default 0)"
+ );
+
+unsigned int comedi_default_buf_size_kb = DEFAULT_BUF_SIZE_KB;
+module_param(comedi_default_buf_size_kb, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(comedi_default_buf_size_kb,
+ "default asynchronous buffer size in KiB (default "
+ __MODULE_STRING(DEFAULT_BUF_SIZE_KB) ")");
+
+unsigned int comedi_default_buf_maxsize_kb = DEFAULT_BUF_MAXSIZE_KB;
+module_param(comedi_default_buf_maxsize_kb, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(comedi_default_buf_maxsize_kb,
+ "default maximum size of asynchronous buffer in KiB (default "
+ __MODULE_STRING(DEFAULT_BUF_MAXSIZE_KB) ")");
static DEFINE_SPINLOCK(comedi_file_info_table_lock);
static struct comedi_device_file_info
static int do_chaninfo_ioctl(struct comedi_device *dev,
struct comedi_chaninfo __user *arg);
static int do_bufinfo_ioctl(struct comedi_device *dev,
- struct comedi_bufinfo __user *arg);
+ struct comedi_bufinfo __user *arg, void *file);
static int do_cmd_ioctl(struct comedi_device *dev,
struct comedi_cmd __user *arg, void *file);
static int do_lock_ioctl(struct comedi_device *dev, unsigned int arg,
static int do_poll_ioctl(struct comedi_device *dev, unsigned int subd,
void *file);
-extern void do_become_nonbusy(struct comedi_device *dev,
+static void do_become_nonbusy(struct comedi_device *dev,
struct comedi_subdevice *s);
static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
break;
case COMEDI_BUFINFO:
rc = do_bufinfo_ioctl(dev,
- (struct comedi_bufinfo __user *)arg);
+ (struct comedi_bufinfo __user *)arg,
+ file);
break;
case COMEDI_LOCK:
rc = do_lock_ioctl(dev, arg, file);
/* fill devinfo structure */
devinfo.version_code = COMEDI_VERSION_CODE;
devinfo.n_subdevs = dev->n_subdevices;
- memcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
- memcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
+ strlcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
+ strlcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
if (read_subdev)
devinfo.read_subdevice = read_subdev - dev->subdevices;
x = (dev->minor << 28) | (it.subdev << 24) | (i << 16) |
(s->range_table_list[i]->length);
- put_user(x, it.rangelist + i);
+ if (put_user(x, it.rangelist + i))
+ return -EFAULT;
}
#if 0
if (copy_to_user(it.rangelist, s->range_type_list,
*/
static int do_bufinfo_ioctl(struct comedi_device *dev,
- struct comedi_bufinfo __user *arg)
+ struct comedi_bufinfo __user *arg, void *file)
{
struct comedi_bufinfo bi;
struct comedi_subdevice *s;
return -EINVAL;
s = dev->subdevices + bi.subdevice;
+
+ if (s->lock && s->lock != file)
+ return -EACCES;
+
async = s->async;
if (!async) {
bi.buf_read_ptr = 0;
bi.buf_write_count = 0;
bi.buf_read_count = 0;
+ bi.bytes_read = 0;
+ bi.bytes_written = 0;
goto copyback;
}
+ if (!s->busy) {
+ bi.bytes_read = 0;
+ bi.bytes_written = 0;
+ goto copyback_position;
+ }
+ if (s->busy != file)
+ return -EACCES;
if (bi.bytes_read && (s->subdev_flags & SDF_CMD_READ)) {
bi.bytes_read = comedi_buf_read_alloc(async, bi.bytes_read);
comedi_buf_write_free(async, bi.bytes_written);
}
+copyback_position:
bi.buf_write_count = async->buf_write_count;
bi.buf_write_ptr = async->buf_write_ptr;
bi.buf_read_count = async->buf_read_count;
}
insns =
- kmalloc(sizeof(struct comedi_insn) * insnlist.n_insns, GFP_KERNEL);
+ kcalloc(insnlist.n_insns, sizeof(struct comedi_insn), GFP_KERNEL);
if (!insns) {
DPRINTK("kmalloc failed\n");
ret = -ENOMEM;
case INSN_BITS:
if (insn->n != 2) {
ret = -EINVAL;
- break;
+ } else {
+ /* Most drivers ignore the base channel in
+ * insn->chanspec. Fix this here if
+ * the subdevice has <= 32 channels. */
+ unsigned int shift;
+ unsigned int orig_mask;
+
+ orig_mask = data[0];
+ if (s->n_chan <= 32) {
+ shift = CR_CHAN(insn->chanspec);
+ if (shift > 0) {
+ insn->chanspec = 0;
+ data[0] <<= shift;
+ data[1] <<= shift;
+ }
+ } else
+ shift = 0;
+ ret = s->insn_bits(dev, s, insn, data);
+ data[0] = orig_mask;
+ if (shift > 0)
+ data[1] >>= shift;
}
- ret = s->insn_bits(dev, s, insn, data);
break;
case INSN_CONFIG:
ret = check_insn_config_length(insn, data);
s->lock = file;
spin_unlock_irqrestore(&s->spin_lock, flags);
+#if 0
if (ret < 0)
return ret;
-#if 0
if (s->lock_f)
ret = s->lock_f(dev, s);
#endif
return ret;
}
-static void comedi_unmap(struct vm_area_struct *area)
+
+static void comedi_vm_open(struct vm_area_struct *area)
+{
+ struct comedi_async *async;
+ struct comedi_device *dev;
+
+ async = area->vm_private_data;
+ dev = async->subdevice->device;
+
+ mutex_lock(&dev->mutex);
+ async->mmap_count++;
+ mutex_unlock(&dev->mutex);
+}
+
+static void comedi_vm_close(struct vm_area_struct *area)
{
struct comedi_async *async;
struct comedi_device *dev;
}
static struct vm_operations_struct comedi_vm_ops = {
- .close = comedi_unmap,
+ .open = comedi_vm_open,
+ .close = comedi_vm_close,
};
static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
{
const unsigned minor = iminor(file->f_dentry->d_inode);
- struct comedi_device_file_info *dev_file_info =
- comedi_get_device_file_info(minor);
- struct comedi_device *dev = dev_file_info->device;
struct comedi_async *async = NULL;
unsigned long start = vma->vm_start;
unsigned long size;
int i;
int retval;
struct comedi_subdevice *s;
+ struct comedi_device_file_info *dev_file_info;
+ struct comedi_device *dev;
+
+ dev_file_info = comedi_get_device_file_info(minor);
+ if (dev_file_info == NULL)
+ return -ENODEV;
+ dev = dev_file_info->device;
+ if (dev == NULL)
+ return -ENODEV;
mutex_lock(&dev->mutex);
if (!dev->attached) {
{
unsigned int mask = 0;
const unsigned minor = iminor(file->f_dentry->d_inode);
- struct comedi_device_file_info *dev_file_info =
- comedi_get_device_file_info(minor);
- struct comedi_device *dev = dev_file_info->device;
struct comedi_subdevice *read_subdev;
struct comedi_subdevice *write_subdev;
+ struct comedi_device_file_info *dev_file_info;
+ struct comedi_device *dev;
+ dev_file_info = comedi_get_device_file_info(minor);
+
+ if (dev_file_info == NULL)
+ return -ENODEV;
+ dev = dev_file_info->device;
+ if (dev == NULL)
+ return -ENODEV;
mutex_lock(&dev->mutex);
if (!dev->attached) {
int n, m, count = 0, retval = 0;
DECLARE_WAITQUEUE(wait, current);
const unsigned minor = iminor(file->f_dentry->d_inode);
- struct comedi_device_file_info *dev_file_info =
- comedi_get_device_file_info(minor);
- struct comedi_device *dev = dev_file_info->device;
+ struct comedi_device_file_info *dev_file_info;
+ struct comedi_device *dev;
+ dev_file_info = comedi_get_device_file_info(minor);
+
+ if (dev_file_info == NULL)
+ return -ENODEV;
+ dev = dev_file_info->device;
+ if (dev == NULL)
+ return -ENODEV;
if (!dev->attached) {
DPRINTK("no driver configured on comedi%i\n", dev->minor);
while (nbytes > 0 && !retval) {
set_current_state(TASK_INTERRUPTIBLE);
+ if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) {
+ if (count == 0) {
+ if (comedi_get_subdevice_runflags(s) &
+ SRF_ERROR) {
+ retval = -EPIPE;
+ } else {
+ retval = 0;
+ }
+ do_become_nonbusy(dev, s);
+ }
+ break;
+ }
+
n = nbytes;
m = n;
n = m;
if (n == 0) {
- if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) {
- if (comedi_get_subdevice_runflags(s) &
- SRF_ERROR) {
- retval = -EPIPE;
- } else {
- retval = 0;
- }
- do_become_nonbusy(dev, s);
- break;
- }
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
+ schedule();
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
- schedule();
if (!s->busy)
break;
if (s->busy != file) {
int n, m, count = 0, retval = 0;
DECLARE_WAITQUEUE(wait, current);
const unsigned minor = iminor(file->f_dentry->d_inode);
- struct comedi_device_file_info *dev_file_info =
- comedi_get_device_file_info(minor);
- struct comedi_device *dev = dev_file_info->device;
+ struct comedi_device_file_info *dev_file_info;
+ struct comedi_device *dev;
+ dev_file_info = comedi_get_device_file_info(minor);
+
+ if (dev_file_info == NULL)
+ return -ENODEV;
+ dev = dev_file_info->device;
+ if (dev == NULL)
+ return -ENODEV;
if (!dev->attached) {
DPRINTK("no driver configured on comedi%i\n", dev->minor);
retval = -EAGAIN;
break;
}
+ schedule();
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
- schedule();
if (!s->busy) {
retval = 0;
break;
}
}
- if (dev->attached && dev->use_count == 0 && dev->open)
- dev->open(dev);
+ if (dev->attached && dev->use_count == 0 && dev->open) {
+ int rc = dev->open(dev);
+ if (rc < 0) {
+ module_put(dev->driver->module);
+ module_put(THIS_MODULE);
+ mutex_unlock(&dev->mutex);
+ return rc;
+ }
+ }
dev->use_count++;
static int comedi_close(struct inode *inode, struct file *file)
{
const unsigned minor = iminor(inode);
- struct comedi_device_file_info *dev_file_info =
- comedi_get_device_file_info(minor);
- struct comedi_device *dev = dev_file_info->device;
struct comedi_subdevice *s = NULL;
int i;
+ struct comedi_device_file_info *dev_file_info;
+ struct comedi_device *dev;
+ dev_file_info = comedi_get_device_file_info(minor);
+
+ if (dev_file_info == NULL)
+ return -ENODEV;
+ dev = dev_file_info->device;
+ if (dev == NULL)
+ return -ENODEV;
mutex_lock(&dev->mutex);
static int comedi_fasync(int fd, struct file *file, int on)
{
const unsigned minor = iminor(file->f_dentry->d_inode);
- struct comedi_device_file_info *dev_file_info =
- comedi_get_device_file_info(minor);
+ struct comedi_device_file_info *dev_file_info;
+ struct comedi_device *dev;
+ dev_file_info = comedi_get_device_file_info(minor);
- struct comedi_device *dev = dev_file_info->device;
+ if (dev_file_info == NULL)
+ return -ENODEV;
+ dev = dev_file_info->device;
+ if (dev == NULL)
+ return -ENODEV;
return fasync_helper(fd, file, on, &dev->async_queue);
}
.mmap = comedi_mmap,
.poll = comedi_poll,
.fasync = comedi_fasync,
+ .llseek = noop_llseek,
};
struct class *comedi_class;
COMEDI_CB_OVERFLOW)) {
runflags_mask |= SRF_RUNNING;
}
- /* remember if an error event has occured, so an error
+ /* remember if an error event has occurred, so an error
* can be returned the next time the user does a read() */
if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
runflags_mask |= SRF_ERROR;
return -EBUSY;
}
info->device->minor = i;
- csdev = COMEDI_DEVICE_CREATE(comedi_class, NULL,
- MKDEV(COMEDI_MAJOR, i), NULL,
- hardware_device, "comedi%i", i);
+ csdev = device_create(comedi_class, hardware_device,
+ MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i", i);
if (!IS_ERR(csdev))
info->device->class_dev = csdev;
dev_set_drvdata(csdev, info);
return -EBUSY;
}
s->minor = i;
- csdev = COMEDI_DEVICE_CREATE(comedi_class, dev->class_dev,
- MKDEV(COMEDI_MAJOR, i), NULL, NULL,
- "comedi%i_subd%i", dev->minor,
- (int)(s - dev->subdevices));
+ csdev = device_create(comedi_class, dev->class_dev,
+ MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i_subd%i",
+ dev->minor, (int)(s - dev->subdevices));
if (!IS_ERR(csdev))
s->class_dev = csdev;
dev_set_drvdata(csdev, info);