Contributions

This page contains information on projects performed by Axis customers that could be valuable for other customers. Please feel free to inform us about your project and how files can be obtained.

I2C Driver Update

olivier guillotin 2007/03/27 11:31 : New update of the i2c driver. See far above.

Several people have recently had issues with the Etrax I2C driver. Briefly the issues are as follows:

a) I2C data and clock lines are supposed to be open drain. The driver does not always use the lines in an open drain fashion and this can cause issues especially in a multimaster system, or a system where clock stretching is required.

b) Clock stretching, or “slave wait state,” is not implemented in the driver. Hardware that uses clock stretching to signal the driver when a transaction is complete will not work with the driver.

c) The driver does not allow for multiple byte read or writes. It implements a strictly SMBus version of the protocol that requires a command byte to be sent out with every I2C request. Some hardware allows for a multi-byte, or single byte, read without first requiring a command byte. It is impossible with the current version of the driver to implement these functions.

To help fix these issues I have edited the driver to include an i2c_read() and i2c_write() support making it possible to read or write multiple blocks independent of a command byte. I have also updated i2c_ioctle() making it possible to open the device as a character device and use standard read()/write() directives from unistd.h to send characters over the bus. Lastly, the new functions that were implemented include clock stretching or slave wait states. This version of the driver does not completely address the open drain issue above as that is a limitation of the hardware.

The new code follows, I have placed comments with ~JB everywhere I have made a change. The files that have changed are i2c.c and etraxi2c.h. I hope these can be added to the code base. Please advise if there is a better way to submit this code to the code base, I do not have access to the cvs repository.

Justin Bennett 2006/03/24 20:51

Note, these updates to the i2c driver were developed in a 2.6.11 kernel environment. The updates may not compile with 2.4.xx versions of the kernel source. If you are having trouble with i2c clock stretching the additions to the code below include commented out delays in several of the previously existing functions that when added back into the driver can effectively eliminate the problem. The delays add time to the code where clock stretching can occur allowing the slave time to complete a given transaction. Note that the commented out delays are only 2mS in length, the i2c specification allows for a clock stretch as long as 35mS.

Justin Bennett 2006/03/27 18:24

/*!***************************************************************************
*!
*! FILE NAME  : i2c.c
*!
*! DESCRIPTION: implements an interface for IIC/I2C, both directly from other
*!              kernel modules (i2c_writereg/readreg) and from userspace using
*!              ioctl()'s
*!
*! Nov 30 1998  Torbjorn Eliasson  Initial version.
*!              Bjorn Wesen        Elinux kernel version.
*! Jan 14 2000  Johan Adolfsson    Fixed PB shadow register stuff - 
*!                                 don't use PB_I2C if DS1302 uses same bits,
*!                                 use PB.
*! $Log: i2c.c,v $
*! Revision 1.13  2005/03/07 13:13:07  starvik
*! Added spinlocks to protect states etc
*!
*! Revision 1.12  2005/01/05 06:11:22  starvik
*! No need to do local_irq_disable after local_irq_save.
*!
*! Revision 1.11  2004/12/13 12:21:52  starvik
*! Added I/O and DMA allocators from Linux 2.4
*!
*! Revision 1.9  2004/08/24 06:49:14  starvik
*! Whitespace cleanup
*!
*! Revision 1.8  2004/06/08 08:48:26  starvik
*! Removed unused code
*!
*! Revision 1.7  2004/05/28 09:26:59  starvik
*! Modified I2C initialization to work in 2.6.
*!
*! Revision 1.6  2004/05/14 07:58:03  starvik
*! Merge of changes from 2.4
*!
*! Revision 1.4  2002/12/11 13:13:57  starvik
*! Added arch/ to v10 specific includes
*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer)
*!
*! Revision 1.3  2002/11/20 11:56:11  starvik
*! Merge of Linux 2.5.48
*!
*! Revision 1.2  2002/11/18 13:16:06  starvik
*! Linux 2.5 port of latest 2.4 drivers
*!
*! Revision 1.9  2002/10/31 15:32:26  starvik
*! Update Port B register and shadow even when running with hardware support
*!   to avoid glitches when reading bits
*! Never set direction to out in i2c_inbyte
*! Removed incorrect clock togling at end of i2c_inbyte
*!
*! Revision 1.8  2002/08/13 06:31:53  starvik
*! Made SDA and SCL line configurable
*! Modified i2c_inbyte to work with PCF8563
*!
*! Revision 1.7  2001/04/04 13:11:36  markusl
*! Updated according to review remarks
*!
*! Revision 1.6  2001/03/19 12:43:00  markusl
*! Made some symbols unstatic (used by the eeprom driver)
*!
*! Revision 1.5  2001/02/27 13:52:48  bjornw
*! malloc.h -> slab.h
*!
*! Revision 1.4  2001/02/15 07:17:40  starvik
*! Corrected usage if port_pb_i2c_shadow
*!
*! Revision 1.3  2001/01/26 17:55:13  bjornw
*! * Made I2C_USES_PB_NOT_PB_I2C a CONFIG option instead of assigning it
*!   magically. Config.in needs to set it for the options that need it, like
*!   Dallas 1302 support. Actually, it should be default since it screws up
*!   the PB bits even if you don't use I2C..
*! * Include linux/config.h to get the above
*!
*! Revision 1.2  2001/01/18 15:49:30  bjornw
*! 2.4 port of I2C including some cleanups (untested of course)
*!
*! Revision 1.1  2001/01/18 15:35:25  bjornw
*! Verbatim copy of the Etrax i2c driver, 2.0 elinux version
*!
*!
*! ---------------------------------------------------------------------------
*!
*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN
*!
*!***************************************************************************/
/* $Id: i2c.c,v 1.13 2005/03/07 13:13:07 starvik Exp $ */
 
/****************** INCLUDE FILES SECTION ***********************************/
 
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/config.h>
 
#include <asm/etraxi2c.h>
#include <asm/uaccess.h>
 
#include <asm/system.h>
#include <asm/arch/svinto.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <asm/arch/io_interface_mux.h>
 
#include "i2c.h"
 
/****************** I2C DEFINITION SECTION *************************/
 
#define D(x)
 
#define I2C_MAJOR 123  /* LOCAL/EXPERIMENTAL */
static const char i2c_name[] = "i2c";
 
#define CLOCK_LOW_TIME            8
#define CLOCK_HIGH_TIME           8
#define START_CONDITION_HOLD_TIME 8
#define STOP_CONDITION_HOLD_TIME  8
#define ENABLE_OUTPUT 0x01
#define ENABLE_INPUT 0x00
#define I2C_CLOCK_HIGH 1
#define I2C_CLOCK_LOW 0
#define I2C_DATA_HIGH 1
#define I2C_DATA_LOW 0
#define I2C_WAIT_USEC 1         //~JB 1uS wait
#define MAX_I2C_WAIT 35000      //~JB total of 35mS maximum clock low timeout
 
#ifdef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C
/* Use PB and not PB_I2C */
#ifndef CONFIG_ETRAX_I2C_DATA_PORT
#define CONFIG_ETRAX_I2C_DATA_PORT 0
#endif
#ifndef CONFIG_ETRAX_I2C_CLK_PORT
#define CONFIG_ETRAX_I2C_CLK_PORT 1
#endif
 
#define SDABIT CONFIG_ETRAX_I2C_DATA_PORT
#define SCLBIT CONFIG_ETRAX_I2C_CLK_PORT
#define i2c_enable() 
#define i2c_disable() 
 
/* enable or disable output-enable, to select output or input on the i2c bus */
 
#define i2c_dir_out() \
  REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, SDABIT, 1); 
#define i2c_dir_in()  \
  REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, SDABIT, 0); 
 
