I need to write an SPI Linux character device driver for omap4 from scratch.
I know some basics of writing device drivers. But, I don't know how to start writing platform specific device driver from scratch.
I've written some basic char drivers, and I thought writing SPI device driver would be similar to it. Char drivers have a structure file_operations which contains the functions implemented in the driver.
struct file_operations Fops = {
.read = device_read,
.write = device_write,
.ioctl = device_ioctl,
.open = device_open,
.release = device_release, /* a.k.a. close */
};
Now, I am going through spi-omap2-mcspi.c code as a reference to get an idea to start developing SPI driver from scratch.
But, I don't see functions such as open, read, write etc.
Don't know from where the program starts.
First, start by writing a generic kernel module. There are multiple places to look up for information but I found this link to be very useful. After you have gone through all examples specified there you can start writing your own Linux Driver Module.
Please note, that you will not get away with just copy-pasting the example code and hope it will work, no. Kernel API can sometimes change and examples will not work. Examples provided there should be looked at as a guide on how to do something. Depending on the kernel version you are using you have to modify the example in order to work.
Consider using TI platform-provided functions as much as you can, because that can really do a lot of work for you, like requesting and enabling needed clocks, buses, and power supplies. If I recall correctly you can use the functions to acquire memory-mapped address ranges for direct access to registers. I have to mention that I have a bad experience with TI-provided functions because they do not properly release/clean up all acquired resources, so for some resources, I had to call other kernel services to release them during module unload.
Edit 1:
I'm not entirely familiar with Linux SPI implementation but I would start by looking at omap2_mcspi_probe() function in drivers/spi/spi-omap2-mcspi.c file. As you can see there, it registers it's methods to Linux master SPI driver using this API: Linux/include/linux/spi/spi.h. In contrast to char driver, the main functions here are *_transfer() functions. Look up the struct descriptions in spi.h file for further details. Also, have a look at this alternative device driver API, too.
I assume your OMAP4 linux uses one of arch/arm/boot/dts/{omap4.dtsi,am33xx.dtsi} device-tree, thus it compiles drivers/spi/spi-omap2-mcspi.c (if you don't know about device-tree, read this). Then:
the SPI master driver is done,
it (most probably) registers with Linux SPI core framework drivers/spi/spi.c,
it (probably) works fine on your OMAP4.
You actually don't need to care about the master driver to write your slave device driver. How do I know spi-omap2-mcspi.c is a master driver? It calls spi_register_master().
SPI master, SPI slave ?
Please refer to Documentation/spi/spi_summary. The doc refers to Controller driver (master) and Protocol drivers (slave). From your description, I understand you want to write a Protocol/Device driver.
SPI protocol ?
To understand that, you need your slave device datasheet, it shall tell you:
the SPI mode understood by your device,
the protocol it expects on the bus.
Contrary to i2c, SPI does not define a protocol or handshake, SPI chips manufacturers have to define their own. So check the datasheet.
SPI mode
From include/linux/spi/spi.h:
* #mode: The spi mode defines how data is clocked out and in.
* This may be changed by the device's driver.
* The "active low" default for chipselect mode can be overridden
* (by specifying SPI_CS_HIGH) as can the "MSB first" default for
* each word in a transfer (by specifying SPI_LSB_FIRST).
Again, check your SPI device datasheet.
An example SPI device driver?
To give you a relevant example, I need to know your SPI device type. You would understand that a SPI flash device driver is different from a SPI FPGA device driver. Unfortunately there are not so many SPI device drivers out there. To find them:
$ cd linux
$ git grep "spi_new_device\|spi_add_device"
I don't know if I understood your question correctly. As m-ric pointed out, there are master drivers and slave drivers.
Usually master drivers are more hardware bound, I mean, they usually manipulate IO registers or do some memory mapped IO.
For some architectures already supported by linux kernel (like omap3 and omap4) master drivers are already implemented (McSPI).
So I assume you want to USE those SPI facilities of omap4 to implement a slave device driver (your protocol, to communicate with your external device through SPI).
I've written the following example for BeagleBoard-xM (omap3). The full code is at https://github.com/rslemos/itrigue/blob/master/alsadriver/itrigue.c (worth a view, but have more initialisation code, for ALSA, GPIO, module parameters). I've tried to set apart code that deals with SPI (maybe I forgot something, but anyway you should get the idea):
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/spi/spi.h>
/* MODULE PARAMETERS */
static uint spi_bus = 4;
static uint spi_cs = 0;
static uint spi_speed_hz = 1500000;
static uint spi_bits_per_word = 16;
/* THIS IS WHERE YOUR DEVICE IS CREATED; THROUGH THIS YOU INTERACT WITH YOUR EXTERNAL DEVICE */
static struct spi_device *spi_device;
/* SETUP SPI */
static inline __init int spi_init(void) {
struct spi_board_info spi_device_info = {
.modalias = "module name",
.max_speed_hz = spi_speed_hz,
.bus_num = spi_bus,
.chip_select = spi_cs,
.mode = 0,
};
struct spi_master *master;
int ret;
// get the master device, given SPI the bus number
master = spi_busnum_to_master( spi_device_info.bus_num );
if( !master )
return -ENODEV;
// create a new slave device, given the master and device info
spi_device = spi_new_device( master, &spi_device_info );
if( !spi_device )
return -ENODEV;
spi_device->bits_per_word = spi_bits_per_word;
ret = spi_setup( spi_device );
if( ret )
spi_unregister_device( spi_device );
return ret;
}
static inline void spi_exit(void) {
spi_unregister_device( spi_device );
}
To write data to your device:
spi_write( spi_device, &write_data, sizeof write_data );
The above code is independent of implementation, that is, it could use McSPI, bit-banged GPIO, or any other implementation of an SPI master device. This interface is described in linux/spi/spi.h
To make it work in BeagleBoard-XM I had to add the following to the kernel command line:
omap_mux=mcbsp1_clkr.mcspi4_clk=0x0000,mcbsp1_dx.mcspi4_simo=0x0000,mcbsp1_dr.mcspi4_somi=0x0118,mcbsp1_fsx.mcspi4_cs0=0x0000
So that an McSPI master device is created for omap3 McSPI4 hardware facility.
Hope that helps.
file_operations minimal runnable example
This example does not interact with any hardware, but it illustrates the simpler file_operations kernel API with debugfs.
Kernel module fops.c:
#include <asm/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/debugfs.h>
#include <linux/errno.h> /* EFAULT */
#include <linux/fs.h> /* file_operations */
#include <linux/kernel.h> /* min */
#include <linux/module.h>
#include <linux/printk.h> /* printk */
#include <uapi/linux/stat.h> /* S_IRUSR */
static struct dentry *debugfs_file;
static char data[] = {'a', 'b', 'c', 'd'};
static int open(struct inode *inode, struct file *filp)
{
pr_info("open\n");
return 0;
}
/* #param[in,out] off: gives the initial position into the buffer.
* We must increment this by the ammount of bytes read.
* Then when userland reads the same file descriptor again,
* we start from that point instead.
* */
static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
ssize_t ret;
pr_info("read\n");
pr_info("len = %zu\n", len);
pr_info("off = %lld\n", (long long)*off);
if (sizeof(data) <= *off) {
ret = 0;
} else {
ret = min(len, sizeof(data) - (size_t)*off);
if (copy_to_user(buf, data + *off, ret)) {
ret = -EFAULT;
} else {
*off += ret;
}
}
pr_info("buf = %.*s\n", (int)len, buf);
pr_info("ret = %lld\n", (long long)ret);
return ret;
}
/* Similar to read, but with one notable difference:
* we must return ENOSPC if the user tries to write more
* than the size of our buffer. Otherwise, Bash > just
* keeps trying to write to it infinitely. */
static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
ssize_t ret;
pr_info("write\n");
pr_info("len = %zu\n", len);
pr_info("off = %lld\n", (long long)*off);
if (sizeof(data) <= *off) {
ret = 0;
} else {
if (sizeof(data) - (size_t)*off < len) {
ret = -ENOSPC;
} else {
if (copy_from_user(data + *off, buf, len)) {
ret = -EFAULT;
} else {
ret = len;
pr_info("buf = %.*s\n", (int)len, data + *off);
*off += ret;
}
}
}
pr_info("ret = %lld\n", (long long)ret);
return ret;
}
/*
Called on the last close:
http://stackoverflow.com/questions/11393674/why-is-the-close-function-is-called-release-in-struct-file-operations-in-the-l
*/
static int release(struct inode *inode, struct file *filp)
{
pr_info("release\n");
return 0;
}
static loff_t llseek(struct file *filp, loff_t off, int whence)
{
loff_t newpos;
pr_info("llseek\n");
pr_info("off = %lld\n", (long long)off);
pr_info("whence = %lld\n", (long long)whence);
switch(whence) {
case SEEK_SET:
newpos = off;
break;
case SEEK_CUR:
newpos = filp->f_pos + off;
break;
case SEEK_END:
newpos = sizeof(data) + off;
break;
default:
return -EINVAL;
}
if (newpos < 0) return -EINVAL;
filp->f_pos = newpos;
pr_info("newpos = %lld\n", (long long)newpos);
return newpos;
}
static const struct file_operations fops = {
/* Prevents rmmod while fops are running.
* Try removing this for poll, which waits a lot. */
.owner = THIS_MODULE,
.llseek = llseek,
.open = open,
.read = read,
.release = release,
.write = write,
};
static int myinit(void)
{
debugfs_file = debugfs_create_file("lkmc_fops", S_IRUSR | S_IWUSR, NULL, NULL, &fops);
return 0;
}
static void myexit(void)
{
debugfs_remove_recursive(debugfs_file);
}
module_init(myinit)
module_exit(myexit)
MODULE_LICENSE("GPL");
Userland shell test program:
#!/bin/sh
mount -t debugfs none /sys/kernel/debug
insmod /fops.ko
cd /sys/kernel/debug/lkmc_fops
## Basic read.
cat f
# => abcd
# dmesg => open
# dmesg => read
# dmesg => len = [0-9]+
# dmesg => close
## Basic write
printf '01' >f
# dmesg => open
# dmesg => write
# dmesg => len = 1
# dmesg => buf = a
# dmesg => close
cat f
# => 01cd
# dmesg => open
# dmesg => read
# dmesg => len = [0-9]+
# dmesg => close
## ENOSPC
printf '1234' >f
printf '12345' >f
echo "$?"
# => 8
cat f
# => 1234
## seek
printf '1234' >f
printf 'z' | dd bs=1 of=f seek=2
cat f
# => 12z4
You should also write a C program that runs those tests if it is not clear to you what system calls are being called for each of those commands. (or you could also use strace and find out :-)).
The other file_operations are a bit more involved, here are some further examples:
ioctl
poll
mmap
Start with software models of simplified hardware in emulators
Actual device hardware development is "hard" because:
you can't always get your hand on a given hardware easily
hardware APIs may be complicated
it is hard to see what is the internal state of the hardware
Emulators like QEMU allow us to overcome all those difficulties, by simulating simplified hardware simulation in software.
QEMU for example, has a built-in educational PCI device called edu, which I explained further at: How to add a new device in QEMU source code? and is a good way to get started with device drivers. I've made a simple driver for it available here.
You can then put printf's or use GDB on QEMU just as for any other program, and see exactly what is going on.
There is also an OPAM SPI model for you specific use case: https://github.com/qemu/qemu/blob/v2.7.0/hw/ssi/omap_spi.c
Related
So I'm working with qemu kvm for a while and now I need to passthrough PCI devices. I did all required procedures to make this work: enabled iommu, modprobed vfio module, binded device to vfio and checked that vfio group was indeed created, etc...
But when I start qemu with any pci devices I get the error message:
vfio: Failed to read device config space
I dig into qemu's code to see what the issue might be and found out that the issue occurs on a pread to the device. This happens even when the offset is 0, and doing a normal read on the file descriptor works without problems, as I changed the code to test it.
Checking errno for the reason of pread failure gives me an 'Illegal seek' error message.
I wrote some code to see if this was happening outside of the qemu context(thought it might be something in qemu's code that was interfering with the device), and had the same issue. I also tried to read a normal file with pread and that works perfectly... Here is the code I wrote to test it, I broke it down a bit to be able to point out the more relevant parts:
#define BUF_SIZE 4096
int main(){
char buf[BUF_SIZE], buf1[BUF_SIZE], buf2[BUF_SIZE];
int ret,group_fd, fd, fd2;
size_t nbytes = 4096;
ssize_t bytes_read;
int iommu1, iommu2;
int container, group, device, i;
struct vfio_group_status group_status = { .argsz = sizeof(group_status) };
struct vfio_iommu_type1_info iommu_info = { .argsz = sizeof(iommu_info) };
struct vfio_iommu_type1_dma_map dma_map = { .argsz = sizeof(dma_map) };
struct vfio_device_info device_info = { .argsz = sizeof(device_info) };
container = open("/dev/vfio/vfio",O_RDWR);
if(ioctl(container,VFIO_GET_API_VERSION)!=VFIO_API_VERSION){
printf("Unknown api version: %m\n");
}
group_fd = open("/dev/vfio/22",O_RDWR); printf("Group fd = %d\n", group_fd);
ioctl(group_fd, VFIO_GROUP_GET_STATUS, &group_status);
if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)){
printf("Group not viable\n");
return 1;
}
ret = ioctl(group_fd, VFIO_GROUP_SET_CONTAINER,&container);
ret = ioctl(container,VFIO_SET_IOMMU,VFIO_TYPE1_IOMMU);
ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info);
/* Allocate some space and setup a DMA mapping */
dma_map.vaddr = (unsigned long int) mmap(0, 1024 * 1024, PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
dma_map.size = 1024 * 1024;
dma_map.iova = 0; /* 1MB starting at 0x0 from device view */
dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;
ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);
printf("\n\nGETTING DEVICE FD\n");
fd = ioctl(group_fd,VFIO_GROUP_GET_DEVICE_FD,"0000:08:00.0");
printf("Fd = %d\n",fd);
printf("VFIO_GROUP_GET_DEV_ID = %lu\n",VFIO_GROUP_GET_DEVICE_FD);
This read works fine, gives me a ret code of nbytes
ret = read(fd,buf,nbytes);
if(ret<1){
printf("ERROR: %m \n");
}
This pread fails with ret code -1 and errno 'Illegal seek'
ret = pread(fd,buf,nbytes,0);
if(ret<0){
printf("ERROR: %m \n");
}
Here I try read and pread on a common file in sysfs to see if pread fails, and on both read and pread work just fine in this case:
printf("TESTING PREAD ON A COMMON FILE\n");
fd2 = open("/sys/bus/pci/devices/0000:08:00.0/device",O_RDONLY);
ret = read(fd2,buf1,nbytes);
if(ret<0){
printf("ERROR: %m\n");
}
printf("Result from read: ret = %d, content = %s\n",ret,buf1);
ret = pread(fd2,buf2,nbytes,2);
if(ret<0){
printf("ERROR: %m\n"); #
}
printf("Result from pread: ret = %d, content = %s\n",ret,buf2);
close(fd2);
getchar();
close(fd);
close(container);
close(group_fd);
return 0;
}
I'm using a generic linux kernel v4.7.8 compiled with uClibc for an embedded system.... Anyone have any ideas of why this might be happening? I'm clueless right now!! T.T
UPDATE:
I installed ubuntu 16.04 (kernel v4.4.0) on the same machine and repeated the steps and pci passthrough works fine and the pread on my test code also works perfectly. So I'm not sure what is going wrong with the custom generic kernel.
As per arash suggestion, I tried pread(fd,buf,nbytes,SEEK_CUR) and it gave me the same 'illegal seek' error. The offset I get from ftell is 0xffffffff both in ubuntu and in the generic kernel.
I found what was the issue and have been meaning to post it here for a while for anyone who might hit this wall. It turns out the pread and pwrite functions of the uClibc version 0.9.33 are broken, resulting in those functions failing to work on offsets bigger than 4G. The patches from the link below fixed the problem for me:
http://uclibc.10924.n7.nabble.com/backport-pread-pwrite-fix-for-0-9-33-branch-td11921.html
I am planning on building a Linux kernel module which will need to interface with a user-space device driver, and I will need to export data to user-space. After some reading I figured that the UIO interface might be what I need.
I looked at some examples and they are all based on the assumption that the kernel module itself will interact directly with hardware, and reference things like a device structure, interrupts, etc.
Is it possible to write a software only kernel module and still use the UIO library? Or would just using sysfs directly be a better approach?
EDIT: I am attaching some test code I was working on. The goal was to try and read a string from user-space through the UIO interface, but I don't think this will work since I cannot see how to properly initiate a struct device which I think is required for uio_register_device.
#include <linux/module.h> // Needed by all modules
#include <linux/kernel.h> // Needed for KERN_ALERT
#include <linux/uio_driver.h>
#include <linux/slab.h> // GFP_ defs
#include <linux/device.h>
char test_data[] = "This is some test data to read in user-space via UIO\n";
int init_module(void)
{
struct uio_info *info;
struct device *dev;
info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
// need to use struct device for uio_register_device
dev = kzalloc(sizeof(struct device), GFP_KERNEL);
dev->parent = 0;
dev->init_name = "UIO test driver";
info->name = "uio_test";
info->version = "0.0.1";
info->mem[0].size = sizeof(test_data);
info->mem[0].memtype = UIO_MEM_LOGICAL;
info->mem[0].addr = (phys_addr_t) kmalloc(sizeof(test_data), GFP_KERNEL);
snprintf((char *) info->mem[0].addr, sizeof(test_data), "%s", test_data);
info->irq = UIO_IRQ_NONE;
// now we need to register the device for it to create /dev/uioN and sysfs files
if (uio_register_device(dev, info)) {
printk(KERN_ALERT "uio_test: couldn't register UIO device\n");
kfree(dev);
kfree((char *) info->mem[0].addr);
kfree(info);
return -ENODEV;
}
printk(KERN_ALERT "uio_test: init complete\n");
return 0;
}
void cleanup_module(void)
{
printk(KERN_ALERT "uio_test: exit\n");
}
MODULE_LICENSE("GPL");
The whole point behind the kernel driver is to talk to hardware. If you don't have any hardware, then you probably don't need a kernel driver at all.
What is kernel module doing, if it isn't talking to hardware? Where is getting its data from? To answer your question, it is totally possible to write a kernel driver that doesn't actually talk to hardware and still talks to UIO, but I'm not sure what it would actually say.
I have developed a simple line discipline using v4.0.5 of the Linux Kernel, running under Mint Linux.
The tty_ldisc_ops structure looks as follows:
static struct tty_ldisc_ops my_ldisc = {
.owner = THIS_MODULE,
.magic = TTY_LDISC_MAGIC,
.name = "my_ldisc",
.open = my_open,
.close = my_close,
.read = my_read,
.write = my_write,
.ioctl = my_ioctl,
.poll = my_poll,
.receive_buf = my_receive,
.write_wakeup = my_wakeup,
};
The module gets added via insmod my_lkm.ko. I know it's getting added correct as I've used printk to indicate it and can see the message via dmesg. Also, at startup, my userspace application uses ioctl and I have also verified that works via printk.
The problem is, in my_write, copy_from_user always returns a non-zero value indicating that it has failed somehow.
Here is my_write():
static ssize_t my_write(struct tty_struct *tty,
struct file *file,
const unsigned char *buf,
size_t nr)
{
int error = 0;
unsigned char data[MAX]; //MAX is 256
if(!my_tty) {
return -EIO;
}
if(nr > MAX) { //too big
return -ENOMEM;
}
error = copy_from_user(data,buf,nr);
printk("copy_from_user returned %d\n",error);
//here, error is always equal to nr
//(which is 12 in my example application)
if(error==0) {
printk("success\n"); //never get here
return nr;
}
return error;
}
From what I've researched, copy_from_user eventually calls pa_memcpy which does validation of the pointers being used. That validation is failing, but I can't tell why. I don't know see how *buf and data overlap or would cause a fault.
Output from uname -a: Linux mint-linux 4.0.5-040005-generic #201506061639 SMP Sat Jun 6 16:40:45 UTC 2015 UTC x86_64 x86_64 x86_64 GNU/Linux
A snippet of the userspace application is:
#define OPEN_FLAGS (O_RDWR|O_NONBLOCK)
int main(int argc, char **argv)
{
int fd=-1;
int bytes_written= 0;
char device="/dev/ttyUSB0";
unsigned char outbuffer[128]={0};
fd=open(device,OPEN_FLAGS);
//set baud rate, etc., switch to my_ldisc (using N_MOUSE)
outbuffer[0]=0x01;
outbuffer[1]=0x02;
outbuffer[2]=0x03;
outbuffer[3]=0x04;
outbuffer[4]=0x05;
outbuffer[5]=0x06;
outbuffer[6]=0x07;
outbuffer[7]=0x08;
outbuffer[8]=0x09;
outbuffer[9]=0x0A;
outbuffer[10]=0x0B;
outbuffer[11]=0x0C;
bytes_written=write(fd,outbuffer,12);
while(true) {
//...
sleep(1);
}
}
In addition, any access of buf in my_write causes instability in the VM. Even following the tty driver example in the o'reilly linux drivers book like this:
printk(KERN_DEBUG "%s - ", __FUNCTION__);
for(i=0;i<nr;i++)
{
printk("%02x ",buf[i]);
}
printk("\n");
Following Tsyvarev's advice, I printed the pointer in the user space application and the kernel module. They were different which meant I should access the incoming buffer directly. I used printf("%p\n",outbuffer); to do that in user space and the equivalent printk in kernel space.
So, slowing down and testing the module line by line helped me to fix the original problem, which it turns out was a bug in the user space application.
FWIW, the compiler never did give me a warning about the use of __user in the original code. Had it worked the way Tsyvarev suggested it would at compile time, it would have made this a lot easier to track down.
Unlike to .write method of struct file_operations, which accepts pointer to user data, .write method for struct tty_operations accepts pointer to kernel data, and these data shold be accessed via usual methods such as memcpy or even directly.
Modern kernel uses __user attribute for mark user-space data, and this attribute is checked (at compile time) when data is accessed. So having compiler warnings enabled will reveal usage of the data with incorrect origin.
I'm trying to configure a SAA6752HS chip (a MPEG-2 encoder) through I2C bus using a Raspberry Pi as a development kit. It was a piece of cake until I had to write at the address 0xC2 of the chip. For this task, I have to use an I2C command that expects a payload of size 189 bytes. So then I stumbled upon a 32 bytes limitation inside the I2C driver, defined by I2C_SMBUS_BLOCK_MAX, in /usr/include/linux/i2c.h. It is not possible to force different values of max limit. Everything around I2C lib end up into the function i2c_smbus_access and any request with more then 32 bytes makes ioctl returns -1. I have no idea how to debug it so far.
static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command,
int size, union i2c_smbus_data *data)
{
struct i2c_smbus_ioctl_data args;
args.read_write = read_write;
args.command = command;
args.size = size;
args.data = data;
return ioctl(file,I2C_SMBUS,&args);
}
I can't understand why there is such limitation, considering that there are devices that require more than 32 bytes of payload data to work (SAA6752HS is such an example).
Are there a way to overcome such limitation without rewrite a new driver?
Thank you in advance.
Here's the documentation for the Linux i2c interface: https://www.kernel.org/doc/Documentation/i2c/dev-interface
At the simplest level you can use ioctl(I2C_SLAVE) to set the slave address and the write system call to write the command. Something like:
i2c_write(int file, int address, int subaddress, int size, char *data) {
char buf[size + 1]; // note: variable length array
ioctl(file, I2C_SLAVE, address); // real code would need to check for an error
buf[0] = subaddress; // need to send everything in one call to write
memcpy(buf + 1, data, size); // so copy subaddress and data to a buffer
write(file, buf, size + 1);
}
if write command is returning -1 make use open fd using
int fd = open("/dev/i2c-1", O_RDWR)
not
int fd = open("/dev/i2c-1", I2C_RDWR)
I'm trying to read/write to a FM24CL64-GTR FRAM chip that is connected over a I2C bus on address 0b 1010 011.
When I'm trying to write 3 bytes (data address 2 bytes, + data one byte), I get a kernel message ([12406.360000] i2c-adapter i2c-0: sendbytes: NAK bailout.), as well as the write returns != 3. See code below:
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
int file;
char filename[20];
int addr = 0x53; // 0b1010011; /* The I2C address */
uint16_t dataAddr = 0x1234;
uint8_t val = 0x5c;
uint8_t buf[3];
sprintf(filename,"/dev/i2c-%d",0);
if ((file = open(filename,O_RDWR)) < 0)
exit(1);
if (ioctl(file,I2C_SLAVE,addr) < 0)
exit(2);
buf[0] = dataAddr >> 8;
buf[1] = dataAddr & 0xff;
buf[2] = val;
if (write(file, buf, 3) != 3)
exit(3);
...
However when I write 2 bytes, then write another byte, I get no kernel error, but when trying to read from the FRAM, I always get back 0. Here is the code to read from the FRAM:
uint8_t val;
if ((file = open(filename,O_RDWR)) < 0)
exit(1);
if (ioctl(file,I2C_SLAVE,addr) < 0)
exit(2);
if (write(file, &dataAddr, 2) != 2) {
exit(3);
if (read(file, &val, 1) != 1) {
exit(3);
None of the functions return an error value, and I have also tried it with:
#include <linux/i2c.h>
struct i2c_rdwr_ioctl_data work_queue;
struct i2c_msg msg[2];
uint8_t ret;
work_queue.nmsgs = 2;
work_queue.msgs = msg;
work_queue.msgs[0].addr = addr;
work_queue.msgs[0].len = 2;
work_queue.msgs[0].flags = 0;
work_queue.msgs[0].buf = &dataAddr;
work_queue.msgs[1].addr = addr;
work_queue.msgs[1].len = 1;
work_queue.msgs[1].flags = I2C_M_RD;
work_queue.msgs[1].buf = &ret;
if (ioctl(file,I2C_RDWR,&work_queue) < 0)
exit(3);
Which also succeeds, but always returns 0. Does this indicate a hardware issue, or am I doing something wrong?
Are there any FRAM drivers for FM24CL64-GTR over I2C on Linux, and what would the API be? Any link would be helpful.
I do not have experience with that particular device, but in our experience many I2C devices have "quirks" that require a work-around, typically above the driver level.
We use linux (CELinux) and an I2C device driver with Linux as well. But our application code also has a non-trivial I2C module that contains all the work-around intelligence for dealing with all the various devices we have experience with.
Also, when dealing with I2C issues, I often find that I need to re-acquaint myself with the source spec:
http://www.nxp.com/acrobat_download/literature/9398/39340011.pdf
as well as the usage of a decent oscilloscope.
Good luck,
Above link is dead, here are some other links:
http://www.nxp.com/documents/user_manual/UM10204.pdf
and of course wikipedia:
http://en.wikipedia.org/wiki/I%C2%B2C
The NAK was a big hint: the WriteProtect pin was externally pulled up, and had to be driven to ground, after that a single write of the address followed by data-bytes is successful (first code segment).
For reading the address can be written out first (using write()), and then sequential data can be read starting from that address.
Note that the method using the struct i2c_rdwr_ioctl_data and the struct i2c_msg (that is, the last code part you've given) is more efficient than the other ones, since with that method you execute the repeated start feature of I2c.
This means you avoid a STA-WRITE-STO -> STA-READ-<data>...-STO transition, because your communication will become STA-WRITE-RS-READ-<data>...STO (RS = repeated start). So, saves you a redundant STO-STA transient.
Not that it differs a lot in time, but if it's not needed, why losing on it...
Just my 2 ct.
Best rgds,
You had some mistakes!
The address of ic is Ax in hex, x can be anything but the 4 upper bits should be A=1010 !!!