1 /*
2  * Author: Nandkishor Sonar
3  * Author: Brendan Le Foll <brendan.le.foll@intel.com>
4  * Copyright (c) 2014, 2015 Intel Corporation.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 
26 #include <stdlib.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 
30 #include "aio.h"
31 #include "mraa_internal.h"
32 
33 #define DEFAULT_BITS 10
34 
35 static int raw_bits;
36 
37 static mraa_result_t
aio_get_valid_fp(mraa_aio_context dev)38 aio_get_valid_fp(mraa_aio_context dev)
39 {
40     if (IS_FUNC_DEFINED(dev, aio_get_valid_fp)) {
41         return dev->advance_func->aio_get_valid_fp(dev);
42     }
43 
44     char file_path[64] = "";
45 
46     // Open file Analog device input channel raw voltage file for reading.
47     snprintf(file_path, 64, "/sys/bus/iio/devices/iio:device0/in_voltage%d_raw", dev->channel);
48 
49     dev->adc_in_fp = open(file_path, O_RDONLY);
50     if (dev->adc_in_fp == -1) {
51         syslog(LOG_ERR, "aio: Failed to open input raw file %s for reading!", file_path);
52         return MRAA_ERROR_INVALID_RESOURCE;
53     }
54 
55     return MRAA_SUCCESS;
56 }
57 
58 static mraa_aio_context
mraa_aio_init_internal(mraa_adv_func_t * func_table)59 mraa_aio_init_internal(mraa_adv_func_t* func_table)
60 {
61     mraa_aio_context dev = calloc(1, sizeof(struct _aio));
62     if (dev == NULL) {
63         return NULL;
64     }
65     dev->advance_func = func_table;
66 
67     return dev;
68 }
69 
70 mraa_aio_context
mraa_aio_init(unsigned int aio)71 mraa_aio_init(unsigned int aio)
72 {
73     if (plat == NULL) {
74         syslog(LOG_ERR, "aio: Platform not initialised");
75         return NULL;
76     }
77     if (mraa_is_sub_platform_id(aio)) {
78         syslog(LOG_NOTICE, "aio: Using sub platform is not supported");
79         return NULL;
80     }
81 
82     // Create ADC device connected to specified channel
83     mraa_aio_context dev = mraa_aio_init_internal(plat->adv_func);
84     if (dev == NULL) {
85         syslog(LOG_ERR, "aio: Insufficient memory for specified input channel %d", aio);
86         return NULL;
87     }
88     int pin = aio + plat->gpio_count;
89     dev->channel = plat->pins[pin].aio.pinmap;
90     dev->value_bit = DEFAULT_BITS;
91 
92     if (IS_FUNC_DEFINED(dev, aio_init_pre)) {
93         mraa_result_t pre_ret = (dev->advance_func->aio_init_pre(aio));
94         if (pre_ret != MRAA_SUCCESS) {
95             free(dev);
96             return NULL;
97         }
98     }
99     if (aio > plat->aio_count) {
100         syslog(LOG_ERR, "aio: requested channel out of range");
101         free(dev);
102         return NULL;
103     }
104 
105     if (plat->pins[pin].capabilites.aio != 1) {
106         syslog(LOG_ERR, "aio: pin uncapable of aio");
107         free(dev);
108         return NULL;
109     }
110 
111     if (plat->pins[pin].aio.mux_total > 0) {
112         if (mraa_setup_mux_mapped(plat->pins[pin].aio) != MRAA_SUCCESS) {
113             free(dev);
114             syslog(LOG_ERR, "aio: unable to setup multiplexers for pin");
115             return NULL;
116         }
117     }
118 
119     // Open valid  analog input file and get the pointer.
120     if (MRAA_SUCCESS != aio_get_valid_fp(dev)) {
121         free(dev);
122         return NULL;
123     }
124     raw_bits = mraa_adc_raw_bits();
125 
126     if (IS_FUNC_DEFINED(dev, aio_init_post)) {
127         mraa_result_t ret = dev->advance_func->aio_init_post(dev);
128         if (ret != MRAA_SUCCESS) {
129             free(dev);
130             return NULL;
131         }
132     }
133 
134     return dev;
135 }
136 
137 unsigned int
mraa_aio_read(mraa_aio_context dev)138 mraa_aio_read(mraa_aio_context dev)
139 {
140     char buffer[17];
141     unsigned int shifter_value = 0;
142 
143     if (dev->adc_in_fp == -1) {
144         if (aio_get_valid_fp(dev) != MRAA_SUCCESS) {
145             syslog(LOG_ERR, "aio: Failed to get to the device");
146             return 0;
147         }
148     }
149 
150     lseek(dev->adc_in_fp, 0, SEEK_SET);
151     if (read(dev->adc_in_fp, buffer, sizeof(buffer)) < 1) {
152         syslog(LOG_ERR, "aio: Failed to read a sensible value");
153     }
154     // force NULL termination of string
155     buffer[16] = '\0';
156     lseek(dev->adc_in_fp, 0, SEEK_SET);
157 
158     errno = 0;
159     char* end;
160     unsigned int analog_value = (unsigned int) strtoul(buffer, &end, 10);
161     if (end == &buffer[0]) {
162         syslog(LOG_ERR, "aio: Value is not a decimal number");
163     } else if (errno != 0) {
164         syslog(LOG_ERR, "aio: Errno was set");
165     }
166 
167     if (dev->value_bit != raw_bits) {
168         /* Adjust the raw analog input reading to supported resolution value*/
169         if (raw_bits > dev->value_bit) {
170             shifter_value = raw_bits - dev->value_bit;
171             analog_value = analog_value >> shifter_value;
172         } else {
173             shifter_value = dev->value_bit - raw_bits;
174             analog_value = analog_value << shifter_value;
175         }
176     }
177 
178     return analog_value;
179 }
180 
181 float
mraa_aio_read_float(mraa_aio_context dev)182 mraa_aio_read_float(mraa_aio_context dev)
183 {
184     if (dev == NULL) {
185         syslog(LOG_ERR, "aio: Device not valid");
186         return 0.0;
187     }
188 
189     float max_analog_value = (1 << dev->value_bit) - 1;
190     unsigned int analog_value_int = mraa_aio_read(dev);
191 
192     return analog_value_int / max_analog_value;
193 }
194 
195 mraa_result_t
mraa_aio_close(mraa_aio_context dev)196 mraa_aio_close(mraa_aio_context dev)
197 {
198     if (NULL != dev) {
199         if (dev->adc_in_fp != -1)
200             close(dev->adc_in_fp);
201         free(dev);
202     }
203 
204     return (MRAA_SUCCESS);
205 }
206 
207 mraa_result_t
mraa_aio_set_bit(mraa_aio_context dev,int bits)208 mraa_aio_set_bit(mraa_aio_context dev, int bits)
209 {
210     if (dev == NULL || bits < 1) {
211         syslog(LOG_ERR, "aio: Device not valid");
212         return MRAA_ERROR_INVALID_RESOURCE;
213     }
214     dev->value_bit = bits;
215     return MRAA_SUCCESS;
216 }
217 
218 int
mraa_aio_get_bit(mraa_aio_context dev)219 mraa_aio_get_bit(mraa_aio_context dev)
220 {
221     if (dev == NULL) {
222         syslog(LOG_ERR, "aio: Device not valid");
223         return 0;
224     }
225     return dev->value_bit;
226 }
227