source: rtems/cpukit/libi2c/libi2c.c @ 6339f467

4.104.114.84.95
Last change on this file since 6339f467 was 6339f467, checked in by Till Straumann <strauman@…>, on 11/03/05 at 02:44:59

2005-11-02 straumanatslacdotstanford.edu

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