Close() filedescriptor using Qt in Linux - c++

Background information:
I have been writing code to control a device attached by a USB cable but emulating an RS-232 serial port.
The device in question is an Arduino microcontroller controlled servo pan and tilt stage (but that's not important).
I have managed to write characters to the USB emulated serial port using the C++ language with the g++ compiler set in NetBeans IDE using the following code:
#include <cstdlib>
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
/*
* 'open_port()' - Open serial port 1.
*
* Returns the file descriptor on success or -1 on error.
*/
int
open_port(void)
{
int fd; /* File descriptor for the port */
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
/*
* Could not open the port.
*/
perror("open_port: Unable to open /dev/ttyUSB0 - ");
}
else
fcntl(fd, F_SETFL, 0);
int pannumber = 90;
char str[256];
int tiltnumber = 90;
char str2[256];
char panchar = 0xA5;
char tiltchar = 0xA5;
while (tiltchar != 0x00) {
printf ("Enter the pan number: ");
gets ( str );
printf ("\n");
pannumber = atoi ( str );
printf ("Enter the tilt number: ");
gets ( str2 );
printf ("\n");
tiltnumber = atoi ( str2 );
panchar = (char) pannumber;
tiltchar = (char) tiltnumber;
int n = 0;
char mydata[] = { 'U' , 'U' , 'U' , 'U' , panchar , tiltchar };
n = write(fd, mydata, 6);
if (n < 0)
fputs("write() of 6 bytes failed!\n", stderr);
}
close(fd);
return (fd);
}
This works fine : (The "UUUU" chars are used to handshake with the sketch running on the arduino and the next two chars set the servo angle).
Question:
I have tried to do the same thing using Qt Creator with two graphical sliders which have values between 0 and 180 which are converted to chars and sent as above.
This also works BUT when compiling with Qt creator IDE it doesn't like the close(fd); command. If I comment this line out the program works but eventually it complains about too many files being open.
Qt code:
void MainWindow::TiltValueChangedHandler() {
tiltValue = ui->verticalSlider->value();
panValue = ui->horizontalSlider->value();
ui->label_3->setText(QString::number(tiltValue));
ui->label_4->setText(QString::number(panValue));
panValue++; // To prevent the error when 0 is sent over serial to range is effectively 1 to 181.
int fd; /* File descriptor for the port. */
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
/*
* Could not open the port.
*/
perror("open_port: Unable to open /dev/ttyUSB0 - ");
}
else
fcntl(fd, F_SETFL, 0);
char panchar = (char) panValue;
char tiltchar = (char) tiltValue;
int n = 0;
char mydata[] = { 'U' , 'U' , 'U' , 'U' , panchar , tiltchar };
n = write(fd, mydata, 6);
if (n < 0)
fputs("write() of 6 bytes failed!\n", stderr);
// close(fd);
// above line has to be commented out or will not compile but file not does not close!
return;
}
The Qt compiler error when close(fd) isn't commented out is:
error: no matching function for call to ‘MainWindow::close(int&)’

Use:
::close(fd);
to use global close function against QWidget::close

You may be missing stdio.h from Qt Creator.

Related

FIFO: One process never reads from pipe