#define i2c_dir_out_scl() \
  REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, SCLBIT, 1);  //~JB  sets the io direction for the clock bit
#define i2c_dir_in_scl() \
  REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, SCLBIT, 0);  //~JB  sets the io directio for the clock bit
 
/* control the i2c clock and data signals */
 
#define i2c_clk(x) \
  REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, SCLBIT, x)
#define i2c_data(x) \
  REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, SDABIT, x)
 
/* read a bit from the i2c interface */
 
#define i2c_getbit() (((*R_PORT_PB_READ & (1 << SDABIT))) >> SDABIT)
#define i2c_getclk() (((*R_PORT_PB_READ & (1 << SCLBIT))) >> SCLBIT)  //~JB  used to read in the value of the clock bit
 
#else
/* enable or disable the i2c interface */
 
#define i2c_enable() *R_PORT_PB_I2C = (port_pb_i2c_shadow |= IO_MASK(R_PORT_PB_I2C, i2c_en))
#define i2c_disable() *R_PORT_PB_I2C = (port_pb_i2c_shadow &= ~IO_MASK(R_PORT_PB_I2C, i2c_en))
 
/* enable or disable output-enable, to select output or input on the i2c bus */
 
#define i2c_dir_out() \
   *R_PORT_PB_I2C = (port_pb_i2c_shadow &= ~IO_MASK(R_PORT_PB_I2C, i2c_oe_)); \
   REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, 0, 1);
#define i2c_dir_in() \
   *R_PORT_PB_I2C = (port_pb_i2c_shadow |= IO_MASK(R_PORT_PB_I2C, i2c_oe_)); \
   REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, 0, 0);
 
#define i2c_dir_out_scl() \
   REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, 1, 1); //~JB sets the io direction for the clock bit
#define i2c_dir_in_scl() \
   REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, 1, 0); //~JB sets the io direction for the clock bit
 
/* control the i2c clock and data signals */
 
#define i2c_clk(x) \
   *R_PORT_PB_I2C = (port_pb_i2c_shadow = (port_pb_i2c_shadow & \
       ~IO_MASK(R_PORT_PB_I2C, i2c_clk)) | IO_FIELD(R_PORT_PB_I2C, i2c_clk, (x))); \
       REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 1, x);
 
#define i2c_data(x) \
   *R_PORT_PB_I2C = (port_pb_i2c_shadow = (port_pb_i2c_shadow & \
      ~IO_MASK(R_PORT_PB_I2C, i2c_d)) | IO_FIELD(R_PORT_PB_I2C, i2c_d, (x))); \
   REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 0, x);
 
/* read a bit from the i2c interface */
 
#define i2c_getbit() (*R_PORT_PB_READ & 0x1)
#define i2c_getclk() (*R_PORT_PB_READ & 0x2)  //~JB  used to read in the value of the clock bit.
#endif
 
/* use the kernels delay routine */
 
#define i2c_delay(usecs) udelay(usecs)
 
static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */
 
/****************** FUNCTION DEFINITION SECTION *************************/
 
 
/* generate i2c start condition */
 
void
i2c_start(void)
{
   /*
    * SCL=1 SDA=1
    */
   i2c_dir_out();
   i2c_delay(CLOCK_HIGH_TIME/6);
   i2c_data(I2C_DATA_HIGH);
   i2c_clk(I2C_CLOCK_HIGH);
   i2c_delay(CLOCK_HIGH_TIME);
   /*
    * SCL=1 SDA=0
    */
   i2c_data(I2C_DATA_LOW);
   i2c_delay(START_CONDITION_HOLD_TIME);
   /*
    * SCL=0 SDA=0
    */
   i2c_clk(I2C_CLOCK_LOW);
   i2c_delay(CLOCK_LOW_TIME);
}
 
/* generate i2c stop condition */
 
void
i2c_stop(void)
{
   /* ~JB My edits to this function were left intact as they have an affect on the
    * functionality of the i2c_read() and i2c_write() functions.  The changes should
    * have no impact on pre-existing functions.
    */
 
   i2c_dir_out();
 
   /*
    * SCL=0 SDA=0
    */
// i2c_clk(I2C_CLOCK_LOW);          //~JB this puts an extra clock on the bus, waitstate waits for slave to release the clock
                                    // the clock should already be in a high state when we reach this point.
   i2c_data(I2C_DATA_LOW);
   i2c_delay(CLOCK_LOW_TIME*2);
   /*
    * SCL=1 SDA=0
    */
   i2c_clk(I2C_CLOCK_HIGH);
   i2c_delay(CLOCK_HIGH_TIME*2);
   /*
    * SCL=1 SDA=1
    */
   i2c_data(I2C_DATA_HIGH);
   i2c_delay(STOP_CONDITION_HOLD_TIME);
 
   i2c_dir_in();
}
 
/* write a byte to the i2c interface */
 
void
i2c_outbyte(unsigned char x)
{
   int i;
 
   i2c_dir_out();
 
   for (i = 0; i < 8; i++) {
      if (x & 0x80) {
         i2c_data(I2C_DATA_HIGH);
      } else {
         i2c_data(I2C_DATA_LOW);
      }
 
      i2c_delay(CLOCK_LOW_TIME/2);
      i2c_clk(I2C_CLOCK_HIGH);
      i2c_delay(CLOCK_HIGH_TIME);
      i2c_clk(I2C_CLOCK_LOW);
      i2c_delay(CLOCK_LOW_TIME/2);
      x <<= 1;
   }
   i2c_data(I2C_DATA_LOW);
   i2c_delay(CLOCK_LOW_TIME/2);
 
   /*
    * enable input
    */
   i2c_dir_in();
}
 
/* read a byte from the i2c interface */
 
unsigned char
i2c_inbyte(void)
{
   unsigned char aBitByte = 0;
   int i;
 
   /* Switch off I2C to get bit */
   i2c_disable();
   i2c_dir_in();
   i2c_delay(CLOCK_HIGH_TIME/2);
 
   /* Get bit */
   aBitByte |= i2c_getbit();
 
   /* Enable I2C */
   i2c_enable();
   i2c_delay(CLOCK_LOW_TIME/2);
 
   for (i = 1; i < 8; i++) {
      aBitByte <<= 1;
      /* Clock pulse */
      i2c_clk(I2C_CLOCK_HIGH);
      i2c_delay(CLOCK_HIGH_TIME);
      i2c_clk(I2C_CLOCK_LOW);
      i2c_delay(CLOCK_LOW_TIME);
 
      /* Switch off I2C to get bit */
      i2c_disable();
      i2c_dir_in();
      i2c_delay(CLOCK_HIGH_TIME/2);
 
      /* Get bit */
      aBitByte |= i2c_getbit();
 
      /* Enable I2C */
      i2c_enable();
      i2c_delay(CLOCK_LOW_TIME/2);
   }
   i2c_clk(I2C_CLOCK_HIGH);
   i2c_delay(CLOCK_HIGH_TIME);
 
    /*
    * we leave the clock low, getbyte is usually followed
    * by sendack/nack, they assume the clock to be low
    */
    i2c_clk(I2C_CLOCK_LOW);
   return aBitByte;
}
 
/*#---------------------------------------------------------------------------
*#
*# FUNCTION NAME: i2c_getack
*#
*# DESCRIPTION  : checks if ack was received from ic2
*#
*#--------------------------------------------------------------------------*/
 
