How can I detect error in ioctl I2C_SLAVE call - c

I have been writing some code to access I²C based on the following (which works).
I was attempting to implement something more general, and include error checking.
All the code examples I have seen include similar code to:-
if(ioctl(file, I2C_SLAVE, 0x68) < 0){
perror("Failed to connect to the sensor\n");
return 1;
}
If I use a nonexistent address the code fails, but there is no error.
Can anyone suggest what I am doing wrong?
/** Simple I2C example to read the time from a DS3231 module
* Written by Derek Molloy for the book "Exploring Raspberry Pi" */
#include<stdio.h>
#include<unistd.h> // IB 2022-6-29
#include<fcntl.h>
#include<sys/ioctl.h>
#include<linux/i2c.h>
#include<linux/i2c-dev.h>
#define BUFFER_SIZE 19
// the time is in the registers in decimal form
int bcdToDec(char b) { return (b/16)*10 + (b%16); }
int main(){
int file;
printf("Starting the DS3231 test application\n");
if((file=open("/dev/i2c-1", O_RDWR)) < 0){
perror("failed to open the bus\n");
return 1;
}
if(ioctl(file, I2C_SLAVE, 0x68) < 0){
perror("Failed to connect to the sensor\n");
return 1;
}
char writeBuffer[1] = {0x00};
if(write(file, writeBuffer, 1)!=1){
perror("Failed to reset the read address\n");
return 1;
}
char buf[BUFFER_SIZE];
if(read(file, buf, BUFFER_SIZE)!=BUFFER_SIZE){
perror("Failed to read in the buffer\n");
return 1;
}
printf("The RTC time is %02d:%02d:%02d\n", bcdToDec(buf[2]),
bcdToDec(buf[1]), bcdToDec(buf[0]));
float temperature = buf[0x11] + ((buf[0x12]>>6)*0.25);
printf("The temperature is %.2f°C\n", temperature);
close(file);
return 0;
}

Related

Proper use of libusb_interrupt_transfer()

I'm trying to replicate the behavior of a Windows app on Linux. Specifically, to control the backlighting on a keyboard.
Using Wireshark (on the linux host) to observer what the Windows tool does (when run on a Windows guest), I see a pair of URB_INTERRUPT out messages followed by a pair of URB_INTERRUPT in messages (one of each in each direction).
I've never used libusb before, but reading docs and examples, I've put together the code below. When run, libusb_interrupt_transfer() returns LIBUSB_ERROR_IO. Maybe I'm passing the wrong parameters, maybe I missed some initialization step, this is where my lack of experience with libusb really shines.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libusb-1.0/libusb.h>
int main(int argc, char *argv[]) {
unsigned int interface = 2;
int bSent = 0;
int retval = 0;
unsigned char msg[64];
unsigned char bytes[] = "\x51\x2c\x00\x00\xff\x64\x00\xff\xff\x72\x67\x62";
bzero(msg, sizeof(msg));
memcpy(msg, bytes, sizeof(bytes));
libusb_device_handle *devh;
libusb_init(NULL);
devh = libusb_open_device_with_vid_pid(NULL, 0x0b05, 0x1875);
if (devh) {
printf("Found device\n");
} else {
printf("ERROR: can't find device\n");
exit(1);
}
retval = libusb_set_auto_detach_kernel_driver(devh, interface);
if (!retval) {
printf("Set auto detach kernel driver\n");
} else {
printf("ERROR: failed to set auto detach kernel driver: %s\n", libusb_strerror(retval));
exit(1);
}
retval = libusb_claim_interface(devh, interface);
if (retval < 0) {
printf("ERROR: failed to claim interface %d: %s\n", interface, libusb_strerror(retval));
exit(1);
} else {
printf("Claimed interface %d\n", interface);
}
retval = libusb_interrupt_transfer(devh, interface, msg, sizeof(msg), &bSent, 1000);
if (retval < 0) {
printf("ERROR: libusb_interrupt_transfer() returned %d: %s\n", retval, libusb_strerror(retval));
} else {
printf("libusb_interrupt_transfer() sent %d bytes\n", bSent);
}
libusb_release_interface(devh, interface);
libusb_close(devh);
libusb_exit(NULL);
return(0);
}
Output:
Found device
Set auto detach kernel driver
Claimed interface 2
ERROR: libusb_interrupt_transfer() returned -1: Input/Output Error

writing and reading data to and from a serial port (to an intel galileo) windows c

