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