Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

sig_i2c_bus.c

/*
 *  $Id: sig_i2c_bus.c,v 1.76 2009-01-27 17:06:41 potyra Exp $
 *
 * Copyright (C) 2008-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "fixme.h"

#include "glue-shm.h"

#include "sig_i2c_bus.h"

/** find the corresponding index to a bus member by object
 *  @param b bus instance
 *  @param s retrieve the member index whose object is s
 *  @return member index.
 *
 *  Error cases: must not happen (will assert that a member is found).
 */
static unsigned int
sig_i2c_bus_find_member(struct sig_i2c_bus *b, void *s)
{
      unsigned int nr;

      for (nr = 0; nr < b->nmembers; nr++) {
            if (b->member[nr].s == s) {
                  return nr;
            }
            if (b->member[nr].cs == s) {
                  return nr;
            }
      }

      assert(0);
      return 0;
}

/** resolve clk/data values of all participants on a bus, that are bus
 *  segments/are not bus segments.
 *
 *  @param b bus instance.
 *  @param segments true to resolve only bus segments, false to resolve only
 *         non-segments
 *  @param data result of resolved data signal.
 *  @param clk result of resolved clk signal.
 */
static void 
sig_i2c_resolve_and_parts(
      const struct sig_i2c_bus *b,
      bool segments,
      bool *data,
      bool *clk
)
{
      unsigned int nr;

      for (nr = 0; nr < b->nmembers; nr++) {
            if (b->member[nr].is_bus_segment != segments) {
                  continue;
            }

            *data = *data && b->member[nr].data;
            *clk = *clk && b->member[nr].clk;
      }
}

/** propagate clock events to either all local members or all other
 *  bus segments.
 *  @param b bus instance.
 *  @param segments true for only bus segments, false for only local members
 *  @param clk new value of the clk signal.
 */
static void
sig_i2c_propagate_clk(
      struct sig_i2c_bus *b,
      bool segments,
      bool clk
)
{
      unsigned int nr;
      void (*func)(void *, bool);

      for (nr = 0; nr < b->nmembers; nr++) {
            if (b->member[nr].is_bus_segment != segments) {
                  continue;
            }
      
            func = b->member[nr].f->clk_event;
            if (func != NULL) {
                  func(b->member[nr].s, clk);
            }
      }
}

/** propagate data events to either all local members or all other
 *  bus segments.
 *  @param b bus instance.
 *  @param segments true for only bus segments, false for only local members
 *  @param data new value of the data signal.
 */
static void
sig_i2c_propagate_data(
      struct sig_i2c_bus *b,
      bool segments,
      bool data
)
{
      unsigned int nr;
      void (*func)(void *, bool);

      for (nr = 0; nr < b->nmembers; nr++) {
            if (b->member[nr].is_bus_segment != segments) {
                  continue;
            }
      
            func = b->member[nr].f->data_event;
            if (func != NULL) {
                  func(b->member[nr].s, data);
            }
      }
}

/** resolve all driving values of the bus to the new signal line
 *  values and emit events as fit.
 *
 *  @param b bus instance
 */
static void
sig_i2c_bus_resolve_and(struct sig_i2c_bus *b)
{
      bool new_clk;
      bool new_data;

      /* as this function calls the event signal handlers, it 
       * can be called recursively. Hence it must be avoided to 
       * distribute later events in a recursion and then continue
       * to distribute the old events.
       * -> if an update is going on, return early, but make sure that
       *  a next updating iteration is done.
       */
      if (b->resolve_in_progress) {
            b->resolve_again = true;
            return;
      }

      do {
            b->resolve_in_progress = true;
            b->resolve_again = false;

            new_clk = true;
            new_data = true;

            /* first resolve the local bus segment */
            sig_i2c_resolve_and_parts(b, false, &new_data, &new_clk);
            /* forward the local bus state to other busses
             * that way, data/clk of bus_forwarders will always reflect the
             * state of the local bus segment. */
            sig_i2c_propagate_clk(b, true, new_clk);
            sig_i2c_propagate_data(b, true, new_data);

            /* finally, resolve other bus segments to value of bus */
            sig_i2c_resolve_and_parts(b, true, &new_data, &new_clk);

            /* and distribute signal changes if present */
            if (b->clk != new_clk) {
                  b->clk = new_clk;
                  sig_i2c_propagate_clk(b, false, new_clk);

            }

            if (b->data != new_data) {
                  b->data = new_data;
                  sig_i2c_propagate_data(b, false, new_data);
            }

            b->resolve_in_progress = false;
      } while (b->resolve_again);
}

