Related
I'm attempting to interface with a measurement device through serial. I have already created one successful program that works and does what I want, but it doesn't have live user input.
My problem comes from a program I'm creating where I want to make a live serial terminal to interact with the device. In this program I have the user type the commands to the machine and for some commands the machine has to return a 1 or 0. This is where the problem comes. In the live environment, when I read a 1 after a command I get a segfault.
This is the segment that deals with commands that require the machine to send a 1 or 0.
buf = malloc(1);
if (buf == NULL)
{
perror("memory error");
goto fail;
}
...
if (write == 0)
{
for (i = 0; i < read_com.num; i++)
{
if (strcmp(buff, read_com.check[i]) == 0)
{
ret = write_port(fd, buff, strlen(buff));
if (ret < 0)
goto fail;
ret = read_port(fd, buf, 1);
if (ret < 0)
goto fail;
printf("Read success");
write = 1;
break;
}
}
}
This is the read port function where the segfault is occurring
int read_port(int fd, char *buf, const size_t size)
{
ssize_t r;
size_t received;
received = 0;
while (received < size)
{
r = read(fd, buf + received, size - received);
if (r < 0)
{
perror("failed to read from port");
return -1;
}
if (r == 0)
{
break;
}
received += r;
}
return received;
}
All code below:
live.c:
#include "GPIB_prof.h"
#include "serial.h"
#include "commands.h"
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
const struct com read_com = {3, {"OPC?;PRES;\r", "OPC?;WAIT;\r", "CORRON;\r"}};
const struct com numc = {3, {"STAR", "STOP", "POIN"}};
int main(int argc, char *argv[])
{
int fd;
int ret;
char *buff;
char *buf;
int i;
bool write = 0;
int fin = 0;
char *numb;
char *rem;
int star;
int stop;
int poin;
char *dat;
int fc = 0;
char *file;
FILE *temp;
FILE *f;
rem = malloc(256);
if (rem == NULL)
{
perror("memory error");
goto fail;
}
numb = malloc(4);
if (numb == NULL)
{
perror("memory error");
goto fail;
}
buff = malloc(256);
if (buff == NULL)
{
perror("memory error");
goto fail;
}
buf = malloc(1);
if (buf == NULL)
{
perror("memory error");
goto fail;
}
file = malloc(5);
if (file == NULL)
{
perror("memory error");
goto fail;
}
if (argc < 3)
{
printf("Usage: %s [serial device] [baud rate]", argv[0]);
goto fail;
}
fd = open_port(argv[1], atoi(argv[2]));
if (fd < 0)
goto fail;
ret = GPIB_conf(fd, 0);
if (ret < 0)
goto fail;
while (fin == 0)
{
write = 0;
scanf("%s", buff);
strcat(buff, "\r");
//if (ret<0) goto fail;
for (i = 0; i < strlen(buff); i++)
{
if (i < 4)
{
numb[i] = buff[i];
}
else
{
rem[i - 4] = buff[i];
}
}
if (strcmp(numb, "OUTP") == 0)
{
ret = write_port(fd, buff, strlen(buff));
if (ret < 0)
goto fail;
write = 1;
ret = read_port(fd, dat, 50 * poin);
if (ret < 0)
goto fail;
else
goto data;
}
if (write == 0)
{
for (i = 0; i < read_com.num; i++)
{
if (strcmp(buff, read_com.check[i]) == 0)
{
ret = write_port(fd, buff, strlen(buff));
if (ret < 0)
goto fail;
ret = read_port(fd, buf, 1);
if (ret < 0)
goto fail;
printf("Read success");
write = 1;
break;
}
}
}
if (write == 0)
{
for (i = 0; i < numc.num; i++)
{
if (strcmp(numb, numc.check[i]) == 0)
{
ret = write_port(fd, buff, strlen(buff));
if (ret < 0)
goto fail;
write = 1;
if (strcmp(numb, "STAR") == 0)
{
star = atoi(rem);
}
else if (strcmp(numb, "STOP") == 0)
{
stop = atoi(rem);
}
else if (strcmp(numb, "POIN") == 0)
{
poin = atoi(rem);
dat = malloc(50 * poin);
if (dat == NULL)
{
perror("memory error");
goto fail;
}
fc++;
}
break;
}
}
if (write == 0)
{
ret = write_port(fd, buff, strlen(buff));
if (ret < 0)
goto fail;
write = 1;
}
}
}
printf("Start freq: %d\nStop freq: %d\n", star, stop);
free(rem);
free(numb);
free(buff);
free(buf);
free(file);
free(dat);
return 0;
data:
ret = sprintf(file, "data%d", fc);
if (ret < 0)
goto fail;
temp = fopen(file, "w+");
if (temp == NULL)
{
perror("failed to open file");
fclose(temp);
goto fail;
}
fclose(temp);
ret = remove(file);
if (ret != 0)
{
perror("failed to remove file");
goto fail;
}
f = fopen(file, "w");
if (f == NULL)
{
perror("failed to open file");
fclose(f);
goto fail;
}
for (i = 0; i < (50 * poin); i++)
{
ret = fprintf(f, "%c", dat[i]);
if (ret < 0)
{
fclose(f);
goto fail;
}
}
fclose(f);
fin++;
fail:
free(rem);
free(numb);
free(buff);
free(buf);
free(file);
free(dat);
return -1;
}
serial.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <stdint.h>
#include <unistd.h>
#include "serial.h"
#include "GPIB_prof.h"
struct termios options;
int open_port(const char *dev, uint32_t baud)
{
int fd;
int ret;
char *baud_f;
struct termios opt;
baud_f = malloc(8 * sizeof(char));
if (baud_f == NULL)
{
perror("memory error");
goto fail;
}
fd = open(dev, O_RDWR, 0777);
if (fd < 0)
{
perror(dev);
goto fail;
}
ret = tcflush(fd, TCIOFLUSH);
if (ret)
{
perror("tcgetattr failed");
goto fail;
}
opt.c_cflag = (opt.c_cflag & ~CSIZE) | CS8;
opt.c_cflag &= ~IGNBRK;
opt.c_lflag = 0;
opt.c_oflag = 0;
opt.c_cc[VTIME] = 5;
opt.c_cc[VMIN] = 1;
opt.c_iflag &= ~(IXON | IXOFF | IXANY);
opt.c_cflag |= (CLOCAL | CREAD);
opt.c_cflag &= ~(PARENB | PARODD);
opt.c_cflag |= 0;
opt.c_cflag &= ~CSTOPB;
ret = sprintf(baud_f, "B%d", baud);
if (ret < 0)
{
perror("memory error");
goto fail;
}
baud = (uintptr_t)baud_f;
cfsetospeed(&opt, baud);
cfsetispeed(&opt, baud);
ret = tcsetattr(fd, TCSANOW, &opt);
if (ret)
{
perror("tcsetarre failed");
goto fail;
}
free(baud_f);
return fd;
fail:
free(baud_f);
close(fd);
return -1;
}
int read_port(int fd, char *buf, const size_t size)
{
ssize_t r;
size_t received;
received = 0;
while (received < size)
{
r = read(fd, buf + received, size - received);
if (r < 0)
{
perror("failed to read from port");
return -1;
}
if (r == 0)
{
break;
}
received += r;
}
return received;
}
int write_port(int fd, const char *buf, const size_t size)
{
ssize_t res;
res = write(fd, buf, size);
if (res != (ssize_t)size)
{
perror("failed to write to port");
return -1;
}
usleep(size * 100);
return 0;
}
int GPIB_conf(int fd, int profile)
{
int ret;
switch (profile)
{
case 0:
ret = def(fd);
if (ret < 0)
goto fail;
}
return 0;
fail:
return -1;
}
serial.h
#ifndef _SERIAL_H_
#define _SERIAL_H_
#include <stdint.h>
#include <stddef.h>
#include "GPIB_prof.h"
int open_port (const char *dev, uint32_t baud);
int read_port (int fd, char *buf, size_t size);
int write_port (int fd, const char *buf, size_t size);
int GPIB_conf (int fd, int profile);
#endif
commands.h:
#ifndef _COMMANDS_H_
#define _COMMANDS_H_
struct com
{
const int num;
const char *check[];
};
#endif
GPIB_prof.c:
#include "GPIB_prof.h"
#include "serial.h"
int def(int fd)
{
int ret;
ret = write_port(fd, "++mode 1\r", 9);
if (ret < 0)
goto fail;
ret = write_port(fd, "++addr 16\r", 10);
if (ret < 0)
goto fail;
ret = write_port(fd, "++eoi 0\r", 8);
if (ret < 0)
goto fail;
ret = write_port(fd, "++eot_enable 1\r", 15);
if (ret < 0)
goto fail;
ret = write_port(fd, "++eot_char 13\r", 14);
if (ret < 0)
goto fail;
ret = write_port(fd, "++ifc\r", 6);
if (ret < 0)
goto fail;
ret = write_port(fd, "++auto 1\r", 9);
if (ret < 0)
goto fail;
return 0;
fail:
return -1;
}
GPIB_prof.h:
#ifndef _PROFILE_H_
#define _PROFILE_H_
int def(int fd);
#endif
Caveat: This may not be a total solution as there are many compiler warnings for possibly uninitialized variables, etc.
Note that I'd rename some variables. You have a global called read. That tends to conflict with the standard read function. (i.e.) don't define functions/variables that conflict with standard functions/variables: caveat emptor.
Here is the compiler output I get for (e.g.) cc -o orig orig.c -Wall -Wextra -O2.
Notably, a number of pointer variables may have uninitialized values. This really should be restructured and the warnings fixed as the code is fragile and the compiler is pointing out things that could easily explain your runtime error(s).
orig.c: In function ‘GPIB_conf’:
orig.c:136:15: warning: unused parameter ‘fd’ [-Wunused-parameter]
GPIB_conf(int fd, int profile)
~~~~^~
orig.c: In function ‘main’:
orig.c:217:17: warning: comparison of integer expressions of different signedness: ‘int’ and ‘size_t’ {aka ‘long unsigned int’} [-Wsign-compare]
for (i = 0; i < strlen(buff); i++) {
^
orig.c:325:5: warning: increment of a boolean expression [-Wbool-operation]
fin++;
^~
orig.c: In function ‘open_port’:
orig.c:96:2: warning: ‘fd’ may be used uninitialized in this function [-Wmaybe-uninitialized]
close(fd);
^~~~~~~~~
orig.c: In function ‘main’:
orig.c:295:27: warning: ‘%d’ directive writing between 1 and 10 bytes into a region of size 1 [-Wformat-overflow=]
ret = sprintf(file, "data%d", fc);
^~
orig.c:295:22: note: directive argument in the range [0, 2147483647]
ret = sprintf(file, "data%d", fc);
^~~~~~~~
orig.c:295:8: note: ‘sprintf’ output between 6 and 15 bytes into a destination of size 5
ret = sprintf(file, "data%d", fc);
^~~~~~~~~~~~~~~~~~~~~~~~~~~
orig.c:332:2: warning: ‘file’ may be used uninitialized in this function [-Wmaybe-uninitialized]
free(file);
^~~~~~~~~~
orig.c:333:2: warning: ‘dat’ may be used uninitialized in this function [-Wmaybe-uninitialized]
free(dat);
^~~~~~~~~
orig.c:329:2: warning: ‘numb’ may be used uninitialized in this function [-Wmaybe-uninitialized]
free(numb);
^~~~~~~~~~
orig.c:331:2: warning: ‘buf’ may be used uninitialized in this function [-Wmaybe-uninitialized]
free(buf);
^~~~~~~~~
orig.c:330:2: warning: ‘buff’ may be used uninitialized in this function [-Wmaybe-uninitialized]
free(buff);
^~~~~~~~~~
orig.c:230:32: warning: ‘poin’ may be used uninitialized in this function [-Wmaybe-uninitialized]
ret = read_port(fd, dat, 50 * poin);
~~~^~~~~~
I had to refactor/nop some of the code to get it to compile without the full definitions in the [missing] .h files. However, these hacks do not account for the warnings above.
#include "GPIB_prof.h"
#include "serial.h"
#include "commands.h"
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#define def(_fd) \
0
struct com {
int num;
char *check[3];
};
const struct com read_com = { 3, {"OPC?;PRES;\r", "OPC?;WAIT;\r", "CORRON;\r"} };
const struct com numc = { 3, {"STAR", "STOP", "POIN"} };
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <stdint.h>
#include <unistd.h>
#include "serial.h"
#include "GPIB_prof.h"
struct termios options;
int
open_port(const char *dev, uint32_t baud)
{
int fd;
int ret;
char *baud_f;
struct termios opt;
baud_f = malloc(8 * sizeof(char));
if (baud_f == NULL) {
perror("memory error");
goto fail;
}
fd = open(dev, O_RDWR, 0777);
if (fd < 0) {
perror(dev);
goto fail;
}
ret = tcflush(fd, TCIOFLUSH);
if (ret) {
perror("tcgetattr failed");
goto fail;
}
opt.c_cflag = (opt.c_cflag & ~CSIZE) | CS8;
opt.c_cflag &= ~IGNBRK;
opt.c_lflag = 0;
opt.c_oflag = 0;
opt.c_cc[VTIME] = 5;
opt.c_cc[VMIN] = 1;
opt.c_iflag &= ~(IXON | IXOFF | IXANY);
opt.c_cflag |= (CLOCAL | CREAD);
opt.c_cflag &= ~(PARENB | PARODD);
opt.c_cflag |= 0;
opt.c_cflag &= ~CSTOPB;
ret = sprintf(baud_f, "B%d", baud);
if (ret < 0) {
perror("memory error");
goto fail;
}
baud = (uintptr_t) baud_f;
cfsetospeed(&opt, baud);
cfsetispeed(&opt, baud);
ret = tcsetattr(fd, TCSANOW, &opt);
if (ret) {
perror("tcsetarre failed");
goto fail;
}
free(baud_f);
return fd;
fail:
free(baud_f);
close(fd);
return -1;
}
int
read_port(int fd, char *buf, const size_t size)
{
ssize_t r;
size_t received;
received = 0;
while (received < size) {
r = read(fd, buf + received, size - received);
if (r < 0) {
perror("failed to read from port");
return -1;
}
if (r == 0) {
break;
}
received += r;
}
return received;
}
int
write_port(int fd, const char *buf, const size_t size)
{
ssize_t res;
res = write(fd, buf, size);
if (res != (ssize_t) size) {
perror("failed to write to port");
return -1;
}
usleep(size * 100);
return 0;
}
int
GPIB_conf(int fd, int profile)
{
int ret;
switch (profile) {
case 0:
ret = def(fd);
if (ret < 0)
goto fail;
}
return 0;
fail:
return -1;
}
int
main(int argc, char *argv[])
{
int fd;
int ret;
char *buff;
char *buf;
int i;
bool write = 0;
bool fin = 0;
char *numb;
char *rem;
int star;
int stop;
int poin;
char *dat;
int fc = 0;
char *file;
FILE *temp;
FILE *f;
rem = malloc(256);
if (rem == NULL) {
perror("memory error");
goto fail;
}
numb = malloc(4);
if (numb == NULL) {
perror("memory error");
goto fail;
}
buff = malloc(256);
if (buff == NULL) {
perror("memory error");
goto fail;
}
buf = malloc(1);
if (buf == NULL) {
perror("memory error");
goto fail;
}
file = malloc(5);
if (file == NULL) {
perror("memory error");
goto fail;
}
if (argc < 3) {
printf("Usage: %s [serial device] [baud rate]", argv[0]);
goto fail;
}
fd = open_port(argv[1], atoi(argv[2]));
if (fd < 0)
goto fail;
ret = GPIB_conf(fd, 0);
if (ret < 0)
goto fail;
while (fin == 0) {
write = 0;
scanf("%s", buff);
strcat(buff, "\r");
// if (ret<0) goto fail;
for (i = 0; i < strlen(buff); i++) {
if (i < 4) {
numb[i] = buff[i];
}
else {
rem[i - 4] = buff[i];
}
}
if (strcmp(numb, "OUTP") == 0) {
ret = write_port(fd, buff, strlen(buff));
if (ret < 0)
goto fail;
write = 1;
ret = read_port(fd, dat, 50 * poin);
if (ret < 0)
goto fail;
else
goto data;
}
if (write == 0) {
for (i = 0; i < read_com.num; i++) {
if (strcmp(buff, read_com.check[i]) == 0) {
ret = write_port(fd, buff, strlen(buff));
if (ret < 0)
goto fail;
ret = read_port(fd, buf, 1);
if (ret < 0)
goto fail;
printf("Read success");
write = 1;
break;
}
}
}
if (write == 0) {
for (i = 0; i < numc.num; i++) {
if (strcmp(numb, numc.check[i]) == 0) {
ret = write_port(fd, buff, strlen(buff));
if (ret < 0)
goto fail;
write = 1;
if (strcmp(numb, "STAR") == 0) {
star = atoi(rem);
}
else if (strcmp(numb, "STOP") == 0) {
stop = atoi(rem);
}
else if (strcmp(numb, "POIN") == 0) {
poin = atoi(rem);
dat = malloc(50 * poin);
if (dat == NULL) {
perror("memory error");
goto fail;
}
fc++;
}
break;
}
}
if (write == 0) {
ret = write_port(fd, buff, strlen(buff));
if (ret < 0)
goto fail;
write = 1;
}
}
}
printf("Start freq: %d\nStop freq: %d\n", star, stop);
free(rem);
free(numb);
free(buff);
free(buf);
free(file);
free(dat);
return 0;
data:
ret = sprintf(file, "data%d", fc);
if (ret < 0)
goto fail;
temp = fopen(file, "w+");
if (temp == NULL) {
perror("failed to open file");
fclose(temp);
goto fail;
}
fclose(temp);
ret = remove(file);
if (ret != 0) {
perror("failed to remove file");
goto fail;
}
f = fopen(file, "w");
if (f == NULL) {
perror("failed to open file");
fclose(f);
goto fail;
}
for (i = 0; i < (50 * poin); i++) {
ret = fprintf(f, "%c", dat[i]);
if (ret < 0) {
fclose(f);
goto fail;
}
}
fclose(f);
fin++;
fail:
free(rem);
free(numb);
free(buff);
free(buf);
free(file);
free(dat);
return -1;
}
I've been trying to find out why my tester is failing, it says that destination and source file don't match. link for testers: https://github.com/ShiraWolf/hwOP.git
Output Requirements & Testing:
It must output one of the following types of messages (precisely and case-sensitive):
Unable to open source file for reading
Unable to open destination file for writing
Unable to write to destination file
Unable to write buffer content to destination file
Unable to read source file
Unable to close source file
Unable to close destination file
File was successfully copied to
Or one of the various arguments parsing errors, as described in the examples above.
My code:
/*
* ex1.c
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_BUFFER_SIZE 65536
#define DESTINATION_FILE_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH
extern int opterr, optind;
void exit_with_usage(const char *message) {
fprintf(stderr, "%s\n", message);
fprintf(stderr, "Usage:\n\tex1 [-f] BUFFER_SIZE SOURCE DEST\n");
exit(EXIT_FAILURE);
}
void copy_file(const char *source_file, const char *dest_file, int buffer_size, int force_flag) {
/*
* Copy source_file content to dest_file, buffer_size bytes at a time.
* If force_flag is true, then also overwrite dest_file. Otherwise print error, and exit.
*
* TODO:
* 1. Open source_file for reading
* 2. Open dest_file for writing (Hint: is force_flag true?)
* 3. Loop reading from source and writing to the destination buffer_size bytes each time
* 4. Close source_file and dest_file
*
* ALWAYS check the return values of syscalls for errors!
* If an error was found, use perror(3) to print it with a message, and then exit(EXIT_FAILURE)
*/
int c = 0;
int sourcef = 0;
int destf = 0;
sourcef = open(source_file, O_RDONLY);
if (sourcef == -1) {
perror("Unable to open source file for reading");
exit(EXIT_FAILURE);
}
destf = open(dest_file, O_WRONLY |O_CREAT | O_EXCL, 00700);
if (destf == -1) {
if (force_flag) {
destf = open(dest_file, O_WRONLY, 00700);
if (destf == -1) {
if (close(sourcef) == -1) {
perror("couldn't close source file");
exit(EXIT_FAILURE);
}
perror("Unable to open destination for writing");
exit(EXIT_FAILURE);
}
} else {
perror("Unable to open destination for writing");
exit(EXIT_FAILURE);
}
}
char *buffer = malloc(sizeof(char) * buffer_size);
while ((c = read(sourcef, buffer, buffer_size)) != 0) {
if (c == -1) {
perror("couldn't read from source file");
if (close(sourcef) == -1) {
perror("couldn't close source file after reading has failed");
exit(EXIT_FAILURE);
}
if (close(destf) == -1) {
perror("couldn't close dest file after reading has failed");
exit(EXIT_FAILURE);
}
exit(EXIT_FAILURE);
}
c = write(destf, buffer, buffer_size);
if (c == -1) {
perror("couldn't write to source file");
if (close(sourcef) == -1) {
perror("couldn't close source file after writing has failed");
exit(EXIT_FAILURE);
}
if (close(destf) == -1) {
perror("couldn't close dest file after writing has failed");
exit(EXIT_FAILURE);
}
exit(EXIT_FAILURE);
}
}
free(buffer);
if (close(sourcef) == -1) {
perror("couldn't close source file");
exit(EXIT_FAILURE);
}
if (close(destf) == -1) {
perror("couldn't close dest file");
exit(EXIT_FAILURE);
}
printf("File %s was copied to %s\n", source_file, dest_file);
exit(EXIT_SUCCESS);
}
void parse_arguments (
int argc, char **argv,
char **source_file, char **dest_file, int *buffer_size, int *force_flag) {
/*
* parses command line arguments and set the arguments required for copy_file
*/
int option_character;
opterr = 0; /* Prevent getopt() from printing an error message to stderr */
while ((option_character = getopt(argc, argv, "f")) != -1) {
switch (option_character) {
case 'f':
*force_flag = 1;
break;
default: /* '?' */
exit_with_usage("Unknown option specified");
}
}
if (argc - optind != 3) {
exit_with_usage("Invalid number of arguments");
} else {
*source_file = argv[argc - 2];
*dest_file = argv[argc - 1];
*buffer_size = atoi(argv[argc - 3]);
if (strlen(*source_file) == 0 || strlen(*dest_file) == 0) {
exit_with_usage("Invalid source / destination file name");
} else if (*buffer_size < 1 || *buffer_size > MAX_BUFFER_SIZE) {
exit_with_usage("Invalid buffer size");
}
}
}
int main(int argc, char **argv) {
int force_flag = 0; /* force flag default: false */
char *source_file = NULL;
char *dest_file = NULL;
int buffer_size = MAX_BUFFER_SIZE;
parse_arguments(argc, argv, &source_file, &dest_file, &buffer_size, &force_flag);
copy_file(source_file, dest_file, buffer_size, force_flag);
return EXIT_SUCCESS;
}
Can anybody see where my mistake is?
c = write(destf, buffer, buffer_size); does not use the correct size: you should write c bytes and store the written count into a separate variable nwritten and keep trying to write more bytes until c bytes have been written or write return 0 or -1.
Here is a modified version of the copying loop:
while ((c = read(sourcef, buffer, buffer_size)) != 0) {
if (c == -1) {
perror("Unable to read from source file");
if (close(sourcef) == -1) {
perror("Unable to close source file");
exit(EXIT_FAILURE);
}
if (close(destf) == -1) {
perror("Unable to close destination file");
exit(EXIT_FAILURE);
}
exit(EXIT_FAILURE);
}
int towrite = c;
int pos = 0;
while (towrite > 0) {
int nwritten = write(destf, buffer + pos, towrite);
if (nwritten <= 0) {
perror("Unable to write to destination file");
if (close(sourcef) == -1) {
perror("Unable to close source file");
exit(EXIT_FAILURE);
}
if (close(destf) == -1) {
perror("Unable to close destination file");
exit(EXIT_FAILURE);
}
exit(EXIT_FAILURE);
}
pos += nwritten;
towrite -= nwritten;
}
}
Also note that the other error messages are different from the specification.
I've searched for quite some time now for a solution to my problem.
I'd like to read RFID tags on my Raspberry, but I want to do it in C-code, as the rest of my project is written in C.
I have a few questions to the following C-code I found here on stackoverflow.com:
What .h includes do I need for the code below?
Where is the interface setup, that I'm using? (/dev/ttyAMA0)
Please help me to get this code snippet working right, as it's been holding me up for a few days already.
char read_rfid(char* rfid_num) {
fd_set input_fdset;
ssize_t length;
int done;
for(done=0; done < 14; ) {
FD_ZERO(&input_fdset);
FD_SET(fd,&input_fdset);
if(select(fd+1 ,&input_fdset, NULL,NULL,NULL) == -1) {
if (errno == EAGAIN) continue;
perror("Terminal select() failed");
return -1;
}
if(FD_ISSET(fd,&input_fdset)) {
if((length = read(fd,rfid_num+done,14-done)) == -1) {
if (errno == EAGAIN) continue;
perror("Terminal: read() failed");
return -1;
}
write(STDOUT_FILENO,rfid_num+done,length);
done += length;
}
}
return 0;
}
int setupRS232()
{
struct termios term_attr;
if((fd = open(RFID,O_RDWR)) == -1)
{
perror("Can't open Device");
return(1);
}
if(tcgetattr(fd,&term_attr) != 0)
{
perror("terminal: tcgetattr() failed");
return(1);
}
term_attr.c_cflag = BAUD|CS8|CRTSCTS|CLOCAL|CREAD;
term_attr.c_iflag = 0;
term_attr.c_oflag = 0;
term_attr.c_lflag = 0;
if(tcsetattr(fd,TCSAFLUSH,&term_attr) != 0)
{
perror("terminal: tcsetattr() failed");
return(1);
}
}
int main(int argc, char** argv)
{
char rfid_num[14];
int i;
if(setupRS232() == 1)
return(1);
puts("Waiting for transponder...");
read_rfid(rfid_num);
for(i=0;i<20;i++)
{
printf("%x\n",rfid_num[i]);
}
}
#include <unistd.h> // for read & write functions
#include <sys/select.h> // fd_set functions
#include <stdio.h> // for perror & printf family
#include <sys/types.h> // for open related function
#include <sys/stat.h> // for open related function
#include <fcntl.h> // for open related function
#include <termios.h> // for terminal functions
#include <errno.h> // for error code
#define RFID "path_to_rfid" // FIXME <- you should set this properly
char read_rfid(char* rfid_num, int fd)
{
fd_set input_fdset;
ssize_t length;
int done;
for(done=0; done < 14; ) {
FD_ZERO(&input_fdset);
FD_SET(fd,&input_fdset);
if(select(fd+1 ,&input_fdset, NULL,NULL,NULL) == -1) {
perror("Terminal select() failed");
return -1;
}
if(FD_ISSET(fd,&input_fdset)) {
if((length = read(fd,rfid_num+done,14-done)) == -1) {
perror("Terminal: read() failed");
return -1;
}
write(STDOUT_FILENO,rfid_num+done,length);
done += length;
}
}
return 0;
}
int setupRS232()
{
struct termios term_attr;
int fd = 0;
if((fd = open(RFID,O_RDWR)) == -1) {
perror("Can't open Device");
return(-1);
}
if(tcgetattr(fd,&term_attr) != 0)
{
perror("terminal: tcgetattr() failed");
close(fd);
return(-1);
}
term_attr.c_cflag = CBAUD|CS8|CRTSCTS|CLOCAL|CREAD;
term_attr.c_iflag = 0;
term_attr.c_oflag = 0;
term_attr.c_lflag = 0;
if(tcsetattr(fd,TCSAFLUSH,&term_attr) != 0) {
perror("terminal: tcsetattr() failed");
close(fd);
return(-1);
}
return (fd);
}
int main(int argc, char** argv)
{
char rfid_num[14];
int i;
int fd;
if((fd = setupRS232()) == -1) {
return(-1);
}
puts("Waiting for transponder...");
read_rfid(rfid_num, fd);
for(i=0;i<20;i++) {
printf("%x\n",rfid_num[i]);
}
return 0;
}
Now your code is ready to be compiled, at least in my machine I could compile with no errors.
I want to open my door with a RFID Transponder. For this I use a Raspberry Pi and an 125Khz RFID Reader with UART. So now I have written a little C programm, wich sets up the RS232 ('ttyAMA0'). This works all fine, and I can read the Transponder, but it reads some sh**
Here is my Code:
char read_rfid(char* rfid_num)
{
fd_set input_fdset;
ssize_t length;
while(1)
{
FD_ZERO(&input_fdset);
FD_SET(fd,&input_fdset);
if(select(fd+1 ,&input_fdset, NULL,NULL,NULL)==-1)
perror("Terminal select() failed");
if(FD_ISSET(fd,&input_fdset))
{
if((length = read(fd,rfid_num,14)) ==-1)
perror("Terminal: read() failed");
else
{
write(STDOUT_FILENO,rfid_num,length);
return;
}
}
}
}
int setupRS232()
{
struct termios term_attr;
if((fd = open(RFID,O_RDWR)) == -1)
{
perror("Can't open Device");
return(1);
}
if(tcgetattr(fd,&term_attr) != 0)
{
perror("terminal: tcgetattr() failed");
return(1);
}
term_attr.c_cflag = BAUD|CS8|CRTSCTS|CLOCAL|CREAD;
term_attr.c_iflag = 0;
term_attr.c_oflag = 0;
term_attr.c_lflag = 0;
if(tcsetattr(fd,TCSAFLUSH,&term_attr) != 0)
{
perror("terminal: tcsetattr() failed");
return(1);
}
}
int main(int argc, char** argv)
{
MYSQL *mysql = NULL;
char rfid_num[14];
int i;
if(init_mysql(mysql) == 1)
return(1);
if(setupRS232() == 1)
return(1);
puts("Warte auf Transponder...");
read_rfid(rfid_num);
for(i=0;i<20;i++)
{
printf("%x\n",rfid_num[i]);
}
}
PS: Sorry for my bad English
Minimal approach to buffering. You should probably check the contents of the buffer before returning valid (is there a final \n ? )
char read_rfid(char* rfid_num) {
fd_set input_fdset;
ssize_t length;
int done;
for(done=0; done < 14; ) {
FD_ZERO(&input_fdset);
FD_SET(fd,&input_fdset);
if(select(fd+1 ,&input_fdset, NULL,NULL,NULL) == -1) {
if (errno == EAGAIN) continue;
perror("Terminal select() failed");
return -1;
}
if(FD_ISSET(fd,&input_fdset)) {
if((length = read(fd,rfid_num+done,14-done)) == -1) {
if (errno == EAGAIN) continue;
perror("Terminal: read() failed");
return -1;
}
write(STDOUT_FILENO,rfid_num+done,length);
done += length;
}
}
return 0;
}
Note: I don't understand why this function returns char.
I want to get the following details for all the NICs attached to my computer:
1) Interface name (eg. eth0)
2) Interface Number (like in Windows) if such a thing exists in Linux
3) NIC bandwidth capacity and mode (eg. 1Gb/s full duplex)
You can use getifaddrs()/freeifaddrs() to obtain a linked list of all interfaces, then ioctl(fd, SIOCGIFINDEX, struct ifreq *) to obtain the interface index for each. Since the interfaces are consecutive and always listed (regardless of whether or they are up (active) or not), I choose to enumerate them with a loop using ioctl(fd, SIOCGIFNAME, struct ifreq *) instead. In all cases fd is an AF_INET socket.
To obtain the duplex and speed of the interface, you need to use the ioctl(fd, SIOCETHTOOL, struct ifreq *) with the ifr_data pointing to a struct ethtool_cmd having cmd = ETHTOOL_GSET.
The ioctls should return -1 if they fail, and a nonnegative value (zero, I believe) if success.
Here is an example program:
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/ip.h>
#include <net/if.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
struct interface {
int index;
int flags; /* IFF_UP etc. */
long speed; /* Mbps; -1 is unknown */
int duplex; /* DUPLEX_FULL, DUPLEX_HALF, or unknown */
char name[IF_NAMESIZE + 1];
};
static int get_interface_common(const int fd, struct ifreq *const ifr, struct interface *const info)
{
struct ethtool_cmd cmd;
int result;
/* Interface flags. */
if (ioctl(fd, SIOCGIFFLAGS, ifr) == -1)
info->flags = 0;
else
info->flags = ifr->ifr_flags;
ifr->ifr_data = (void *)&cmd;
cmd.cmd = ETHTOOL_GSET; /* "Get settings" */
if (ioctl(fd, SIOCETHTOOL, ifr) == -1) {
/* Unknown */
info->speed = -1L;
info->duplex = DUPLEX_UNKNOWN;
} else {
info->speed = ethtool_cmd_speed(&cmd);
info->duplex = cmd.duplex;
}
do {
result = close(fd);
} while (result == -1 && errno == EINTR);
if (result == -1)
return errno;
return 0;
}
int get_interface_by_index(const int index, struct interface *const info)
{
int socketfd, result;
struct ifreq ifr;
if (index < 1 || !info)
return errno = EINVAL;
socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (socketfd == -1)
return errno;
ifr.ifr_ifindex = index;
if (ioctl(socketfd, SIOCGIFNAME, &ifr) == -1) {
do {
result = close(socketfd);
} while (result == -1 && errno == EINTR);
return errno = ENOENT;
}
info->index = index;
strncpy(info->name, ifr.ifr_name, IF_NAMESIZE);
info->name[IF_NAMESIZE] = '\0';
return get_interface_common(socketfd, &ifr, info);
}
int get_interface_by_name(const char *const name, struct interface *const info)
{
int socketfd, result;
struct ifreq ifr;
if (!name || !*name || !info)
return errno = EINVAL;
socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (socketfd == -1)
return errno;
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(socketfd, SIOCGIFINDEX, &ifr) == -1) {
do {
result = close(socketfd);
} while (result == -1 && errno == EINTR);
return errno = ENOENT;
}
info->index = ifr.ifr_ifindex;
strncpy(info->name, name, IF_NAMESIZE);
info->name[IF_NAMESIZE] = '\0';
return get_interface_common(socketfd, &ifr, info);
}
int main(int argc, char *argv[])
{
struct interface iface;
int arg;
int status = 0;
if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s { -h | --help }\n", argv[0]);
fprintf(stderr, " %s\n", argv[0]);
fprintf(stderr, " %s INTERFACE ...\n", argv[0]);
fprintf(stderr, "\n");
return 1;
}
if (argc > 1) {
for (arg = 1; arg < argc; arg++) {
if (get_interface_by_name(argv[arg], &iface) != 0) {
fprintf(stderr, "%s: No such interface.\n", argv[arg]);
status = 1;
continue;
}
printf("%s: Interface %d", iface.name, iface.index);
if (iface.flags & IFF_UP)
printf(", up");
if (iface.duplex == DUPLEX_FULL)
printf(", full duplex");
else
if (iface.duplex == DUPLEX_HALF)
printf(", half duplex");
if (iface.speed > 0)
printf(", %ld Mbps", iface.speed);
printf("\n");
}
} else {
for (arg = 1; ; arg++) {
if (get_interface_by_index(arg, &iface) != 0)
break;
printf("%s: Interface %d", iface.name, iface.index);
if (iface.flags & IFF_UP)
printf(", up");
if (iface.duplex == DUPLEX_FULL)
printf(", full duplex");
else
if (iface.duplex == DUPLEX_HALF)
printf(", half duplex");
if (iface.speed > 0)
printf(", %ld Mbps", iface.speed);
printf("\n");
}
}
return status;
}
If you save the above as iflist.c, you can compile it using
gcc -W -Wall -O3 iflist.c -o iflist
To see the usage, run iflist -h. To list all interfaces, run it without parameters:
./iflist
The above will use the enumeration method I described. To list only specific interfaces, run it naming the interfaces:
./iflist eth0 lo
Duplex and speed is only listed for ethernet interfaces, of course.
Edited to add:
If the above program does not supply the bandwidth and mode for an interface, here is a simplified version which reports the exact reason (errors). This one takes the interface names as commandline parameters; it does not enumerate interfaces.
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/ip.h>
#include <net/if.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int ethernet_interface(const char *const name,
int *const index, int *const speed, int *const duplex)
{
struct ifreq ifr;
struct ethtool_cmd cmd;
int fd, result;
if (!name || !*name) {
fprintf(stderr, "Error: NULL interface name.\n");
fflush(stderr);
return errno = EINVAL;
}
if (index) *index = -1;
if (speed) *speed = -1;
if (duplex) *duplex = -1;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
const int err = errno;
fprintf(stderr, "%s: Cannot create AF_INET socket: %s.\n", name, strerror(err));
fflush(stderr);
return errno = err;
}
strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
ifr.ifr_data = (void *)&cmd;
cmd.cmd = ETHTOOL_GSET;
if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) {
const int err = errno;
do {
result = close(fd);
} while (result == -1 && errno == EINTR);
fprintf(stderr, "%s: SIOCETHTOOL ioctl: %s.\n", name, strerror(err));
return errno = err;
}
if (speed)
*speed = ethtool_cmd_speed(&cmd);
if (duplex)
switch (cmd.duplex) {
case DUPLEX_HALF: *duplex = 0; break;
case DUPLEX_FULL: *duplex = 1; break;
default:
fprintf(stderr, "%s: Unknown mode (0x%x).\n", name, cmd.duplex);
fflush(stderr);
*duplex = -1;
}
if (index && ioctl(fd, SIOCGIFINDEX, &ifr) >= 0)
*index = ifr.ifr_ifindex;
do {
result = close(fd);
} while (result == -1 && errno == EINTR);
if (result == -1) {
const int err = errno;
fprintf(stderr, "%s: Error closing socket: %s.\n", name, strerror(err));
return errno = err;
}
return 0;
}
int main(int argc, char *argv[])
{
int arg, speed, index, duplex;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s INTERFACE ...\n", argv[0]);
fprintf(stderr, "\n");
return 0;
}
for (arg = 1; arg < argc; arg++) {
if (ethernet_interface(argv[arg], &index, &speed, &duplex))
return 1;
if (index == -1)
printf("%s: (no interface index)", argv[arg]);
else
printf("%s: interface %d", argv[arg], index);
if (speed == -1)
printf(", unknown bandwidth");
else
printf(", %d Mbps bandwidth", speed);
if (duplex == 0)
printf(", half duplex.\n");
else if (duplex == 1)
printf(", full duplex.\n");
else
printf(", unknown mode.\n");
}
return 0;
}
Questions?
(1) getifaddrs()
(2) if_indextoname(), if_nameindex(), if_nametoindex()
(3) I'm not sure about this one but you might be able to get at it through ioctl() and one of the SIOCGIF* parameters or from /proc.
the following link well explain the getifaddrs function with a working example
getifaddrs()
ethtool eth1
this command will list all the details about eth1 including speed, duplex, port...
you can use popen() to get the output and map it.
POPEN(3) Linux Programmer's Manual
POPEN(3)
NAME
popen, pclose - pipe stream to or from a process
SYNOPSIS
#include
FILE *popen(const char *command, const char *type);