Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * comedi/range.c
0004  * comedi routines for voltage ranges
0005  *
0006  * COMEDI - Linux Control and Measurement Device Interface
0007  * Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
0008  */
0009 
0010 #include <linux/uaccess.h>
0011 #include <linux/comedi/comedidev.h>
0012 #include "comedi_internal.h"
0013 
0014 const struct comedi_lrange range_bipolar10 = { 1, {BIP_RANGE(10)} };
0015 EXPORT_SYMBOL_GPL(range_bipolar10);
0016 const struct comedi_lrange range_bipolar5 = { 1, {BIP_RANGE(5)} };
0017 EXPORT_SYMBOL_GPL(range_bipolar5);
0018 const struct comedi_lrange range_bipolar2_5 = { 1, {BIP_RANGE(2.5)} };
0019 EXPORT_SYMBOL_GPL(range_bipolar2_5);
0020 const struct comedi_lrange range_unipolar10 = { 1, {UNI_RANGE(10)} };
0021 EXPORT_SYMBOL_GPL(range_unipolar10);
0022 const struct comedi_lrange range_unipolar5 = { 1, {UNI_RANGE(5)} };
0023 EXPORT_SYMBOL_GPL(range_unipolar5);
0024 const struct comedi_lrange range_unipolar2_5 = { 1, {UNI_RANGE(2.5)} };
0025 EXPORT_SYMBOL_GPL(range_unipolar2_5);
0026 const struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} };
0027 EXPORT_SYMBOL_GPL(range_0_20mA);
0028 const struct comedi_lrange range_4_20mA = { 1, {RANGE_mA(4, 20)} };
0029 EXPORT_SYMBOL_GPL(range_4_20mA);
0030 const struct comedi_lrange range_0_32mA = { 1, {RANGE_mA(0, 32)} };
0031 EXPORT_SYMBOL_GPL(range_0_32mA);
0032 const struct comedi_lrange range_unknown = { 1, {{0, 1000000, UNIT_none} } };
0033 EXPORT_SYMBOL_GPL(range_unknown);
0034 
0035 /*
0036  * COMEDI_RANGEINFO ioctl
0037  * range information
0038  *
0039  * arg:
0040  *  pointer to comedi_rangeinfo structure
0041  *
0042  * reads:
0043  *  comedi_rangeinfo structure
0044  *
0045  * writes:
0046  *  array of comedi_krange structures to rangeinfo->range_ptr pointer
0047  */
0048 int do_rangeinfo_ioctl(struct comedi_device *dev,
0049                struct comedi_rangeinfo *it)
0050 {
0051     int subd, chan;
0052     const struct comedi_lrange *lr;
0053     struct comedi_subdevice *s;
0054 
0055     subd = (it->range_type >> 24) & 0xf;
0056     chan = (it->range_type >> 16) & 0xff;
0057 
0058     if (!dev->attached)
0059         return -EINVAL;
0060     if (subd >= dev->n_subdevices)
0061         return -EINVAL;
0062     s = &dev->subdevices[subd];
0063     if (s->range_table) {
0064         lr = s->range_table;
0065     } else if (s->range_table_list) {
0066         if (chan >= s->n_chan)
0067             return -EINVAL;
0068         lr = s->range_table_list[chan];
0069     } else {
0070         return -EINVAL;
0071     }
0072 
0073     if (RANGE_LENGTH(it->range_type) != lr->length) {
0074         dev_dbg(dev->class_dev,
0075             "wrong length %d should be %d (0x%08x)\n",
0076             RANGE_LENGTH(it->range_type),
0077             lr->length, it->range_type);
0078         return -EINVAL;
0079     }
0080 
0081     if (copy_to_user(it->range_ptr, lr->range,
0082              sizeof(struct comedi_krange) * lr->length))
0083         return -EFAULT;
0084 
0085     return 0;
0086 }
0087 
0088 /**
0089  * comedi_check_chanlist() - Validate each element in a chanlist.
0090  * @s: comedi_subdevice struct
0091  * @n: number of elements in the chanlist
0092  * @chanlist: the chanlist to validate
0093  *
0094  * Each element consists of a channel number, a range index, an analog
0095  * reference type and some flags, all packed into an unsigned int.
0096  *
0097  * This checks that the channel number and range index are supported by
0098  * the comedi subdevice.  It does not check whether the analog reference
0099  * type and the flags are supported.  Drivers that care should check those
0100  * themselves.
0101  *
0102  * Return: %0 if all @chanlist elements are valid (success),
0103  *         %-EINVAL if one or more elements are invalid.
0104  */
0105 int comedi_check_chanlist(struct comedi_subdevice *s, int n,
0106               unsigned int *chanlist)
0107 {
0108     struct comedi_device *dev = s->device;
0109     unsigned int chanspec;
0110     int chan, range_len, i;
0111 
0112     for (i = 0; i < n; i++) {
0113         chanspec = chanlist[i];
0114         chan = CR_CHAN(chanspec);
0115         if (s->range_table)
0116             range_len = s->range_table->length;
0117         else if (s->range_table_list && chan < s->n_chan)
0118             range_len = s->range_table_list[chan]->length;
0119         else
0120             range_len = 0;
0121         if (chan >= s->n_chan ||
0122             CR_RANGE(chanspec) >= range_len) {
0123             dev_warn(dev->class_dev,
0124                  "bad chanlist[%d]=0x%08x chan=%d range length=%d\n",
0125                  i, chanspec, chan, range_len);
0126             return -EINVAL;
0127         }
0128     }
0129     return 0;
0130 }
0131 EXPORT_SYMBOL_GPL(comedi_check_chanlist);