source: roaraudio/plugins/universal/piface.c @ 5902:b82b16021032

Last change on this file since 5902:b82b16021032 was 5902:b82b16021032, checked in by phi, 11 years ago
  • Added GPIO service.
  • Added GPIO driver: piface.
File size: 12.5 KB
Line 
1//piface.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2013
5 *
6 *  This file is part of roard a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  RoarAudio is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include <roaraudio.h>
27
28#ifdef ROAR_HAVE_H_LINUX_SPI_SPIDEV
29#include <linux/spi/spidev.h>
30#include <sys/ioctl.h>
31
32#define NUM_PORTS (8+8+1)
33
34#define TRANSFER_LEN   3
35#define TRANSFER_DELAY 5
36#define TRANSFER_SPEED 1000000
37#define TRANSFER_BPW   8
38
39#define SPI_WRITE_CMD 0x40
40#define SPI_READ_CMD 0x41
41
42#define IODIRA 0x00 // I/O direction A
43#define IODIRB 0x01 // I/O direction B
44#define IOCON  0x0A // I/O config
45#define GPIOA  0x12 // port A
46#define GPIOB  0x13 // port B
47#define GPPUA  0x0C // port A pullups
48#define GPPUB  0x0D // port B pullups
49#define OUTPUT_PORT GPIOA
50#define INPUT_PORT  GPIOB
51
52struct state {
53 int inited;
54 int bus;
55 int device;
56 struct roar_vio_calls vio_store;
57 struct roar_vio_calls * vio;
58 unsigned char buffer_output;
59 unsigned char buffer_input;
60 struct roar_service_gpio_port ports[NUM_PORTS];
61};
62
63static struct state * state = NULL;
64static struct state   state_init = {
65 .inited = 0,
66 .bus    = 0,
67 .device = 0,
68 .vio    = NULL,
69 .buffer_output = 0x00,
70 .buffer_input  = 0x00,
71 .ports  = {
72// inputs:
73  {.id = 0, .name = "gpin0", .mode = ROAR_SERVICE_GPIO_FINPUT|ROAR_SERVICE_GPIO_FPULLUP, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
74  {.id = 1, .name = "gpin1", .mode = ROAR_SERVICE_GPIO_FINPUT|ROAR_SERVICE_GPIO_FPULLUP, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
75  {.id = 2, .name = "gpin2", .mode = ROAR_SERVICE_GPIO_FINPUT|ROAR_SERVICE_GPIO_FPULLUP, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
76  {.id = 3, .name = "gpin3", .mode = ROAR_SERVICE_GPIO_FINPUT|ROAR_SERVICE_GPIO_FPULLUP, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
77  {.id = 4, .name = "gpin4", .mode = ROAR_SERVICE_GPIO_FINPUT|ROAR_SERVICE_GPIO_FPULLUP, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
78  {.id = 5, .name = "gpin5", .mode = ROAR_SERVICE_GPIO_FINPUT|ROAR_SERVICE_GPIO_FPULLUP, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
79  {.id = 6, .name = "gpin6", .mode = ROAR_SERVICE_GPIO_FINPUT|ROAR_SERVICE_GPIO_FPULLUP, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
80  {.id = 7, .name = "gpin7", .mode = ROAR_SERVICE_GPIO_FINPUT|ROAR_SERVICE_GPIO_FPULLUP, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
81
82// outputs:
83  {.id = 8, .name = "gpout0", .mode = ROAR_SERVICE_GPIO_FOUTPUT, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
84  {.id = 9, .name = "gpout1", .mode = ROAR_SERVICE_GPIO_FOUTPUT, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
85  {.id = 10, .name = "gpout2", .mode = ROAR_SERVICE_GPIO_FOUTPUT, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
86  {.id = 11, .name = "gpout3", .mode = ROAR_SERVICE_GPIO_FOUTPUT, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
87  {.id = 12, .name = "gpout4", .mode = ROAR_SERVICE_GPIO_FOUTPUT, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
88  {.id = 13, .name = "gpout5", .mode = ROAR_SERVICE_GPIO_FOUTPUT, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
89  {.id = 14, .name = "gpout6", .mode = ROAR_SERVICE_GPIO_FOUTPUT, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
90  {.id = 15, .name = "gpout7", .mode = ROAR_SERVICE_GPIO_FOUTPUT, .unit = NULL, .type = ROAR_SERVICE_GPIO_TBOOL, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 1, .frange_min = 0, .frange_max = 1},
91
92// special:
93  {.id = 16, .name = "bar0", .mode = ROAR_SERVICE_GPIO_FOUTPUT, .unit = NULL, .type = ROAR_SERVICE_GPIO_TINT, .state = ROAR_SERVICE_GPIO_SUNINITED, .irange_min = 0, .irange_max = 8, .frange_min = 0, .frange_max = 8}
94 }
95};
96
97static int piface__transfer(char cmd, char port, char value) {
98 unsigned char txbuffer[3] = {cmd, port, value};
99 unsigned char rxbuffer[3];
100 struct spi_ioc_transfer transfer_buffer = {
101  .tx_buf = (unsigned long) txbuffer,
102  .rx_buf = (unsigned long) rxbuffer,
103  .len = TRANSFER_LEN,
104  .delay_usecs = TRANSFER_DELAY,
105  .speed_hz = TRANSFER_SPEED,
106  .bits_per_word = TRANSFER_BPW,
107 };
108 struct roar_vio_sysio_ioctl ctl = {.cmd = SPI_IOC_MESSAGE(1), .argp = &transfer_buffer};
109
110 if ( roar_vio_ctl(state->vio, ROAR_VIO_CTL_SYSIO_IOCTL, &ctl) == -1 )
111  return -1;
112
113 return (unsigned int)rxbuffer[2];
114}
115
116static int piface__init(void) {
117 char path[80];
118
119 if ( state->inited ) {
120  roar_err_set(ROAR_ERROR_ALREADY);
121  return -1;
122 }
123
124 snprintf(path, sizeof(path), "file://dev/spidev%i.%i", state->bus, state->device);
125
126 state->vio = &(state->vio_store);
127 if ( roar_vio_open_dstr_simple(state->vio, path, ROAR_VIOF_READWRITE) == -1 )
128  return -1;
129
130 piface__transfer(SPI_WRITE_CMD, IOCON,  8);    // enable hardware addressing
131 piface__transfer(SPI_WRITE_CMD, GPIOA,  0x00); // turn on port A
132 piface__transfer(SPI_WRITE_CMD, IODIRA, 0);    // set port A as an output
133 piface__transfer(SPI_WRITE_CMD, IODIRB, 0xFF); // set port B as an input
134 piface__transfer(SPI_WRITE_CMD, GPPUB,  0xFF); // turn on port B pullups
135 piface__transfer(SPI_WRITE_CMD, OUTPUT_PORT, 0x00); // turn of all outputs.
136
137 state->inited = 1;
138
139 return 0;
140}
141
142static int piface__free(void) {
143 if ( !state->inited ) {
144  roar_err_set(ROAR_ERROR_BADSTATE);
145  return -1;
146 }
147
148 roar_vio_close(state->vio);
149
150 state->inited = 0;
151
152 return 0;
153}
154
155static void piface__write_output(unsigned char val) {
156 piface__transfer(SPI_WRITE_CMD, OUTPUT_PORT, state->buffer_output = val);
157}
158
159static unsigned char piface__read_output(void) {
160 return state->buffer_output = piface__transfer(SPI_READ_CMD, OUTPUT_PORT, 0xFF);
161}
162
163static unsigned char piface__read_input(void) {
164 return state->buffer_input = piface__transfer(SPI_READ_CMD, INPUT_PORT, 0xFF);
165}
166
167static inline int __portval_to_bool(const struct roar_service_gpio_port * port, unsigned int val) {
168 int bit = 1 << (port->id & 0x07);
169 return val & bit ? 1 : 0;
170}
171
172 // get list of gpio IDs.
173 // buffer is passed as ids, buffer size (in elements) is passed as len.
174 // returns the number of elements stored in ids or -1 on error.
175static ssize_t __list(int * ids, size_t len) {
176 size_t i;
177
178 if ( ids == NULL ) {
179  roar_err_set(ROAR_ERROR_FAULT);
180  return -1;
181 }
182
183 for (i = 0; i < len && i < NUM_PORTS; i++)
184  ids[i] = i;
185
186 return i;
187}
188
189 // get the number of gpios. See also comments above on what.
190static ssize_t __num(enum roar_service_num what) {
191 (void)what;
192
193 return NUM_PORTS; // 8 inputs, 8 outputs and a a virtual bar
194}
195 // get a gpio by ID. The object returned is a copy and must not be motified or freed.
196static int __get(int id, struct roar_service_gpio_port * port) {
197 if ( id < 0 || id >= NUM_PORTS ) {
198  roar_err_set(ROAR_ERROR_RANGE);
199  return -1;
200 }
201
202 if ( port == NULL ) {
203  roar_err_set(ROAR_ERROR_FAULT);
204  return -1;
205 }
206
207 *port = state->ports[id];
208 return 0;
209}
210
211 // Sets up ports.
212 // the controler should set mode and state of port to given target mode and state.
213 // If id is given as -1 this is about the controler. mode MUST NOT contain any
214 // flags beside ROAR_SERVICE_GPIO_FCACHED. If ROAR_SERVICE_GPIO_FCACHED
215 // is set it is applied all ports.
216static int __setup(int id, uint_least32_t mode, enum roar_service_gpio_state tstate) {
217 size_t i;
218
219 if ( id < -1 || id >= NUM_PORTS ) {
220  roar_err_set(ROAR_ERROR_RANGE);
221  return -1;
222 }
223
224 if ( id == -1 ) {
225  if ( mode != 0 && mode != ROAR_SERVICE_GPIO_FCACHED ) {
226   roar_err_set(ROAR_ERROR_INVAL);
227   return -1;
228  }
229
230  switch (tstate) {
231   case ROAR_SERVICE_GPIO_SREADY:
232    if ( state->inited )
233     return 0;
234    if ( piface__init() == -1 )
235     return -1;
236    for (i = 0; i < NUM_PORTS; i++) {
237     state->ports[i].mode |= ROAR_SERVICE_GPIO_FCACHED;
238     state->ports[i].mode -= ROAR_SERVICE_GPIO_FCACHED;
239     state->ports[i].mode |= mode;
240    }
241    return 0;
242   case ROAR_SERVICE_GPIO_SUNINITED:
243    if ( !state->inited )
244     return 0;
245    return piface__free();
246   default:
247     roar_err_set(ROAR_ERROR_INVAL);
248     return -1;
249    break;
250  }
251 }
252
253 roar_err_set(ROAR_ERROR_NOSYS);
254 return -1;
255}
256
257 // get value of port (input or output) as int or float.
258static int __get_int(int id) {
259 const struct roar_service_gpio_port * port;
260 unsigned char val;
261
262 if ( !state->inited ) {
263  roar_err_set(ROAR_ERROR_BADSTATE);
264  return -1;
265 }
266
267 if ( id < 0 || id >= NUM_PORTS ) {
268  roar_err_set(ROAR_ERROR_RANGE);
269  return -1;
270 }
271
272 port = &(state->ports[id]);
273
274 if ( port->type != ROAR_SERVICE_GPIO_TBOOL ) {
275  roar_err_set(ROAR_ERROR_NOTSUP);
276  return -1;
277 }
278
279 if ( port->mode & ROAR_SERVICE_GPIO_FINPUT ) {
280  val = piface__read_input();
281 } else {
282  val = piface__read_output();
283 }
284
285 return __portval_to_bool(port, val);
286}
287
288static double __get_float(int id) {
289 return __get_int(id);
290}
291
292 // set value of output (as int or float).
293static int __set_int(int id, int val) {
294 const struct roar_service_gpio_port * port;
295 unsigned char buffer;
296 int bit;
297
298 if ( !state->inited ) {
299  roar_err_set(ROAR_ERROR_BADSTATE);
300  return -1;
301 }
302
303 if ( id < 0 || id >= NUM_PORTS ) {
304  roar_err_set(ROAR_ERROR_RANGE);
305  return -1;
306 }
307
308 port = &(state->ports[id]);
309
310 if ( !(port->mode & ROAR_SERVICE_GPIO_FOUTPUT) || port->type != ROAR_SERVICE_GPIO_TBOOL ) {
311  roar_err_set(ROAR_ERROR_NOTSUP);
312  return -1;
313 }
314
315 bit = 1 << (id & 0x07);
316
317 buffer = state->buffer_output;
318 buffer |= bit;
319
320 if ( !val )
321  buffer -= bit;
322
323 piface__write_output(buffer);
324
325 return 0;
326}
327
328static int __set_float(int id, double val) {
329 return __set_int(id, val);
330}
331
332static struct roar_service_gpio api = {
333 .list = __list,
334 .num  = __num,
335 .get  = __get,
336 .setup = __setup,
337 .get_int = __get_int,
338 .get_float = __get_float,
339 .set_int = __set_int,
340 .set_float = __set_float,
341 .convert_int = NULL,
342 .convert_float = NULL
343};
344
345ROAR_DL_PLUGIN_REG_SERVICES_GET_API(get_api, api)
346
347static const struct roar_dl_service service[1] = {
348 {
349  .appname = NULL,
350  .appabi = NULL,
351  .servicename = ROAR_SERVICE_GPIO_NAME,
352  .serviceabi = ROAR_SERVICE_GPIO_ABI,
353  .description = "Interface to PiFace",
354  .flags = ROAR_DL_SERVICE_FLAGS_NONE,
355  .userdata = NULL,
356  .get_api = get_api
357 }
358};
359
360ROAR_DL_PLUGIN_REG_SERVICES(service);
361
362ROAR_DL_PLUGIN_START(piface) {
363 ROAR_DL_PLUGIN_META_PRODUCT_NIV("piface", ROAR_VID_ROARAUDIO, ROAR_VNAME_ROARAUDIO);
364 ROAR_DL_PLUGIN_META_VERSION(ROAR_VERSION_STRING);
365 ROAR_DL_PLUGIN_META_LICENSE_TAG(GPLv3_0);
366 ROAR_DL_PLUGIN_META_CONTACT_FLNE("Philipp", "Schafft", "ph3-der-loewe", "lion@lion.leolix.org");
367 ROAR_DL_PLUGIN_META_DESC("This plugin provides a simple interface to the PiFace board.");
368 ROAR_DL_PLUGIN_REG_FNFUNC(ROAR_DL_FN_SERVICE);
369 ROAR_DL_PLUGIN_REG_GLOBAL_DATA(state, state_init);
370} ROAR_DL_PLUGIN_END
371
372#endif
373
374//ll
Note: See TracBrowser for help on using the repository browser.