at v5.1 4.0 kB view raw
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 "comedidev.h" 12#include "comedi_internal.h" 13 14const struct comedi_lrange range_bipolar10 = { 1, {BIP_RANGE(10)} }; 15EXPORT_SYMBOL_GPL(range_bipolar10); 16const struct comedi_lrange range_bipolar5 = { 1, {BIP_RANGE(5)} }; 17EXPORT_SYMBOL_GPL(range_bipolar5); 18const struct comedi_lrange range_bipolar2_5 = { 1, {BIP_RANGE(2.5)} }; 19EXPORT_SYMBOL_GPL(range_bipolar2_5); 20const struct comedi_lrange range_unipolar10 = { 1, {UNI_RANGE(10)} }; 21EXPORT_SYMBOL_GPL(range_unipolar10); 22const struct comedi_lrange range_unipolar5 = { 1, {UNI_RANGE(5)} }; 23EXPORT_SYMBOL_GPL(range_unipolar5); 24const struct comedi_lrange range_unipolar2_5 = { 1, {UNI_RANGE(2.5)} }; 25EXPORT_SYMBOL_GPL(range_unipolar2_5); 26const struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} }; 27EXPORT_SYMBOL_GPL(range_0_20mA); 28const struct comedi_lrange range_4_20mA = { 1, {RANGE_mA(4, 20)} }; 29EXPORT_SYMBOL_GPL(range_4_20mA); 30const struct comedi_lrange range_0_32mA = { 1, {RANGE_mA(0, 32)} }; 31EXPORT_SYMBOL_GPL(range_0_32mA); 32const struct comedi_lrange range_unknown = { 1, {{0, 1000000, UNIT_none} } }; 33EXPORT_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 */ 48int do_rangeinfo_ioctl(struct comedi_device *dev, 49 struct comedi_rangeinfo __user *arg) 50{ 51 struct comedi_rangeinfo it; 52 int subd, chan; 53 const struct comedi_lrange *lr; 54 struct comedi_subdevice *s; 55 56 if (copy_from_user(&it, arg, sizeof(struct comedi_rangeinfo))) 57 return -EFAULT; 58 subd = (it.range_type >> 24) & 0xf; 59 chan = (it.range_type >> 16) & 0xff; 60 61 if (!dev->attached) 62 return -EINVAL; 63 if (subd >= dev->n_subdevices) 64 return -EINVAL; 65 s = &dev->subdevices[subd]; 66 if (s->range_table) { 67 lr = s->range_table; 68 } else if (s->range_table_list) { 69 if (chan >= s->n_chan) 70 return -EINVAL; 71 lr = s->range_table_list[chan]; 72 } else { 73 return -EINVAL; 74 } 75 76 if (RANGE_LENGTH(it.range_type) != lr->length) { 77 dev_dbg(dev->class_dev, 78 "wrong length %d should be %d (0x%08x)\n", 79 RANGE_LENGTH(it.range_type), 80 lr->length, it.range_type); 81 return -EINVAL; 82 } 83 84 if (copy_to_user(it.range_ptr, lr->range, 85 sizeof(struct comedi_krange) * lr->length)) 86 return -EFAULT; 87 88 return 0; 89} 90 91/** 92 * comedi_check_chanlist() - Validate each element in a chanlist. 93 * @s: comedi_subdevice struct 94 * @n: number of elements in the chanlist 95 * @chanlist: the chanlist to validate 96 * 97 * Each element consists of a channel number, a range index, an analog 98 * reference type and some flags, all packed into an unsigned int. 99 * 100 * This checks that the channel number and range index are supported by 101 * the comedi subdevice. It does not check whether the analog reference 102 * type and the flags are supported. Drivers that care should check those 103 * themselves. 104 * 105 * Return: %0 if all @chanlist elements are valid (success), 106 * %-EINVAL if one or more elements are invalid. 107 */ 108int comedi_check_chanlist(struct comedi_subdevice *s, int n, 109 unsigned int *chanlist) 110{ 111 struct comedi_device *dev = s->device; 112 unsigned int chanspec; 113 int chan, range_len, i; 114 115 for (i = 0; i < n; i++) { 116 chanspec = chanlist[i]; 117 chan = CR_CHAN(chanspec); 118 if (s->range_table) 119 range_len = s->range_table->length; 120 else if (s->range_table_list && chan < s->n_chan) 121 range_len = s->range_table_list[chan]->length; 122 else 123 range_len = 0; 124 if (chan >= s->n_chan || 125 CR_RANGE(chanspec) >= range_len) { 126 dev_warn(dev->class_dev, 127 "bad chanlist[%d]=0x%08x chan=%d range length=%d\n", 128 i, chanspec, chan, range_len); 129 return -EINVAL; 130 } 131 } 132 return 0; 133} 134EXPORT_SYMBOL_GPL(comedi_check_chanlist);