int
i2c_getack(void)
{
 
   /* ~JB My edits to this function were left intact as they have an affect on the
    * functionality of the i2c_read() and i2c_write() functions.  The changes should
    * have no impact on pre-existing functions.
    */
 
   int ack = 1;
   /*
    * enable output
    */
   i2c_dir_out();
   /*
    * Release data bus by setting
    * data high
    */
   i2c_data(I2C_DATA_HIGH);
   /*
    * enable input
    */
   i2c_dir_in();
   i2c_delay(CLOCK_HIGH_TIME/4);
   /*
    * generate ACK clock pulse
    */
   i2c_clk(I2C_CLOCK_HIGH);
   /*
    * Use PORT PB instead of I2C
    * for input. (I2C not working)
    */
   i2c_clk(1);
   i2c_data(1);
   /*
    * switch off I2C
    */
   i2c_data(1);
   i2c_disable();
   i2c_dir_in();
   /*
    * now wait for ack
    */
   i2c_delay(CLOCK_HIGH_TIME/2);
   /*
    * check for ack
    */
   if(i2c_getbit())
      ack = 0;
   i2c_delay(CLOCK_HIGH_TIME/2);
   if(!ack){
      if(!i2c_getbit()) /* receiver pulld SDA low */
         ack = 1;
      i2c_delay(CLOCK_HIGH_TIME/2);
   }
 
   /*
    * our clock is high now, make sure data is low
    * before we enable our output. If we keep data high
    * and enable output, we would generate a stop condition.
    */
   //i2c_data(I2C_DATA_LOW);
   /* ~JB This is incorrect, a stop condition is generated when the clock is
    * high and the data line toggles (typically from low to high).  The data
    * line should never toggle while the clock is high unless a start or stop
    * condition is desired.
    */
 
   /*
    * end clock pulse
    */
   i2c_enable();
   i2c_dir_out();
   i2c_clk(I2C_CLOCK_LOW);
   i2c_delay(CLOCK_HIGH_TIME/4);
   /*
    * enable output
    */
// i2c_dir_out();    //~JB already done above
   /*
    * remove ACK clock pulse
    */
   //i2c_data(I2C_DATA_HIGH);
   //i2c_delay(CLOCK_LOW_TIME/2);
   /* ~JB No need to remove ACK pulse.  We will be prefetching the next data 
    * bit while the slave holds the clock line low for the slave wait state
    */
   return ack;
}
 
/*#---------------------------------------------------------------------------
*#
*# FUNCTION NAME: I2C::sendAck
*#
*# DESCRIPTION  : Send ACK on received data
*#
*#--------------------------------------------------------------------------*/
void
i2c_sendack(void)
{
   /*
    * enable output
    */
   i2c_delay(CLOCK_LOW_TIME);
   i2c_dir_out();
   /*
    * set ack pulse high
    */
   i2c_data(I2C_DATA_LOW);
   /*
    * generate clock pulse
    */
   i2c_delay(CLOCK_HIGH_TIME/6);
   i2c_clk(I2C_CLOCK_HIGH);
   i2c_delay(CLOCK_HIGH_TIME);
   i2c_clk(I2C_CLOCK_LOW);
   i2c_delay(CLOCK_LOW_TIME/6);
   /*
    * reset data out
    */
   i2c_data(I2C_DATA_HIGH);
   i2c_delay(CLOCK_LOW_TIME);
 
   i2c_dir_in();
}
 
/*#---------------------------------------------------------------------------
*#
*# FUNCTION NAME: i2c_sendnack
*#
*# DESCRIPTION  : Sends NACK on received data
*#
*#--------------------------------------------------------------------------*/
void
i2c_sendnack(void)
{
   /*
    * enable output
    */
   i2c_delay(CLOCK_LOW_TIME);
   i2c_dir_out();
   /*
    * set data high
    */
   i2c_data(I2C_DATA_HIGH);
   /*
    * generate clock pulse
    */
   i2c_delay(CLOCK_HIGH_TIME/6);
   i2c_clk(I2C_CLOCK_HIGH);
   i2c_delay(CLOCK_HIGH_TIME);
   i2c_clk(I2C_CLOCK_LOW);
   i2c_delay(CLOCK_LOW_TIME);
 
   i2c_dir_in();
}
 
/*#--------------------------------------------------------------------------
 *#
 *# FUNCTION NAME: i2c_waitstate
 *#
 *# DESCTIPTION  : Handles the slave wait state where the slave is holding the
 *#                clock line low during its busy time.
 *#
 *# RETURN : 0 on failure or bus timeout
 *#          1 on success
 *#-------------------------------------------------------------------------*/
int i2c_waitstate(void){
 
   unsigned long int lcv;
 
   i2c_delay(CLOCK_LOW_TIME);   //Delay with clock low for at least 1 low period before reading clk
 
   //Change data direction to input
   i2c_disable();     //Can't read the clock unless i2c mode is disable... bad hardware! :(
   i2c_dir_in_scl();
 
   for(lcv=0; lcv<MAX_I2C_WAIT; lcv++){
      i2c_delay(I2C_WAIT_USEC);
      if( i2c_getclk() ) break;
   }
 
   i2c_clk(I2C_CLOCK_HIGH);        //Should already be high, we want to make sure it stays that way.
   i2c_dir_out_scl();
   i2c_enable();
 
   if( lcv==MAX_I2C_WAIT )
      return 0;
   else
     return 1;
}
 
 
 
/*#---------------------------------------------------------------------------
*#
*# FUNCTION NAME: i2c_writereg
*#
*# DESCRIPTION  : Writes a value to an I2C device
*#
*#     ~JB This is actually an SMBus type transaction where a pointer is
*#     sent out and a data byte is written
*#
*#--------------------------------------------------------------------------*/
int
i2c_writereg(unsigned char theSlave, unsigned char theReg, 
        unsigned char theValue)
{   
   /* ~JB My edits to this function were removed (commented out) to ensure 
    * compatibility with legacy programs.  Note that these functions will
    * have issues with slaves that hold the clock line low for any amount
    * of time.  In such cases it is recommended that i2c_read() and i2c_write()
    * be used.
    */
 
   int error, cntr = 3;
   unsigned long flags;
 
   spin_lock(&i2c_lock);
 
   do {
      error = 0;
      /*
       * we don't like to be interrupted
       */
      local_irq_save(flags);
 
      i2c_start();
      /*
       * send slave address
       */
      i2c_outbyte((theSlave & 0xfe));
      /*
       * wait for ack
       */
      if(!i2c_getack())
         error = 1;
      /*
       * now select register
       */
//      i2c_delay(2000); //~JB The bus should wait for the slave to release the clock line here
                         //i2c_waitstate() has been created for this purpose.  Commented out code
                         //was a hack to get these functions working.
      i2c_dir_out();
      i2c_outbyte(theReg);
      /*
       * now it's time to wait for ack
       */
      if(!i2c_getack())
         error |= 2;
      /*
       * send register register data
       */
//      i2c_delay(2000); //~JB The bus should wait for the slave to release the clock line here
                         //i2c_waitstate() has been created for this purpose.  Commented out code
                         //was a hack to get these functions working.
      i2c_outbyte(theValue);
      /*
       * now it's time to wait for ack
       */
      if(!i2c_getack())
         error |= 4;
      /*
       * end byte stream
       */
//      i2c_delay(2000); //~JB The bus should wait for the slave to release the clock line here
                         //i2c_waitstate() has been created for this purpose.  Commented out code
                         //was a hack to get these functions working.
      i2c_stop();
      /*
       * enable interrupt again
       */
      local_irq_restore(flags);
 
   } while(error && cntr--);
 
   i2c_delay(CLOCK_LOW_TIME);
 
   spin_unlock(&i2c_lock);
 
   return -error;
}
 