/* ***************************************** *
 * raw -> cooked functions/protocol handling *
 * ***************************************** *

 This implements an i2c slave device. Hence the clock signal is a strict
 input, which must never get set by the device.
 The data signal serves as input/output, but it may only get set when
 the clock signal changes, as otherwise there could be a loopback in the
 circuit. (which will lead to an infinite recursion in this implementation).
 */

/** small serial to byte converter.
 *  take the value on the data line and shift it into the shift register.
 *  ignore every 9th bit (that's the acknowledge cycle).
 *  @param m corresponding sig_i2c_bus_member instance
 */
static void
sig_i2c_proxy_shift_in_bit(struct sig_i2c_bus_member *m)
{
      m->shift_register <<= 1;
      m->shift_register |= (m->last_data & 1);
}

/** an address is lying in the shift_register. Is this our addres? Update
 *  FSM according to results.
 *  @param m member instance
 */
static void
sig_i2c_proxy_handle_addr(struct sig_i2c_bus_member *m)
{
      unsigned char addr = m->shift_register;
      bool ret;

      if (m->cf->ack_addr == NULL) {
            m->state = SIG_I2C_NOT_INTERESTED;
            return;
      }

      ret = m->cf->ack_addr(m->cs, addr);
      if (! ret) {
            /* no ack on the address. device not interested in 
             * bus transaction */
            m->state = SIG_I2C_NOT_INTERESTED;
            return;
      }

      /* ok, we'll be slave in this transaction. send an acknowledge. */
      m->state = SIG_I2C_SLAVE_ACK;
      m->read_mode = (addr & 1) == 1;
}

/** 8 bits have been read/written. Handle the next data phase.
 *  @param m slave member.
 */
static void
sig_i2c_proxy_handle_data(struct sig_i2c_bus_member *m)
{
      bool ret;

      if (m->read_mode) {
            m->state = SIG_I2C_MASTER_ACK;
            return;
      }

      ret = m->cf->write_byte(m->cs, m->shift_register);
      if (ret) {
            /* generate an ACK in the next cycle */
            m->state = SIG_I2C_SLAVE_ACK;
      }
}

static void
sig_i2c_proxy_write_next(struct sig_i2c_bus_member *m)
{
      struct sig_i2c_bus *b = (struct sig_i2c_bus *)m->bus;
      bool master_ack = (m->shift_register & 1) == 0;

      if (! master_ack) {
            m->state = SIG_I2C_NOT_INTERESTED;
            return;
      }

      /* master sent an ack, fetch next byte into register. */
      m->cf->read_byte(m->cs, &m->shift_register);
      /* and put first bit on bus. */
      sig_i2c_bus_set_data(b, m, (m->shift_register & 0x80) == 0x80);
}

/** clock just changed from high to low.
 *  @param m slave member.
 */
