ioctrl using SCSI pass through - c++

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

Related

Capturing YUYV in c++ using v4l2

I have a webcam connected to beaglebone via usb. I am coding in c++ and my goal is to capture raw UNCOMPRESSED picture from the webcam.
Firstly i checked what formats are supported via command v4l2-ctl --list-formats and the result was:
Index : 0
Type : Video Capture
Pixel Format: 'MJPG' (compressed)
Name : Motion-JPEG
Index : 1
Type : Video Capture
Pixel Format: 'YUYV'
Name : YUYV 4:2:2
So from this I assume it has to be possible to get an uncompressed picture if i try to use YUYV format.
Knowing this I started writing a program in c++. I successfully written a program to capture a compressed picture, but when trying to capture using format YUYV it doesnt work and i really need some help to get this done.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <libv4l2.h>
template <typename typeXX>
void clear_memmory(typeXX* x) {
memset(x, 0, sizeof(*x));
}
void xioctl(int cd, int request, void *arg){
int response;
do{
//ensures we get the correct response.
response = v4l2_ioctl(cd, request, arg);
}
while (response == -1 && ((errno == EINTR) || (errno == EAGAIN)));
if (response == -1) {
fprintf(stderr, "error %d, %s\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
struct LMSBBB_buffer{
void* start;
size_t length;
};
int main(){
const char* dev_name = "/dev/video0";
int width=1920;
int height=1080;
int fd = v4l2_open(dev_name, O_RDWR | O_NONBLOCK, 0);
struct v4l2_format format = {0};
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width = width;
format.fmt.pix.height = height;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;//V4L2_PIX_FMT_YUYV //V4L2_PIX_FMT_RGB24
format.fmt.pix.field = V4L2_FIELD_NONE; //V4L2_FIELD_NONE
xioctl(fd, VIDIOC_S_FMT, &format);
printf("Device initialized.\n");
///request buffers
struct v4l2_requestbuffers req = {0};
req.count = 2;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
xioctl(fd, VIDIOC_REQBUFS, &req);
printf("Buffers requested.\n");
///mapping buffers
struct v4l2_buffer buf;
LMSBBB_buffer* buffers;
unsigned int i;
buffers = (LMSBBB_buffer*) calloc(req.count, sizeof(*buffers));
for (i = 0; i < req.count; i++) {
clear_memmory(&(buf));
(buf).type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
(buf).memory = V4L2_MEMORY_MMAP;
(buf).index = i;
xioctl(fd, VIDIOC_QUERYBUF, &buf);
buffers[i].length = (buf).length;
printf("A buff has a len of: %i\n",buffers[i].length);
buffers[i].start = v4l2_mmap(NULL, (buf).length, PROT_READ | PROT_WRITE, MAP_SHARED,fd, (buf).m.offset);
if (MAP_FAILED == buffers[i].start) {
perror("Can not map the buffers.");
exit(EXIT_FAILURE);
}
}
printf("Buffers mapped.\n");
for (i = 0; i < req.count; i++) {
clear_memmory(&(buf));
(buf).type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
(buf).memory = V4L2_MEMORY_MMAP;
(buf).index = i;
ioctl(fd,VIDIOC_QBUF, &(buf));
}
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd,VIDIOC_STREAMON, &type);
printf("buffers queued and streaming.\n");
int pic_count=0;
///CAPTURE
fd_set fds;
struct timeval tv;
int r;
char out_name[256];
FILE* fout;
do {
FD_ZERO(&fds);
FD_SET(fd, &fds);
// Timeout.
tv.tv_sec = 2;
tv.tv_usec = 0;
r = select(fd + 1, &fds, NULL, NULL, &tv);
} while ((r == -1 && (errno = EINTR)));
if (r == -1) {
perror("select");
exit(EXIT_FAILURE);
}
clear_memmory(&(buf));
(buf).type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
(buf).memory = V4L2_MEMORY_MMAP;
xioctl(fd,VIDIOC_DQBUF, &(buf));
printf("Buff index: %i\n",(buf).index);
sprintf(out_name, "image%03d.ppm",pic_count);
fout = fopen(out_name, "w");
if (!fout) {
perror("Cannot open image");
exit(EXIT_FAILURE);
}
fprintf(fout, "P6\n%d %d 255\n",width, height);
fwrite(buffers[(buf).index].start, (buf).bytesused, 1, fout);
fclose(fout);
pic_count++;
clear_memmory(&(buf));
(buf).type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
(buf).memory = V4L2_MEMORY_MMAP;
xioctl(fd,VIDIOC_DQBUF, &(buf));
printf("Buff index: %i\n",(buf).index);
sprintf(out_name, "image%03d.ppm",pic_count);
fout = fopen(out_name, "w");
if (!fout) {
perror("Cannot open image");
exit(EXIT_FAILURE);
}
fprintf(fout, "P6\n%d %d 255\n",width, height);
fwrite(buffers[(buf).index].start, (buf).bytesused, 1, fout);
fclose(fout);
pic_count++;
///xioctl(fd,VIDIOC_QBUF, &(buf));
return 0;
}
in line 50, i can choose the format between V4L2_PIX_FMT_YUYV and V4L2_PIX_FMT_RGB24.
for V4L2_PIX_FMT_RGB24 i get the picture, but when using V4L2_PIX_FMT_YUYV I get this error:
libv4l2: error dequeuing buf: Resource temporarily unavailable
libv4l2: error dequeuing buf: Resource temporarily unavailable
libv4l2: error dequeuing buf: Resource temporarily unavailable
libv4l2: error dequeuing buf: Resource temporarily unavailable
libv4l2: error dequeuing buf: Resource temporarily unavailable
the error lines goes for ever until i end the program manually.
Does anyone have an idea what to do? I spent over 2 weeks on this and i can't move anywhere from here. I would really appreciate any advice.
From what I see you are requesting a FullHD (1920x1080) buffer in YUYV format from a camera. You did not mention the camera type/model/specs, but if it is a generic USB-attached hardware most likely you will not get a raw FullHD YUYV buffer as an output, only the MJPEG one (which you can decode to YUV, if you hack around with libjpeg) or the decoded RGB buffer (which is pretty much the decoded MJPEG with YUV->RGB conversion) which is not mmapped.
The exact list of formats with framerates can be requested by this command, which would probably tell you it does not provide a 1920x1080 YUYV, only something smaller, like 640x480:
v4l2-ctl --list-formats
If you need video processing with "true" zero-copy access to raw YUYV camera frames, you need direct access to hardware and that specific hardware in the first place. Once you have the USB interface between your software and the camera, you get an extra indirection and that means the speed goes down. Think for a moment, the YUYV frame at 1920x1080 takes up approximately 4 Megabytes of memory. At 30 FPS this is 120 Megabytes (or 960 Megabits) per second of bus throughput. If you have a USB2.0 camera, there is just no bandwidth to support this (thus the need for MJPEG). Even at 15FPS this is 480 Megabits, not counting the USB latency and protocol overhead.
To provide some "actionable feedback" I would advice to first concentrate on the algorithms (probably, you just don't want to loose the processing speed at the very first step) which you want to apply to the image. Don't hesitate to use OpenCV for camera input and basic image processing, later you can switch to some hardware interface and hand-written algorithms.
The easier way of getting raw frames would be to use Android's camera interface and try to process the incoming frames with GLSL shaders using the GL_TEXTURE_EXTERNAL_OES extension, about which there information and code samples available. There you can connect GL textures to AHardwareBuffer instances and then use AHardwareBuffer_lock function to get raw pointers. The exact supported formats also may vary across the hardware, so do not expect this to be super-easy.
I've recently had a similar issue. In my case the camera driver needed the VIDIOC_S_PARM ioctl in order to set the frame rate and initialize the camera for the selected capture mode.
You can try to add this code after the VIDIOC_S_FMT and see if it works for you as well:
struct v4l2_streamparm streamparam;
memset(&streamparam, 0, sizeof(streamparam));
streamparam.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
xioctl(fd, VIDIOC_G_PARM, &streamparam);
streamparam.parm.capture.timeperframe.numerator = 1;
streamparam.parm.capture.timeperframe.denominator = 5;
xioctl(fd, VIDIOC_S_PARM, &streamparam);

Running an executable from a C++ program in the same process

Is that possible? I'd like an easy access to the executable's memory to edit it. Alternately, when I'm not the administrator, is it possible to edit the executable's memory from another process? I've tried the ptrace library and it fails if I'm not the administrator. I'm on Linux
I'm not entirely sure what you are asking, but this is possible with shared memory.
See here: http://www.kernel.org/doc/man-pages/online/pages/man7/shm_overview.7.html
This is what a debugger does. You could look at the code of an open source debugger, e.g. gdb, to see how it works.
The answer:
Yes - it works: you don't have to be administrator / root, but of course you need the rights to access the process' memory, i.e. same user.
No - it is not easy
The possibility to write to /proc/pid/mem was added some time ago to the Linux kernel. Therefore it depends on the kernel you are using. The small programs were checked with kernel 3.2 where this works and 2.6.32 where it fails.
The solution consists of two programs:
A 'server' which is started, allocates some memory, writes some pattern into this memory and outputs every three seconds the memory contents which is placed after the pattern is printed.
A 'client' which connects via the /proc/pid/maps and /proc/pid/mem to the server, searches for the pattern and writes some other string into the server's memory.
The implementation uses heap - but as long as the permissions allow - it is also possible to change other portions of the other process' memory.
This is implemented in C, because it is very 'low level' - but it should work in C++. It is a proof of concept - no production code - e.g. there are some error checks missing and it has some fixed size buffers.
memholder.c
/*
* Alloc memory - write in some pattern and print out the some bytes
* after the pattern.
*
* Compile: gcc -Wall -Werror memholder.c -o memholder.o
*/
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
char * m = (char*) malloc(2048);
memset(m, '\xAA', 1024);
strcpy(m + 1024, "Some local data.");
printf("PID: %d\n", getpid());
while(1) {
printf("%s\n", m + 1024);
sleep(3);
}
return 0;
}
memwriter.c
/*
* Searches for a pattern in the given PIDs memory
* and changes some bytes after them.
*
* Compile: gcc -Wall -std=c99 -Werror memwriter.c -o memwriter
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
int open_proc_file(pid_t other_pid, char const * const sn,
int flags) {
char fname[1024];
snprintf(fname, 1023, "/proc/%d/%s", other_pid, sn);
// Open file for reading and writing
int const fd = open(fname, flags );
if(fd==-1) {
perror("Open file");
exit(1);
}
return fd;
}
void get_heap(int fd_maps, size_t * heap_start, size_t * heap_end) {
char buf[65536];
ssize_t const r = read(fd_maps, buf, 65535);
if(r==-1) {
perror("Reading maps file");
exit(1);
}
buf[r] = '\0';
char * const heap = strstr(buf, "[heap]");
if(heap==NULL) {
printf("[heap] not found in maps file");
exit(1);
}
// Look backward to the latest newline
char const * hl_start;
for(hl_start = heap; hl_start > buf && *hl_start != '\n';
--hl_start) {}
// skip \n
++hl_start;
// Convert to beginnig and end address
char * lhe;
*heap_start = strtol(hl_start, &lhe, 16);
++lhe;
*heap_end = strtol(lhe, &lhe, 16);
}
int main(int argc, char *argv[]) {
if(argc!=2) {
printf("Usage: memwriter <pid>\n");
return 1;
}
pid_t const other_pid = atoi(argv[1]);
int fd_mem = open_proc_file(other_pid, "mem", O_RDWR);
int fd_maps = open_proc_file(other_pid, "maps", O_RDONLY);
size_t other_mem_start;
size_t other_mem_end;
get_heap(fd_maps, &other_mem_start, &other_mem_end);
ptrace(PTRACE_ATTACH, other_pid, NULL, NULL);
waitpid(other_pid, NULL, 0);
if( lseek(fd_mem, other_mem_start, SEEK_SET) == -1 ) {
perror("lseek");
return 1;
}
char buf[512];
do {
ssize_t const r = read(fd_mem, buf, 512);
if(r!=512) {
perror("read?");
break;
}
// Check for pattern
int pat_found = 1;
for(int i = 0; i < 512; ++i) {
if( buf[i] != '\xAA' )
pat_found = 0;
break;
}
if( ! pat_found ) continue;
// Write about one k of strings
char const * const wbuf = "REMOTE DATA - ";
for(int i = 0; i < 70; ++i) {
ssize_t const w = write(fd_mem, wbuf, strlen(wbuf));
if( w == -1) {
perror("Write");
return 1;
}
}
// Append a \0
write(fd_mem, "\0", 1);
break;
} while(1);
ptrace(PTRACE_DETACH, other_pid, NULL, NULL);
close(fd_mem);
close(fd_maps);
return 0;
}
Example output
$ ./memholder
PID: 2621
Some local data.
Some local data.
MOTE DATA - REMOTE DA...
Other interpretation
There is also another interpretation of your question (when reading the headline and not the question), that you want to replace the 'executable' from one process with another one. That can be easily handled by exec() (and friends):
From man exec:
The exec() family of functions replaces the current process image with a new process image.
In Windows, the methods used for this are named ReadProcessMemory / WriteProcessMemory, you will, however, need administrative rights for this. The same is for linux, as I've said in my comment, no sane system would allow user process to modify non-owned memory.
For linux, the only function is ptrace. You will need to be administrator.
http://cboard.cprogramming.com/cplusplus-programming/92093-readprocessmemory-writeprocessmemory-linux-equivalent.html contains more detailed discussion.
Can you imagine the consequences of allowing process to modify other process memory, without being administrator?

Close() filedescriptor using Qt in Linux

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.

C++(Serial Communicatio using the <windows.h>) - How can i find out before hand, how many characters will be read by the ReadFile() method

ReadFile( hSerial , buffer , 25, &dwBytesRead , 0);
Hey ppl
My question is how do i find out how many characters my ReadFile statement will return before calling the ReadFile?. The device i am communicating with, returns different data based on what was sent. Concerning the above ReadFile, in that instance i knew that the returned data would be 25 characters long, but what if i dont know the answer, how can i substitute 25 with a variable that will be enough for any amount of data received.
In my code you will see i have 2 Readfile statements, in both cases i knew the amount of data i would receive, to i sent a fixed number, what happens when i dont know that amount?
#include "stdafx.h"
#include "windows.h"
BOOL SetCommDefaults(HANDLE hSerial);
void StripCRLFandPrint(char *command);
char buffer[1000];
HANDLE hSerial;
DWORD dwBytesRead = 0;
DWORD dwBytesWritten = 0;
char trash;
int main(int argc, char* argv[])
{
hSerial = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0 , 0 , OPEN_EXISTING , 0 , 0);
if (hSerial == INVALID_HANDLE_VALUE) return GetLastError();
SetCommDefaults(hSerial);//Initializing the Device Control Block
COMMTIMEOUTS timeouts={0};
timeouts.ReadIntervalTimeout=50;
timeouts.ReadTotalTimeoutConstant=50;
timeouts.ReadTotalTimeoutMultiplier=10;
timeouts.WriteTotalTimeoutConstant=50;
timeouts.WriteTotalTimeoutMultiplier=10;
char szRxChar[3];//varialble holds characters that will be sent
szRxChar[0] = '?';
DWORD y =0, z =0;
char buf[327];// will hold the data received
memset(buf,0,327);//initializing the buf[]
memset(buffer,0,10000);
WriteFile( hSerial , &szRxChar , 1, &dwBytesWritten ,0);
ReadFile( hSerial , buf , sizeof(buf), &dwBytesRead , 0);
printf("Retrieving data...\n\n");
//Displaying the buffer
printf( "%s",buf);
printf("\nData Read: %i\n",dwBytesRead);
printf("Enter an option:");
scanf("%c%c",&szRxChar,&trash);//Reading the next command to be sent
while(szRxChar[0] != '1')//Press one to exit
{
memset(buffer,0,10000);
//StripCRLFandPrint(szRxChar);
WriteFile( hSerial , &szRxChar, 1, &dwBytesWritten ,0);
ReadFile( hSerial , buffer , 25, &dwBytesRead , 0);
printf("%s",buffer);
printf("\nData Read: %i\n",dwBytesRead);
printf("\n");
printf("Enter an Option:");
scanf("%c%c",&szRxChar,&trash);
}
CloseHandle(hSerial);// Closing the handle
return 0;
}
You can't know what you are asking for, because no software can make predictions regarding the behaviour of a remote end. For this reason, the reading should take place in a different thread. In the reading thread you can instruct ReadFile to read one byte at a time. You can choose to read more bytes at the same time, but then you are running the risk of having received a full message from the other part and still do not get a notification, because ReadFile is blocked waiting for more data.
It may be challenging to create the threading code yourself. I recommend that you search for a library that already handles this for you.
You won't ever know exactly what was sent, but instead of putting 25, use sizeof(buffer) instead.
Keep in mind that ReadFile() isn't perfect. I have experienced issues on slower hardware whereas ReadFile() does not always read in the complete message sent over the COM port. Therefore, it may be beneficial to read in byte-by-byte, albeit slower, to ensure you get the entire message:
int c;
DWORD dwBytesRead = 0;
if (!(pcState[readerPort] & PORT_OPEN)) {
RecvIndex = 0;
Sleep(1000);
return;
}
ReadFile(hComm[readerPort], buff, 1, &dwBytesRead, NULL); // array of handles used here
c = buff[0];
if (dwBytesRead == 0) { // possible end of transmission
if (RecvTimer++ > 3) {
RecvTimer = 0;
if (RecvIndex) { // have receive some data prior
keyBuf[RecvIndex] = 0;
RecvIndex = 0;
processBuffer(keyBuf);
memset(keyBuf, 0, sizeof(keyBuf));
}
}
} else {
RecvTimer = 0; //Restart timer
if (RecvIndex == 0) { // first character
memset(keyBuf, 0, sizeof(keyBuf));
keyBuf[0] = (unsigned char)c;
RecvIndex = 1;
} else { // get remaining characters
if (RecvIndex < sizeof(keyBuf))
keyBuf[RecvIndex++] = (unsigned char)c;
}
}
in the example above, keyBuf is a private class variable and the above code is part of a function that is called in a while loop.

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;
}