source: roaraudio/roard/driver_i2cdmx.c @ 5999:73c676b8e6b5

Last change on this file since 5999:73c676b8e6b5 was 5999:73c676b8e6b5, checked in by phi, 10 years ago

make it compile

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