source: roaraudio/roard/driver_i2cdmx.c @ 6003:7f0e74d54fdb

Last change on this file since 6003:7f0e74d54fdb was 6003:7f0e74d54fdb, checked in by phi, 10 years ago

added support for block transpfer mode

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