1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 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> 8 */ 9 10 #include <linux/uaccess.h> 11 #include <linux/comedi/comedidev.h> 12 #include "comedi_internal.h" 13 14 const struct comedi_lrange range_bipolar10 = { 1, {BIP_RANGE(10)} }; 15 EXPORT_SYMBOL_GPL(range_bipolar10); 16 const struct comedi_lrange range_bipolar5 = { 1, {BIP_RANGE(5)} }; 17 EXPORT_SYMBOL_GPL(range_bipolar5); 18 const struct comedi_lrange range_bipolar2_5 = { 1, {BIP_RANGE(2.5)} }; 19 EXPORT_SYMBOL_GPL(range_bipolar2_5); 20 const struct comedi_lrange range_unipolar10 = { 1, {UNI_RANGE(10)} }; 21 EXPORT_SYMBOL_GPL(range_unipolar10); 22 const struct comedi_lrange range_unipolar5 = { 1, {UNI_RANGE(5)} }; 23 EXPORT_SYMBOL_GPL(range_unipolar5); 24 const struct comedi_lrange range_unipolar2_5 = { 1, {UNI_RANGE(2.5)} }; 25 EXPORT_SYMBOL_GPL(range_unipolar2_5); 26 const struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} }; 27 EXPORT_SYMBOL_GPL(range_0_20mA); 28 const struct comedi_lrange range_4_20mA = { 1, {RANGE_mA(4, 20)} }; 29 EXPORT_SYMBOL_GPL(range_4_20mA); 30 const struct comedi_lrange range_0_32mA = { 1, {RANGE_mA(0, 32)} }; 31 EXPORT_SYMBOL_GPL(range_0_32mA); 32 const struct comedi_lrange range_unknown = { 1, {{0, 1000000, UNIT_none} } }; 33 EXPORT_SYMBOL_GPL(range_unknown); 34 35 /* 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 */ 48 int do_rangeinfo_ioctl(struct comedi_device *dev, 49 struct comedi_rangeinfo *it) 50 { 51 int subd, chan; 52 const struct comedi_lrange *lr; 53 struct comedi_subdevice *s; 54 55 subd = (it->range_type >> 24) & 0xf; 56 chan = (it->range_type >> 16) & 0xff; 57 58 if (!dev->attached) 59 return -EINVAL; 60 if (subd >= dev->n_subdevices) 61 return -EINVAL; 62 s = &dev->subdevices[subd]; 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 73 if (RANGE_LENGTH(it->range_type) != lr->length) { 74 dev_dbg(dev->class_dev, 75 "wrong length %d should be %d (0x%08x)\n", 76 RANGE_LENGTH(it->range_type), 77 lr->length, it->range_type); 78 return -EINVAL; 79 } 80 81 if (copy_to_user(it->range_ptr, lr->range, 82 sizeof(struct comedi_krange) * lr->length)) 83 return -EFAULT; 84 85 return 0; 86 } 87 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 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 */ 105 int comedi_check_chanlist(struct comedi_subdevice *s, int n, 106 unsigned int *chanlist) 107 { 108 struct comedi_device *dev = s->device; 109 unsigned int chanspec; 110 int chan, range_len, i; 111 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 || 122 CR_RANGE(chanspec) >= range_len) { 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 } 129 return 0; 130 } 131 EXPORT_SYMBOL_GPL(comedi_check_chanlist); 132