source: roaraudio/roard/driver_i2cdmx.c @ 5912:57ee7d410296

Last change on this file since 5912:57ee7d410296 was 5912:57ee7d410296, checked in by phi, 11 years ago

updated device type checks

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