static void
sig_i2c_proxy_clock_after_falling_flank(struct sig_i2c_bus_member *m)
{
      struct sig_i2c_bus *b = (struct sig_i2c_bus *)m->bus;

      switch (m->state) {
      case SIG_I2C_SLAVE_ACK:
            m->state = SIG_I2C_SLAVE_ACK_DOWN;
            /* pull down data to generate an ACK */
            sig_i2c_bus_set_data(b, m, false);
            break;

      case SIG_I2C_SLAVE_ACK_DOWN:
            m->state = SIG_I2C_DATA;
            /* release data again to high. */
            sig_i2c_bus_set_data(b, m, true);

            if (m->read_mode) {
                  sig_i2c_proxy_write_next(m);
            }

            break;

      case SIG_I2C_DATA:
            if (m->read_mode) {
                  /* read mode (master reads), 
                   * put highest bit of shift_register on bus */
                  sig_i2c_bus_set_data(b, m, 
                        (m->shift_register & 0x80) == 0x80);
            }
            break;

      case SIG_I2C_MASTER_ACK:
            m->state = SIG_I2C_MASTER_ACK_DOWN;
            /* turn around bus to master */
            sig_i2c_bus_set_data(b, m, true);
            break;

      case SIG_I2C_MASTER_ACK_DOWN:
            if (m->read_mode) {
                  sig_i2c_proxy_write_next(m);
            }
            m->state = SIG_I2C_DATA;
            break;

      default:
            /* do nothing */
            break;
      }
}

/** the clock just changed from low to high.
 *  @param m slave member.
 */
static void
sig_i2c_proxy_clock_after_rising_flank(struct sig_i2c_bus_member *m)
{
      /* shift in bit into shift register (apart from ACK cycle) */
      sig_i2c_proxy_shift_in_bit(m);

      /* the last bit of a complete byte was shifted into the shift 
       * register
       */
      if (m->counter == 7) {
            switch (m->state) {
            case SIG_I2C_ADR:
                  sig_i2c_proxy_handle_addr(m);
                  break;
            case SIG_I2C_DATA:
                  sig_i2c_proxy_handle_data(m);
                  break;
            default:
                  /* nothing to do */
                  break;
            }
      }

      /* increase the counter at each rising clock flank */
      m->counter++;
      if (m->counter > 8) {
            m->counter = 0;
      }
}

/** emit a stop transaction event, in case the slave involved in a
 *  transaction.
 *  TODO  eventually it might be better, to always send a stop 
 *        event? (imo HW logic will simply react on any stop 
 *        condition found on the bus, not just dependant on its very own
 *        state).
 *  @param m slave member
 */
static void
sig_i2c_proxy_emit_stop_event(struct sig_i2c_bus_member *m)
{
      switch (m->state) {
      case SIG_I2C_NOT_INTERESTED:
      case SIG_I2C_ADR:
            break;

      default:
            if (m->cf->stop_transaction != NULL) {
                  m->cf->stop_transaction(m->cs);
            }
      }
}

/** callback handler for raw->cooked translation, in case the data signal
 *  changes. Will implement the FSM for the device.
 *  An event on the data signal, must never cause a change on any bus signal.
 *  @param s pointer to a sig_i2c_bus_member structure.
 *  @param data new signal of the data line.
 */
static void
sig_i2c_proxy_data_event(void *s, bool data)
{
      struct sig_i2c_bus_member *m = (struct sig_i2c_bus_member*)s;

      /* should not happen */
      assert(data != m->last_data);
      
      if (m->last_clk && data) {
            /* data changes from low to high on bus: stop condition */
            sig_i2c_proxy_emit_stop_event(m);
            m->state = SIG_I2C_NOT_INTERESTED;
            m->read_mode = true;
            m->counter = 0;
      }

      if (m->last_clk && m->last_data) {
            /* data changes from high to low on bus: start condition. */
            m->state = SIG_I2C_ADR;
            m->read_mode = true;
            m->counter = 0;
      }

      m->last_data = data;
}

/** callback handler for raw->cooked translation, in case the clock signal
 *  changes. Will implement the FSM for the device.
 *  @param s pointer to a sig_i2c_bus_member structure.
 *  @param clk new signal of the clock line.
 */
static void
sig_i2c_proxy_clk_event(void *s, bool clk)
{
      struct sig_i2c_bus_member *m = (struct sig_i2c_bus_member*)s;

      /* sanity check */
      assert(m->last_clk != clk);
      m->last_clk = clk;
      
      if (clk) {
            sig_i2c_proxy_clock_after_rising_flank(m);
      } else {
            sig_i2c_proxy_clock_after_falling_flank(m);
      }
}