I'm following THIS TutorialsPoint guide to Linux Piping, and I specifically need to use FIFOs.
However, the code doesn't work at all for the server side.
The server file either hangs indefinitely or it reads nothing, while the client instead writes on the FIFO and immediately reads it has just written.
Here's the full code for both files in case you don't want to go through TutorialsPoint:
fifoserver_twoway.cpp
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
void reverse_string(char *);
int main() {
int fd;
char readbuf[80];
char end[10];
int to_end;
int read_bytes;
/* Create the FIFO if it does not exist */
mkfifo(FIFO_FILE, S_IFIFO|0640);
strcpy(end, "end");
fd = open(FIFO_FILE, O_RDWR);
while(1) {
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOSERVER: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
to_end = strcmp(readbuf, end);
if (to_end == 0) {
close(fd);
break;
}
reverse_string(readbuf);
printf("FIFOSERVER: Sending Reversed String: \"%s\" and length is %d\n", readbuf, (int) strlen(readbuf));
write(fd, readbuf, strlen(readbuf));
/*
sleep - This is to make sure other process reads this, otherwise this
process would retrieve the message
*/
sleep(2);
}
return 0;
}
void reverse_string(char *str) {
int last, limit, first;
char temp;
last = strlen(str) - 1;
limit = last/2;
first = 0;
while (first < last) {
temp = str[first];
str[first] = str[last];
str[last] = temp;
first++;
last--;
}
return;
}
fifoclient_twoway.cpp
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FIFO_FILE "/tmp/fifo_twoway"
int main() {
int fd;
int end_process;
int stringlen;
int read_bytes;
char readbuf[80];
char end_str[5];
printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
fd = open(FIFO_FILE, O_CREAT|O_RDWR);
strcpy(end_str, "end");
while (1) {
printf("Enter string: ");
fgets(readbuf, sizeof(readbuf), stdin);
stringlen = strlen(readbuf);
readbuf[stringlen - 1] = '\0';
end_process = strcmp(readbuf, end_str);
//printf("end_process is %d\n", end_process);
if (end_process != 0) {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("FIFOCLIENT: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
} else {
write(fd, readbuf, strlen(readbuf));
printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
close(fd);
break;
}
}
return 0;
}
When I run both processes, this is what I get:
./fifoserver_twoway
FIFOSERVER: Received string: "" and length is 0
FIFOSERVER: Sending Reversed String: "" and length is 0
FIFOSERVER: Received string: "" and length is 0
FIFOSERVER: Sending Reversed String: "" and length is 0
./fifoclient_twoway
FIFOCLIENT: Sent string: "ciao" and string length is 4
FIFOCLIENT: Received string: "ciao" and length is 4
Enter string: why won't you reverse?
FIFOCLIENT: Sent string: "why won't you reverse?" and string length is 29
FIFOCLIENT: Received string: "why won't you reverse?" and length is 29
It's also worth noting that before starting to write this question, the server behaviour was completely different: instead of receiving nothing and printing like you see here, it would hang indefinitely after the "read" (and I haven't changed the code one bit, except for changing the FIFO_FILE path)
You let the server sleep after writing – but not the client. That way, the client still might read its own output back before the server can fetch it. So at very least you should add a sleep after both writes, letting the server sleep a bit longer to make sure the client wakes up first to read the servers output.
Accessing the same end of unnamed pipes (created via pipe functions) concurrently is undefined behaviour. While not sure for named pipes, I'd assume pretty much the same there as well. Synchronising concurrent access to such ends via simple delays (sleep, usleep) might perhaps do the trick, but it is a pretty unsafe method.
I'd rather recommend two separate pipes instead (as Tony Tannous proposed already), one for each direction (open the respective ends RDONLY or WRONLY as needed), then you get full duplex communication instead of half duplex and you don't need further synchronisation either (delays in most simple variant):
// server
int fd_cs = open(FIFO_FILE_CS, O_RDONLY);
int fd_sc = open(FIFO_FILE_SC, O_WRONLY);
read(fd_cs, ...);
write(fd_sc, ...);
// client
int fd_cs = open(FIFO_FILE_CS, O_WRONLY);
int fd_sc = open(FIFO_FILE_SC, O_RDONLY);
write(fd_cs, ...);
read(fd_sc, ...);

Slow response to serial AT commands using C, but fast response with minicom

