source: roaraudio/plugins/universal/piface.c @ 5904:768618299b76

Last change on this file since 5904:768618299b76 was 5904:768618299b76, checked in by phi, 11 years ago

added write support to special bar port

File size: 13.0 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) ^ 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
172static inline unsigned char __render_bar(int bits) {
173 unsigned char ret = 0;
174 int i;
175
176 // this is a generic version. As long as this is not called too often it should work fine.
177
178 for (i = 0; i < bits; i++) {
179  ret <<= 1;
180  ret  |= 1;
181 }
182
183 return ret;
184}
185
186 // get list of gpio IDs.
187 // buffer is passed as ids, buffer size (in elements) is passed as len.
188 // returns the number of elements stored in ids or -1 on error.
189static ssize_t __list(int * ids, size_t len) {
190 size_t i;
191
192 if ( ids == NULL ) {
193  roar_err_set(ROAR_ERROR_FAULT);
194  return -1;
195 }
196
197 for (i = 0; i < len && i < NUM_PORTS; i++)
198  ids[i] = i;
199
200 return i;
201}
202
203 // get the number of gpios. See also comments above on what.
204static ssize_t __num(enum roar_service_num what) {
205 (void)what;
206
207 return NUM_PORTS; // 8 inputs, 8 outputs and a a virtual bar
208}
209 // get a gpio by ID. The object returned is a copy and must not be motified or freed.
210static int __get(int id, struct roar_service_gpio_port * port) {
211 if ( id < 0 || id >= NUM_PORTS ) {
212  roar_err_set(ROAR_ERROR_RANGE);
213  return -1;
214 }
215
216 if ( port == NULL ) {
217  roar_err_set(ROAR_ERROR_FAULT);
218  return -1;
219 }
220
221 *port = state->ports[id];
222 return 0;
223}
224
225 // Sets up ports.
226 // the controler should set mode and state of port to given target mode and state.
227 // If id is given as -1 this is about the controler. mode MUST NOT contain any
228 // flags beside ROAR_SERVICE_GPIO_FCACHED. If ROAR_SERVICE_GPIO_FCACHED
229 // is set it is applied all ports.
230static int __setup(int id, uint_least32_t mode, enum roar_service_gpio_state tstate) {
231 size_t i;
232
233 if ( id < -1 || id >= NUM_PORTS ) {
234  roar_err_set(ROAR_ERROR_RANGE);
235  return -1;
236 }
237
238 if ( id == -1 ) {
239  if ( mode != 0 && mode != ROAR_SERVICE_GPIO_FCACHED ) {
240   roar_err_set(ROAR_ERROR_INVAL);
241   return -1;
242  }
243
244  switch (tstate) {
245   case ROAR_SERVICE_GPIO_SREADY:
246    if ( state->inited )
247     return 0;
248    if ( piface__init() == -1 )
249     return -1;
250    for (i = 0; i < NUM_PORTS; i++) {
251     state->ports[i].mode |= ROAR_SERVICE_GPIO_FCACHED;
252     state->ports[i].mode -= ROAR_SERVICE_GPIO_FCACHED;
253     state->ports[i].mode |= mode;
254    }
255    return 0;
256   case ROAR_SERVICE_GPIO_SUNINITED:
257    if ( !state->inited )
258     return 0;
259    return piface__free();
260   default:
261     roar_err_set(ROAR_ERROR_INVAL);
262     return -1;
263    break;
264  }
265 }
266
267 roar_err_set(ROAR_ERROR_NOSYS);
268 return -1;
269}
270
271 // get value of port (input or output) as int or float.
272static int __get_int(int id) {
273 const struct roar_service_gpio_port * port;
274 unsigned char val;
275
276 if ( !state->inited ) {
277  roar_err_set(ROAR_ERROR_BADSTATE);
278  return -1;
279 }
280
281 if ( id < 0 || id >= NUM_PORTS ) {
282  roar_err_set(ROAR_ERROR_RANGE);
283  return -1;
284 }
285
286 port = &(state->ports[id]);
287
288 if ( port->type != ROAR_SERVICE_GPIO_TBOOL ) {
289  roar_err_set(ROAR_ERROR_NOTSUP);
290  return -1;
291 }
292
293 if ( port->mode & ROAR_SERVICE_GPIO_FINPUT ) {
294  val = piface__read_input();
295 } else {
296  val = piface__read_output();
297 }
298
299 return __portval_to_bool(port, val);
300}
301
302static double __get_float(int id) {
303 return __get_int(id);
304}
305
306 // set value of output (as int or float).
307static int __set_int(int id, int val) {
308 const struct roar_service_gpio_port * port;
309 unsigned char buffer;
310 int bit;
311
312 if ( !state->inited ) {
313  roar_err_set(ROAR_ERROR_BADSTATE);
314  return -1;
315 }
316
317 if ( id < 0 || id >= NUM_PORTS ) {
318  roar_err_set(ROAR_ERROR_RANGE);
319  return -1;
320 }
321
322 port = &(state->ports[id]);
323
324 if ( !(port->mode & ROAR_SERVICE_GPIO_FOUTPUT) ) {
325  roar_err_set(ROAR_ERROR_NOTSUP);
326  return -1;
327 }
328
329 if ( val < port->irange_min || val > port->irange_max ) {
330  roar_err_set(ROAR_ERROR_RANGE);
331  return -1;
332 }
333
334 switch (port->type) {
335  case ROAR_SERVICE_GPIO_TBOOL:
336    bit = 1 << (id & 0x07);
337
338    buffer = state->buffer_output;
339    buffer |= bit;
340
341    if ( !val )
342     buffer -= bit;
343
344    piface__write_output(buffer);
345
346    return 0;
347   break;
348  case ROAR_SERVICE_GPIO_TINT:
349    piface__write_output(__render_bar(val));
350    return 0;
351   break;
352  default:
353    roar_err_set(ROAR_ERROR_NOTSUP);
354    return -1;
355   break;
356 }
357}
358
359static int __set_float(int id, double val) {
360 return __set_int(id, val);
361}
362
363static struct roar_service_gpio api = {
364 .list = __list,
365 .num  = __num,
366 .get  = __get,
367 .setup = __setup,
368 .get_int = __get_int,
369 .get_float = __get_float,
370 .set_int = __set_int,
371 .set_float = __set_float,
372 .convert_int = NULL,
373 .convert_float = NULL
374};
375
376ROAR_DL_PLUGIN_REG_SERVICES_GET_API(get_api, api)
377
378static const struct roar_dl_service service[1] = {
379 {
380  .appname = NULL,
381  .appabi = NULL,
382  .servicename = ROAR_SERVICE_GPIO_NAME,
383  .serviceabi = ROAR_SERVICE_GPIO_ABI,
384  .description = "Interface to PiFace",
385  .flags = ROAR_DL_SERVICE_FLAGS_NONE,
386  .userdata = NULL,
387  .get_api = get_api
388 }
389};
390
391ROAR_DL_PLUGIN_REG_SERVICES(service);
392
393ROAR_DL_PLUGIN_START(piface) {
394 ROAR_DL_PLUGIN_META_PRODUCT_NIV("piface", ROAR_VID_ROARAUDIO, ROAR_VNAME_ROARAUDIO);
395 ROAR_DL_PLUGIN_META_VERSION(ROAR_VERSION_STRING);
396 ROAR_DL_PLUGIN_META_LICENSE_TAG(GPLv3_0);
397 ROAR_DL_PLUGIN_META_CONTACT_FLNE("Philipp", "Schafft", "ph3-der-loewe", "lion@lion.leolix.org");
398 ROAR_DL_PLUGIN_META_DESC("This plugin provides a simple interface to the PiFace board.");
399 ROAR_DL_PLUGIN_REG_FNFUNC(ROAR_DL_FN_SERVICE);
400 ROAR_DL_PLUGIN_REG_GLOBAL_DATA(state, state_init);
401} ROAR_DL_PLUGIN_END
402
403#endif
404
405//ll
Note: See TracBrowser for help on using the repository browser.