/*#---------------------------------------------------------------------------
*#
*# FUNCTION NAME: i2c_readreg 
*#
*# DESCRIPTION  : Reads a value from the decoder registers.
*#  
*#     ~JB This is actually an SMBus type transaction where a pointer is
*#     sent out and a data bit is read in.
*#
*#--------------------------------------------------------------------------*/
unsigned char
i2c_readreg(unsigned char theSlave, unsigned char theReg)
{
 
   /* ~JB My edits to this function were removed (commented out) to ensure 
    * compatibility with legacy programs.  Note that these functions will
    * have issues with slaves that hold the clock line low for any amount
    * of time.  In such cases it is recommended that i2c_read() and i2c_write()
    * be used.
    */
 
   unsigned char b = 0;
   int error, cntr = 3;
   unsigned long flags;
 
   spin_lock(&i2c_lock);
 
   do {
      error = 0;
      /*
       * we don't like to be interrupted
       */
      local_irq_save(flags);
      /*
       * generate start condition
       */
      i2c_start();
 
      /*
       * send slave address
       */
      i2c_outbyte((theSlave & 0xfe));
      /*
       * wait for ack
      */
      if(!i2c_getack())
         error = 1;
//      i2c_delay(2000); //~JB The bus should wait for the slave to release the clock line here
                         //i2c_waitstate() has been created for this purpose.  Commented out code
                         //was a hack to get these functions working.
      /*
       * now select register
       */
      i2c_dir_out();
      i2c_outbyte(theReg);
      /*
       * now it's time to wait for ack
       */
      if(!i2c_getack())
         error = 1;
//      i2c_delay(2000); //~JB The bus should wait for the slave to release the clock line here
                         //i2c_waitstate() has been created for this purpose.  Commented out code
                         //was a hack to get these functions working.
      /*
       * repeat start condition
       */
      i2c_delay(CLOCK_LOW_TIME);
      i2c_start();
      /*
       * send slave address
       */
      i2c_outbyte(theSlave | 0x01);
      /*
       * wait for ack
       */
      if(!i2c_getack())
         error = 1;
      /*
       * fetch register
       */
//      i2c_delay(2000); //~JB The bus should wait for the slave to release the clock line here
                         //i2c_waitstate() has been created for this purpose.  Commented out code
                         //was a hack to get these functions working.
      b = i2c_inbyte();
      /*
       * last received byte needs to be nacked
       * instead of acked
       */
      i2c_sendnack();
      /*
       * end sequence
       */
//      i2c_delay(2000); //~JB The bus should wait for the slave to release the clock line here
                         //i2c_waitstate() has been created for this purpose.  Commented out code
                         //was a hack to get these functions working.
      i2c_stop();
      /*
       * enable interrupt again
       */
      local_irq_restore(flags);
 
   } while(error && cntr--);
 
   spin_unlock(&i2c_lock);
 
   return b;
}
 
 
/*#--------------------------------------------------------------------------
 *# ~JB
 *# FUNCTION NAME: i2c_open
 *#
 *# DESCTIPTION  : Allocates memory space to store the slave device address
 *#                so that it can be accessed using the proper file descriptor
 *#
 *#-------------------------------------------------------------------------*/
static int
i2c_open(struct inode *inode, struct file *filp)
{
   unsigned char *address;
 
   address = kmalloc(sizeof(*address), GFP_KERNEL);
   if(!address) 
      return -ENOMEM;
   filp->private_data = address;
 
   return 0;
}
 
/*#--------------------------------------------------------------------------
 *# ~JB
 *# FUNCTION NAME: i2c_release
 *#
 *# DESCTIPTION  : Frees up memory space that was used to store the i2c slave
 *#                device address.
 *#
 *#-------------------------------------------------------------------------*/
static int
i2c_release(struct inode *inode, struct file *filp)
{
   unsigned char *address = (unsigned char *)filp->private_data;
 
   kfree(address);
   filp->private_data = NULL;
 
   return 0;
}
 
/* Main device API. ioctl's to write or read to/from i2c registers.
 */
 
static int
i2c_ioctl(struct inode *inode, struct file *file,
     unsigned int cmd, unsigned long arg)
{
 
   unsigned char *address = (unsigned char *)file->private_data;
 
 
   if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) {
      return -EINVAL;
   }
 
   switch (_IOC_NR(cmd)) {
      case I2C_WRITEREG:
         /* write to an i2c slave */
         D(printk("i2cw %d %d %d\n", 
             I2C_ARGSLAVE(arg),
             I2C_ARGREG(arg),
             I2C_ARGVALUE(arg)));
 
         return i2c_writereg(I2C_ARGSLAVE(arg),
                   I2C_ARGREG(arg),
                   I2C_ARGVALUE(arg));
      case I2C_READREG:
      {
         unsigned char val;
         /* read from an i2c slave */
         D(printk("i2cr %d %d ", 
            I2C_ARGSLAVE(arg),
            I2C_ARGREG(arg)));
         val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg));
         D(printk("= %d\n\r", val));
         return val;
      }
      /* ~JB I2C_SLAVE state was added in order to save the address of the slave being used.
       * This allows a program to open two seperate slave devices and access them using different
       * file descriptors.
       */
      case I2C_SLAVE:
         *address = (unsigned char) ((arg & 0xff) << 1);
         D(printk("i2ca %x\n\r", *address));
         return 0;
      default:
         return -EINVAL;
 
   }
 
   return 0;
}
 
/*#--------------------------------------------------------------------------
 *#
 *# FUNCTION NAME: i2c_read
 *#
 *# DESCTIPTION  : Reads in a specified number of bytes from an i2c slave
 *#                device.
 *#
 *# RETURN : <0 on failure  (-1=NACK error -2=BUS_TIMEOUT
 *#          >0 Successful bytes read
 *#
 *#-------------------------------------------------------------------------*/
ssize_t i2c_read(struct file *file, char __user *data, size_t size, loff_t *off){
 
   char *tmp;
   int count = 0;
   int error = 0;
   unsigned long flags;
 
   unsigned char *address = (unsigned char *)file->private_data;
 
   if( size > 8192 )
         size = 8192;
 
   tmp = kmalloc(size,GFP_KERNEL);
   if( tmp == NULL)
            return -ENOMEM;
 
   D(printk("i2cr %u\n\r", size));
 
   spin_lock(&i2c_lock);
 
   // we don't like to be interrupted
   local_irq_save(flags);
 
   //Send out address with read bit set
 
   // generate start condition
   i2c_start();
 
   //send slave address with the read bit set 
   i2c_outbyte((*address | 0x01));
   D(printk("addr %x\r\n", *address));   
 
   //wait for ack
   if(!i2c_getack())
      error = 1;
 
   i2c_dir_in();  //setup data line for reading in a byte
 
   //wait for slave to be ready, if scl is held low for too long signal an error
   if( !i2c_waitstate() ){
      D(printk("BUS_TIMEOUT"));
      error = 2;
   }
 
 
   //Start reading in bytes
   while( (count < size) && (error == 0) )
   {
      //fetch register
      tmp[count++] = i2c_inbyte();
      D(printk(" %x", tmp[count-1]));
      //Send out ACK, or NACK if necessary
      if( count != size )
         i2c_sendack();
      else
      {
         i2c_sendnack();
 
         //Set data line to low in preparation for stop.
         i2c_dir_out();
         i2c_data(I2C_DATA_LOW);
      }
 
      //wait for slave to be read, if scl is held low for too long signal an error
      if( !i2c_waitstate() ){
         D(printk("BUS_TIMEOUT\n\r"));
         error = 2;
      }
   }
 
   i2c_stop();
 
   //enable interrupt again
   local_irq_restore(flags);
 
   spin_unlock(&i2c_lock);
 
   copy_to_user(data, tmp, count);
   kfree(tmp);
 
   if( error )
      return -error;
   else
      return count;
}
 
