source: rtems/cpukit/libi2c/libi2c.c @ dbe8e519

4.104.114.84.9
Last change on this file since dbe8e519 was 199e748, checked in by Ralf Corsepius <ralf.corsepius@…>, on Nov 6, 2005 at 9:22:09 AM

Eliminate obsolete types.

  • Property mode set to 100644
File size: 13.8 KB
Line 
1/* $Id$ */
2
3/* libi2c Implementation */
4
5#if HAVE_CONFIG_H
6#include "config.h"
7#endif
8
9#include <string.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <errno.h>
13#include <assert.h>
14
15#include <rtems.h>
16#include <rtems/error.h>
17#include <rtems/libio.h>
18
19#include <rtems/libi2c.h>
20
21#define DRVNM "libi2c:"
22
23#define MAX_NO_BUSSES   8       /* Also limited by the macro building minor numbers */
24#define MAX_NO_DRIVERS  16      /* Number of high level drivers we support          */
25
26#define MINOR2ADDR(minor)       ((minor)&((1<<10)-1))
27#define MINOR2BUS(minor)        (((minor)>>10)&7)
28#define MINOR2DRV(minor)        ((minor)>>13)
29
30/* Check the 'minor' argument, i.e., verify that
31 * we have a driver connected
32 */
33#define DECL_CHECKED_BH(b, bh, m, s)\
34        unsigned b = MINOR2BUS(m);              \
35        rtems_libi2c_bus_t      *bh;            \
36        if ( b >= MAX_NO_BUSSES || 0 == (bh=busses[b].bush) ) { \
37                return s RTEMS_INVALID_NUMBER;  \
38        }
39
40#define DECL_CHECKED_DRV(d, b, m)       \
41        unsigned d = MINOR2DRV(m);              \
42        unsigned b = MINOR2BUS(m);              \
43        if (   b >= MAX_NO_BUSSES  || 0 == busses[b].bush       \
44                || d >  MAX_NO_DRIVERS || (d && 0 == drvs[d-1].drv )) {\
45                return RTEMS_INVALID_NUMBER;    \
46        }
47
48#define DISPATCH(rval, entry, dflt)     \
49        do {                            \
50                rtems_driver_address_table *ops = drvs[--drv].drv->ops; \
51                rval = ops->entry ? ops->entry(major,minor,arg) : dflt; \
52        } while (0)
53
54
55rtems_device_major_number rtems_libi2c_major;
56
57static struct i2cbus
58{
59  rtems_libi2c_bus_t *bush;
60  volatile rtems_id mutex;      /* lock this across start -> stop */
61  volatile short waiting;
62  volatile char started;
63  char *name;
64} busses[MAX_NO_BUSSES] = { { 0 } };
65
66static struct
67{
68  rtems_libi2c_drv_t *drv;
69} drvs[MAX_NO_DRIVERS] = { { 0} };
70
71static rtems_id libmutex = 0;
72
73#define LOCK(m)         assert(!rtems_semaphore_obtain((m), RTEMS_WAIT, RTEMS_NO_TIMEOUT))
74#define UNLOCK(m)       rtems_semaphore_release((m))
75
76#define LIBLOCK()       LOCK(libmutex)
77#define LIBUNLOCK()     UNLOCK(libmutex)
78
79#define MUTEX_ATTS      \
80        (  RTEMS_PRIORITY                       \
81         | RTEMS_BINARY_SEMAPHORE       \
82         |RTEMS_INHERIT_PRIORITY        \
83         |RTEMS_NO_PRIORITY_CEILING     \
84         |RTEMS_LOCAL )
85
86static rtems_id
87mutexCreate (rtems_name nm)
88{
89  rtems_status_code sc;
90  rtems_id rval;
91  if (RTEMS_SUCCESSFUL !=
92      (sc = rtems_semaphore_create (nm, 1, MUTEX_ATTS, 0, &rval))) {
93    rtems_error (sc, DRVNM " unable to create mutex\n");
94    return 0;
95  }
96  return rval;
97}
98
99/* Lock a bus avoiding to have a mutex, which is mostly
100 * unused, hanging around all the time. We just create
101 * and delete it on the fly...
102 *
103 * ASSUMES: argument checked by caller
104 */
105
106static void
107lock_bus (int busno)
108{
109  struct i2cbus *bus = &busses[busno];
110  LIBLOCK ();
111  if (!bus->waiting) {
112    /* nobody is holding the bus mutex - it's not there. Create it on the fly */
113    if (!
114        (bus->mutex =
115         mutexCreate (rtems_build_name ('i', '2', 'c', '0' + busno)))) {
116      LIBUNLOCK ();
117      rtems_panic (DRVNM " unable to create bus lock");
118    }
119  }
120  /* count number of people waiting on this bus; only the last one deletes the mutex */
121  bus->waiting++;
122  LIBUNLOCK ();
123  /* Now lock this bus */
124  LOCK (bus->mutex);
125}
126
127static void
128unlock_bus (int busno)
129{
130  struct i2cbus *bus = &busses[busno];
131  LIBLOCK ();
132  UNLOCK (bus->mutex);
133  if (!--bus->waiting) {
134    rtems_semaphore_delete (bus->mutex);
135  }
136  LIBUNLOCK ();
137}
138
139/* Note that 'arg' is always passed in as NULL */
140static rtems_status_code
141i2c_init (rtems_device_major_number major, rtems_device_minor_number minor,
142          void *arg)
143{
144  rtems_status_code rval;
145  DECL_CHECKED_DRV (drv, busno, minor)
146
147    if (0 == drv) {
148    rval = 0;
149  } else {
150    /* this is probably never called */
151    DISPATCH (rval, initialization_entry, RTEMS_SUCCESSFUL);
152  }
153  return rval;
154}
155
156static rtems_status_code
157i2c_open (rtems_device_major_number major, rtems_device_minor_number minor,
158          void *arg)
159{
160  rtems_status_code rval;
161  DECL_CHECKED_DRV (drv, busno, minor)
162
163    if (0 == drv) {
164    rval = RTEMS_SUCCESSFUL;
165  } else {
166    DISPATCH (rval, open_entry, RTEMS_SUCCESSFUL);
167  }
168  return rval;
169}
170
171static rtems_status_code
172i2c_close (rtems_device_major_number major, rtems_device_minor_number minor,
173           void *arg)
174{
175  rtems_status_code rval;
176  DECL_CHECKED_DRV (drv, busno, minor)
177
178    if (0 == drv) {
179    rval = RTEMS_SUCCESSFUL;
180  } else {
181    DISPATCH (rval, close_entry, RTEMS_SUCCESSFUL);
182  }
183  return rval;
184}
185
186static rtems_status_code
187i2c_read (rtems_device_major_number major, rtems_device_minor_number minor,
188          void *arg)
189{
190  int rval;                     /* int so we can check for negative value */
191  rtems_libio_rw_args_t *rwargs = arg;
192  DECL_CHECKED_DRV (drv, busno, minor)
193
194    if (0 == rwargs->count) {
195    rwargs->bytes_moved = 0;
196    return RTEMS_SUCCESSFUL;
197  }
198
199  if (0 == drv) {
200    rval =
201      rtems_libi2c_start_read_bytes (minor, (unsigned char *) rwargs->buffer,
202                                     rwargs->count);
203    if (rval >= 0) {
204      rwargs->bytes_moved = rval;
205      rtems_libi2c_send_stop (minor);
206      rval = RTEMS_SUCCESSFUL;
207    } else {
208      rval = -rval;
209    }
210  } else {
211    DISPATCH (rval, read_entry, RTEMS_NOT_IMPLEMENTED);
212  }
213  return rval;
214}
215
216static rtems_status_code
217i2c_write (rtems_device_major_number major, rtems_device_minor_number minor,
218           void *arg)
219{
220  int rval;                     /* int so we can check for negative value */
221  rtems_libio_rw_args_t *rwargs = arg;
222  DECL_CHECKED_DRV (drv, busno, minor)
223
224    if (0 == rwargs->count) {
225    rwargs->bytes_moved = 0;
226    return RTEMS_SUCCESSFUL;
227  }
228
229  if (0 == drv) {
230    rval =
231      rtems_libi2c_start_write_bytes (minor, (unsigned char *) rwargs->buffer,
232                                      rwargs->count);
233    if (rval >= 0) {
234      rwargs->bytes_moved = rval;
235      rtems_libi2c_send_stop (minor);
236      rval = RTEMS_SUCCESSFUL;
237    } else {
238      rval = -rval;
239    }
240  } else {
241    DISPATCH (rval, write_entry, RTEMS_NOT_IMPLEMENTED);
242  }
243  return rval;
244}
245
246static rtems_status_code
247i2c_ioctl (rtems_device_major_number major, rtems_device_minor_number minor,
248           void *arg)
249{
250  rtems_status_code rval;
251  DECL_CHECKED_DRV (drv, busno, minor)
252
253    if (0 == drv) {
254    rval = RTEMS_NOT_IMPLEMENTED;
255  } else {
256    DISPATCH (rval, control_entry, RTEMS_NOT_IMPLEMENTED);
257  }
258  return rval;
259}
260
261
262/* Our ops just dispatch to the registered drivers */
263static rtems_driver_address_table libi2c_io_ops = {
264  initialization_entry:  i2c_init,
265  open_entry:            i2c_open,
266  close_entry:           i2c_close,
267  read_entry:            i2c_read,
268  write_entry:           i2c_write,
269  control_entry:         i2c_ioctl,
270};
271
272
273int
274rtems_libi2c_initialize ()
275{
276  rtems_status_code sc;
277
278  if (!(libmutex = mutexCreate (rtems_build_name ('l', 'I', '2', 'C'))))
279    return -1;
280
281  sc = rtems_io_register_driver (0, &libi2c_io_ops, &rtems_libi2c_major);
282  if (RTEMS_SUCCESSFUL != 0) {
283    fprintf (stderr,
284             DRVNM " Claiming driver slot failed (rtems status code %i)\n",
285             sc);
286    rtems_semaphore_delete (libmutex);
287    libmutex = 0;
288    return -1;
289  }
290
291  return 0;
292}
293
294int
295rtems_libi2c_register_bus (char *name, rtems_libi2c_bus_t * bus)
296{
297  int i;
298  rtems_status_code err;
299  char *nmcpy = malloc (name ? strlen (name) + 1 : 20);
300  char tmp, *chpt;
301  struct stat sbuf;
302
303  strcpy (nmcpy, name ? name : "/dev/i2c");
304
305
306  /* check */
307  if ('/' != *nmcpy) {
308    fprintf (stderr,
309             "Bad name; must be an absolute path starting with '/'\n");
310    return -RTEMS_INVALID_NAME;
311  }
312  /* file must not exist */
313  if (!stat (nmcpy, &sbuf)) {
314    fprintf (stderr, "Bad name; file exists already\n");
315    return -RTEMS_INVALID_NAME;
316  }
317
318  /* we already verified that there is at least one '/' */
319  chpt = strrchr (nmcpy, '/') + 1;
320  tmp = *chpt;
321  *chpt = 0;
322  i = stat (nmcpy, &sbuf);
323  *chpt = tmp;
324  if (i) {
325    fprintf (stderr, "Bad name '%s'; parent directory doesn't exist\n",
326             nmcpy);
327    return -RTEMS_INVALID_NAME;
328  }
329  /* should be a directory since name terminates in '/' */
330
331
332  if (!libmutex) {
333    fprintf (stderr, DRVNM " library not initialized\n");
334    return -RTEMS_NOT_DEFINED;
335  }
336
337  if (bus->size < sizeof (*bus)) {
338    fprintf (stderr, DRVNM " bus-ops size too small -- misconfiguration?\n");
339    return -RTEMS_NOT_CONFIGURED;
340  }
341
342  LIBLOCK ();
343  for (i = 0; i < MAX_NO_BUSSES; i++) {
344    if (!busses[i].bush) {
345      /* found a free slot */
346      busses[i].bush = bus;
347      busses[i].mutex = 0;
348      busses[i].waiting = 0;
349      busses[i].started = 0;
350
351      if (!name)
352        sprintf (nmcpy + strlen (nmcpy), "%i", i);
353
354      if ((err = busses[i].bush->ops->init (busses[i].bush))) {
355        /* initialization failed */
356        i = -err;
357      } else {
358        busses[i].name = nmcpy;;
359        nmcpy = 0;
360      }
361
362      break;
363    }
364  }
365  LIBUNLOCK ();
366
367  if (i >= MAX_NO_BUSSES) {
368    i = -RTEMS_TOO_MANY;
369  }
370
371  free (nmcpy);
372
373  return i;
374}
375
376static int
377not_started (int busno)
378{
379  int rval;
380  lock_bus (busno);
381  rval = !busses[busno].started;
382  unlock_bus (busno);
383  return rval;
384}
385
386rtems_status_code
387rtems_libi2c_send_start (uint32_t minor)
388{
389  int rval;
390  DECL_CHECKED_BH (busno, bush, minor, +)
391
392    lock_bus (busno);
393  rval = bush->ops->send_start (bush);
394
395  /* if this failed or is not the first start, unlock */
396  if (rval || busses[busno].started) {
397    /* HMM - what to do if the 1st start failed ?
398     * try to reset...
399     */
400    if (!busses[busno].started) {
401      /* just in case the bus driver fiddles with errno */
402      int errno_saved = errno;
403      bush->ops->init (bush);
404      errno = errno_saved;
405    } else if (rval) {
406      /* failed restart */
407      rtems_libi2c_send_stop (minor);
408    }
409    unlock_bus (busno);
410  } else {
411    /* successful 1st start; keep bus locked until stop is sent */
412    busses[busno].started = 1;
413  }
414  return rval;
415}
416
417rtems_status_code
418rtems_libi2c_send_stop (uint32_t minor)
419{
420  rtems_status_code rval;
421  DECL_CHECKED_BH (busno, bush, minor, +)
422
423    if (not_started (busno))
424    return RTEMS_NOT_OWNER_OF_RESOURCE;
425
426  rval = bush->ops->send_stop (bush);
427
428  busses[busno].started = 0;
429
430  unlock_bus (busno);
431  return rval;
432}
433
434rtems_status_code
435rtems_libi2c_send_addr (uint32_t minor, int rw)
436{
437  rtems_status_code sc;
438  DECL_CHECKED_BH (busno, bush, minor, +)
439
440    if (not_started (busno))
441    return RTEMS_NOT_OWNER_OF_RESOURCE;
442
443  sc = bush->ops->send_addr (bush, MINOR2ADDR (minor), rw);
444  if (RTEMS_SUCCESSFUL != sc)
445    rtems_libi2c_send_stop (minor);
446  return sc;
447}
448
449int
450rtems_libi2c_read_bytes (uint32_t minor, unsigned char *bytes, int nbytes)
451{
452  int sc;
453  DECL_CHECKED_BH (busno, bush, minor, -)
454
455    if (not_started (busno))
456    return -RTEMS_NOT_OWNER_OF_RESOURCE;
457
458  sc = bush->ops->read_bytes (bush, bytes, nbytes);
459  if (sc < 0)
460    rtems_libi2c_send_stop (minor);
461  return sc;
462}
463
464int
465rtems_libi2c_write_bytes (uint32_t minor, unsigned char *bytes, int nbytes)
466{
467  int sc;
468  DECL_CHECKED_BH (busno, bush, minor, -)
469
470    if (not_started (busno))
471    return -RTEMS_NOT_OWNER_OF_RESOURCE;
472
473  sc = bush->ops->write_bytes (bush, bytes, nbytes);
474  if (sc < 0)
475    rtems_libi2c_send_stop (minor);
476  return sc;
477}
478
479static int
480do_s_rw (uint32_t minor, unsigned char *bytes, int nbytes, int rw)
481{
482  rtems_status_code sc;
483  rtems_libi2c_bus_t *bush;
484
485  if ((sc = rtems_libi2c_send_start (minor)))
486    return -sc;
487
488  /* at this point, we hold the bus and are sure the minor number is valid */
489  bush = busses[MINOR2BUS (minor)].bush;
490
491  if ((sc = bush->ops->send_addr (bush, MINOR2ADDR (minor), rw))) {
492    rtems_libi2c_send_stop (minor);
493    return -sc;
494  }
495
496  if (rw)
497    sc = bush->ops->read_bytes (bush, bytes, nbytes);
498  else
499    sc = bush->ops->write_bytes (bush, bytes, nbytes);
500
501  if (sc < 0) {
502    rtems_libi2c_send_stop (minor);
503  }
504  return sc;
505}
506
507int
508rtems_libi2c_start_read_bytes (uint32_t minor, unsigned char *bytes,
509                               int nbytes)
510{
511  return do_s_rw (minor, bytes, nbytes, 1);
512}
513
514int
515rtems_libi2c_start_write_bytes (uint32_t minor, unsigned char *bytes,
516                                int nbytes)
517{
518  return do_s_rw (minor, bytes, nbytes, 0);
519}
520
521int
522rtems_libi2c_register_drv (char *name, rtems_libi2c_drv_t * drvtbl,
523                           unsigned busno, unsigned i2caddr)
524{
525  int i;
526  rtems_status_code err;
527  rtems_device_minor_number minor;
528
529  if (!libmutex) {
530    fprintf (stderr, DRVNM " library not initialized\n");
531    return -RTEMS_NOT_DEFINED;
532  }
533
534  if (name && strchr (name, '/')) {
535    fprintf (stderr, "Invalid name: '%s' -- must not contain '/'\n", name);
536    return -RTEMS_INVALID_NAME;
537  }
538
539  if (busno >= MAX_NO_BUSSES || !busses[busno].bush || i2caddr >= 1 << 10) {
540    errno = EINVAL;
541    return -RTEMS_INVALID_NUMBER;
542  }
543
544  if (drvtbl->size < sizeof (*drvtbl)) {
545    fprintf (stderr, DRVNM " drv-ops size too small -- misconfiguration?\n");
546    return -RTEMS_NOT_CONFIGURED;
547  }
548
549  /* allocate slot */
550  LIBLOCK ();
551  for (i = 0; i < MAX_NO_DRIVERS; i++) {
552    /* driver # 0 is special, it is the built-in raw driver */
553    if (!drvs[i].drv) {
554      char *str;
555      dev_t dev;
556      uint32_t mode;
557
558      /* found a free slot; encode slot + 1 ! */
559      minor = ((i + 1) << 13) | RTEMS_LIBI2C_MAKE_MINOR (busno, i2caddr);
560
561      if (name) {
562        str = malloc (strlen (busses[busno].name) + strlen (name) + 2);
563        sprintf (str, "%s.%s", busses[busno].name, name);
564
565        dev = rtems_filesystem_make_dev_t (rtems_libi2c_major, minor);
566
567        mode = 0111 | S_IFCHR;
568        if (drvtbl->ops->read_entry)
569          mode |= 0444;
570        if (drvtbl->ops->write_entry)
571          mode |= 0222;
572
573        /* note that 'umask' is applied to 'mode' */
574        if (mknod (str, mode, dev)) {
575          fprintf (stderr,
576                   "Creating device node failed: %s; you can try to do it manually...\n",
577                   strerror (errno));
578        }
579
580        free (str);
581      }
582
583      drvs[i].drv = drvtbl;
584
585      if (drvtbl->ops->initialization_entry)
586        err =
587          drvs[i].drv->ops->initialization_entry (rtems_libi2c_major, minor,
588                                                  0);
589      else
590        err = RTEMS_SUCCESSFUL;
591
592      LIBUNLOCK ();
593      return err ? -err : minor;
594    }
595  }
596  LIBUNLOCK ();
597  return -RTEMS_TOO_MANY;
598}
Note: See TracBrowser for help on using the repository browser.