1*8ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 2*8ffdff6aSGreg Kroah-Hartman /* 3*8ffdff6aSGreg Kroah-Hartman * comedi/range.c 4*8ffdff6aSGreg Kroah-Hartman * comedi routines for voltage ranges 5*8ffdff6aSGreg Kroah-Hartman * 6*8ffdff6aSGreg Kroah-Hartman * COMEDI - Linux Control and Measurement Device Interface 7*8ffdff6aSGreg Kroah-Hartman * Copyright (C) 1997-8 David A. Schleef <[email protected]> 8*8ffdff6aSGreg Kroah-Hartman */ 9*8ffdff6aSGreg Kroah-Hartman 10*8ffdff6aSGreg Kroah-Hartman #include <linux/uaccess.h> 11*8ffdff6aSGreg Kroah-Hartman #include "comedidev.h" 12*8ffdff6aSGreg Kroah-Hartman #include "comedi_internal.h" 13*8ffdff6aSGreg Kroah-Hartman 14*8ffdff6aSGreg Kroah-Hartman const struct comedi_lrange range_bipolar10 = { 1, {BIP_RANGE(10)} }; 15*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(range_bipolar10); 16*8ffdff6aSGreg Kroah-Hartman const struct comedi_lrange range_bipolar5 = { 1, {BIP_RANGE(5)} }; 17*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(range_bipolar5); 18*8ffdff6aSGreg Kroah-Hartman const struct comedi_lrange range_bipolar2_5 = { 1, {BIP_RANGE(2.5)} }; 19*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(range_bipolar2_5); 20*8ffdff6aSGreg Kroah-Hartman const struct comedi_lrange range_unipolar10 = { 1, {UNI_RANGE(10)} }; 21*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(range_unipolar10); 22*8ffdff6aSGreg Kroah-Hartman const struct comedi_lrange range_unipolar5 = { 1, {UNI_RANGE(5)} }; 23*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(range_unipolar5); 24*8ffdff6aSGreg Kroah-Hartman const struct comedi_lrange range_unipolar2_5 = { 1, {UNI_RANGE(2.5)} }; 25*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(range_unipolar2_5); 26*8ffdff6aSGreg Kroah-Hartman const struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} }; 27*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(range_0_20mA); 28*8ffdff6aSGreg Kroah-Hartman const struct comedi_lrange range_4_20mA = { 1, {RANGE_mA(4, 20)} }; 29*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(range_4_20mA); 30*8ffdff6aSGreg Kroah-Hartman const struct comedi_lrange range_0_32mA = { 1, {RANGE_mA(0, 32)} }; 31*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(range_0_32mA); 32*8ffdff6aSGreg Kroah-Hartman const struct comedi_lrange range_unknown = { 1, {{0, 1000000, UNIT_none} } }; 33*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(range_unknown); 34*8ffdff6aSGreg Kroah-Hartman 35*8ffdff6aSGreg Kroah-Hartman /* 36*8ffdff6aSGreg Kroah-Hartman * COMEDI_RANGEINFO ioctl 37*8ffdff6aSGreg Kroah-Hartman * range information 38*8ffdff6aSGreg Kroah-Hartman * 39*8ffdff6aSGreg Kroah-Hartman * arg: 40*8ffdff6aSGreg Kroah-Hartman * pointer to comedi_rangeinfo structure 41*8ffdff6aSGreg Kroah-Hartman * 42*8ffdff6aSGreg Kroah-Hartman * reads: 43*8ffdff6aSGreg Kroah-Hartman * comedi_rangeinfo structure 44*8ffdff6aSGreg Kroah-Hartman * 45*8ffdff6aSGreg Kroah-Hartman * writes: 46*8ffdff6aSGreg Kroah-Hartman * array of comedi_krange structures to rangeinfo->range_ptr pointer 47*8ffdff6aSGreg Kroah-Hartman */ 48*8ffdff6aSGreg Kroah-Hartman int do_rangeinfo_ioctl(struct comedi_device *dev, 49*8ffdff6aSGreg Kroah-Hartman struct comedi_rangeinfo *it) 50*8ffdff6aSGreg Kroah-Hartman { 51*8ffdff6aSGreg Kroah-Hartman int subd, chan; 52*8ffdff6aSGreg Kroah-Hartman const struct comedi_lrange *lr; 53*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 54*8ffdff6aSGreg Kroah-Hartman 55*8ffdff6aSGreg Kroah-Hartman subd = (it->range_type >> 24) & 0xf; 56*8ffdff6aSGreg Kroah-Hartman chan = (it->range_type >> 16) & 0xff; 57*8ffdff6aSGreg Kroah-Hartman 58*8ffdff6aSGreg Kroah-Hartman if (!dev->attached) 59*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 60*8ffdff6aSGreg Kroah-Hartman if (subd >= dev->n_subdevices) 61*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 62*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[subd]; 63*8ffdff6aSGreg Kroah-Hartman if (s->range_table) { 64*8ffdff6aSGreg Kroah-Hartman lr = s->range_table; 65*8ffdff6aSGreg Kroah-Hartman } else if (s->range_table_list) { 66*8ffdff6aSGreg Kroah-Hartman if (chan >= s->n_chan) 67*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 68*8ffdff6aSGreg Kroah-Hartman lr = s->range_table_list[chan]; 69*8ffdff6aSGreg Kroah-Hartman } else { 70*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 71*8ffdff6aSGreg Kroah-Hartman } 72*8ffdff6aSGreg Kroah-Hartman 73*8ffdff6aSGreg Kroah-Hartman if (RANGE_LENGTH(it->range_type) != lr->length) { 74*8ffdff6aSGreg Kroah-Hartman dev_dbg(dev->class_dev, 75*8ffdff6aSGreg Kroah-Hartman "wrong length %d should be %d (0x%08x)\n", 76*8ffdff6aSGreg Kroah-Hartman RANGE_LENGTH(it->range_type), 77*8ffdff6aSGreg Kroah-Hartman lr->length, it->range_type); 78*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 79*8ffdff6aSGreg Kroah-Hartman } 80*8ffdff6aSGreg Kroah-Hartman 81*8ffdff6aSGreg Kroah-Hartman if (copy_to_user(it->range_ptr, lr->range, 82*8ffdff6aSGreg Kroah-Hartman sizeof(struct comedi_krange) * lr->length)) 83*8ffdff6aSGreg Kroah-Hartman return -EFAULT; 84*8ffdff6aSGreg Kroah-Hartman 85*8ffdff6aSGreg Kroah-Hartman return 0; 86*8ffdff6aSGreg Kroah-Hartman } 87*8ffdff6aSGreg Kroah-Hartman 88*8ffdff6aSGreg Kroah-Hartman /** 89*8ffdff6aSGreg Kroah-Hartman * comedi_check_chanlist() - Validate each element in a chanlist. 90*8ffdff6aSGreg Kroah-Hartman * @s: comedi_subdevice struct 91*8ffdff6aSGreg Kroah-Hartman * @n: number of elements in the chanlist 92*8ffdff6aSGreg Kroah-Hartman * @chanlist: the chanlist to validate 93*8ffdff6aSGreg Kroah-Hartman * 94*8ffdff6aSGreg Kroah-Hartman * Each element consists of a channel number, a range index, an analog 95*8ffdff6aSGreg Kroah-Hartman * reference type and some flags, all packed into an unsigned int. 96*8ffdff6aSGreg Kroah-Hartman * 97*8ffdff6aSGreg Kroah-Hartman * This checks that the channel number and range index are supported by 98*8ffdff6aSGreg Kroah-Hartman * the comedi subdevice. It does not check whether the analog reference 99*8ffdff6aSGreg Kroah-Hartman * type and the flags are supported. Drivers that care should check those 100*8ffdff6aSGreg Kroah-Hartman * themselves. 101*8ffdff6aSGreg Kroah-Hartman * 102*8ffdff6aSGreg Kroah-Hartman * Return: %0 if all @chanlist elements are valid (success), 103*8ffdff6aSGreg Kroah-Hartman * %-EINVAL if one or more elements are invalid. 104*8ffdff6aSGreg Kroah-Hartman */ 105*8ffdff6aSGreg Kroah-Hartman int comedi_check_chanlist(struct comedi_subdevice *s, int n, 106*8ffdff6aSGreg Kroah-Hartman unsigned int *chanlist) 107*8ffdff6aSGreg Kroah-Hartman { 108*8ffdff6aSGreg Kroah-Hartman struct comedi_device *dev = s->device; 109*8ffdff6aSGreg Kroah-Hartman unsigned int chanspec; 110*8ffdff6aSGreg Kroah-Hartman int chan, range_len, i; 111*8ffdff6aSGreg Kroah-Hartman 112*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < n; i++) { 113*8ffdff6aSGreg Kroah-Hartman chanspec = chanlist[i]; 114*8ffdff6aSGreg Kroah-Hartman chan = CR_CHAN(chanspec); 115*8ffdff6aSGreg Kroah-Hartman if (s->range_table) 116*8ffdff6aSGreg Kroah-Hartman range_len = s->range_table->length; 117*8ffdff6aSGreg Kroah-Hartman else if (s->range_table_list && chan < s->n_chan) 118*8ffdff6aSGreg Kroah-Hartman range_len = s->range_table_list[chan]->length; 119*8ffdff6aSGreg Kroah-Hartman else 120*8ffdff6aSGreg Kroah-Hartman range_len = 0; 121*8ffdff6aSGreg Kroah-Hartman if (chan >= s->n_chan || 122*8ffdff6aSGreg Kroah-Hartman CR_RANGE(chanspec) >= range_len) { 123*8ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 124*8ffdff6aSGreg Kroah-Hartman "bad chanlist[%d]=0x%08x chan=%d range length=%d\n", 125*8ffdff6aSGreg Kroah-Hartman i, chanspec, chan, range_len); 126*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 127*8ffdff6aSGreg Kroah-Hartman } 128*8ffdff6aSGreg Kroah-Hartman } 129*8ffdff6aSGreg Kroah-Hartman return 0; 130*8ffdff6aSGreg Kroah-Hartman } 131*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_check_chanlist); 132