/*#--------------------------------------------------------------------------
 *#
 *# FUNCTION NAME: i2c_write
 *#
 *# DESCTIPTION  : Writes out a specified number of bytes from to i2c slave
 *#                device.
 *#
 *# RETURN : <0 on failure  (-1=NACK error -2=BUS_TIMEOUT
 *#          >0 Successful bytes written
 *#-------------------------------------------------------------------------*/
ssize_t i2c_write(struct file *file, const char __user *data, size_t size, loff_t *off){
 
   char *tmp;
   int count = 0;
   int error = 0;
   unsigned long flags;
 
   unsigned char *address = (unsigned char *)file->private_data;
 
   if( size > 8192 )
         size = 8192;
 
   tmp = kmalloc(size,GFP_KERNEL);  
   if( tmp == NULL)
            return -ENOMEM;
   if( copy_from_user(tmp, data, size)){
      kfree(tmp);
      return -EFAULT;
   }
 
   spin_lock(&i2c_lock);
 
   // we don't like to be interrupted
   local_irq_save(flags);
 
   //Send out address with read bit set
   D(printk("addr %x\n\r", *address));
 
   // generate start condition
   i2c_start();
 
   //send slave address with the write bit set 
   i2c_outbyte((*address & 0xfe));
 
   //wait for ack
   if(!i2c_getack()){
      D(printk("NACK\n\r"));
      error = 1;
   }
 
   //prefetch first data bit and put on data line
   i2c_dir_out();
   if (tmp[count] & 0x80) {
      i2c_data(I2C_DATA_HIGH);
   } else {
      i2c_data(I2C_DATA_LOW);
   }
 
   //wait for slave to be ready
   if( !i2c_waitstate() ){
      D(printk("BUS_TIMEOUT\n\r"));
      error = 2;
   }
 
   //Start writing out bytes
   while( (count < size) && (error == 0) )
   {
      //Print out debugging message with next byte that will be placed on bus
      D(printk("i2co byte %x\n\r", tmp[count]));
 
      //place data on bus
      i2c_outbyte(tmp[count++]);
 
      //wait for ack
      if( !i2c_getack() ){
          D(printk("NACK\n\r"));
          error = 1;
      }
 
      //prefetch next data bit and put on data line
      i2c_dir_out();
      if ((tmp[count]&0x80) && (count<size)) {
         i2c_data(I2C_DATA_HIGH);
      } else {
         i2c_data(I2C_DATA_LOW);
      }
 
      //wait for slave to be ready
      if( !i2c_waitstate() ){
         D(printk("BUS_TIMEOUT\n\r"));
         error = 2;
      }
   }
 
   i2c_stop();
 
   //enable interrupt again
   local_irq_restore(flags);
 
   spin_unlock(&i2c_lock);
 
   kfree(tmp);
 
   if( error > 0)
      return -error;
   else
      return count;
}
 
 
 
 
 
static struct file_operations i2c_fops = {
   .owner    = THIS_MODULE,
   .ioctl    = i2c_ioctl,
   .open     = i2c_open,
   .release  = i2c_release,
   .read     = i2c_read,       //~JB added read() support
   .write    = i2c_write,      //~JB added write() support
};
 
int __init
i2c_init(void)
{
   static int res = 0;
   static int first = 1;
 
   if (!first) {
      return res;
   }
 
   /* Setup and enable the Port B I2C interface */
 
#ifndef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C
   if ((res = cris_request_io_interface(if_i2c, "I2C"))) {
      printk(KERN_CRIT "i2c_init: Failed to get IO interface\n");
      return res;
   }
 
   *R_PORT_PB_I2C = port_pb_i2c_shadow |= 
      IO_STATE(R_PORT_PB_I2C, i2c_en,  on) |
      IO_FIELD(R_PORT_PB_I2C, i2c_d,   1)  |
      IO_FIELD(R_PORT_PB_I2C, i2c_clk, 1)  |
      IO_STATE(R_PORT_PB_I2C, i2c_oe_, enable);
 
   port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir0);
   port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir1);
 
   *R_PORT_PB_DIR = (port_pb_dir_shadow |=
           IO_STATE(R_PORT_PB_DIR, dir0, input)  |
           IO_STATE(R_PORT_PB_DIR, dir1, output));
#else
        if ((res = cris_io_interface_allocate_pins(if_i2c,
                     'b',
                                                   CONFIG_ETRAX_I2C_DATA_PORT,
                     CONFIG_ETRAX_I2C_DATA_PORT))) {
      printk(KERN_WARNING "i2c_init: Failed to get IO pin for I2C data port\n");
      return res;
   } else if ((res = cris_io_interface_allocate_pins(if_i2c,
                       'b',
                       CONFIG_ETRAX_I2C_CLK_PORT,
                       CONFIG_ETRAX_I2C_CLK_PORT))) {
      cris_io_interface_free_pins(if_i2c,
                   'b',
                   CONFIG_ETRAX_I2C_DATA_PORT,
                   CONFIG_ETRAX_I2C_DATA_PORT);
      printk(KERN_WARNING "i2c_init: Failed to get IO pin for I2C clk port\n");
   }
#endif
 
   return res;
}
 
static int __init
i2c_register(void)
{
   int res;
 
   res = i2c_init();
   if (res < 0)
      return res;
   res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops);
   if(res < 0) {
      printk(KERN_ERR "i2c: couldn't get a major number.\n");
      return res;
   }
 
   printk(KERN_INFO "I2C driver v2.2, (c) 1999-2004 Axis Communications AB\n");
 
   return 0;
}
 
/* this makes sure that i2c_register is called during boot */
 
module_init(i2c_register);
 
/****************** END OF FILE i2c.c ********************************/
/* $Id: etraxi2c.h,v 1.1 2001/01/18 15:49:57 bjornw Exp $ */
 
#ifndef _LINUX_ETRAXI2C_H
#define _LINUX_ETRAXI2C_H
 
/* etraxi2c _IOC_TYPE, bits 8 to 15 in ioctl cmd */
 
#define ETRAXI2C_IOCTYPE 44
 
/* supported ioctl _IOC_NR's */
 
/* in write operations, the argument contains both i2c
 * slave, register and value.
 */
 
#define I2C_WRITEARG(slave, reg, value) (((slave) << 16) | ((reg) << 8) | (value))
#define I2C_READARG(slave, reg) (((slave) << 16) | ((reg) << 8))
 
#define I2C_ARGSLAVE(arg) ((arg) >> 16)
#define I2C_ARGREG(arg) (((arg) >> 8) & 0xff)
#define I2C_ARGVALUE(arg) ((arg) & 0xff)
 
#define I2C_WRITEREG 0x1   /* write to an i2c register */
#define I2C_READREG  0x2   /* read from an i2c register */
#define I2C_SLAVE    0x3   /* save the slave address given in ioctl */ //~JB Added to support opening an i2c device for read and write
 
/*
EXAMPLE usage:
 
    i2c_arg = I2C_WRITEARG(STA013_WRITE_ADDR, reg, val);
    ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_WRITEREG), i2c_arg);
 
    i2c_arg = I2C_READARG(STA013_READ_ADDR, reg);
    val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg);
 
*/
#endif

New update (25/03/2007)

olivier guillotin 2007/03/25 13:45

I give here sections of the code i modified/developped in i2c.c and etraxi2c.h, to fix bug and improve the ioctl() function with a “write-and-read” command. It permits the user application to communicate with I2C chips like DS1621. Indeed, some i2c chips need communication with a sequence like this : < start - addr write - write datas - start again - addr read - read datas - stop >

The ioctl command i wrote does it with :

  • “I2C_WRITE_READ” is the ioctl() command, declared in the etraxi2c.h above.
  • arg is supposed to be the address of a structure like this : {int lenw; int lenr; char data[max(lenw, lenw)];}
  • after ioctl(), data[] contains bytes read from slave.
