source: roaraudio/roard/driver_i2cdmx.c @ 6033:20a513aa6306

Last change on this file since 6033:20a513aa6306 was 6033:20a513aa6306, checked in by phi, 10 years ago

removed unused stuff

File size: 12.9 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_write_block(struct driver_i2cdmx * self, size_t off, const uint8_t * value) {
144 union i2c_smbus_data data;
145 struct i2c_smbus_ioctl_data args = {.read_write = I2C_SMBUS_WRITE, .command = off, .size = I2C_SMBUS_I2C_BLOCK_BROKEN, .data = &data};
146 struct roar_vio_sysio_ioctl ctl = {.cmd = I2C_SMBUS, .argp = &args};
147 size_t i;
148
149 for (i = 0; i < sizeof(data.block); i++)
150  data.block[i] = 0xAF;
151
152 data.block[0] = I2C_SMBUS_BLOCK_MAX;
153
154 for (i = 0; i < I2C_SMBUS_BLOCK_MAX; i++)
155  data.block[i+1] = value[i];
156
157 return roar_vio_ctl(&(self->vio), ROAR_VIO_CTL_SYSIO_IOCTL, &ctl);
158}
159
160static inline int __i2c_command(struct driver_i2cdmx * self, uint8_t command) {
161 int16_t ret;
162
163 if ( __i2c_write(self, ADDR_COMMAND, command) == -1 )
164  return -1;
165
166 ret = __i2c_read(self, ADDR_DEVERROR);
167 if ( ret == (int16_t)-1 )
168  return -1;
169
170 if ( ret == ROAR_ERROR_NONE )
171  return 0;
172
173 roar_err_set(ret);
174 return -1;
175}
176
177static inline int __i2c_start_dmx(struct driver_i2cdmx * self) {
178 return __i2c_command(self, COMMAND_DMX);
179}
180
181static int __open_test_device_type(int16_t vendor, int16_t type, int16_t subtype) {
182 if ( vendor == -1 || type == -1 || subtype == -1 )
183  return -1;
184
185 if ( vendor != ROAR_VID_ROARAUDIO || type != DEV_TYPE || subtype != DEV_SUBTYPE ) {
186  roar_err_set(ROAR_ERROR_TYPEMM);
187  return -1;
188 }
189
190 return 0;
191}
192
193static int __open_test_device(struct driver_i2cdmx * self) {
194 int16_t ret;
195 int16_t vendor, type, subtype;
196
197#define __check_response(resp,test,error) if ( (resp) == (int16_t)-1 ) return -1; if ( !(test) ) { roar_err_set((error)); return -1; }
198
199 // test for device interface version
200 ret = __i2c_read(self, ADDR_IFVERSION);
201 __check_response(ret, ret == 0, ROAR_ERROR_NSVERSION);
202
203 // test for device overall status
204 ret = __i2c_read(self, ADDR_DEVSTATUS);
205 __check_response(ret, ret & DEVSTATUS_READY, ROAR_ERROR_BADSTATE);
206
207 // Request device infos
208 if ( __i2c_command(self, COMMAND_DEVINFO) == -1 )
209  return -1;
210
211 // Check device infos
212 // first check device type, then parent device type:
213 vendor  = __i2c_read(self, ADDR_VENDOR);
214 type    = __i2c_read(self, ADDR_TYPE);
215 subtype = __i2c_read(self, ADDR_SUBTYPE);
216
217 if ( __open_test_device_type(vendor, type, subtype) == -1 ) {
218  vendor  = __i2c_read(self, ADDR_PVENDOR);
219  type    = __i2c_read(self, ADDR_PTYPE);
220  subtype = __i2c_read(self, ADDR_PSUBTYPE);
221
222  if (  __open_test_device_type(vendor, type, subtype) == -1 )
223   return -1;
224 }
225
226 // check for DMX512 support:
227
228 ret = __i2c_read(self, ADDR_CAPS0);
229 __check_response(ret, ret & CAPS0_DMX512, ROAR_ERROR_TYPEMM);
230
231 return 0;
232}
233
234static int __open_scan_devices(struct driver_i2cdmx * self) {
235 uint8_t i;
236
237 for (i = MIN_ADDR; i < MAX_ADDR; i++) {
238  self->slave = i;
239
240  if ( __i2c_set_slave(self) == -1 )
241   continue;
242
243  if ( __open_test_device(self) == -1 )
244   continue;
245
246  return 0;
247 }
248
249 self->slave = 0;
250 roar_err_set(ROAR_ERROR_NOENT);
251 return -1;
252}
253
254static int __open_device_and_slave(struct driver_i2cdmx * self, int autoconf, const char * device) {
255 char filename[40];
256 char * p;
257 int need_autoconf = 0;
258 int ret;
259 int err;
260
261 // test if the device exist.
262 if ( roar_vio_open_dstr_simple(&(self->vio), device, ROAR_VIOF_READWRITE) == 0 ) {
263  need_autoconf = 1;
264 } else {
265  // the device doesn't exist. We guss it is in form $device/$slaveid.
266  strncpy(filename, device, sizeof(filename));
267  p = strrchr(filename, '/');
268  if ( p == NULL ) {
269   // it doesn't seem to be in the given form so it seems it was just a device name of an missingd evice.
270   roar_err_set(ROAR_ERROR_NOENT);
271   return -1;
272  }
273
274  *p = 0;
275  p++;
276
277  // now we have the device name in filename, and the slave ID in p.
278  self->slave = atoi(p);
279
280  // little test to protect the user from doing dumb things.
281  if ( self->slave == 0 ) {
282   roar_err_set(ROAR_ERROR_INVAL);
283   return -1;
284  }
285
286  if ( roar_vio_open_dstr_simple(&(self->vio), filename, ROAR_VIOF_READWRITE) == -1 )
287   return -1;
288 }
289
290 // ok, the bus is now open. Let's open the device:
291
292 if ( need_autoconf ) {
293  ret = __open_scan_devices(self);
294 } else {
295  ret = __i2c_set_slave(self);
296  if ( ret == 0 )
297   ret = __open_test_device(self);
298 }
299
300 if ( ret == -1 ) {
301  err = roar_error;
302  roar_vio_close(&(self->vio));
303  roar_err_set(err);
304 }
305
306 return ret;
307}
308
309static int __open_scan_busses(struct driver_i2cdmx * self) {
310 char filename[20];
311 int i;
312 int ret;
313
314 for (i = 0; i < MAX_BUSSES; i++) {
315  snprintf(filename, sizeof(filename), "/dev/i2c-%i", i);
316  ret = __open_device_and_slave(self, 1, filename);
317  if ( ret == 0 )
318   return 0;
319 }
320
321 roar_err_set(ROAR_ERROR_NOENT);
322 return -1;
323}
324
325static int __i2c_write_channel(struct driver_i2cdmx * self, size_t channel, uint8_t value) {
326 size_t bank, offset;
327
328 bank = channel/32;
329 offset = bank*32;
330
331 if ( __i2c_write(self, ADDR_BANK, bank) == -1 )
332  return -1;
333
334 return __i2c_write(self, ADDR_DATA+channel-offset, value);
335}
336
337static int __i2c_write_channel_block(struct driver_i2cdmx * self, size_t channel, const uint8_t * value) {
338 size_t bank, offset;
339
340 bank = channel/32;
341 offset = bank*32;
342
343 if ( __i2c_write(self, ADDR_BANK, bank) == -1 )
344  return -1;
345
346 return __i2c_write_block(self, ADDR_DATA+channel-offset, value);
347}
348
349static ssize_t        __vio_read    (struct roar_vio_calls * vio, void *buf, size_t count) {
350 struct driver_i2cdmx * self;
351
352 if ( vio == NULL ) {
353  roar_err_set(ROAR_ERROR_FAULT);
354  return -1;
355 }
356
357 self = vio->inst;
358
359 if ( count != 512 ) {
360  roar_err_set(ROAR_ERROR_INVAL);
361  return -1;
362 }
363
364 if ( __i2c_start_dmx(self) == -1 )
365  return -1;
366
367 memset(buf, 0, count); // optimize this...
368#ifdef ROAR_HAVE_H_LINUX_SPI_SPIDEV
369 memcpy(buf, self->rxtransfer+6, sizeof(self->rxtransfer)-6);
370#endif
371 return 0;
372}
373
374static ssize_t        __vio_write   (struct roar_vio_calls * vio, void *buf, size_t count) {
375 struct driver_i2cdmx * self;
376 size_t i;
377// size_t endaddr;
378 size_t todo;
379
380 if ( vio == NULL ) {
381  roar_err_set(ROAR_ERROR_FAULT);
382  return -1;
383 }
384
385 self = vio->inst;
386
387 if ( count != 512 ) {
388  roar_err_set(ROAR_ERROR_INVAL);
389  return -1;
390 }
391
392 if ( __i2c_start_dmx(self) == -1 )
393  return -1;
394
395/*
396 for (i = self->startaddr, endaddr = self->startaddr + self->len; i < endaddr; i++)
397  if ( __i2c_write_channel(self, i, ((uint8_t*)buf)[i]) == -1 )
398   return -1;
399 */
400
401 i = self->startaddr;
402 todo = self->len;
403 while (todo) {
404  if ( todo >= I2C_SMBUS_BLOCK_MAX ) {
405   if ( __i2c_write_channel_block(self, i, &(((const uint8_t*)buf)[i])) == -1 )
406    return -1;
407   i += I2C_SMBUS_BLOCK_MAX;
408   todo -= I2C_SMBUS_BLOCK_MAX;
409  } else {
410   if ( __i2c_write_channel(self, i, ((uint8_t*)buf)[i]) == -1 )
411    return -1;
412   i++;
413   todo--;
414  }
415 }
416#ifdef ROAR_HAVE_H_LINUX_SPI_SPIDEV
417 memcpy(self->txtransfer+6, buf, sizeof(self->txtransfer)-6);
418 __spi_transfer(self, 0, 0, TRANSFER_CHANS);
419#endif
420
421 return count;
422}
423
424static int            __vio_ctl     (struct roar_vio_calls * vio, roar_vio_ctl_t cmd, void * data) {
425 struct driver_i2cdmx * self;
426
427 if ( vio == NULL ) {
428  roar_err_set(ROAR_ERROR_FAULT);
429  return -1;
430 }
431
432 self = vio->inst;
433
434 switch (cmd) {
435  case ROAR_VIO_CTL_SET_SSTREAM:
436    if ( ROAR_STREAM(data)->dir != ROAR_DIR_LIGHT_OUT && ROAR_STREAM(data)->dir != ROAR_DIR_LIGHT_IN ) {
437     ROAR_STREAM(data)->dir = ROAR_DIR_LIGHT_OUT;
438    }
439    ROAR_STREAM_SERVER(data)->codec_orgi = ROAR_CODEC_DMX512;
440   break;
441  default:
442   roar_err_set(ROAR_ERROR_BADRQC);
443   return -1;
444 }
445
446 return 0;
447}
448
449static int            __vio_close   (struct roar_vio_calls * vio) {
450 struct driver_i2cdmx * self = vio->inst;
451 roar_vio_close(&(self->vio));
452 if ( self->have_spi )
453  roar_vio_close(&(self->spi));
454 roar_mm_free(self);
455 return 0;
456}
457
458int driver_i2cdmx_open_vio  (struct roar_vio_calls * inst, char * device, struct roar_audio_info * info, int fh, struct roar_stream_server * sstream) {
459 struct driver_i2cdmx * self;
460 int autoconf = 1;
461 int ret;
462
463 if ( fh != -1 ) {
464  roar_err_set(ROAR_ERROR_NOSYS);
465  return -1;
466 }
467
468 self = roar_mm_malloc(sizeof(struct driver_i2cdmx));
469 if ( self == NULL ) {
470  return -1;
471 }
472 memset(self, 0, sizeof(*self));
473 self->have_spi  = 0;
474 self->startaddr = 0;
475 self->len       = info->channels;
476
477 info->bits      = 8;
478 info->codec     = ROAR_CODEC_DMX512;
479
480 if ( device == NULL && !autoconf ) {
481  device = DEFAULT_DEVICE;
482 }
483
484 if ( device == NULL ) {
485  ret = __open_scan_busses(self);
486 } else {
487  ret = __open_device_and_slave(self, autoconf, device);
488 }
489
490 if ( ret == -1 ) {
491  roar_mm_free_noerror(self);
492  return -1;
493 }
494
495#if defined(ROAR_HAVE_H_LINUX_SPI_SPIDEV) && defined(SPI_DEVICE)
496 if ( roar_vio_open_dstr_simple(&(self->spi), SPI_DEVICE, ROAR_VIOF_READWRITE) == 0 )
497  self->have_spi = 1;
498#endif
499
500 roar_vio_clear_calls(inst);
501 inst->inst  = self;
502 inst->read  = __vio_read;
503 inst->write = __vio_write;
504 inst->close = __vio_close;
505 inst->ctl   = __vio_ctl;
506
507 return 0;
508}
509#endif
Note: See TracBrowser for help on using the repository browser.