I am having an issue reading from a serial port on a Beaglebone Black using a C++ script I've written. The script sends commands to and receives responses from an Adafruit FONA GSM/GPS device and works, except that there is a long delay between sending the commands and actually receiving any bytes back from the device (I have to put a 1 sec delay between the write and read commands in order to get the response from the device). However, when I use the minicom serial terminal emulator there is no perceptible delay between sending the command and receiving the response. I'm guessing it has to do with how I am opening the serial port, but I don't know what else I can change. Right now I have the settings set to raw input and output modes with no line control or echoing, but still unable to reduce the response time. Any help or ideas are more than welcome and appreciated! Thanks!
CPP file:
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <linux/serial.h>
#include <termios.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include "Fona_control.h"
Fona_control::Fona_control(void)
{
begin();
}
void Fona_control::get_gps(void)
{
printf("Reading GPS\n");
unsigned char gpsbuff[250];
memset(gpsbuff,'\0',250);
sleep(0.1);
int bytes_a = 0;
int n_write = write(fona_fd,GPS_GET_DATA,sizeof(GPS_GET_DATA)-1);
sleep(1);
ioctl(fona_fd,FIONREAD,&bytes_a);
printf("Bytes avail: %i\n",bytes_a);
int n_read = read(fona_fd,gpsbuff,bytes_a);
printf("Buffer: %s\n",gpsbuff);
printf("Bytes read: %i\n",n_read);
return;
}
void Fona_control::begin(void)
{
printf("FONA Beginning\n");
struct termios oldtio, newtio;
struct serial_struct serinfo;
// Load the pin configuration
/* Open modem device for reading and writing and not as controlling tty
because we don't want to get killed if linenoise sends CTRL-C. */
fona_fd = open(FONA_DEVICE, O_RDWR | O_NOCTTY | O_NDELAY);
if (fona_fd < 0) { perror(FONA_DEVICE); exit(-1); }
bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */
/* BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
CRTSCTS : output hardware flow control (only used if the cable has
all necessary lines. See sect. 7 of Serial-HOWTO)
CS8 : 8n1 (8bit,no parity,1 stopbit)
CLOCAL : local connection, no modem contol
CREAD : enable receiving characters */
cfsetspeed(&newtio,BAUDRATE_Fona);
newtio.c_cflag |= ( CLOCAL | CREAD );
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS8;
newtio.c_cflag &= ~PARENB;
newtio.c_cflag &= ~CRTSCTS;
newtio.c_cflag &= ~CSTOPB;
newtio.c_cc[VMIN] = 1;
newtio.c_cc[VTIME] = 1;
ioctl (fona_fd, TIOCGSERIAL, &serinfo);
serinfo.flags |= 0x4000;
ioctl (fona_fd, TIOCSSERIAL, &serinfo);
/* setup for non-canonical mode */
//newtio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | ISTRIP | INLCR | INPCK | ICRNL | IXON | IGNCR);
newtio.c_iflag = 0;
/* Set line flags */
//newtio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
newtio.c_lflag = 0;
/* Raw output
newtio.c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | ONOEOT| OFILL | OLCUC | OPOST); */
newtio.c_oflag = 0;
/* now clean the modem line and activate the settings for the port */
tcflush(fona_fd, TCIOFLUSH);
if(tcsetattr(fona_fd,TCSANOW,&newtio) < 0)
{
printf("Error Setting FONA Serial Port Attributes!");
exit(0);
};
/* terminal settings done, now send FONA initialization commands*/
sleep(1);
unsigned char buffer[50];
int bytes_avail = 0;
int n_write = 0;
int n_read = 0;
int cnt = 0;
memset(buffer,'\0',50);
tcflush(fona_fd, TCIOFLUSH);
while(strstr((char *)buffer,"OK") == NULL && cnt < 5)
{
memset(buffer,'\0',50);
n_write = write(fona_fd,FONA_AT,sizeof(FONA_AT)-1);
sleep(1);
ioctl(fona_fd,FIONREAD,&bytes_avail);
printf("BA: %i\n",bytes_avail);
if(bytes_avail > 0)
{
n_read = read(fona_fd,buffer,bytes_avail);
printf("%s\n",buffer);
}
sleep(1);
cnt++;
}
sleep(1);
n_write = write(fona_fd,"+++",3);
bytes_avail = 0;
sleep(1);
ioctl(fona_fd,FIONREAD,&bytes_avail);
printf("BA2: %i\n",bytes_avail);
n_read = read(fona_fd,buffer,bytes_avail);
printf("%s",buffer);
printf("AT Accepted\n");
sleep(1);
tcflush(fona_fd, TCIOFLUSH);
unsigned char buffer1[50];
memset(buffer1,'\0',50);
int n = write(fona_fd,FONA_ECHO_OFF,sizeof(FONA_ECHO_OFF)-1);
printf("Writ: %i\n",n);
bytes_avail = 0;
sleep(1);
ioctl(fona_fd,FIONREAD,&bytes_avail);
printf("BA2: %i\n",bytes_avail);
n = read(fona_fd,buffer1,bytes_avail);
printf("%s",buffer1);
memset(buffer1,'\0',50);
sleep(1);
n = write(fona_fd,GPS_POWER_ON,sizeof(GPS_POWER_ON)-1);
printf("Writ: %i\n",n);
bytes_avail = 0;
sleep(1);
ioctl(fona_fd,FIONREAD,&bytes_avail);
printf("BA2: %i\n",bytes_avail);
n = read(fona_fd,buffer1,bytes_avail);
printf("%s\n",buffer1);
memset(buffer1,'\0',50);
sleep(1);
n = write(fona_fd,FONA_SMS_TYPE,sizeof(FONA_SMS_TYPE)-1);
printf("Writ: %i\n",n);
bytes_avail = 0;
sleep(1);
ioctl(fona_fd,FIONREAD,&bytes_avail);
printf("BA2: %i\n",bytes_avail);
n = read(fona_fd,buffer1,bytes_avail);
printf("%s\n",buffer1);
sleep(1);
}
H File:
#ifndef _fona_control_H
#define _fona_control_H
#include <stdint.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#define FONA_DEVICE "/dev/ttyO5" //Beaglebone Black serial port
//#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define BAUDRATE_Fona B115200 // Change as needed, keep B
/* Define FONA AT Commands */
#define FONA_AT "AT\r\n"
#define FONA_ECHO_OFF "ATE0\r\n"
#define FONA_CMD_REPEAT "A/\r\n"
#define FONA_NO_ECHO "ATE0\r\n"
#define FONA_PIN_CHECK "AT+CPIN?\r\n"
#define FONA_PIN_SEND "AT+CPIN=1234\r\n"
#define FONA_SMS_TYPE "AT+CMGF=1\r\n"
/* Define FONA GPS AT Commands */
#define GPS_POWER_ON "AT+CGNSPWR=1\r\n"
#define GPS_POWER_OFF "AT+CGNSPWR=0\r\n"
#define GPS_GET_DATA "AT+CGNSINF\r\n"
/* Define FONA GPS NMEA Commands */
#define PMTK_CMD_HOT_START "AT+CGNSCMD=0,"$PMTK101*32"\r\n"
#define PMTK_CMD_WARM_START "AT+CGNSCMD=0,"$PMTK102*31"\r\n"
#define PMTK_CMD_COLD_START "AT+CGNSCMD=0,"$PMTK103*30"\r\n"
#define PMTK_SET_NMEA_5HZ "AT+CGNSCMD=0,"$PMTK220,200*2C"\r\n"
#define PMTK_SET_BAUD_38400 "AT+CGNSCMD=0,"$PMTK251,38400*27"\r\n"
#define PMTK_SET_WAAS "AT+CGNSCMD=0,"$PMTK301,2*2E"\r\n"
#define PMTK_SET_SBAS "AT+CGNSCMD=0,"$PMTK313,1*2E"\r\n"
#define PMTK_NMEA_TYPES "AT+CGNSCMD=0,"$PMTK314,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0*29"\r\n"
#define PMTK_STANDY_MODE "AT+CGNSCMD=0,"$PMTK161,0*28"\r\n" //Send any byte to exit standby mode
class Fona_control {
public:
void begin(void);
void get_gps(void);
Fona_control(void); // Constructor when using HardwareSerial
uint8_t fix_status, fix_mode, sats, sats_used, glo_sats, cn0;
uint8_t month, day, minute;
uint32_t year;
double seconds, latitude, longitude, speed, course, hdop, vdop, pdop, hpa, vpa;
int fona_fd;
private:
};
#endif
1 second sleeps are murderous and I can assure you minicom is not doing them. It will be waiting for data to come in, rather than polling, then displaying it. Serial data is slow. Every character sent at 9600 baud takes about a millisecond to arrive. at 115.2k baud, you're moving a character in a little less than 85 microseconds. Your Beaglebone, on the other hand, is working in nanoseconds, so if you don't wait before reading, the data won't be there yet. That said, you should have that "OK" in less than 1 ms and waiting a whole second is vast overkill.
Consider blocking and waiting for the three bytes needed for "OK"
while(strstr((char *)buffer,"OK") == NULL && cnt < 5)
{
memset(buffer,'\0',50);
n_write = write(fona_fd,FONA_AT,sizeof(FONA_AT)-1);
n_read = read(fona_fd,buffer,3);
cnt++;
}
This can block forever if the device never responds so you need a timeout
The easiest catch-all timeout mechanism I can think of off the top of my head is something like this:
int retry= 5;
while (retry)
{
fd_set readfs; // file descriptor set used by select
struct timeval timeout;
FD_ZERO(&readfs); // clear file descriptor set
FD_SET(fona_fd, &readfs); // set our port as the one item in the set
timeout.tv_sec = 1;
timeout.tv_usec = 0;
if (select(fona_fd+1, &readfs, NULL, NULL, &timeout) !=0)
{
rval = read(fona_fd, buffer, sizeof(buffer)-1);
// will stop reading after configurable gap between bytes read
buffer[rval] = '\0'; // NULL terminate the buffer or it ain't a string
// If not a string, results of strstr are undefined
if (strstr((char *)buffer,"OK") != NULL)
{
break;
}
}
retry--;
}
Documentation on select. It's very very cool function. Not as cool as epoll, but easier to find tutorials for. If you do a lot of select give epoll a good look over.
Select in this case will wait for data for 1 second before giving up. If it finds data, the program will enter the if body and read will read until some user configurable number of character-times have passed since the last character was received or the buffer is full.
We then null terminate the data in the buffer so we can use string handling routines on it. This also eliminates the need to memset the buffer. If the buffer contains "OK", we exit the loop.
Otherwise the we decrement the retry counter and loop around to see if there are more retries.