EXAMPLE with DS1621: 

float temp;
struct i2c_wr_msg
{
   int lenw; 
   int lenr; 
   char data[2];
} wr_msg;
...
wr_msg.lenw=1; wr_msg.lenr=2;
data[0]=0xAA; // ReadTemperature of the DS1621 means write 1 byte for the command and read two bytes for the temperature
ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_WRITE_READ), &wr_msg);
temp=(float)wr_msg.data[0] - 0.5*((wr_msg.data[1]&0x80)==0x80);

I give here the code of the write_read function() with the improved ioctl() you have to insert/replace in the i2c.c : (I tested it on a 2.6 kernel.)

    
/*#--------------------------------------------------------------------------- 
 *# O.G 
 *# FUNCTION NAME : i2c_write_read
 *#
 *# DESCRIPTION :	Used by ioctl to implement the write-read sequence to the slave.
 *#					Returns < 0 if error or lenr in success
 *#					
 *#-------------------------------------------------------------------------*/

static int
i2c_write_read(char adr, char *datawr, int lenw, int lenr)
{
	int count = 0;
	int error = 0;
	unsigned long flags;
	
	// we don't like to be interrupted
	spin_lock(&i2c_lock);
	local_irq_save(flags);
	
	// generate start condition
	i2c_start();	
	//send slave address with the write bit set 
	i2c_outbyte((adr & 0xfe));
	//wait for ack
	if(!i2c_getack())
      error = 1;
  
	//prefetch first data bit and put on data line
	i2c_dir_out();
	if (datawr[count] & 0x80)
		i2c_data(I2C_DATA_HIGH);
	else 
		i2c_data(I2C_DATA_LOW);
   
	//wait for slave to be ready
   if( !i2c_waitstate() )
      error = 2;
 
   //Start writing datawr to slave
   while( (count < lenw) && (error == 0) )
   {
      //Print out debugging message with next byte that will be placed on bus
      D(printk("i2cwr-w %x\n", (unsigned char)datawr[count]));    
      //place data on bus
      i2c_outbyte(datawr[count++]);
      //wait for ack
      if( !i2c_getack() )
          error = 1;
      //prefetch next data bit and put on data line
      i2c_dir_out();
      if ((datawr[count]&0x80) && (count<lenw))
         i2c_data(I2C_DATA_HIGH);
      else 
         i2c_data(I2C_DATA_LOW);
      //wait for slave to be ready
      if(!i2c_waitstate())
          error = 2;
    }

	if(error == 0) // if ok, read lenr bytes from slave
	{
		count = 0;
		// generate restart condition
		i2c_start();
		//send slave address with the read bit set 
		i2c_outbyte((adr | 0x01));
		//wait for ack
		if(!i2c_getack())
			error = 1;
   
		i2c_dir_in();  //setup data line for reading in a byte
		//wait for slave to be ready, if scl is held low for too long signal an error
		if(!i2c_waitstate())
			error = 2;
		//Start reading in bytes
		while( (count < lenr) && (error == 0) )
		{
			//fetch register
			datawr[count++] = i2c_inbyte();
			D(printk("i2cwr-r byte %x\n\r", (unsigned char)datawr[count-1]));
			//Send out ACK, or NACK if necessary
			if( count != lenr )
				i2c_sendack();
			else
			{
				i2c_sendnack();		
				//Set data line to low in preparation for stop.
				i2c_dir_out();
				i2c_data(I2C_DATA_LOW);
			}
			//wait for slave to be read, if scl is held low for too long signal an error
			if(!i2c_waitstate())
				error = 2;
		}
	}
 
	i2c_stop();
 
	//enable interrupt again
	local_irq_restore(flags); 
	spin_unlock(&i2c_lock);

	if(error)
		return -error;
	else
		return lenr;
}

static int 
i2c_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) 
{
   unsigned char *address = (unsigned char *)file->private_data;
   /* O.G : needed for write-read cmd */
   int status, maxlen;
   int lenwr[2]; 
   char *bufwr;
   // verify that it's a real i2c cmd
   if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE)
      return -EINVAL;

   switch (_IOC_NR(cmd)) 
   {
	case I2C_WRITEREG:
         /* write to an i2c slave */
         D(printk("i2cw %d %d %d\n", I2C_ARGSLAVE(arg),I2C_ARGREG(arg),I2C_ARGVALUE(arg)));
         return i2c_writereg(I2C_ARGSLAVE(arg),I2C_ARGREG(arg),I2C_ARGVALUE(arg));

	case I2C_READREG:
	{
         unsigned char val;
         /* read from an i2c slave */
         D(printk("i2cr %d %d ",I2C_ARGSLAVE(arg),I2C_ARGREG(arg)));
         val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg));
         D(printk("= %d\n\r", val));
         return val;
	}
    /* ~JB I2C_SLAVE state was added in order to save the address of the slave being used.
     * This allows a program to open two seperate slave devices and access them using different
     * file descriptors.
    */
	case I2C_SLAVE:
         *address = (unsigned char) (arg & 0xfe); // i fix the bug : (arg & 0xff) << 1
         printk("i2c_ioctl adr %x\n", *address);
         return 0;
	case I2C_WRITE_READ: 
		/* O.G I2C_WRITE_READ CMD to do write-read sequence with slave */
		/* supposed to receive in arg, the adress of an data structure like :
		** struct i2c_rw_seq 
		**	{	int lenw; int lenr; 
		**		char buf[xx]; // 
		**	};
		** MUST be defined by the user application.
		**
		** datas to write are put in the buf by the user application.
		** IMPORTANT : At the end of ioctl, read datas overwrite the write datas
		*/
		// get the sizes of the data
		copy_from_user((char *)lenwr, (char *)arg, 2*sizeof(int));
		/* pour verification */
		D(printk("ioctl write-read cmd : lenw : %d, lenr : %d.\n", lenwr[0], lenwr[1])); 
		// limit the write-read sequence under 8192 bytes
		maxlen = lenwr[0] > lenwr[1] ? lenwr[0] : lenwr[1];
		if(maxlen > 8192)
			return -EINVAL;
		/* get kernel memory to store data */
		bufwr = kmalloc(maxlen, GFP_KERNEL);
		if(bufwr==NULL) 
			return -ENOMEM;
		/* get datas to write */
		copy_from_user(bufwr, (char *)(arg+2*sizeof(int)), lenwr[0]);
		//D(printk("datawr[0] : %x.\n", bufwr[0]));
		/* execute the write_read seq */
		status=i2c_write_read(*address, bufwr, lenwr[0], lenwr[1]);
		if(status > 0)
		{
			D(printk("i2c_ioctl bufwr[0]=%02x.\n",(unsigned char) bufwr[0]));
			copy_to_user((char *)(arg+2*sizeof(int)), bufwr, lenwr[1]);
		}
		kfree(bufwr);
                return status;	
	default:
           return -EINVAL;
   }
   return 0;
}

- Here is the new etraxi2c.h :

/* $Id: etraxi2c.h,v 1.1 2001/01/18 15:49:57 bjornw Exp $ */

#ifndef _LINUX_ETRAXI2C_H
#define _LINUX_ETRAXI2C_H

/* etraxi2c _IOC_TYPE, bits 8 to 15 in ioctl cmd */

#define ETRAXI2C_IOCTYPE 44

/* supported ioctl _IOC_NR's */

/* in write operations, the argument contains both i2c
 * slave, register and value.
 */

#define I2C_WRITEARG(slave, reg, value) (((slave) << 16) | ((reg) << 8) | (value))
#define I2C_READARG(slave, reg) (((slave) << 16) | ((reg) << 8))

