Why I2C_SMBUS_BLOCK_MAX is limited to 32 bytes? - c

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)

Related

How do you inject code onto a process without ptrace

Is there a way to inject code into an ELF binary without ptrace, I can't use it since the program I'm writing this for is using GDB and I don't want to stop the process for the while it's injecting. I read it's possible by using /proc/pid/mem but I couldn't quite find anything about how to do it. I don't want to use LD_PRELOAD either since it would require restarting the program and I'd want to do it during runtime.
EDIT: I can't use ptrace since the process might already be attached to by gdb
/proc/pid/mem behaves like an image of the process's memory. To read/write the process's memory, just open /proc/pid/mem, then lseek to the desired address and read() or write() however many bytes you want.
For instance, to overwrite the byte at address 0x12345 in the process with 0x90, you can just do
fd = open("/proc/XXX/mem", O_RDWR);
lseek(fd, 0x12345, SEEK_SET);
unsigned char new = 0x90;
write(fd, &new, 1);
On a 32-bit system, use lseek64 instead (and add #define _LARGEFILE64_SOURCE before the standard includes).
Note that accessing /proc/XXX/mem requires the same permissions as to ptrace the process. In particular, on some systems you may need to be root.
I decided I'd use process_vm_writev, which seems to work I don't know why it didn't want to write to /proc/pid/mem which is odd.
/**
* #brief write_process_memory Writes to the memory of a given process
* #param pid Program pid
* #param address The base memory address
* #param buffer Buffer to write
* #param n How many bytes to write
* #return Returns bytes written
*/
ssize_t write_process_memory(pid_t pid, void *address, void *buffer, ssize_t n) {
struct iovec local, remote;
/* this might have to be made so that if n > _SC_PAGESIZE
* local would be split into multiple locals, similar to how
* read_process_memory works, no fucking clue though if it's necessary */
remote.iov_base = address;
remote.iov_len = n;
local.iov_base = buffer;
local.iov_len = n;
ssize_t amount_read = process_vm_writev(pid, &local, 1, &remote, 1, 0);
return amount_read;
}

Unable to write the complete script onto a device on the serial port

The script file has over 6000 bytes which is copied into a buffer.The contents of the buffer are then written to the device connected to the serial port.However the write function only returns 4608 bytes whereas the buffer contains 6117 bytes.I'm unable to understand why this happens.
{
FILE *ptr;
long numbytes;
int i;
ptr=fopen("compass_script(1).4th","r");//Opening the script file
if(ptr==NULL)
return 1;
fseek(ptr,0,SEEK_END);
numbytes = ftell(ptr);//Number of bytes in the script
printf("number of bytes in the calibration script %ld\n",numbytes);
//Number of bytes in the script is 6117.
fseek(ptr,0,SEEK_SET);
char writebuffer[numbytes];//Creating a buffer to copy the file
if(writebuffer == NULL)
return 1;
int s=fread(writebuffer,sizeof(char),numbytes,ptr);
//Transferring contents into the buffer
perror("fread");
fclose(ptr);
fd = open("/dev/ttyUSB3",O_RDWR | O_NOCTTY | O_NONBLOCK);
//Opening serial port
speed_t baud=B115200;
struct termios serialset;//Setting a baud rate for communication
tcgetattr(fd,&serialset);
cfsetispeed(&serialset,baud);
cfsetospeed(&serialset,baud);
tcsetattr(fd,TCSANOW,&serialset);
long bytesw=0;
tcflush(fd,TCIFLUSH);
printf("\nnumbytes %ld",numbytes);
bytesw=write(fd,writebuffer,numbytes);
//Writing the script into the device connected to the serial port
printf("bytes written%ld\n",bytesw);//Only 4608 bytes are written
close (fd);
return 0;
}
Well, that's the specification. When you write to a file, your process normally is blocked until the whole data is written. And this means your process will run again only when all the data has been written to the disk buffers. This is not true for devices, as the device driver is the responsible of determining how much data is to be written in one pass. This means that, depending on the device driver, you'll get all data driven, only part of it, or even none at all. That simply depends on the device, and how the driver implements its control.
On the floor, device drivers normally have a limited amount of memory to fill buffers and are capable of a limited amount of data to be accepted. There are two policies here, the driver can block the process until more buffer space is available to process it, or it can return with a partial write only.
It's your program resposibility to accept a partial read and continue writing the rest of the buffer, or to pass back the problem to the client module and return only a partial write again. This approach is the most flexible one, and is the one implemented everywhere. Now you have a reason for your partial write, but the ball is on your roof, you have to decide what to do next.
Also, be careful, as you use long for the ftell() function call return value and int for the fwrite() function call... Although your amount of data is not huge and it's not probable that this values cannot be converted to long and int respectively, the return type of both calls is size_t and ssize_t resp. (like the speed_t type you use for the baudrate values) long can be 32bit and size_t a 64bit type.
The best thing you can do is to ensure the whole buffer is written by some code snippet like the next one:
char *p = buffer;
while (numbytes > 0) {
ssize_t n = write(fd, p, numbytes);
if (n < 0) {
perror("write");
/* driver signals some error */
return 1;
}
/* writing 0 bytes is weird, but possible, consider putting
* some code here to cope for that possibility. */
/* n >= 0 */
/* update pointer and numbytes */
p += n;
numbytes -= n;
}
/* if we get here, we have written all numbytes */

How to write a simple Linux device driver?

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

how to write 4 bytes and read 2nd byte after some delay in C programming?

I am having an embedded SBC(master) and slave a 8051 based RF module having 32kbs of Internal ROM. I am having SPI bus to access that internal ROM.When i am sending some data from my master to slave using SPI bus, i am able see some data on MOSI line and after that data, i need to get some response from the slave.I am not confident that data is written properly on 00,01,02,03 address of flash ROM.I am expecting some data on MISO line also,but i am not getting anything.My doubt is whether the four bytes are written properly on my flash ROM starting 4 addresses or not?? I have added the code for your reference, please let me know what's wrong i am doing.
typedef unsigned char uint8;
void run_test(int fd)
{
int i;
uint8 buffer[20];
//int size,l,size1;
uint8 *value[4] = {0xAC,0x53,0xAA,0x55};
uint8 address=0x0000;
/*Writing 4 bytes*/
for(i=0;i<4;i++)
{
printf("address:%.4x \t value : %2X\n",address,value[i]);
write(fd,&value,4);
address++;
}
/*Reading the 2nd byte*/
read (fd, buffer, sizeof (buffer));
printf("%2X\n",);
}
I want to read my second byte from buffer.Please let me know what's wrong i am doing?
And Moreover i need to have my address keeps on changing, and i want to write the first byte on zeroth address and so on.
Regards,
Ravi
I'm not really familiar with your particular application so I don't really know what happens when you call "write" and "read" in your device's library. However, from a pure C/C++ point of view I noticed a couple of things that may or may not require attention. As I understood it you wanted to write 4 bytes of data to the first 4 bytes of memory via the SPI bus. In your write loop there are a couple of things I saw.
First, you loop 4 times and write 4 bytes each time. That is 16 total. Also, I don't see where "address" comes into play at all when you write. I noticed that the array of "value" you are passing the address of a uint* array. The write function takes a void* and you are essentially passing in a void***. This means you definitely are not writing the bytes that are in the value array you declared.
So the way I see it you can write 4 bytes, one byte at a time or 4 bytes at once like this.
int i;
uint8 value[4] = { 0xAC, 0x53, 0xAA, 0x55 };
uint8 address = 0;
// Writing 4 bytes METHOD 1
for (i = 0; i < 4; ++i)
{
write(fd, &(value[i]), 1);
}
// Writing 4 bytes METHOD 2
write(fd, value, 4);
And you can print the second byte from your buffer like this.
uint8 buffer[20];
read(fd, buffer, sizeof(buffer));
printf("%2X\n", buffer[1]);
I have worked with some microcontrollers and have not used file descriptors before to read/write on an SPI bus. So I hope this helps.

Reading / writing from using I2C on Linux

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 !!!

Resources