/* ************************************* *
 *         raw access functions          *
 * ************************************* */

/** generate a clock pulse and check if it gets acknowledged
 *  @param b bus instance
 *  @param s master instance
 *  @return true, if an ACK is found, false otherwise
 */
static bool
sig_i2c_get_ack(struct sig_i2c_bus *b, void *s)
{
      bool ret;
      sig_i2c_bus_set_clk(b, s, true);
      ret = ! b->data;
      sig_i2c_bus_set_clk(b, s, false);

      return ret;
}

/** put size bytes from buffer on the bus, raw mode.
 *  @param b bus instance
 *  @param s master instance
 *  @param size number of bytes to put on the bus
 *  @param buffer contains bytes to put on the bus.
 *  @return number of bytes acknowledged by the slave.
 */
static unsigned int
sig_i2c_put_bytes(
      struct sig_i2c_bus *b, 
      void *s, 
      unsigned int size,
      const unsigned char *buffer
)
{
      unsigned int i;
      bool bit;
      bool ret;
      unsigned char val;
      unsigned int j = 0;

      for (j = 0; j < size; j++) {
            val = *buffer;

            for (i = 0; i < 8; i++) {
                  /* flag out MSB */
                  bit = (val & 0x80) == 0x80;

                  sig_i2c_bus_set_data(b, s, bit);
                  sig_i2c_bus_set_clk(b, s, true);
                  sig_i2c_bus_set_clk(b, s, false);

                  val <<= 1;
            }

            sig_i2c_bus_set_data(b, s, true);
            ret = sig_i2c_get_ack(b, s);

            if (! ret) {
                  break;
            }

            buffer++;
      }

      return j;
}

/** get size bytes from slave into buffer.
 *  @param b bus instance.
 *  @param s master instance.
 *  @param size number of bytes to read from slave.
 *  @param buffer store read bytes in buffer.
 */
static void
sig_i2c_get_bytes(
      struct sig_i2c_bus *b,
      void *s,
      unsigned int size,
      unsigned char *buffer
)
{
      unsigned int i;
      unsigned int j;
      unsigned char val = 0;

      for (j = 0; j < size; j++) {
            for (i = 0; i < 8; i++) {
                  val <<= 1;
                  sig_i2c_bus_set_clk(b, s, true);

                  if (b->data) {
                        val |= 1;
                  } else {
                        val &= 0xFE;
                  }

                  sig_i2c_bus_set_clk(b, s, false);
            }

            /* generate master ACK for all but last byte */
            if (j < size - 1) {
                  sig_i2c_bus_set_data(b, s, false);
                  sig_i2c_bus_set_clk(b, s, true);
                  sig_i2c_bus_set_clk(b, s, false);
                  sig_i2c_bus_set_data(b, s, true);
            }

            *buffer = val;
            buffer++;
      }

      /* finally, generate a master NACK after last byte */
      sig_i2c_bus_set_clk(b, s, true);
      sig_i2c_bus_set_clk(b, s, false);
}

/** send a start condition to the bus.
 *  @param b bus instance.
 *  @param s instance of initiator (who becomes bus master)
 */
static void
sig_i2c_send_start(struct sig_i2c_bus *b, void *s)
{
      sig_i2c_bus_set_clk(b, s, false);
      sig_i2c_bus_set_data(b, s, true);
      sig_i2c_bus_set_clk(b, s, true);
      sig_i2c_bus_set_data(b, s, false);
      sig_i2c_bus_set_clk(b, s, false);
      sig_i2c_bus_set_data(b, s, true);

      /* TODO arbitration */
}

/** send a stop condition to the bus.
 *  @param b bus instance.
 *  @param s instance of initiator (who becomes bus master)
 */
static void
sig_i2c_send_stop(struct sig_i2c_bus *b, void *s)
{
      sig_i2c_bus_set_clk(b, s, false);
      sig_i2c_bus_set_data(b, s, false);
      sig_i2c_bus_set_clk(b, s, true);
      sig_i2c_bus_set_data(b, s, true);
}

