EDIT: Adding this to the top because it is critical information being missed. The FSCC_REGISTERS_INIT call does EXACTLY what it is supposed to do, which is fill the entire struct with '-1's. The segfault occurs when individual members in the struct are accessed.
I am humbly learning that I am not very good at c despite being an experienced c++ developer. I am working on a test tool for Fastcomm fscc cards.
I was originally passing the functions by refernce (c++ style) and compiling with g++ until I decided to just compile with c. Why you ask? I guess I just wanted to. I was getting good output in c++ without segfaults.
With gcc, the program compiles without warning but a get a segfault. looking at it with gdb, it appears that this is being caused by the way I am accessing the members of the regs object within the update_all_registers(...) function.
Is the way I am passing values incorrect? Like I said, this worked with c++ references.
#include <fcntl.h>
#include <unistd.h>
#include <fscc.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <ncurses.h>
int64_t get_current_time_ms()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (int64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
void update_all_registers(int fd, struct fscc_registers* regs)
{
FSCC_REGISTERS_INIT(regs);
regs->CMDR = FSCC_UPDATE_VALUE;
regs->STAR = FSCC_UPDATE_VALUE;
regs->CCR0 = FSCC_UPDATE_VALUE;
regs->CCR1 = FSCC_UPDATE_VALUE;
regs->CCR2 = FSCC_UPDATE_VALUE;
regs->BGR = FSCC_UPDATE_VALUE;
regs->SSR = FSCC_UPDATE_VALUE;
regs->SMR = FSCC_UPDATE_VALUE;
regs->TSR = FSCC_UPDATE_VALUE;
regs->TMR = FSCC_UPDATE_VALUE;
regs->RAR = FSCC_UPDATE_VALUE;
regs->RAMR = FSCC_UPDATE_VALUE;
regs->PPR = FSCC_UPDATE_VALUE;
regs->TCR = FSCC_UPDATE_VALUE;
regs->VSTR = FSCC_UPDATE_VALUE;
ioctl(fd, FSCC_GET_REGISTERS, ®s);
}
void print_registers(struct fscc_registers* regs)
{
printf("%.10llu 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x "
"0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
get_current_time_ms(), regs->CMDR, regs->STAR, regs->CCR0,
regs->CCR1, regs->CCR2, regs->BGR, regs->SSR, regs->SMR, regs->TSR,
regs->TMR, regs->RAR, regs->RAMR, regs->PPR, regs->TCR, regs->VSTR);
}
int main(void)
{
int fd = 0;
struct fscc_registers regs;
fd = open("/dev/fscc0", O_RDWR);
if (fd == -1)
{
perror("open");
return EXIT_FAILURE;
}
FSCC_REGISTERS_INIT(regs);
ioctl(fd, FSCC_GET_REGISTERS, ®s);
printf("TS (ms) CMDR STAR CCR0 CCR1 CCR2 BGR"
" SSR SMR TSR TMR RAR"
" RAMR PPR TCR VSTR\n");
print_registers(®s);
char odata[] = "This is a test";
write(fd, odata, sizeof(odata));
update_all_registers(fd, ®s);
print_registers(®s);
close(fd);
return 0;
}
For reference the FSCC library is here https://github.com/commtech/fscc-linux
Disclaimer: I don't know anything about these libs, I only took a peek at their source for 5 mins.
From where we can see that FSCC_REGISTERS_INIT
requires the macro parameter to be passed by value. From the fscc.h library internals:
#define FSCC_REGISTERS_INIT(registers) memset(®isters, -1, sizeof(registers))
Therefore you got a bug here:
void update_all_registers(int fd, struct fscc_registers* regs)
{
FSCC_REGISTERS_INIT(regs);
This needs to be FSCC_REGISTERS_INIT(*regs);
inside the function since you passed a pointer to it. Otherwise the macro will silently write -1 into la-la land which explains your seg fault.
The reason why it compiles is because memset
takes a void*
so if you accidentally pass a pointer-to-pointer, that bug will go under the radar. Better written libraries would type check the macro parameter with _Generic
and also wrap macro parameters in parenthesis.
Similarly, you got a bug here, also inside update_all_registers
:
ioctl(fd, FSCC_GET_REGISTERS, ®s);
should be regs
since it is already a pointer.