#define I2C_ARGSLAVE(arg) ((arg) >> 16)
#define I2C_ARGREG(arg) (((arg) >> 8) & 0xff)
#define I2C_ARGVALUE(arg) ((arg) & 0xff)

#define I2C_WRITEREG 0x1   /* write to an i2c register */
#define I2C_READREG  0x2   /* read from an i2c register */
//~JB Added to support opening an i2c device for read and write
#define I2C_SLAVE    0x3   /* save the slave address given in ioctl */

//O.G added to support write/read sequence with ioctl 
/* supposed to receive in arg, the adress of an i2c_write_read structure like this :
** struct i2c_write_read { int lenw, int lenr, char data[MAX(lenr, lenw)] }
** has to be defined by the user application
*/
#define I2C_WRITE_READ 0x4	/* a write-read seq to/from an i2c chip */

/*
EXAMPLE usage:

    i2c_arg = I2C_WRITEARG(STA013_WRITE_ADDR, reg, val);
    ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_WRITEREG), i2c_arg);

    i2c_arg = I2C_READARG(STA013_READ_ADDR, reg);
    val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg);

*/
#endif

Indunet EtraxFlasher

EtraxFlasher is a simple Windows program that can be used to write a flash image to a fresh ETRAX LX processor using a serial connection.

For more information on the EtraxFlasher visit the EtraxFlasher page at Indunet.

DSP STA013 Interfacing

This article shows how to interface DSP STA013 with the synchronous serial port (/dev/syncser1) of the ETRAX100LX (devboard_lx).

  1. Linux kernel 2.4.31
  2. SDK 2.01

For more information go to the sta013 page.

PHYter DP83848 Interfacing

Since the ETRAX100LX is intended to be interfaced with many MII based Ethernet PHY, here is a schematic of the 10/100Base TX PHY ethernet chip interface, i.e. National Semi DP83848. PHYter DP83848 interfacing

DSP VS1002 basic Interfacing

How to interface ETRAX100LX /dev/syncserX with VS1002 DSP (NewMode Only).

  1. SDK 2.10 and 2.4 or 2.6 Linux kernel

for more infos see DSP VS1002 interfacing

Patch to utelnetd to support windows telnet client

This patch for utelnetd of SDK R2_01 fixes the problem of not being able to login using windows based telnet clients. Before the patch, the unit would interpret 'enter' as a double 'enter', giving the user no chance to input his/her password. I am no expert, so hopefully some more knowledgeable people can comment on this or offer a better solution.

http://www.nethop.net/~brian/wintelnet.diff

Simple DynDNS update script

#!/bin/sh
# DynDNS updater compatible with BusyBox v1.00
# requires: wget, ifconfig, sed, cut, date
 
#####################################
IFACE=eth1                      # Internet interface
DYNHOST=myhost.dyndns.org       # DynDNS hostname
DYNUSER=username                # Username
DYNPASS=password                # Password
LOGFILE=/var/log/dyndnsbb.log   # Path for logfile
######################################
 
IPDIN=`ifconfig $IFACE | sed -e '/inet/!d' -e 's/.*addr://' -e 's/[ ].*//'`
 
if [ "$1" = "-f" ]; then    # force ip update
  IPDINX="";
else
  IPDINX="$IPDIN";
fi
 
while [ "1" = "1" ]; do     # infinite loop
 
  IPVALID=`echo $IPDIN | cut -d "." -f 4`
  if [ "$IPVALID" -gt 0 ]; then
    if [ "$IPDIN" != "$IPDINX" ]; then
      DYNRES=`wget -O - "http://$DYNUSER:$DYNPASS@members.dyndns.org/nic/update?system=dyndns&hostname=$DYNHOST&myip=$IPDIN"`
      DYNDATE=`date -R`
      echo "$DYNDATE: New IP $IPDIN. DynDNS response: $DYNRES" >> $LOGFILE
      IPDINX="$IPDIN"
    fi
  fi
  sleep 120                 # check for IP changes every 120 seconds
  IPDIN=`ifconfig $IFACE | sed -e '/inet/!d' -e 's/.*addr://' -e 's/[ ].*//'`
 
done

Hotplug handler script

A script to handle hotplug events for attached/detached usb mass storage devices. The script keeps a list of all detected attached usb-storage devices with the following information: <device id> <device node> <mountpoint>

Execute the script as “mount_hotplug.sh” or “unmount_hotplug.sh” to mount/unmount a device from this list. The script can also be executed as “coldplug.sh” to detect already attached devices during boot-time which are not reported by hotplug.

Note: The script isn't perfect, especially the device node probing if more than one usb device is used! It has been tested with kernel 2.4.26 and usb-storage compiled as module.

- Installation:

echo $(pwd)/hotplug-handler.sh > /proc/sys/kernel/hotplug
ln -s ./hotplug-handler.sh unmount_hotplug.sh
ln -s ./hotplug-handler.sh mount_hotplug.sh
ln -s ./hotplug-handler.sh coldplug.sh

- Script:

#!/bin/sh
# Copyright 2006, Hannes Mayr, Indunet GmbH <www.indunet.it>
#
# Simple Hotplug Handler
#
# Install the hotplug handler:
#   hotplug-handler.sh > /proc/sys/kernel/hotplug
#
# Create the following links to the script to coldplug/mount/unmount:
#   coldplug.sh > hotplug-handler.sh
#   mount_hotplug.sh > hotplug-handler.sh
#   umount_hotplug.sh > hotplug-handler.sh
#
 
try_device_nodes="/dev/sda1 /dev/sdb1"
device_list=/tmp/usb-storage-devices
 
# set 1 to automatically mount a device when a hotplug "add" event arrives
automount=0
mountpoint=/mnt/flash/mnt_usbstick
 
 
############################################################################
 
check_if_mounted()
{
  checkfor=$1
  mount | grep $checkfor
  return $?
}
 
try_mount() {
  dev=$1
  mpoint=$2
  opts=$3
 
  logger "try_mount(): dev=$dev, mountpoint=$mpoint, options=$opts"
 
  # device node already mounted
  check_if_mounted $dev && [ $? -eq 0 ] && return 1
  # mountpoint already in use by something else
  check_if_mounted $mpoint && [ $? -eq 0 ] && return 1
 
  mount $dev $mpoint $opts
 
  return $?
}
 
product_to_scsi()
{
  prodid=$1
 
  vendor=$(echo $prodid | cut -d "/" -f1);
  prod=$(echo $prodid | cut -d "/" -f2);
 
  vendor_len=$(echo $vendor | wc -L)
  prod_len=$(echo $prod | wc -L)
 
  i=4
  while [ $i -gt $vendor_len ]; do
    vendor="0$vendor";
    let i=i-1
  done
  i=4
  while [ $i -gt $prod_len ]; do
    vendor="0$prod";
    let i=i-1
  done
 
  echo "$vendor$prod"
}
 
 
action_new_device() {
 
  guid=$1
 
  # probing for device node
  success=1
  mounted_device=
  mpoint=
 
  if [ $automount -eq 0 ]; then
    mpoint=/tmp/hotplug-testmount # temp. mountpoint for probing
    mkdir -p $mpoint
  else
    mpoint=$mountpoint
  fi
 
  for devnode in $try_device_nodes; do
    try_mount $devnode $mpoint "-t vfat -o sync,dirsync,noatime";
    if [ $? -eq 0 ]; then  # mount was successfull
      mounted_device=$devnode
      success=0;
      logger "$0: usb-storage device on $devnode detected."
 
      if [ $automount -eq 0 ]; then
        umount -f $mpoint
        mpoint=
      else
        logger "$0: Auto-mounted $devnode on $mountpoint"
      fi
      break
    fi
  done
 
  if [ $success -eq 0 ]; then
 
    # save usb device-id with device node and current mountpoint
    echo "$guid $mounted_device $mpoint" >> $device_list
 
  else
    logger "Could not find a mountable device node for the attached usb device"
  fi
 
  return $success
 
}
 