/** end a bus transaction, raw mode.
 *  @param b bus instance.
 *  @param s master instance.
 */
static void
sig_i2c_end_trans(struct sig_i2c_bus *b, void *s)
{
      sig_i2c_send_stop(b, s);
}


/* ********************************************* *
 * forwarders from one bus segment to the other  *
 * ********************************************* */

/** data event handler to forward an event from bus 0 to bus 1
 *  @param _f instance containing both bus segments
 *  @param data new data value.
 */
static void
sig_i2c_bus_forward0_data_event(void *_f, bool data)
{
      struct sig_i2c_bus_merge *f = (struct sig_i2c_bus_merge *)_f;
      
      sig_i2c_bus_set_data(f->s1, f, data);
}

/** clock event handler to forward an event from bus 0 to bus 1
 *  @param _f instance containing both bus segments
 *  @param data new data value.
 */
static void
sig_i2c_bus_forward0_clk_event(void *_f, bool clk)
{
      struct sig_i2c_bus_merge *f = (struct sig_i2c_bus_merge *)_f;
      
      sig_i2c_bus_set_clk(f->s1, f, clk);
}

/** data event handler to forward an event from bus 1 to bus 0
 *  @param _f instance containing both bus segments
 *  @param data new data value.
 */
static void
sig_i2c_bus_forward1_data_event(void *_f, bool data)
{
      struct sig_i2c_bus_merge *f = (struct sig_i2c_bus_merge *)_f;
      
      sig_i2c_bus_set_data(f->s0, f, data);
}

/** clock event handler to forward an event from bus 1 to bus 0
 *  @param _f instance containing both bus segments
 *  @param data new data value.
 */
static void
sig_i2c_bus_forward1_clk_event(void *_f, bool clk)
{
      struct sig_i2c_bus_merge *f = (struct sig_i2c_bus_merge *)_f;
      
      sig_i2c_bus_set_clk(f->s0, f, clk);
}

/* **************** *
 * extern interface *
 * **************** */

bool
sig_i2c_bus_read_bytes(
      struct sig_i2c_bus *b,
      void *s,
      unsigned char addr,
      unsigned int size,
      unsigned char *buffer
)
{
      unsigned int ret;
      assert((addr & 0x01) == 0x01);
      sig_i2c_send_start(b, s);

      /* put slave address on bus */
      ret = sig_i2c_put_bytes(b, s, 1, &addr);
      if (ret != 1) {
            return false;
      }


      sig_i2c_get_bytes(b, s, size, buffer);
      return true;
}

int
sig_i2c_bus_write_bytes(
      struct sig_i2c_bus *b,
      void *s,
      unsigned char addr,
      unsigned int size,
      const unsigned char *buffer
)
{
      unsigned int ret;
      assert((addr & 0x01) == 0);

      sig_i2c_send_start(b, s);
      ret = sig_i2c_put_bytes(b, s, 1, &addr);
      if (ret != 1) {
            return -1;
      }

      return sig_i2c_put_bytes(b, s, size, buffer);
}

void
sig_i2c_bus_stop_transaction(struct sig_i2c_bus *b, void *s)
{
      sig_i2c_end_trans(b, s);
}

void
sig_i2c_bus_set_data(struct sig_i2c_bus *b, void *s, bool val)
{
      unsigned int nr;

      nr = sig_i2c_bus_find_member(b, s);

      if (b->member[nr].data == val) {
            /* nothing changed */
            return;
      }

      b->member[nr].data = val;
      sig_i2c_bus_resolve_and(b);
}

void
sig_i2c_bus_set_clk(struct sig_i2c_bus *b, void *s, bool val)
{
      unsigned int nr;

      nr = sig_i2c_bus_find_member(b, s);

      if (b->member[nr].clk == val) {
            /* nothing changed */
            return;
      }

      b->member[nr].clk = val;
      sig_i2c_bus_resolve_and(b);
}

