Skip to content

Commit

Permalink
Updates to I2C driver to make it more Linux userspace compatible.
Browse files Browse the repository at this point in the history
  • Loading branch information
bakerstu committed Jan 15, 2015
1 parent 8d2d53a commit d7a343f
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 139 deletions.
17 changes: 15 additions & 2 deletions include/i2c-dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#if defined (__linux__)
#include <linux/i2c-dev.h>
#elif defined (__FreeRTOS__)
#include <stdint.h>
/** magic number for this driver's ioctl calls */
#define I2C_MAGIC ('i')

Expand All @@ -46,7 +47,19 @@
#define I2C_SLAVE IOW(I2C_MAGIC, 1, sizeof(long))

/** Combined R/W transfer, one stop only. */
#define I2C_RDWR IOW(I2C_MAGIC, 2, sizeof(void*))
#define I2C_RDWR IOWR(I2C_MAGIC, 2, sizeof(void*))

/** Combined R/W transfer, one stop only. */
#define I2C_SMBUS IOWR(I2C_MAGIC, 3, sizeof(void*))

/** This is the structure as used in the I2C_SMBUS ioctl call */
struct i2c_smbus_ioctl_data
{
uint8_t read_write;
uint8_t command;
uint32_t size;
union i2c_smbus_data *data;
};

/** This is the structure as used in the I2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data
Expand All @@ -56,7 +69,7 @@
};

/** maximum number of message segments in @ref i2c_rdwr_ioctl_data struct */
#define I2C_RDRW_IOCTL_MAX_MSGS 2
#define I2C_RDRW_IOCTL_MAX_MSGS 42
#else
#error I2C drivers not supported on this OS
#endif
Expand Down
27 changes: 27 additions & 0 deletions include/i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#if defined (__linux__)
#include <linux/i2c.h>
#elif defined (__FreeRTOS__)
#include <stdint.h>
/** Used in @ref i2c_rdwr_ioctl_data to describe a transaction segment. */
struct i2c_msg
{
Expand All @@ -46,6 +47,32 @@
uint16_t len; /**< msg length */
uint8_t *buf; /**< pointer to msg data */
};

#define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */

/** Data for SMBus Messages */
union i2c_smbus_data {
uint8_t byte;
uint16_t word;
uint8_t block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
/* and one more for user-space compatibility */
};

/* i2c_smbus_xfer read or write markers */
#define I2C_SMBUS_READ 1
#define I2C_SMBUS_WRITE 0

/* SMBus transaction types (size parameter in the above functions)
Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */
#define I2C_SMBUS_QUICK 0
#define I2C_SMBUS_BYTE 1
#define I2C_SMBUS_BYTE_DATA 2
#define I2C_SMBUS_WORD_DATA 3
#define I2C_SMBUS_PROC_CALL 4
#define I2C_SMBUS_BLOCK_DATA 5
#define I2C_SMBUS_I2C_BLOCK_BROKEN 6
#define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */
#define I2C_SMBUS_I2C_BLOCK_DATA 8
#else
#error I2C drivers not supported on this OS
#endif
Expand Down
109 changes: 91 additions & 18 deletions src/freertos_drivers/common/I2C.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -40,45 +40,53 @@
* @param file file reference for this device
* @param buf location to place read data
* @param count number of bytes to read
* @return number of bytes read upon success, -1 upon failure with errno containing the cause
* @return number of bytes read upon success, -errno upon failure
*/
ssize_t I2C::read(File *file, void *buf, size_t count)
{
unsigned char *data = (unsigned char*)buf;
bool result;
int result;

struct i2c_msg msg;
msg.addr = (uintptr_t)file->priv;
msg.flags = I2C_M_RD;
msg.len = count;
msg.buf = (uint8_t*)buf;

Transaction t = {data, count, FLAG_START | FLAG_STOP | FLAG_RD};
lock_.lock();
result = transfer(&t);
result = transfer(&msg, true);
lock_.unlock();

return result == 0 ? count : -1;
return result;
}

/** Write to a file or device.
* @param file file reference for this device
* @param buf location to find write data
* @param count number of bytes to write
* @return number of bytes written upon success, -1 upon failure with errno containing the cause
* @return number of bytes written upon success, -errno upon failure
*/
ssize_t I2C::write(File *file, const void *buf, size_t count)
{
const unsigned char *data = (const unsigned char*)buf;
bool result;
int result;

struct i2c_msg msg;
msg.addr = (uintptr_t)file->priv;
msg.flags = 0;
msg.len = count;
msg.buf = (uint8_t*)buf;

Transaction t = {data, count, FLAG_START | FLAG_STOP};
lock_.lock();
result = transfer(&t);
result = transfer(&msg, true);
lock_.unlock();

return result == 0 ? count : -1;
return result;
}

/** Request an ioctl transaction
* @param file file reference for this device
* @param node node reference for this device
* @param key ioctl key
* @param data key data
* @return 0 upon success, -errno upon failure
*/
int I2C::ioctl(File *file, unsigned long int key, unsigned long data)
{
Expand All @@ -87,15 +95,80 @@ int I2C::ioctl(File *file, unsigned long int key, unsigned long data)
switch (key)
{
default:
errno = EINVAL;
return -1;
return -EINVAL;
case I2C_SLAVE:
address = data;
file->priv = (void*)data;
break;
case I2C_RDWR:
/* not yet implemented */
HASSERT(0 && "I2C_RDWR not yet implemented");
{
struct i2c_rdwr_ioctl_data *rdwr_data = (struct i2c_rdwr_ioctl_data*)data;
return transfer_messages(rdwr_data->msgs, rdwr_data->nmsgs);
}
case I2C_SMBUS:
return smbus(file, data);
}
return 0;
}

