David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 1 | /* |
Ian Abbott | 5824ec7 | 2015-01-28 18:41:49 +0000 | [diff] [blame] | 2 | * comedi/range.c |
| 3 | * comedi routines for voltage ranges |
| 4 | * |
| 5 | * COMEDI - Linux Control and Measurement Device Interface |
| 6 | * Copyright (C) 1997-8 David A. Schleef <ds@schleef.org> |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License as published by |
| 10 | * the Free Software Foundation; either version 2 of the License, or |
| 11 | * (at your option) any later version. |
| 12 | * |
| 13 | * This program is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | * GNU General Public License for more details. |
| 17 | */ |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 18 | |
Greg Kroah-Hartman | ecfe20d | 2010-04-30 15:35:37 -0700 | [diff] [blame] | 19 | #include <linux/uaccess.h> |
Greg Kroah-Hartman | 3b6b25b | 2010-05-03 15:50:09 -0700 | [diff] [blame] | 20 | #include "comedidev.h" |
Ian Abbott | 3a5fa27 | 2012-06-19 10:17:44 +0100 | [diff] [blame] | 21 | #include "comedi_internal.h" |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 22 | |
Bill Pemberton | 9ced1de | 2009-03-16 22:05:31 -0400 | [diff] [blame] | 23 | const struct comedi_lrange range_bipolar10 = { 1, {BIP_RANGE(10)} }; |
H Hartley Sweeten | 5660e74 | 2013-04-12 10:11:54 -0700 | [diff] [blame] | 24 | EXPORT_SYMBOL_GPL(range_bipolar10); |
Mark Rankilor | f0f2918 | 2010-05-03 17:39:09 +0800 | [diff] [blame] | 25 | const struct comedi_lrange range_bipolar5 = { 1, {BIP_RANGE(5)} }; |
H Hartley Sweeten | 5660e74 | 2013-04-12 10:11:54 -0700 | [diff] [blame] | 26 | EXPORT_SYMBOL_GPL(range_bipolar5); |
Mark Rankilor | f0f2918 | 2010-05-03 17:39:09 +0800 | [diff] [blame] | 27 | const struct comedi_lrange range_bipolar2_5 = { 1, {BIP_RANGE(2.5)} }; |
H Hartley Sweeten | 5660e74 | 2013-04-12 10:11:54 -0700 | [diff] [blame] | 28 | EXPORT_SYMBOL_GPL(range_bipolar2_5); |
Mark Rankilor | f0f2918 | 2010-05-03 17:39:09 +0800 | [diff] [blame] | 29 | const struct comedi_lrange range_unipolar10 = { 1, {UNI_RANGE(10)} }; |
H Hartley Sweeten | 5660e74 | 2013-04-12 10:11:54 -0700 | [diff] [blame] | 30 | EXPORT_SYMBOL_GPL(range_unipolar10); |
Mark Rankilor | f0f2918 | 2010-05-03 17:39:09 +0800 | [diff] [blame] | 31 | const struct comedi_lrange range_unipolar5 = { 1, {UNI_RANGE(5)} }; |
H Hartley Sweeten | 5660e74 | 2013-04-12 10:11:54 -0700 | [diff] [blame] | 32 | EXPORT_SYMBOL_GPL(range_unipolar5); |
H Hartley Sweeten | 5f8eb72 | 2013-04-03 13:38:26 -0700 | [diff] [blame] | 33 | const struct comedi_lrange range_unipolar2_5 = { 1, {UNI_RANGE(2.5)} }; |
H Hartley Sweeten | 5660e74 | 2013-04-12 10:11:54 -0700 | [diff] [blame] | 34 | EXPORT_SYMBOL_GPL(range_unipolar2_5); |
H Hartley Sweeten | 2c71c4f | 2013-04-03 13:40:13 -0700 | [diff] [blame] | 35 | const struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} }; |
H Hartley Sweeten | 5660e74 | 2013-04-12 10:11:54 -0700 | [diff] [blame] | 36 | EXPORT_SYMBOL_GPL(range_0_20mA); |
H Hartley Sweeten | 2c71c4f | 2013-04-03 13:40:13 -0700 | [diff] [blame] | 37 | const struct comedi_lrange range_4_20mA = { 1, {RANGE_mA(4, 20)} }; |
H Hartley Sweeten | 5660e74 | 2013-04-12 10:11:54 -0700 | [diff] [blame] | 38 | EXPORT_SYMBOL_GPL(range_4_20mA); |
H Hartley Sweeten | 2c71c4f | 2013-04-03 13:40:13 -0700 | [diff] [blame] | 39 | const struct comedi_lrange range_0_32mA = { 1, {RANGE_mA(0, 32)} }; |
H Hartley Sweeten | 5660e74 | 2013-04-12 10:11:54 -0700 | [diff] [blame] | 40 | EXPORT_SYMBOL_GPL(range_0_32mA); |
Mark Rankilor | f0f2918 | 2010-05-03 17:39:09 +0800 | [diff] [blame] | 41 | const struct comedi_lrange range_unknown = { 1, {{0, 1000000, UNIT_none} } }; |
H Hartley Sweeten | 5660e74 | 2013-04-12 10:11:54 -0700 | [diff] [blame] | 42 | EXPORT_SYMBOL_GPL(range_unknown); |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 43 | |
| 44 | /* |
Ian Abbott | 1a4f01b | 2015-01-28 18:41:48 +0000 | [diff] [blame] | 45 | * COMEDI_RANGEINFO ioctl |
| 46 | * range information |
| 47 | * |
| 48 | * arg: |
| 49 | * pointer to comedi_rangeinfo structure |
| 50 | * |
| 51 | * reads: |
| 52 | * comedi_rangeinfo structure |
| 53 | * |
| 54 | * writes: |
| 55 | * array of comedi_krange structures to rangeinfo->range_ptr pointer |
| 56 | */ |
Greg Kroah-Hartman | 3b6b25b | 2010-05-03 15:50:09 -0700 | [diff] [blame] | 57 | int do_rangeinfo_ioctl(struct comedi_device *dev, |
| 58 | struct comedi_rangeinfo __user *arg) |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 59 | { |
Bill Pemberton | d0a353f | 2009-03-16 22:06:26 -0400 | [diff] [blame] | 60 | struct comedi_rangeinfo it; |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 61 | int subd, chan; |
Bill Pemberton | 9ced1de | 2009-03-16 22:05:31 -0400 | [diff] [blame] | 62 | const struct comedi_lrange *lr; |
Bill Pemberton | 34c4392 | 2009-03-16 22:05:14 -0400 | [diff] [blame] | 63 | struct comedi_subdevice *s; |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 64 | |
Bill Pemberton | d0a353f | 2009-03-16 22:06:26 -0400 | [diff] [blame] | 65 | if (copy_from_user(&it, arg, sizeof(struct comedi_rangeinfo))) |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 66 | return -EFAULT; |
| 67 | subd = (it.range_type >> 24) & 0xf; |
| 68 | chan = (it.range_type >> 16) & 0xff; |
| 69 | |
| 70 | if (!dev->attached) |
| 71 | return -EINVAL; |
| 72 | if (subd >= dev->n_subdevices) |
| 73 | return -EINVAL; |
H Hartley Sweeten | d08d6cf | 2012-09-05 18:59:44 -0700 | [diff] [blame] | 74 | s = &dev->subdevices[subd]; |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 75 | if (s->range_table) { |
| 76 | lr = s->range_table; |
| 77 | } else if (s->range_table_list) { |
| 78 | if (chan >= s->n_chan) |
| 79 | return -EINVAL; |
| 80 | lr = s->range_table_list[chan]; |
| 81 | } else { |
| 82 | return -EINVAL; |
| 83 | } |
| 84 | |
| 85 | if (RANGE_LENGTH(it.range_type) != lr->length) { |
H Hartley Sweeten | 2cde476 | 2013-11-22 10:45:58 -0700 | [diff] [blame] | 86 | dev_dbg(dev->class_dev, |
| 87 | "wrong length %d should be %d (0x%08x)\n", |
| 88 | RANGE_LENGTH(it.range_type), |
| 89 | lr->length, it.range_type); |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 90 | return -EINVAL; |
| 91 | } |
| 92 | |
| 93 | if (copy_to_user(it.range_ptr, lr->range, |
Mithlesh Thukral | 0a85b6f | 2009-06-08 21:04:41 +0530 | [diff] [blame] | 94 | sizeof(struct comedi_krange) * lr->length)) |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 95 | return -EFAULT; |
| 96 | |
| 97 | return 0; |
| 98 | } |
| 99 | |
H Hartley Sweeten | 065b091 | 2013-07-24 10:00:45 -0700 | [diff] [blame] | 100 | /** |
| 101 | * comedi_check_chanlist() - Validate each element in a chanlist. |
| 102 | * @s: comedi_subdevice struct |
| 103 | * @n: number of elements in the chanlist |
| 104 | * @chanlist: the chanlist to validate |
Ian Abbott | d27da4d | 2015-08-05 18:13:26 +0100 | [diff] [blame] | 105 | * |
| 106 | * Each element consists of a channel number, a range index, an analog |
| 107 | * reference type and some flags, all packed into an unsigned int. |
| 108 | * |
| 109 | * This checks that the channel number and range index are supported by |
| 110 | * the comedi subdevice. It does not check whether the analog reference |
| 111 | * type and the flags are supported. Drivers that care should check those |
| 112 | * themselves. |
| 113 | * |
| 114 | * Return: %0 if all @chanlist elements are valid (success), |
| 115 | * %-EINVAL if one or more elements are invalid. |
| 116 | */ |
Greg Kroah-Hartman | 0fd0ca7 | 2010-05-01 12:33:17 -0700 | [diff] [blame] | 117 | int comedi_check_chanlist(struct comedi_subdevice *s, int n, |
| 118 | unsigned int *chanlist) |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 119 | { |
Ian Abbott | 4f870fe | 2012-08-16 14:38:05 +0100 | [diff] [blame] | 120 | struct comedi_device *dev = s->device; |
H Hartley Sweeten | 065b091 | 2013-07-24 10:00:45 -0700 | [diff] [blame] | 121 | unsigned int chanspec; |
| 122 | int chan, range_len, i; |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 123 | |
Fred Akers | afdc37e | 2014-03-06 01:57:18 -0500 | [diff] [blame] | 124 | for (i = 0; i < n; i++) { |
| 125 | chanspec = chanlist[i]; |
| 126 | chan = CR_CHAN(chanspec); |
| 127 | if (s->range_table) |
| 128 | range_len = s->range_table->length; |
| 129 | else if (s->range_table_list && chan < s->n_chan) |
| 130 | range_len = s->range_table_list[chan]->length; |
| 131 | else |
| 132 | range_len = 0; |
| 133 | if (chan >= s->n_chan || |
Tapasweni Pathak | c6c04f0 | 2014-10-07 23:20:24 +0530 | [diff] [blame] | 134 | CR_RANGE(chanspec) >= range_len) { |
Fred Akers | afdc37e | 2014-03-06 01:57:18 -0500 | [diff] [blame] | 135 | dev_warn(dev->class_dev, |
| 136 | "bad chanlist[%d]=0x%08x chan=%d range length=%d\n", |
| 137 | i, chanspec, chan, range_len); |
| 138 | return -EINVAL; |
| 139 | } |
| 140 | } |
David Schleef | ed9eccb | 2008-11-04 20:29:31 -0800 | [diff] [blame] | 141 | return 0; |
| 142 | } |
H Hartley Sweeten | 5660e74 | 2013-04-12 10:11:54 -0700 | [diff] [blame] | 143 | EXPORT_SYMBOL_GPL(comedi_check_chanlist); |