Linux write to serial port in c++ returns bad file descriptor

I am writing a c++ program to send a receive data from a device located at /dev/ttyACM0. I am able to read from the device without a problem, I can write to it from the command line, but I can not write to it within my c++ program. Here is what I have so far:
#include "SerialComms.h"
#include <errno.h>
#include <string.h>
SerialComms::SerialComms() {
serial_filestream = -1;
//CONFIGURE THE PORT
//The flags (defined in /usr/include/termios.h - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html):
// Baud rate:- B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800, B500000, B576000, B921600, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, B3500000, B4000000
// CSIZE:- CS5, CS6, CS7, CS8
// CLOCAL - Ignore modem status lines
// CREAD - Enable receiver
// IGNPAR = Ignore characters with parity errors
// ICRNL - Map CR to NL on input
// PARENB - Parity enable
// PARODD - Odd parity (else even)
tcgetattr(serial_filestream, &options);
options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; //<Set baud rate
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
}
bool SerialComms::Init(string type){
//The flags (defined in fcntl.h):
// Access modes (use 1 of these):
// O_RDONLY - Open for reading only.
// O_RDWR - Open for reading and writing.
// O_WRONLY - Open for writing only.
//
// O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
// if there is no input immediately available (instead of blocking). Likewise, write requests can also return
// immediately with a failure status if the output can't be written immediately.
//
// O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process.
if (type == "USB"){ //if type equals USB, open USB
serial_filestream = open(USBSerial, O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
}else if (type == "UART1"){
serial_filestream = open(UART1, O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
}
if (serial_filestream == -1)
{
//ERROR - CAN'T OPEN SERIAL PORT
printf("Error - Unable to open: %s\n", type.c_str());
}else{
printf("Opened serial type: %s\n", type.c_str());
}
tcflush(serial_filestream, TCIFLUSH);
tcsetattr(serial_filestream, TCSANOW, &options);
printf("Serial Comms for type: %s intialized", type.c_str());
return 1;
}
bool SerialComms::Ping(){
WriteSerial("p,1,1;\n");
return true;
}
char* SerialComms::ReadSerial(){
if (serial_filestream != -1)
{
// Read up to 255 characters from the port if they are there
char rx_buffer[256];
int rx_length = read(serial_filestream, (void*)rx_buffer, 255); //Filestream, buffer to store in, number of bytes to read (max)
if (rx_length < 0)
{
//An error occured (will occur if there are no bytes)
}
else if (rx_length == 0)
{
//No data waiting
}
else
{
//Bytes received
rx_buffer[rx_length] = '\0';
int j = 0;
while (rx_buffer[j] != ';' && rx_buffer[j] != '\0' && rx_buffer[j] != '\n'){
j++;
}
char rx_return[j+1];
for(int i = 0; i < j+1; i++){
rx_return[i] = rx_buffer[i];
}
rx_return[j+1] = '\0';
return rx_return;
}
}
return '\0';
}
bool SerialComms::WriteSerial(string data_out){
const char * tx_buffer = data_out.c_str();
if (serial_filestream != -1)
{
int wr = write(serial_filestream, tx_buffer, strlen(tx_buffer));
if (wr < 0){
printf("Error writing to Serial: %s \n", strerror(errno));
return 0;
} else{
printf("WroteSerial: %s, %d \n", tx_buffer, strlen(tx_buffer));
return 1;
}
}else{
printf("Error writing to Serial filestream errors \n");
return 0;
}
return 1;
}
SerialComms::~SerialComms() {
close(serial_filestream);
}
If I use the command:
echo "p,1,1;\n" > /dev/ttyACM0
It works without a problem, and I receive back the expected data from the serial device.
However, my main loop in the c++ program calls the WriteSerial function once a minute like so:
void Ping(SerialComms serialLine){
serialLine.WriteSerial("p,1,1;\n");
printf("pinged at: %s \n", ctime(&now));
}
The first time the write is performed it doesn't give an error, but it doesn't seem to work, and the second write returns a bad file descriptor error. Also, it screws up reading from the port as well. Here is my program output:
WroteSerial: p,1,1;
, 7
pinged at: Wed Dec 30 19:08:05 2015
Error writing to Serial: Bad file descriptor
pinged at: Wed Dec 30 19:09:06 2015
I'm really not sure where to go from here so any suggestion would be appreciated. I'm pretty sure I'm missing something basic but I can't see it at the moment.

ioctrl using SCSI pass through

Using Windows I can easily communicate with my USB device using the following simplified code:
DWORD dwJunk; // discard results from DeviceIOControl()
int iReply;
char cBuffer[100];
// cBuffer is initialized here.
HANDLE hDevice; // handle to the drive to be examined
CString sDrive = _T(\\\\.\\H:); // drive H: for this test
hDevice = CreateFile(sDrive, // drive to open
GENERIC_READ | GENERIC_WRITE, // read and write access to the drive
FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
NULL, // default security attributes
OPEN_EXISTING, // disposition
0, // file attributes
NULL); // do not copy file attributes
iReply = DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &cBuffer, sizeof(cBuffer), &cBuffer, sizeof(cBuffer), &dwJunk, (LPOVERLAPPED)NULL);
I'm trying to do the same in linux but have not been able to figure out the ioctrl() parameters, or better put the structure. A code snippet would be vey much appreciated. Thanks.
Unfortunately the code I modified using your link didn't return any results. Here's the stripped code I used. ioctl() returned without errors:
#define DEF_TIMEOUT 5000 // 5 seconds
char cDiskName[] = "/dev/sg3";
int fd = open(cDiskName, O_RDWR);
if (fd < 0)
{
printf("Open error: %s, errno=%d (%s)\n", cDiskName, errno, strerror(errno));
return 1;
}
unsigned char turCmbBlk[] = {0x00, 0, 0, 0, 0, 0};
struct sg_io_hdr io_hdr;
unsigned char cIOBuffer[100];
// buffer initialization code omitted
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof(turCmbBlk);
io_hdr.mx_sb_len = sizeof(cIOBuffer);
io_hdr.dxfer_direction = SG_DXFER_NONE;
io_hdr.cmdp = turCmbBlk;
io_hdr.sbp = cIOBuffer;
io_hdr.timeout = DEF_TIMEOUT;
if (ioctl(fd, SG_IO, &io_hdr) < 0)
{
printf("ioctl error: errno=%d (%s)\n", errno, strerror(errno));
}
// Code returned here without any errors but cIOBuffer remains unchanged.
Maybe the call needs a different request code?
Here's some more documentation:
Notes on Linux's SG driver version 2.1.36
SCSI-Programming, page 8 (handle_SCSI_cmd function), page 9, page 11 (example) and some more
Generic SCSI Target Subsystem for Linux
See here:
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
Parameters:
Filedescriptor (must be open!)
Request code number (depends on device)
Untyped pointer to memory (going to / coming from driver)
Example
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>
void main(int argc, char **argv)
{
const char *filename;
int fd;
filename = argv[1];
fd = open(filename, O_WRONLY);
ioctl(fd, USBDEVFS_RESET, 0);
close(fd);
return;
}
Documentation:
ioctl(2) - Linux man page
IOCTL(2)
Generic I/O Control operations (GNU libc)
The ioctl() Requests
usb.c (Example that might help you)
Linux / Unix Command: ioctl
How to Reset USB Device in Linux (Example)
An example Program with IOCTL
Edit
#define BUFF_SIZE 100 // - Buffersize
#define DEF_TIMEOUT 5000 // 5 seconds
char cDiskName[] = "/dev/sg3";
int fd = open(cDiskName, O_RDWR);
if (fd < 0)
{
printf("Open error: %s, errno=%d (%s)\n", cDiskName, errno, strerror(errno));
return 1;
}
unsigned char turCmbBlk[] = {0x00, 0, 0, 0, 0, 0};
struct sg_io_hdr *p = (struct sg_io_hdr *) malloc(sizeof(struct sg_io_hdr)); // - dynamic memory allocation - free() required somewhere
unsigned char cIOBuffer[BUFF_SIZE];
unsigned char replyBuffer[BUFF_SIZE]; // - dxfer buffer
// buffer initialization code omitted
memset(p, 0, sizeof(struct sg_io_hdr));
p->interface_id = 'S';
p->cmd_len = sizeof(turCmbBlk);
p->mx_sb_len = BUFF_SIZE;
p->dxfer_direction = SG_DXFER_NONE;
p->cmdp = turCmbBlk;
p->sbp = cIOBuffer;
p->timeout = DEF_TIMEOUT;
p->flags = SG_FLAG_DIRECT_IO; // - Does this help?
p->dxferp = replyBuffer; // - Set dxferp buffer - (A)
p->dxfer_len = BUFF_SIZE; // - buffersize
if (ioctl(fd, SG_IO, p) < 0)
{
printf("ioctl error: errno=%d (%s)\n", errno, strerror(errno));
}
// Code returned here without any errors but cIOBuffer remains unchanged.
Note (A): Please try setting your input / output buffer you work on here.
Documentation:
SCSI Generic HOWTO, SG_IO_HDR_T
Tour the Linux generic SCSI driver

Winpcap saving raw packets not from an adapter

I am trying to build an application that converts my old custom Ethernet logs (bin files) to standard winpcap style logs.
The problem is that I can't seem to find an example of how to opening a pcap_t* without using an adapter (network card). The temp.pkt has not been created.
I have looked thou the examples provided with Winpcap and all of them use a live adapter when dumping packets. This example is the closest \WpdPack\Examples-pcap\savedump\savedump.c is the closest, see example below slightly modified.
#ifdef _MSC_VER
/*
* we do not want the warnings about the old deprecated and unsecure CRT functions
* since these examples can be compiled under *nix as well
*/
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "pcap.h"
int main(int argc, char **argv)
{
pcap_if_t *alldevs;
pcap_if_t *d;
int inum;
int i=0;
pcap_t *adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
pcap_dumper_t *dumpfile;
/* Open the adapter */
if ((adhandle= pcap_open(??????, // name of the device
65536, // portion of the packet to capture.
// 65536 grants that the whole packet will be captured on all the MACs.
1, // promiscuous mode (nonzero means promiscuous)
1000, // read timeout
errbuf // error buffer
)) == NULL)
{
fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
/* Open the dump file */
dumpfile = pcap_dump_open(adhandle, argv[1]);
if(dumpfile==NULL) {
fprintf(stderr,"\nError opening output file\n");
return -1;
}
// ---------------------------
struct pcap_pkthdr header;
header.ts.tv_sec = 1 ; /* seconds */
header.ts.tv_usec = 1; /* and microseconds */
header.caplen = 100; /* length of portion present */
header.len = 100 ; /* length this packet (off wire) */
u_char pkt_data[100];
for( int i = 0 ; i < 100 ; i++ ) {
pkt_data[i] = i ;
}
pcap_dump( (u_char *) dumpfile, &header, (u_char *) &pkt_data);
// ---------------------------
/* start the capture */
// pcap_loop(adhandle, 0, packet_handler, (unsigned char *)dumpfile);
pcap_close(adhandle);
return 0;
}
I suggest doing that using pcap_t since using WinPcap is better than writing it yourself.
The following steps is how to do it:
Use pcap_open_dead() function to create a pcap_t. Read the function description here. The linktype for Ethernet is 1.
Use pcap_dump_open() function to create a pcap_dumper_t.
Use pcap_dump() function to write the packet to the dump file.
I hope this would help you.
If all you're doing is converting your own file format to .pcap, you don't need a pcap_t*, you can just use something like:
FILE* create_pcap_file(const char *filename, int linktype)
{
struct pcap_file_header fh;
fh.magic = TCPDUMP_MAGIC;
fh.sigfigs = 0;
fh.version_major = 2;
fh.version_minor = 4;
fh.snaplen = 2<<15;
fh.thiszone = 0;
fh.linktype = linktype;
FILE *file = fopen(filename, "wb");
if(file != NULL) {
if(fwrite(&fh, sizeof(fh), 1, file) != 1) {
fclose(file);
file = NULL;
}
}
return file;
}
int write_pcap_packet(FILE* file,size_t length,const unsigned char *data,const struct timeval *tval)
{
struct pcap_pkthdr pkhdr;
pkhdr.caplen = length;
pkhdr.len = length;
pkhdr.ts = *tval;
if(fwrite(&pkhdr, sizeof(pkhdr), 1, file) != 1) {
return 1;
}
if(fwrite(data, 1, length, file) != length) {
return 2;
}
return 0;
}