source: roaraudio/roard/driver_i2cdmx.c @ 5908:66940b2023ee

Last change on this file since 5908:66940b2023ee was 5908:66940b2023ee, checked in by phi, 11 years ago

Added roard driver: i2cdmx.

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