range.c revision 3b6b25b5ddf4485e89432a35a7d79d371ba6eba1
1/* 2 module/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 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 22*/ 23 24#include <linux/uaccess.h> 25#include "comedidev.h" 26#include "internal.h" 27 28const struct comedi_lrange range_bipolar10 = { 1, {BIP_RANGE(10)} }; 29EXPORT_SYMBOL(range_bipolar10); 30const struct comedi_lrange range_bipolar5 = { 1, {BIP_RANGE(5)} }; 31EXPORT_SYMBOL(range_bipolar5); 32const struct comedi_lrange range_bipolar2_5 = { 1, {BIP_RANGE(2.5)} }; 33EXPORT_SYMBOL(range_bipolar2_5); 34const struct comedi_lrange range_unipolar10 = { 1, {UNI_RANGE(10)} }; 35EXPORT_SYMBOL(range_unipolar10); 36const struct comedi_lrange range_unipolar5 = { 1, {UNI_RANGE(5)} }; 37EXPORT_SYMBOL(range_unipolar5); 38const struct comedi_lrange range_unknown = { 1, {{0, 1000000, UNIT_none} } }; 39EXPORT_SYMBOL(range_unknown); 40 41/* 42 COMEDI_RANGEINFO 43 range information ioctl 44 45 arg: 46 pointer to rangeinfo structure 47 48 reads: 49 range info structure 50 51 writes: 52 n struct comedi_krange structures to rangeinfo->range_ptr 53*/ 54int do_rangeinfo_ioctl(struct comedi_device *dev, 55 struct comedi_rangeinfo __user *arg) 56{ 57 struct comedi_rangeinfo it; 58 int subd, chan; 59 const struct comedi_lrange *lr; 60 struct comedi_subdevice *s; 61 62 if (copy_from_user(&it, arg, sizeof(struct comedi_rangeinfo))) 63 return -EFAULT; 64 subd = (it.range_type >> 24) & 0xf; 65 chan = (it.range_type >> 16) & 0xff; 66 67 if (!dev->attached) 68 return -EINVAL; 69 if (subd >= dev->n_subdevices) 70 return -EINVAL; 71 s = dev->subdevices + subd; 72 if (s->range_table) { 73 lr = s->range_table; 74 } else if (s->range_table_list) { 75 if (chan >= s->n_chan) 76 return -EINVAL; 77 lr = s->range_table_list[chan]; 78 } else { 79 return -EINVAL; 80 } 81 82 if (RANGE_LENGTH(it.range_type) != lr->length) { 83 DPRINTK("wrong length %d should be %d (0x%08x)\n", 84 RANGE_LENGTH(it.range_type), lr->length, it.range_type); 85 return -EINVAL; 86 } 87 88 if (copy_to_user(it.range_ptr, lr->range, 89 sizeof(struct comedi_krange) * lr->length)) 90 return -EFAULT; 91 92 return 0; 93} 94 95static int aref_invalid(struct comedi_subdevice *s, unsigned int chanspec) 96{ 97 unsigned int aref; 98 99 /* disable reporting invalid arefs... maybe someday */ 100 return 0; 101 102 aref = CR_AREF(chanspec); 103 switch (aref) { 104 case AREF_DIFF: 105 if (s->subdev_flags & SDF_DIFF) 106 return 0; 107 break; 108 case AREF_COMMON: 109 if (s->subdev_flags & SDF_COMMON) 110 return 0; 111 break; 112 case AREF_GROUND: 113 if (s->subdev_flags & SDF_GROUND) 114 return 0; 115 break; 116 case AREF_OTHER: 117 if (s->subdev_flags & SDF_OTHER) 118 return 0; 119 break; 120 default: 121 break; 122 } 123 DPRINTK("subdevice does not support aref %i", aref); 124 return 1; 125} 126 127/* 128 This function checks each element in a channel/gain list to make 129 make sure it is valid. 130*/ 131int comedi_check_chanlist(struct comedi_subdevice *s, int n, 132 unsigned int *chanlist) 133{ 134 int i; 135 int chan; 136 137 if (s->range_table) { 138 for (i = 0; i < n; i++) 139 if (CR_CHAN(chanlist[i]) >= s->n_chan || 140 CR_RANGE(chanlist[i]) >= s->range_table->length 141 || aref_invalid(s, chanlist[i])) { 142 printk(KERN_ERR "bad chanlist[%d]=0x%08x " 143 "in_chan=%d range length=%d\n", i, 144 chanlist[i], s->n_chan, 145 s->range_table->length); 146 return -EINVAL; 147 } 148 } else if (s->range_table_list) { 149 for (i = 0; i < n; i++) { 150 chan = CR_CHAN(chanlist[i]); 151 if (chan >= s->n_chan || 152 CR_RANGE(chanlist[i]) >= 153 s->range_table_list[chan]->length 154 || aref_invalid(s, chanlist[i])) { 155 printk(KERN_ERR "bad chanlist[%d]=0x%08x\n", 156 i, chanlist[i]); 157 return -EINVAL; 158 } 159 } 160 } else { 161 printk(KERN_ERR "comedi: (bug) no range type list!\n"); 162 return -EINVAL; 163 } 164 return 0; 165} 166EXPORT_SYMBOL(comedi_check_chanlist); 167