I'm building a cnc machine for a school project, it consists of an Intel galileo with a shield that controls stepper motors, this is controlled by a program on a windows machine(windows 7), what the program basically does is read a text file containing gcode and then sends it line by line to the Galileo, the Galileo then takes that line breaks it down into coordinates and instructions, moves the spindle to where it needs to go and when the instruction is finished it sends a message back to the computer through serial telling it that its finished, the computer then sends the next line of code and the process is repeated.
so far I can read the gcode file and send it line by line (using a keypress to send each line) to the galileo and everything works fine. my problem is reading from the galileo I have tried a few methods with no joy. this is what I think is closest to what I should be doing. I wont post the whole source because I think it will be just to much to read through, ive posted the main() which has all the setup functions and the function that is supposed to read the serial.
/************************************************************************************************************
application for controling galileo or arduino cnc machine
by brendan scullion
10/11/2014
**********************************************************************************************************/
#include <string.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <ctype.h>
#include <conio.h>
#include "functions.h"
int main()
{
system("COLOR 1F");
// Declare variables and structures
unsigned char text_to_send[MAX_PATH];
unsigned char digits[MAX_PATH];
int baudrate = 19200;
int dev_num = 50;
char dev_name[MAX_PATH];
HANDLE hSerial;
HANDLE screen = GetStdHandle(STD_OUTPUT_HANDLE);
DCB dcbSerialParams = {0};
COMMTIMEOUTS timeouts = {0};
printf("Searching serial ports...\n");
while(dev_num >= 0)
{
printf("\r ");
printf("\rTrying COM%d...", dev_num);
sprintf(dev_name, "\\\\.\\COM%d", dev_num);
hSerial = CreateFile(
dev_name,
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if (hSerial == INVALID_HANDLE_VALUE) dev_num--;
else break;
}
if (dev_num < 0)
{
printf("No serial port available\n");
return 1;
}
printf("OK\n");
// Set device parameters (38400 baud, 1 start bit,
// 1 stop bit, no parity)
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if (GetCommState(hSerial, &dcbSerialParams) == 0)
{
printf("Error getting device state\n");
CloseHandle(hSerial);
return 1;
}
//dcbSerialParams.BaudRate = CBR_38400;
dcbSerialParams.BaudRate = baudrate;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
if(SetCommState(hSerial, &dcbSerialParams) == 0)
{
printf("Error setting device parameters\n");
CloseHandle(hSerial);
return 1;
}
// Set COM port timeout settings
timeouts.ReadIntervalTimeout = 1;
timeouts.ReadTotalTimeoutConstant = 1;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if(SetCommTimeouts(hSerial, &timeouts) == 0)
{
printf("Error setting timeouts\n");
CloseHandle(hSerial);
return 1;
}
char *cmd = NULL;
char *para1 = NULL;
char *para2 = NULL;
char *para3 = NULL;
char comPort[10];
float baudRate;
int keepGoing = 1;
unsigned char message[MAX_STRING_LENGHT];
//*********************************************************************************************************************
char cmdLine[200];
heading();
while(keepGoing == 1)
{
printf(">>");
gets(cmdLine);
cmd = strtok(cmdLine, " ");
if(cmd!=false)
{
if(cmd != NULL)
{
para1 = strtok(NULL, " ");
}
else if(para1 != NULL)
{
para2 = strtok(NULL, " ");
}
else if(para2 != NULL)
{
para3 = strtok(NULL, " ");
}
if(strcmp(cmd, "help")== 0)
{
help();
}
else if(strcmp(cmd, "comset")== 0)
{
setupComs(comPort, baudRate);
}
else if(strcmp(cmd, "getg")== 0)
{
getgcode(hSerial,text_to_send,dev_name);
}
else if(strcmp(cmd, "readserial")== 0)
{
read_serial(hSerial, message, dev_name);
}
else if(strcmp(cmd, "offset")==0)
{
getOffset(hSerial, text_to_send, dev_name);
}
else if(strcmp(cmd, "setup") == 0)
{
setup(hSerial, text_to_send, dev_name);
}
else if(strcmp(cmd, "exit") == 0)
{
keepGoing = 0;
}
else
{
printf("Unknown command!\n");
}
printf("\n");
}
}
// Close serial port
printf("Closing serial port...");
if (CloseHandle(hSerial) == 0)
{
printf("Error\n");
return 1;
}
printf("OK\n");
return 0;
}
and the function for reading
void read_serial(HANDLE hComm, HANDLE screen, char *message, char *devName )
{
char buffer[MAX_STRING_LENGHT];
unsigned char ch;
DWORD bytes_recieved = MAX_STRING_LENGHT, written = 0;
strcpy(buffer,""); //empty buffer
strcpy(buffer,"");
while(buffer!=NULL){ // wait untill serail message revieved
ReadFile(hComm, &buffer,sizeof(buffer), // read serial
&bytes_recieved, NULL );
if(bytes_recieved){ // if something to read
WriteFile(screen, buffer, bytes_recieved, &written, NULL);
strcpy(message, buffer);
printf("%s", message);
if(kbhit()){
ch = getch();
if(ch== 'q')
break;
}
}
}
}
i can post the entire source code if anybody wants to have a look at it
In read_serial, you can try replacing &buffer with buffer in the call to ReadFile. Name of a character array should be a pointer to the first element of the array.
What i would do is i would frame every message that goes from and to the Galileo board in a simple and useful protocol. For example i would use STX and ETX to frame and send messages from the Windows 7 via serial to the board, use ACK NAK to acknowledge the received packet and use SYN for signalling end of execution of a line on Galileo.
With the above framing you can construct receive and send functions where you can wait for specific characters (ETX,STX,SYN,ACK,etc) and exit once you have a full frame or an control character.

Remap a keyboard with ioctl under linux

I am actually trying to write a small program to catch global keyboard inputs from specific USB keyboards under linux.
I am testing with this piece of code :
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <string.h>
#include <stdio.h>
static const char *const evval[3] = {
"RELEASED",
"PRESSED ",
"REPEATED"
};
int main(void)
{
const char *dev = "/dev/input/event2";
struct input_event ev;
ssize_t n;
int fd;
char name[256]= "Unknown";
// int codes[2];
// codes[0] = 58; /* M keycap */
// codes[1] = 49; /* assign to N */
fd = open(dev, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "Cannot open %s: %s.\n", dev, strerror(errno));
return EXIT_FAILURE;
}
if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) > 0)
{
printf("The device on %s says its name is '%s'\n", dev, name);
}
/*
int err = ioctl(fd, EVIOCSKEYCODE, codes);
if (err) {
perror("evdev ioctl");
}*/
while (1) {
n = read(fd, &ev, sizeof ev);
if (n == (ssize_t)-1) {
if (errno == EINTR)
continue;
else
break;
} else
if (n != sizeof ev) {
errno = EIO;
break;
}
if (ev.type == EV_KEY && ev.value >= 0 && ev.value <= 2)
printf("%s 0x%04x (%d)\n", evval[ev.value], (int)ev.code, (int)ev.code);
}
fflush(stdout);
fprintf(stderr, "%s.\n", strerror(errno));
return EXIT_FAILURE;
}
Ths point is that I don't know how to change some input key by other. I tried by calling write() on currently red event by changing the event code, sent key was still previous one, and I tried to used ioctl with EVIOCSKEYCODE, but the call failed with an "invalid argument" error (and I'm not sure to call it correctly).
How can I change outputed key correctly ?
Use the EVIOCGRAB ioctl to grab the input device, so that by reading the events you consume them. Normally (not-grabbed) the events are not consumed when you read them. The ioctl takes an additional parameter, (int)1 for grabbing, (int)0 for releasing.
To re-inject any events, just write them to the uinput device. See eg. a mouse example here. (The event structures are the same type, you only need to write a struct uinput_user_dev structure to the uinput device first, to describe your new input device (which provides the mapped events).)
In other words, you don't remap: you consume and forward.

