source: roaraudio/roard/driver_i2cdmx.c @ 6000:e96f8ed8280b

Last change on this file since 6000:e96f8ed8280b was 6000:e96f8ed8280b, checked in by phi, 10 years ago

hard disable SPI mode

File size: 11.7 KB
Line 
1//driver_i2cdmx.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2011-2014
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 "roard.h"
27
28#ifdef ROAR_HAVE_DRIVER_I2CDMX
29
30#include <linux/i2c.h>
31#include <linux/i2c-dev.h>
32
33// hard disable SPI mode.
34#ifdef ROAR_HAVE_H_LINUX_SPI_SPIDEV
35#undef ROAR_HAVE_H_LINUX_SPI_SPIDEV
36#endif
37
38#ifdef ROAR_HAVE_H_LINUX_SPI_SPIDEV
39#include <linux/spi/spidev.h>
40#include <sys/ioctl.h>
41
42#define TRANSFER_DELAY 50
43#define TRANSFER_SPEED 100000
44#define TRANSFER_BPW   8
45#define TRANSFER_CHANS 24
46#define TRANSFER_SIZE  (TRANSFER_CHANS+6)
47#define SPI_DEVICE     "file://dev/spidev0.0"
48#endif
49
50#define DEFAULT_DEVICE  "/dev/i2c-1"
51
52#define DEV_TYPE        0x01 /* RI2C_DEV_BRIDGE */
53#define DEV_SUBTYPE     0x01 /* RI2C_SUBTYPE_BRIDGE_CONVERTER */
54#define DEVSTATUS_READY 0x01 /* RI2C_STATUS0_DEVICE_READY */
55#define CAPS0_DMX512    0x10 /* RI2C_CAPS0_BRIDGE_DMX512 */
56
57#define MIN_ADDR        0x20
58#define MAX_ADDR        0x70
59#define MAX_BUSSES      8
60
61#define ADDR_IFVERSION  0
62#define ADDR_DEVSTATUS  1
63#define ADDR_COMMAND    2
64#define ADDR_DEVERROR   3
65#define ADDR_BANK       4
66#define ADDR_DATA       5
67#define ADDR_VENDOR     (ADDR_BANK+2)
68#define ADDR_TYPE       (ADDR_BANK+3)
69#define ADDR_SUBTYPE    (ADDR_BANK+4)
70#define ADDR_PVENDOR    (ADDR_BANK+10)
71#define ADDR_PTYPE      (ADDR_BANK+11)
72#define ADDR_PSUBTYPE   (ADDR_BANK+12)
73#define ADDR_CAPS0      (ADDR_BANK+13)
74
75#define COMMAND_DEVINFO 0x00
76#define COMMAND_DMX     0x3f
77
78struct driver_i2cdmx {
79 struct roar_vio_calls vio;
80 struct roar_vio_calls spi;
81 int have_spi;
82 uint8_t slave;
83 size_t startaddr;
84 size_t len;
85#ifdef ROAR_HAVE_H_LINUX_SPI_SPIDEV
86 char txtransfer[TRANSFER_SIZE];
87 char rxtransfer[TRANSFER_SIZE];
88#endif
89};
90
91#ifdef ROAR_HAVE_H_LINUX_SPI_SPIDEV
92static int __spi_transfer(struct driver_i2cdmx * self, size_t offset_in, size_t offset_out, size_t len) {
93 struct spi_ioc_transfer transfer_buffer = {
94  .tx_buf = (unsigned long) self->txtransfer,
95  .rx_buf = (unsigned long) self->rxtransfer,
96  .len = len,
97  .delay_usecs = TRANSFER_DELAY,
98  .speed_hz = TRANSFER_SPEED,
99  .bits_per_word = TRANSFER_BPW,
100 };
101 struct roar_vio_sysio_ioctl ctl = {.cmd = SPI_IOC_MESSAGE(1), .argp = &transfer_buffer};
102
103 self->txtransfer[0] = 1; // command
104 self->txtransfer[1] = len;
105 self->txtransfer[2] = (offset_in & 0xFF00) >> 8;
106 self->txtransfer[3] =  offset_in & 0x00FF;
107 self->txtransfer[4] = (offset_in & 0xFF00) >> 8;
108 self->txtransfer[5] =  offset_in & 0x00FF;
109
110 if ( roar_vio_ctl(&(self->spi), ROAR_VIO_CTL_SYSIO_IOCTL, &ctl) == -1 )
111  return -1;
112
113 return 0;
114}
115#endif
116
117static inline int __i2c_set_slave(struct driver_i2cdmx * self) {
118 struct roar_vio_sysio_ioctl ctl = {.cmd = I2C_SLAVE, .argp = (void*)(int)self->slave};
119
120 return roar_vio_ctl(&(self->vio), ROAR_VIO_CTL_SYSIO_IOCTL, &ctl);
121}
122
123static inline int16_t __i2c_read(struct driver_i2cdmx * self, size_t off) {
124 union i2c_smbus_data data = {.byte = 0};
125 struct i2c_smbus_ioctl_data args = {.read_write = I2C_SMBUS_READ, .command = off, .size = I2C_SMBUS_BYTE_DATA, .data = &data};
126 struct roar_vio_sysio_ioctl ctl = {.cmd = I2C_SMBUS, .argp = &args};
127 int ret = roar_vio_ctl(&(self->vio), ROAR_VIO_CTL_SYSIO_IOCTL, &ctl);
128
129 if ( ret == -1 )
130  return (int16_t)-1;
131
132 return (int16_t)(uint16_t)(uint8_t)data.byte;
133}
134
135static inline int __i2c_write(struct driver_i2cdmx * self, size_t off, const uint8_t value) {
136 union i2c_smbus_data data = {.byte = value};
137 struct i2c_smbus_ioctl_data args = {.read_write = I2C_SMBUS_WRITE, .command = off, .size = I2C_SMBUS_BYTE_DATA, .data = &data};
138 struct roar_vio_sysio_ioctl ctl = {.cmd = I2C_SMBUS, .argp = &args};
139
140 return roar_vio_ctl(&(self->vio), ROAR_VIO_CTL_SYSIO_IOCTL, &ctl);
141}
142
143static inline int __i2c_command(struct driver_i2cdmx * self, uint8_t command) {
144 int16_t ret;
145
146 if ( __i2c_write(self, ADDR_COMMAND, command) == -1 )
147  return -1;
148
149 ret = __i2c_read(self, ADDR_DEVERROR);
150 if ( ret == (int16_t)-1 )
151  return -1;
152
153 if ( ret == ROAR_ERROR_NONE )
154  return 0;
155
156 roar_err_set(ret);
157 return -1;
158}
159
160static inline int __i2c_start_dmx(struct driver_i2cdmx * self) {
161 return __i2c_command(self, COMMAND_DMX);
162}
163
164static int __open_test_device_type(int16_t vendor, int16_t type, int16_t subtype) {
165 if ( vendor == -1 || type == -1 || subtype == -1 )
166  return -1;
167
168 if ( vendor != ROAR_VID_ROARAUDIO || type != DEV_TYPE || subtype != DEV_SUBTYPE ) {
169  roar_err_set(ROAR_ERROR_TYPEMM);
170  return -1;
171 }
172
173 return 0;
174}
175
176static int __open_test_device(struct driver_i2cdmx * self) {
177 int16_t ret;
178 int16_t vendor, type, subtype;
179
180#define __check_response(resp,test,error) if ( (resp) == (int16_t)-1 ) return -1; if ( !(test) ) { roar_err_set((error)); return -1; }
181
182 // test for device interface version
183 ret = __i2c_read(self, ADDR_IFVERSION);
184 __check_response(ret, ret == 0, ROAR_ERROR_NSVERSION);
185
186 // test for device overall status
187 ret = __i2c_read(self, ADDR_DEVSTATUS);
188 __check_response(ret, ret & DEVSTATUS_READY, ROAR_ERROR_BADSTATE);
189
190 // Request device infos
191 if ( __i2c_command(self, COMMAND_DEVINFO) == -1 )
192  return -1;
193
194 // Check device infos
195 // first check device type, then parent device type:
196 vendor  = __i2c_read(self, ADDR_VENDOR);
197 type    = __i2c_read(self, ADDR_TYPE);
198 subtype = __i2c_read(self, ADDR_SUBTYPE);
199
200 if ( __open_test_device_type(vendor, type, subtype) == -1 ) {
201  vendor  = __i2c_read(self, ADDR_PVENDOR);
202  type    = __i2c_read(self, ADDR_PTYPE);
203  subtype = __i2c_read(self, ADDR_PSUBTYPE);
204
205  if (  __open_test_device_type(vendor, type, subtype) == -1 )
206   return -1;
207 }
208
209 // check for DMX512 support:
210
211 ret = __i2c_read(self, ADDR_CAPS0);
212 __check_response(ret, ret & CAPS0_DMX512, ROAR_ERROR_TYPEMM);
213
214 return 0;
215}
216
217static int __open_scan_devices(struct driver_i2cdmx * self) {
218 uint8_t i;
219
220 for (i = MIN_ADDR; i < MAX_ADDR; i++) {
221  self->slave = i;
222
223  if ( __i2c_set_slave(self) == -1 )
224   continue;
225
226  if ( __open_test_device(self) == -1 )
227   continue;
228
229  return 0;
230 }
231
232 self->slave = 0;
233 roar_err_set(ROAR_ERROR_NOENT);
234 return -1;
235}
236
237static int __open_device_and_slave(struct driver_i2cdmx * self, int autoconf, const char * device) {
238 char filename[40];
239 char * p;
240 int need_autoconf = 0;
241 int ret;
242 int err;
243
244 // test if the device exist.
245 if ( roar_vio_open_dstr_simple(&(self->vio), device, ROAR_VIOF_READWRITE) == 0 ) {
246  need_autoconf = 1;
247 } else {
248  // the device doesn't exist. We guss it is in form $device/$slaveid.
249  strncpy(filename, device, sizeof(filename));
250  p = strrchr(filename, '/');
251  if ( p == NULL ) {
252   // it doesn't seem to be in the given form so it seems it was just a device name of an missingd evice.
253   roar_err_set(ROAR_ERROR_NOENT);
254   return -1;
255  }
256
257  *p = 0;
258  p++;
259
260  // now we have the device name in filename, and the slave ID in p.
261  self->slave = atoi(p);
262
263  // little test to protect the user from doing dumb things.
264  if ( self->slave == 0 ) {
265   roar_err_set(ROAR_ERROR_INVAL);
266   return -1;
267  }
268
269  if ( roar_vio_open_dstr_simple(&(self->vio), filename, ROAR_VIOF_READWRITE) == -1 )
270   return -1;
271 }
272
273 // ok, the bus is now open. Let's open the device:
274
275 if ( need_autoconf ) {
276  ret = __open_scan_devices(self);
277 } else {
278  ret = __i2c_set_slave(self);
279  if ( ret == 0 )
280   ret = __open_test_device(self);
281 }
282
283 if ( ret == -1 ) {
284  err = roar_error;
285  roar_vio_close(&(self->vio));
286  roar_err_set(err);
287 }
288
289 return ret;
290}
291
292static int __open_scan_busses(struct driver_i2cdmx * self) {
293 char filename[20];
294 int i;
295 int ret;
296
297 for (i = 0; i < MAX_BUSSES; i++) {
298  snprintf(filename, sizeof(filename), "/dev/i2c-%i", i);
299  ret = __open_device_and_slave(self, 1, filename);
300  if ( ret == 0 )
301   return 0;
302 }
303
304 roar_err_set(ROAR_ERROR_NOENT);
305 return -1;
306}
307
308static int __i2c_write_channel(struct driver_i2cdmx * self, size_t channel, uint8_t value) {
309 size_t bank, offset;
310
311 bank = channel/32;
312 offset = bank*32;
313
314 if ( __i2c_write(self, ADDR_BANK, bank) == -1 )
315  return -1;
316
317 return __i2c_write(self, ADDR_DATA+channel-offset, value);
318}
319
320static ssize_t        __vio_read    (struct roar_vio_calls * vio, void *buf, size_t count) {
321 struct driver_i2cdmx * self;
322
323 if ( vio == NULL ) {
324  roar_err_set(ROAR_ERROR_FAULT);
325  return -1;
326 }
327
328 self = vio->inst;
329
330 if ( count != 512 ) {
331  roar_err_set(ROAR_ERROR_INVAL);
332  return -1;
333 }
334
335 if ( __i2c_start_dmx(self) == -1 )
336  return -1;
337
338 memset(buf, 0, count); // optimize this...
339#ifdef ROAR_HAVE_H_LINUX_SPI_SPIDEV
340 memcpy(buf, self->rxtransfer+6, sizeof(self->rxtransfer)-6);
341#endif
342 return 0;
343}
344
345static ssize_t        __vio_write   (struct roar_vio_calls * vio, void *buf, size_t count) {
346 struct driver_i2cdmx * self;
347 size_t i;
348 size_t endaddr;
349
350 if ( vio == NULL ) {
351  roar_err_set(ROAR_ERROR_FAULT);
352  return -1;
353 }
354
355 self = vio->inst;
356
357 if ( count != 512 ) {
358  roar_err_set(ROAR_ERROR_INVAL);
359  return -1;
360 }
361
362 if ( __i2c_start_dmx(self) == -1 )
363  return -1;
364
365 for (i = self->startaddr, endaddr = self->startaddr + self->len; i < endaddr; i++)
366  if ( __i2c_write_channel(self, i, ((uint8_t*)buf)[i]) == -1 )
367   return -1;
368#ifdef ROAR_HAVE_H_LINUX_SPI_SPIDEV
369 memcpy(self->txtransfer+6, buf, sizeof(self->txtransfer)-6);
370 __spi_transfer(self, 0, 0, TRANSFER_CHANS);
371#endif
372
373 return count;
374}
375
376static int            __vio_ctl     (struct roar_vio_calls * vio, roar_vio_ctl_t cmd, void * data) {
377 struct driver_i2cdmx * self;
378
379 if ( vio == NULL ) {
380  roar_err_set(ROAR_ERROR_FAULT);
381  return -1;
382 }
383
384 self = vio->inst;
385
386 switch (cmd) {
387  case ROAR_VIO_CTL_SET_SSTREAM:
388    if ( ROAR_STREAM(data)->dir != ROAR_DIR_LIGHT_OUT && ROAR_STREAM(data)->dir != ROAR_DIR_LIGHT_IN ) {
389     ROAR_STREAM(data)->dir = ROAR_DIR_LIGHT_OUT;
390    }
391    ROAR_STREAM_SERVER(data)->codec_orgi = ROAR_CODEC_DMX512;
392   break;
393  default:
394   roar_err_set(ROAR_ERROR_BADRQC);
395   return -1;
396 }
397
398 return 0;
399}
400
401static int            __vio_close   (struct roar_vio_calls * vio) {
402 struct driver_i2cdmx * self = vio->inst;
403 roar_vio_close(&(self->vio));
404 if ( self->have_spi )
405  roar_vio_close(&(self->spi));
406 roar_mm_free(self);
407 return 0;
408}
409
410int driver_i2cdmx_open_vio  (struct roar_vio_calls * inst, char * device, struct roar_audio_info * info, int fh, struct roar_stream_server * sstream) {
411 struct driver_i2cdmx * self;
412 int autoconf = 1;
413 int ret;
414
415 if ( fh != -1 ) {
416  roar_err_set(ROAR_ERROR_NOSYS);
417  return -1;
418 }
419
420 self = roar_mm_malloc(sizeof(struct driver_i2cdmx));
421 if ( self == NULL ) {
422  return -1;
423 }
424 memset(self, 0, sizeof(*self));
425 self->have_spi  = 0;
426 self->startaddr = 0;
427 self->len       = info->channels;
428
429 info->bits      = 8;
430 info->codec     = ROAR_CODEC_DMX512;
431
432 if ( device == NULL && !autoconf ) {
433  device = DEFAULT_DEVICE;
434 }
435
436 if ( device == NULL ) {
437  ret = __open_scan_busses(self);
438 } else {
439  ret = __open_device_and_slave(self, autoconf, device);
440 }
441
442 if ( ret == -1 ) {
443  roar_mm_free_noerror(self);
444  return -1;
445 }
446
447#if defined(ROAR_HAVE_H_LINUX_SPI_SPIDEV) && defined(SPI_DEVICE)
448 if ( roar_vio_open_dstr_simple(&(self->spi), SPI_DEVICE, ROAR_VIOF_READWRITE) == 0 )
449  self->have_spi = 1;
450#endif
451
452 roar_vio_clear_calls(inst);
453 inst->inst  = self;
454 inst->read  = __vio_read;
455 inst->write = __vio_write;
456 inst->close = __vio_close;
457 inst->ctl   = __vio_ctl;
458
459 return 0;
460}
461#endif
Note: See TracBrowser for help on using the repository browser.