action_remove_device()
{
  guid=$1
 
  # search for the device in our device-list
  listentry=$(grep $guid $device_list)
  [ $? -ne 0 ] && return 1 # not in list, we didn't mount this device
 
  dev=$(echo $listentry | cut -d " " -f2)
  check_if_mounted $dev
 
  if [ $? -eq 0 ]; then
   mpoint=$(echo $listentry | cut -d " " -f3)
   logger "$0: Unmounting detached device $dev on $mpoint"
    umount -f $mpoint
    [ $? -ne 0 ] && logger "$0: WARNING: Error unmount device"
  fi
 
  # remove list entry
  grep -v $guid $device_list > $device_list
 
  return 0
}
 
action_mount() {
 
  item=$1
  mpoint=$2
 
  if [ "$1" = "first" ]; then
    # get first usb-storage device avail
    listentry=$(head -1 $device_list);
  else
    # look for device id
    listentry=$(grep $item $device_list)
  fi
  [ $? -ne 0 ] && echo "No device for item $item found" && return 1
 
  devid=$(echo $listentry | cut -d " " -f1)
  dev=$(echo $listentry | cut -d " " -f2)
 
  try_mount $dev $2 "-t vfat -o sync,dirsync,noatime";
 
  if [ $? -ne 0 ]; then
    echo "Unable to mount $dev on $mpoint"
    return 2
  fi
 
  # write mountpoint to device list
  grep -v $item $device_list > $device_list
  echo "$devid $dev $mpoint" >> $device_list
 
  logger "$0: $dev mounted on $mpoint"
  return 0
}
 
action_unmount() {
 
  item=$1
 
  # search for the device in our device-list
  listentry=$(grep $item $device_list)
  [ $? -ne 0 ] && return 1 # not in list, we didn't mount this device
 
  mpoint=$(echo $listentry | cut -d " " -f3)
  logger "$0: Unmounting mountpoint $mpoint"
  umount -f $mpoint
 
  return 0
}
 
#### no hotplug events
 
# Mount/Unmount a device which was detected by hotplug/coldplug
if [ "$(basename $0)" = "mount_hotplug.sh" ]; then
  if [ -z "$1" ]; then
    echo "Usage: $0 <device-id|device-node|first> <mountpoint>"
    echo "  Use absolute paths as mountpoint!"
    echo "  'first' mounts the first available usb device"
    exit 1
  fi
 
  action_mount $1 $2
  exit $?
fi
 
if [ "$(basename $0)" = "umount_hotplug.sh" ]; then
  [ -z "$1" ] && echo "Usage: $0 <device-id|device-node|mountpoint>" && exit 1
 
  action_unmount $1
 
  exit $?
fi
 
# Coldplug
if [ "$(basename $0)" = "coldplug.sh" ]; then
 
  insmod /lib/modules/$(uname -r)/kernel/drivers/usb/storage/usb-storage.o
 
  sleep 1
 
  if [ -e /proc/scsi/usb-storage-* ]; then
    # loop through /proc and get all available usb-storage devices
    for file in $(find /proc/scsi/usb-storage-*/*); do
 
      attached=$(cat $file | grep "Attached" | cut -d ":" -f2);
      if [ "$attached" = " Yes" ]; then
        guid=$(cat $file | grep "GUID:" | cut -d ":" -f2 | cut -d " " -f2)
 
        # if device is not in our list yet, add it
        grep $guid $device_list > /dev/null 2>&1
        [ $? -ne 0 ] && action_new_device $guid
      fi
 
    done
  fi
 
  # remove module if no devices were found
  num_devices=$(cat $device_list | wc -l)
  if [ $num_devices -eq 0 ]; then
    rmmod usb-storage
  fi
 
  exit 0
fi
 
#### hotplug events
 
 
# we only handle usb-storage events
if [ "$1" != "usb" -o "$INTERFACE" != "8/6/80" ]; then
  logger "$0: No USB event or unsupported interface ($INTERFACE)"
  exit 0
fi
 
logger "the hotplug handler is in action:"
logger "  parameters: $@"
env_vars=$(env)
logger "  env: $env_vars"
 
if [ "$ACTION" = "add" ]; then
  # load module if not already loaded
  insmod /lib/modules/$(uname -r)/kernel/drivers/usb/storage/usb-storage.o
 
  sleep 1 # wait a second to be sure the partition table has been read
  action_new_device $(product_to_scsi $PRODUCT)
else
  action_remove_device $(product_to_scsi $PRODUCT)
 
  # if there are no more usb-storage devices attached unload the module
  num_devices=$(cat $device_list | wc -l)
  if [ $num_devices -eq 0 ]; then
    rmmod usb-storage
  fi
fi
 
exit 0

Ruby Scripting Library

A library for controlling network cameras using the Ruby language is now available at http://rubyforge.org/projects/axis-netcam/.

Here's an example of how to connect to a camera, point at some target, and take a JPEG snapshot:

  require 'axis-netcam'
 
  c = AxisNetcam::Camera.new(:hostname => '192.168.2.25',
        :username => 'root', :password => 'pass')
  c.tilt(90)
  c.zoom(500)
 
  f = File.open('/tmp/test.jpg', 'wb')
  f.bin
  f.write(c.snapshot_jpeg)
  f.close

Axis Profiler

mark richards 2007/08/31 14:30z : Running the Axis profiler.

The following pertains to the Axis R2_10 release using the linux 2.6 kernel.

The Axis profiler is not gprof. Try not to confuse the two as I did. My adventure included trying to figure out why my code would not compile with the -pg switch. No compile/link changes are required to use it. Instead, you must enable “System Profiling Support” in the kernel.

  cd os/linux-2.6
  make menuconfig

Select the option “kernel hacking” and enable “System Profiling Support”. Danger: I found that enabling Kernel Profiling Support will yield a kernel that will not build.

After building the kernel and re-building your image, flash the target system.

The profiler, called axis_profile, needs the following services running on your target:

	http server running on port 80
	ftp server running on port 21
	telnet server running on port 23
	AXIS_KERNEL_DIR set to os/linux_2.6
	AXIS_TOP_DIR set properly

Adding to the overall confusion was the fact that on my target none of these services were running (intentional) and if they were enabled the ports are non-standard. Don't let this happen to you.

The user can run the profiler in tools/build/profiler as:

	./axis_profile 192.168.1.100

Substitute the ip address for your target. There are several options available and they can be seen by executing the profiler with no parameters.

After some period of time press CTRL-C to stop the profiler. You will see output that looks similar to this:

Total sample time: 9.49 seconds

Application kernel(pid 0) 100% (9.49 seconds)

     3.14s  33.09%  etrax_gpio_wake_up_check
     2.99s  31.51%  handle_IRQ_event
     2.53s  26.66%  tc_dma_tx_interrupt
     0.68s   7.17%  cpu_idle
     0.04s   0.42%  schedule
     0.02s   0.21%  do_page_fault
     0.02s   0.21%  __queue_work
     0.02s   0.21%  ser_interrupt
     0.01s   0.11%  __posix_lock_file_conf
     0.01s   0.11%  memset
     0.01s   0.11%  memcpy
     0.01s   0.11%  __copy_user
     0.01s   0.11%  __Umod

Lost 0 kernel samples and 32 user mode samples

Clearly the userspace samples are not working. When I fix this little problem, I will update the wiki page.

 
contributions.txt · Last modified: 2007/08/31 17:23 by k1mgy
 
All text is available under the terms of the GNU Free Documentation License (see Copyrights for details).