How to send a UDP packet to a GPRS module

I use a GSM/GPRS module called SIM340DZ, and use AT commands to be able manage the module.
I am able to send a UDP packet to a remote computer at a specific port from the GPRS module. Now, I would like to transfer a UDP packet from computer to the GPRS unit. But, the GPRS unit has a private IP address (e.g. 10.46.123.25), and the access point name (APN) is internet.tele2.se
Could anyone please explain how I can send a UDP data from a (linux) computer to the GPRS unit? What information do I need to know and how can I find it out?
Additionally, if you have experience in AT commands, I would also appreciate if you could explain me what command sequence do I need to use to configure the module on UDP listening mode?
For the ones who needs to deal with similar system, I am posting the code that you can send UDP packets to a destinated port and IPaddress through the serial port by using AT commands. Explanations are included as comment on the code:
int Serial_Close(int *fd);
int Serial_Send(int *fd, char *string);
int Serial_Open(int* fd, char *serial_Name, speed_t baudrate);
int main(int argc, char** argv)
{
int fd;
Serial_Open(&fd, "/dev/ttyAPP0", B115200); //open the UART interface with 115200 boundrate, 8 1 N
if(tcflush(fd, TCIOFLUSH) != 0)
{
exit(1); //error
fprintf(stderr, "tcflush error\n");
}
Serial_Send(&fd, "ATE0\r\n"); //ATE0 = echo mode(ATE) is off (0)
sleep(1);
Serial_Send(&fd, "AT+CGATT=1\r\n");
sleep(1);
Serial_Send(&fd, "AT+AT+CSTT=\"internet.tele2.se\"\r\n"); //here you define the name the APN
sleep(1);
Serial_Send(&fd, "AT+CIICR\r\n");
sleep(1);
Serial_Send(&fd, "AT+cipstart=\"UDP\",\"85.1.2.3\",\"20000\"\r\n"); //85.1.2.3 is the destination IP address, 20000 is the destination Port number
sleep(1);
Serial_Send(&fd, "AT+CIPSEND=5\r\n");
sleep(1);
Serial_Send(&fd, "12345\r\n"); //12345 is the message
sleep(1);
Serial_Send(&fd, "AT+CIPSHUT\r\n");
sleep(1);
Serial_Close(&fd);
return 0;
}
int Serial_Open(int* fd, char *serial_Name, speed_t baudrate)
{
struct termios serCfg;
memset(&serCfg, 0, sizeof(serCfg));
if((*fd = open(serial_Name, O_RDWR)) < 0)
return -1;
else
if(tcgetattr(*fd, &serCfg) != 0)
return -1;
cfsetispeed(&serCfg, baudrate);
cfsetospeed(&serCfg, baudrate);
cfmakeraw(&serCfg);
if(tcsetattr(*fd, TCSANOW, &serCfg) != 0)
return -1;
return 0;
}
int Serial_Send(int *fd, char *string)
{
int len;
char *buffer;
int bytes_sent;
len = strlen(string);
if(len > 255)
return -1;
buffer = (char*)malloc((len+1)*sizeof(char));
if(buffer == NULL)
return -1;
strcpy(buffer, string);
buffer[len] = 0;
bytes_sent = write(*fd, buffer, strlen(buffer));
if(bytes_sent < 0)
{
close (*fd);
return -1;
}
else
{
free(buffer);
return 0;
}
}
int Serial_Close(int *fd)
{
if(close (*fd) < 0)
return -1;
else
return 0;
}
I hope it helps to someone.