/** Conduct multiple message transfers with one stop at the end.
* @param msgs array of messages to transfer
* @param num number of messages to transfer
* @return total number of bytes transfered, -errno upon failure
*/
int I2C::transfer_messages(struct i2c_msg *msgs, int num)
{
HASSERT(num > 0);

int count = 0;
int result;

lock_.lock();
for (int i = 0; i < num; ++i)
{
count += msgs[i].len;
result = transfer(msgs + i, (num == (i + 1)));
if (result < 0)
{
lock_.unlock();
return result;
}
}
lock_.unlock();

return count;
}

/** Request an smbus (ioctl) transaction.
* @param file file reference for this device
* @param data smbus data
* @return 0 upon success, -errno upon failure
*/
int I2C::smbus(File *file, unsigned long data)
{
struct i2c_smbus_ioctl_data *sm_data = (struct i2c_smbus_ioctl_data *)data;

/* check that we have a valid transaction type */
if ((sm_data->size != I2C_SMBUS_QUICK) &&
(sm_data->size != I2C_SMBUS_BYTE) &&
(sm_data->size != I2C_SMBUS_BYTE_DATA) &&
(sm_data->size != I2C_SMBUS_WORD_DATA) &&
(sm_data->size != I2C_SMBUS_PROC_CALL) &&
(sm_data->size != I2C_SMBUS_BLOCK_DATA) &&
(sm_data->size != I2C_SMBUS_I2C_BLOCK_BROKEN) &&
(sm_data->size != I2C_SMBUS_BLOCK_PROC_CALL) &&
(sm_data->size != I2C_SMBUS_I2C_BLOCK_DATA))
{
return -EINVAL;
}

/* check that we have a read or write */
if ((sm_data->read_write != I2C_SMBUS_READ) &&
(sm_data->read_write != I2C_SMBUS_WRITE))
{
return -EINVAL;
}

/// @todo need to finish implementation (Stuart Baker)
return 0;
}

53 changes: 21 additions & 32 deletions src/freertos_drivers/common/I2C.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -46,34 +46,11 @@
class I2C : public Node
{
protected:
/** Flags used to determine the kind of I2C transaction.
*/
enum Flags
{
FLAG_START = 0x01,
FLAG_REPEAT_START = 0x02,
FLAG_STOP = 0x04,
FLAG_RD = 0x08
};

/** Metadata for an I2C transaction */
struct Transaction
{
union
{
const unsigned char *tx_data;
unsigned char *rx_data;
};
size_t count;
int flags;
};

/** Constructor
* @param name device name in file system
*/
I2C(const char *name)
: Node(name)
, address(0)
{
}

Expand All @@ -85,39 +62,51 @@ protected:
}

/** Method to transmit/receive the data.
* @param t transaction to take place
* @return 0 upon success or -1 with errno set
* @param msg message to transact.
* @param stop produce a stop condition at the end of the transfer
* @return bytes transfered upon success or -1 with errno set
*/
virtual int transfer(Transaction *t) = 0;

/** Address of target I2C slave */
long address;
virtual int transfer(struct i2c_msg *msg, bool stop) = 0;

private:
/** Read from a file or device.
* @param file file reference for this device
* @param buf location to place read data
* @param count number of bytes to read
* @return number of bytes read upon success, -1 upon failure with errno containing the cause
* @return number of bytes read upon success, -errno upon failure
*/
ssize_t read(File *file, void *buf, size_t count) OVERRIDE;

/** Write to a file or device.
* @param file file reference for this device
* @param buf location to find write data
* @param count number of bytes to write
* @return number of bytes written upon success, -1 upon failure with errno containing the cause
* @return number of bytes written upon success, -errno upon failure
*/
ssize_t write(File *file, const void *buf, size_t count) OVERRIDE;

/** Request an ioctl transaction.
* @param file file reference for this device
* @param node node reference for this device
* @param key ioctl key
* @param data key data
* @return >= 0 upon success, -errno upon failure
*/
int ioctl(File *file, unsigned long int key, unsigned long data) OVERRIDE;

/** Conduct multiple message transfers with one stop at the end.
* @param msgs array of messages to transfer
* @param num number of messages to transfer
* @return total number of bytes transfered, -errno upon failure
*/
int transfer_messages(struct i2c_msg *msgs, int num);

/** Request an smbus (ioctl) transaction.
* @param file file reference for this device
* @param data smbus data
* @return 0 upon success, -errno upon failure
*/
int smbus(File *file, unsigned long data);

/** Discards all pending buffers. Called after disable(). */
void flush_buffers() OVERRIDE {}

Expand Down
11 changes: 7 additions & 4 deletions src/freertos_drivers/ti/TivaDev.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,17 @@ private:
void disable() OVERRIDE {}; /**< function to disable device */

/** Method to transmit/receive the data.
* @param t transaction to take place
* @return 0 upon success or -1 with errno set
* @param msg message to transact.
* @param stop produce a stop condition at the end of the transfer
* @return bytes transfered upon success or -1 with errno set
*/
int transfer(Transaction *t) OVERRIDE;
int transfer(struct i2c_msg *msg, bool stop) OVERRIDE;

unsigned long base; /**< base address of this device */
unsigned long interrupt; /**< interrupt of this device */
Transaction *trans;
struct i2c_msg *msg_; /**< message for current transaction */
bool stop_; /**< current transaction ends in a stop if true */
int count_; /**< current count index within transaction */

/** Semaphore to wakeup task level from ISR */
OSSem sem;
Expand Down
Loading

0 comments on commit d7a343f

Please sign in to comment.