132 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * comedi/range.c4 * comedi routines for voltage ranges5 *6 * COMEDI - Linux Control and Measurement Device Interface7 * 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 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 ioctl37 * range information38 *39 * arg:40 * pointer to comedi_rangeinfo structure41 *42 * reads:43 * comedi_rangeinfo structure44 *45 * writes:46 * array of comedi_krange structures to rangeinfo->range_ptr pointer47 */48int 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 struct91 * @n: number of elements in the chanlist92 * @chanlist: the chanlist to validate93 *94 * Each element consists of a channel number, a range index, an analog95 * reference type and some flags, all packed into an unsigned int.96 *97 * This checks that the channel number and range index are supported by98 * the comedi subdevice. It does not check whether the analog reference99 * type and the flags are supported. Drivers that care should check those100 * themselves.101 *102 * Return: %0 if all @chanlist elements are valid (success),103 * %-EINVAL if one or more elements are invalid.104 */105int 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 else120 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}131EXPORT_SYMBOL_GPL(comedi_check_chanlist);132