ioctl giving Invalid Argument

I want to send a opened file descriptor between two different programs. So I am using ioctl with named pipes to do so. But there I am getting "Invalid argument" error for ioctl().
#include <stropts.h>
#include "accesories.c"
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#define MSGSIZ 63
char *fifo = "fifo";
int send_err(int fd, int errcode, const char *msg)
{
int n;
if ((n = strlen(msg)) > 0)
if (write(fd, msg, n) != n) /* send the error message */
return(-1);
if (errcode >= 0)
errcode = -1; /* must be negative */
if (send_fd(fd, errcode) < 0)
return(-1);
return(0);
}
int send_fd(int fd, int fd_to_send)
{
char buf[2]; /* send_fd()/recv_fd() 2-byte protocol */
buf[0] = 0; /* null byte flag to recv_fd() */
if (fd_to_send < 0) {
buf[1] = -fd_to_send; /* nonzero status means error */
if (buf[1] == 0)
buf[1] = 1; /* -256, etc. would screw up protocol */
} else {
buf[1] = 0; /* zero status means OK */
}
//printf("From the write %d\n",buf[0]);
if (write(fd, buf, 2) != 2)
return(-1);
if (fd_to_send >= 0)
if (ioctl(fd, I_SENDFD, fd_to_send) < 0)
{
printf("Eroor ::: %s\n",strerror(errno));
return(-1);
}
return(0);
}
int main(int argc, char const *argv[])
{
int fd, j, nwrite;
char msgbuf[MSGSIZ+1];
int fd_to_send;
if((fd_to_send = open("vi",O_RDONLY)) < 0)
printf("vi open failed");
if(argc < 2)
{
fprintf(stderr, "Usage: sendmessage msg ... \n");
exit(1);
}
/* open fifo with O_NONBLOCK set */
if((fd = open(fifo, O_WRONLY | O_NONBLOCK)) < 0)
printf("fifo open failed");
/* send messages */
for (j = 1; j < argc; j++)
{
if(strlen(argv[j]) > MSGSIZ)
{
fprintf(stderr, "message too long %s\n", argv[j]);
continue;
}
strcpy(msgbuf, argv[j]);
if((nwrite = write(fd, msgbuf, 6)) == -1)
printf("message write failed");
}
printf("From send_fd %d \n",send_fd(fd,fd_to_send));
exit(0);
}
The file accessories .h only contain some common include files nothing else.
First I am sending a simple message and then calling send_fd which is first sending a 2 byte message and then have to send file descriptor using ioctl(). But it is not.
It looks like linux doesn't support I_SENDFD. The comments indicate that I_SENDFD is in the documentation, but is not actually supported, and results in the error message you encountered. The wikipedia entry for STREAMS states the linux kernel does not have any support for streams. The wikipedia entry does point to a couple of third-party packages that could be used to add streams support, but LiS has not been ported to the 2.6 kernel, and OpenSS7 hasn't had any active development in 4 years.
However, linux does support something similar. This mechanism uses a special message type SCM_RIGHTS to deliver a file descriptor over a UNIX domain socket with sendmsg and obtained from recvmsg. Examples can be found with a simple web search, a complete example seems to be from the book The Linux Programming Interface, with source for sending and receiving.

Resources