162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * comedi/range.c 462306a36Sopenharmony_ci * comedi routines for voltage ranges 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * COMEDI - Linux Control and Measurement Device Interface 762306a36Sopenharmony_ci * Copyright (C) 1997-8 David A. Schleef <ds@schleef.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/uaccess.h> 1162306a36Sopenharmony_ci#include <linux/comedi/comedidev.h> 1262306a36Sopenharmony_ci#include "comedi_internal.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ciconst struct comedi_lrange range_bipolar10 = { 1, {BIP_RANGE(10)} }; 1562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(range_bipolar10); 1662306a36Sopenharmony_ciconst struct comedi_lrange range_bipolar5 = { 1, {BIP_RANGE(5)} }; 1762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(range_bipolar5); 1862306a36Sopenharmony_ciconst struct comedi_lrange range_bipolar2_5 = { 1, {BIP_RANGE(2.5)} }; 1962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(range_bipolar2_5); 2062306a36Sopenharmony_ciconst struct comedi_lrange range_unipolar10 = { 1, {UNI_RANGE(10)} }; 2162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(range_unipolar10); 2262306a36Sopenharmony_ciconst struct comedi_lrange range_unipolar5 = { 1, {UNI_RANGE(5)} }; 2362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(range_unipolar5); 2462306a36Sopenharmony_ciconst struct comedi_lrange range_unipolar2_5 = { 1, {UNI_RANGE(2.5)} }; 2562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(range_unipolar2_5); 2662306a36Sopenharmony_ciconst struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} }; 2762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(range_0_20mA); 2862306a36Sopenharmony_ciconst struct comedi_lrange range_4_20mA = { 1, {RANGE_mA(4, 20)} }; 2962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(range_4_20mA); 3062306a36Sopenharmony_ciconst struct comedi_lrange range_0_32mA = { 1, {RANGE_mA(0, 32)} }; 3162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(range_0_32mA); 3262306a36Sopenharmony_ciconst struct comedi_lrange range_unknown = { 1, {{0, 1000000, UNIT_none} } }; 3362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(range_unknown); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * COMEDI_RANGEINFO ioctl 3762306a36Sopenharmony_ci * range information 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * arg: 4062306a36Sopenharmony_ci * pointer to comedi_rangeinfo structure 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * reads: 4362306a36Sopenharmony_ci * comedi_rangeinfo structure 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * writes: 4662306a36Sopenharmony_ci * array of comedi_krange structures to rangeinfo->range_ptr pointer 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ciint do_rangeinfo_ioctl(struct comedi_device *dev, 4962306a36Sopenharmony_ci struct comedi_rangeinfo *it) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci int subd, chan; 5262306a36Sopenharmony_ci const struct comedi_lrange *lr; 5362306a36Sopenharmony_ci struct comedi_subdevice *s; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci subd = (it->range_type >> 24) & 0xf; 5662306a36Sopenharmony_ci chan = (it->range_type >> 16) & 0xff; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (!dev->attached) 5962306a36Sopenharmony_ci return -EINVAL; 6062306a36Sopenharmony_ci if (subd >= dev->n_subdevices) 6162306a36Sopenharmony_ci return -EINVAL; 6262306a36Sopenharmony_ci s = &dev->subdevices[subd]; 6362306a36Sopenharmony_ci if (s->range_table) { 6462306a36Sopenharmony_ci lr = s->range_table; 6562306a36Sopenharmony_ci } else if (s->range_table_list) { 6662306a36Sopenharmony_ci if (chan >= s->n_chan) 6762306a36Sopenharmony_ci return -EINVAL; 6862306a36Sopenharmony_ci lr = s->range_table_list[chan]; 6962306a36Sopenharmony_ci } else { 7062306a36Sopenharmony_ci return -EINVAL; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (RANGE_LENGTH(it->range_type) != lr->length) { 7462306a36Sopenharmony_ci dev_dbg(dev->class_dev, 7562306a36Sopenharmony_ci "wrong length %d should be %d (0x%08x)\n", 7662306a36Sopenharmony_ci RANGE_LENGTH(it->range_type), 7762306a36Sopenharmony_ci lr->length, it->range_type); 7862306a36Sopenharmony_ci return -EINVAL; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (copy_to_user(it->range_ptr, lr->range, 8262306a36Sopenharmony_ci sizeof(struct comedi_krange) * lr->length)) 8362306a36Sopenharmony_ci return -EFAULT; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/** 8962306a36Sopenharmony_ci * comedi_check_chanlist() - Validate each element in a chanlist. 9062306a36Sopenharmony_ci * @s: comedi_subdevice struct 9162306a36Sopenharmony_ci * @n: number of elements in the chanlist 9262306a36Sopenharmony_ci * @chanlist: the chanlist to validate 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * Each element consists of a channel number, a range index, an analog 9562306a36Sopenharmony_ci * reference type and some flags, all packed into an unsigned int. 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * This checks that the channel number and range index are supported by 9862306a36Sopenharmony_ci * the comedi subdevice. It does not check whether the analog reference 9962306a36Sopenharmony_ci * type and the flags are supported. Drivers that care should check those 10062306a36Sopenharmony_ci * themselves. 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * Return: %0 if all @chanlist elements are valid (success), 10362306a36Sopenharmony_ci * %-EINVAL if one or more elements are invalid. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ciint comedi_check_chanlist(struct comedi_subdevice *s, int n, 10662306a36Sopenharmony_ci unsigned int *chanlist) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct comedi_device *dev = s->device; 10962306a36Sopenharmony_ci unsigned int chanspec; 11062306a36Sopenharmony_ci int chan, range_len, i; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci for (i = 0; i < n; i++) { 11362306a36Sopenharmony_ci chanspec = chanlist[i]; 11462306a36Sopenharmony_ci chan = CR_CHAN(chanspec); 11562306a36Sopenharmony_ci if (s->range_table) 11662306a36Sopenharmony_ci range_len = s->range_table->length; 11762306a36Sopenharmony_ci else if (s->range_table_list && chan < s->n_chan) 11862306a36Sopenharmony_ci range_len = s->range_table_list[chan]->length; 11962306a36Sopenharmony_ci else 12062306a36Sopenharmony_ci range_len = 0; 12162306a36Sopenharmony_ci if (chan >= s->n_chan || 12262306a36Sopenharmony_ci CR_RANGE(chanspec) >= range_len) { 12362306a36Sopenharmony_ci dev_warn(dev->class_dev, 12462306a36Sopenharmony_ci "bad chanlist[%d]=0x%08x chan=%d range length=%d\n", 12562306a36Sopenharmony_ci i, chanspec, chan, range_len); 12662306a36Sopenharmony_ci return -EINVAL; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_check_chanlist); 132