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

4.104.114.95
Last change on this file since c57316a was c57316a, checked in by Joel Sherrill <joel.sherrill@…>, on 10/11/07 at 12:46:50

2007-10-11 Daniel Hellstrom <daniel@…>

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