void
sig_i2c_bus_connect_raw(
      struct sig_i2c_bus *b,
      void *s,
      const struct sig_i2c_bus_funcs *f
)
{
      assert(b->nmembers < sizeof(b->member) / sizeof(b->member[0]));

      assert(f->clk_event != NULL);
      assert(f->data_event != NULL);
      assert(f->read_byte == NULL);
      assert(f->write_byte == NULL);
      assert(f->ack_addr == NULL);
      assert(f->stop_transaction == NULL);

      b->member[b->nmembers].s = s;
      b->member[b->nmembers].cs = NULL;
      b->member[b->nmembers].bus = NULL; /* must not be used here! */
      b->member[b->nmembers].f = f;
      b->member[b->nmembers].data = true;
      b->member[b->nmembers].clk = true;
      b->member[b->nmembers].is_bus_segment = false;
      b->member[b->nmembers].cf = NULL;
      b->nmembers++;
}

void
sig_i2c_bus_connect_cooked(
      struct sig_i2c_bus *b,
      void *s,
      const struct sig_i2c_bus_funcs *f
)
{
      static const struct sig_i2c_bus_funcs pf = {
            .clk_event = sig_i2c_proxy_clk_event,
            .data_event = sig_i2c_proxy_data_event,
      };

      assert(b->nmembers < sizeof(b->member) / sizeof(b->member[0]));
      assert(f->clk_event == NULL);
      assert(f->data_event == NULL);

      b->member[b->nmembers].s = &b->member[b->nmembers];
      b->member[b->nmembers].cs = s;
      b->member[b->nmembers].bus = b;
      b->member[b->nmembers].f = &pf;
      b->member[b->nmembers].data = true;
      b->member[b->nmembers].clk = true;
      b->member[b->nmembers].last_clk = true;
      b->member[b->nmembers].last_data = true;
      b->member[b->nmembers].state = SIG_I2C_NOT_INTERESTED;
      b->member[b->nmembers].shift_register = 0;
      b->member[b->nmembers].counter = 0;
      b->member[b->nmembers].read_mode = true;
      b->member[b->nmembers].is_bus_segment = false;
      b->member[b->nmembers].cf = f;
      b->nmembers++;
}


struct sig_i2c_bus *
sig_i2c_bus_init(const char *name, int nr)
{
      struct sig_i2c_bus *bus;

      bus = shm_map(name, nr, sizeof(*bus), 0);

      bus->nmembers = 0;

      /* initialize state */

      /* transistors pull bus to high */
      bus->clk = true;
      bus->data = true;
      bus->slave = -1;
      bus->resolve_in_progress = false;
      bus->resolve_again = false;

      return bus;
}

struct sig_i2c_bus_merge *
sig_i2c_bus_merge(
      struct sig_i2c_bus *s0, 
      struct sig_i2c_bus *s1
)
{
      static const struct sig_i2c_bus_funcs f0to1 = {
            .data_event = sig_i2c_bus_forward0_data_event,
            .clk_event = sig_i2c_bus_forward0_clk_event,
      };

      static const struct sig_i2c_bus_funcs f1to0 = {
            .data_event = sig_i2c_bus_forward1_data_event,
            .clk_event = sig_i2c_bus_forward1_clk_event,
      };
      struct sig_i2c_bus_merge *m;

      m = malloc(sizeof(*m));
      assert(m);
      
      m->s0 = s0;
      m->s1 = s1;

      sig_i2c_bus_connect_raw(s0, m, &f0to1);
      sig_i2c_bus_connect_raw(s1, m, &f1to0);

      s0->member[s0->nmembers - 1].is_bus_segment = true;
      s1->member[s1->nmembers - 1].is_bus_segment = true;

      return m;
}

void
sig_i2c_bus_split(struct sig_i2c_bus_merge *m)
{
      fixme();
}

void
sig_i2c_bus_create(const char *name, int nr)
{
      shm_create(name, nr, sizeof(struct sig_i2c_bus));
}

void
sig_i2c_bus_destroy(const char *name, int nr)
{
      shm_destroy(name, nr);
}

Generated by  Doxygen 1.6.0   Back to index