Driver accessing ports with inb() and outb() - c

I am making a device driver that turns on and off the keyboard leds by receiving any combination of three, two, one or none digit which should be 1, 2 or 3, if I make:
echo 12 > /dev/ledDevice
The program should turn on Num lock, Caps lock and turn off scroll lock, if I write:
echo "" > /dev/ledDevice
Every led should be turned off, or turn on if it would be echo 123 but this does not happen, they always turn off. They are ubicated (in debian 6) in a port represented with an integer, in the positions o, 1 and 2. Additionally but I don't know if it's related, outb produce this exit on system log
atkbd.c: Spurious ACK on isa0060/serio0. Some program might be trying access hardware directly.
This is my source
static ssize_t
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
char aux[BUF_LEN];
int state = 0x00;
int stateInitial = 0x00;
int i =0;
int timeout = 0;
int retries = 7;
printk(KERN_ALERT "Entering device_write");
if(copy_from_user(aux,buff,len)){
printk(KERN_ALERT "Problems in copy from user");
return -EINVAL;
}
if (len <= 4){
for (i=0; i<len;i++){
if(aux[i] == '3'){
state = state | 0x01; //Scroll lock
}else if(aux[i] == '1'){
state = state | 0x02; //Caps lock
}else if(aux[i]== '2'){
state= state | 0x04; //Num lock
}else if (aux[i] != '\n'){
printk(KERN_ALERT "Error, wrong input.");
return -EINVAL;
}
}
}else return -EINVAL;
if (down_interruptible(&mtx)) /*SEMAPHORE LOCK*/
return -EINTR;
stateInitial = inb(0xed);
stateInitial = stateInitial & 0xF8; //248 mask that deletes the 1, 2 and 3 bits (the led ones)
state = stateInitial | state;
/*
AquĆ­ se modifican los leds
*/
timeout = 1000;
outb(0xed,0x60); // Telling the keyboard that we want to modify the leds
udelay(timeout);
while (retries!=0 && inb(0x60)!=0xfa) { // Waiting for the controller
retries--;
udelay(timeout);
}
if (retries!=0) { // prooving the keyboard is ready
outb(state,0x60);
}else{
up(&mtx);
return -EINVAL;
}
up(&mtx);
printk(KERN_ALERT "getting out from device_write, %d bytes read",len);
return len;
}

This can be triggered in a myriad of situations. Quite a few key switchers and other tools trigger the ATKBD_RET_NAK and in some cases we sure are helpless.
Considering your code to be just, let us try to break the error code. From the looks of it, the error seems to arise from the atkbd_interrupt call.
atkbd_interrupt() deals with the processing of data received from the keyboard to events.
static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char
data, unsigned int flags)
{----}
The specific error message occurs due to the triggering of case ATKBD_RET_NAK coupled to unsigned char data argument.
case ATKBD_RET_NAK:
if (printk_ratelimit())
dev_warn(&serio->dev,
"Spurious %s on %s. "
"Some program might be trying access hardware directly.\n",
data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
The atkbd provides access to AT enhanced keyboard connected to the AT keyboard controller. Try bypassing KVM .

Related

State machine per thread for socket client communication

I am working on threaded TCP socket server for handling multiple socket client connection. Clients can connect and disconnect asynchronously with server, upon connection, the client should send some data in predefined custom packet protocol format.
The protocol has start of frame(SOP) and end of frame (EOP) defined.
I have written a C code such that for each successful client connection, a thread gets created that keeps on receiving the bytes from client in the predefined packet format, the thread has a thread-local state machine because each client can connect asynchronously so the states for each client may be different.
Below is the thread that receives that data from client and maintains a state based on the type of byte received:
static void *receive_handler(void *args) {
struct thread_args *local_args = args;
struct sockaddr_in6 *client_address = local_args->client_address;
//struct itimerval timer_val;
int32_t conn_fd = local_args->conn_fd;
int32_t val_read = 0;
int32_t resp_code = 0;
uint32_t sendBuffLen = 0;
int8_t buffer[BUFFER_SIZE] = { 0 };
uint8_t RetBuff[1024] = { 0 };
int8_t rx_addr_str[INET6_ADDRSTRLEN];
int8_t byte = 0;
int16_t idx = ePacketType;
int16_t packet_len = 0;
int16_t calculated_crc = 0, recv_crc = 0;
uint16_t num_bytes = 0;
memset(rx_addr_str, 0, INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &(client_address->sin6_addr), rx_addr_str, INET6_ADDRSTRLEN);
printf("\nRx Thread (%d) Created for %s\n", local_args->connection_no, rx_addr_str);
int eState = eStart_Frame;
memcpy(rx_Packet_Info[local_args->connection_no].inet6, rx_addr_str, INET6_ADDRSTRLEN);
//timerclear(&timer_val.it_interval); /* zero interval means no reset of timer */
//timerclear(&timer_val.it_value);
//timer_val.it_value.tv_sec = 10; /* 10 second timeout */
//(void) signal(SIGALRM, state_reset_handler);
while (1) {
if (eState != eChecksum_Verify) {
val_read = -1;
val_read = recv(conn_fd, &byte, sizeof(byte), 0);
debug_printf(INFO, "Amount Read: %d Byte Rxd: 0x%x => 0x%X\n", val_read, (byte & 0xFF), byte);
if (val_read <= 0) {
if (parse_packet("ERR_DISCONNECT", rx_addr_str, local_args->connection_no) < 0) {
debug_printf(ERR, "Error parsing packet: %s\n", strerror(errno));
}
debug_printf(ERR, "May be closed by client %s: %s\n", rx_addr_str, strerror(errno));
debug_printf(ERR, "Exiting Rx Thread: ConnIdx: %d", num_connections);
close(conn_fd);
pthread_exit(NULL);
}
}
switch (eState) {
case eStart_Frame:
debug_printf(DEBG, "Current State: %d\n", eState);
if ((val_read > 0) && (byte & 0xFF) == SOP) {
memset(buffer, 0, BUFFER_SIZE);
val_read = -1;
buffer[eSOP] = (byte & 0xFF);
eState = eFrame_Len;
}
break;
case eFrame_Len: {
static char MSB_Rxd = 0;
debug_printf(DEBG, "Current State: %d\n", eState);
if (val_read > 0) {
if (MSB_Rxd == 0) {
buffer[ePacket_length] = byte;
MSB_Rxd = 1;
}
else {
buffer[ePacket_length + 1] = byte;
eState = eFrame;
num_bytes = 0;
MSB_Rxd = 0;
packet_len = (buffer[ePacket_length] & 0xFF << 8) | (buffer[ePacket_length + 1]);
debug_printf(INFO, "Packet Length: %d : 0x%x 0x%x\n", packet_len,
buffer[ePacket_length], buffer[ePacket_length + 1]);
}
}
}
break;
case eFrame:
debug_printf(DEBG, "Current State: %d\n", eState);
num_bytes++;
buffer[idx] = byte;
if (num_bytes == packet_len) {
eState = eEnd_Frame;
debug_printf(DEBG, "Num bytes: 0x%x\n", num_bytes);
}
else {
debug_printf(ERR, "Num bytes: 0x%x Pkt Len: 0x%x\n", num_bytes, packet_len);
}
idx++;
break;
case eEnd_Frame:
debug_printf(ERR, "Current State: %d val read %d\n", eState, val_read);
if ((val_read > 0) && (byte & 0xFF) == EOP) {
val_read = -1;
eState = eChecksum_Verify;
}
break;
case eChecksum_Verify: {
calculated_crc = crc_16(&buffer[ePacket_length], (num_bytes));
recv_crc = buffer[num_bytes + 1] << 8 | (buffer[num_bytes + 2] & 0xFF);
if (calculated_crc != recv_crc) {
debug_printf(ERR, "CRC Error! CRC do not match!!\n");
debug_printf(ERR, "Calculated CRC: 0x%X\nCRC Rxd: 0x%X\n", calculated_crc, recv_crc);
resp_code = CRC_ERR;
send(conn_fd, &resp_code, sizeof(resp_code), 0);
}
else {
if (rx_Packet_Info[local_args->connection_no].packetUUID != NULL) {
free(rx_Packet_Info[local_args->connection_no].packetUUID);
rx_Packet_Info[local_args->connection_no].packetUUID = NULL;
}
rx_Packet_Info[local_args->connection_no].packetUUID = calloc(buffer[ePacketUUIDLen],
sizeof(uint8_t));
memcpy(rx_Packet_Info[local_args->connection_no].packetUUID, &buffer[ePacketUUID],
buffer[ePacketUUIDLen]);
rx_Packet_Info[local_args->connection_no].packetUUIDlength = buffer[ePacketUUIDLen];
printf("\nRX-Thread-UUID %d: ConnNo: %d\n", buffer[ePacketUUIDLen],
local_args->connection_no);
for (char i = 0; i < buffer[ePacketUUIDLen]; i++) {
printf("0x%x ", rx_Packet_Info[local_args->connection_no].packetUUID[i]);
}
printf("\n");
if (parse_packet(buffer, rx_addr_str, local_args->connection_no) < 0) {
debug_printf(ERR, "Error parsing packet: %s\n", strerror(errno));
}
}
num_bytes = 0;
eState = eStart_Frame;
idx = ePacketType;
}
break;
default:
debug_printf(DEBG, "Invalid State!! Should not come here.\n");
num_bytes = 0;
eState = eStart_Frame;
idx = ePacketType;
break;
}
}
return NULL;
}
My question is how should I reset this state machine if let's say after receiving start of frame the client gets stuck and is not able to send frame length or complete frame till end of frame?
One way I thought is to implement timer callback but I am not sure how should I keep track of state machine of multiple threads.
Can any one please suggest what should I do in this scenario or if I am doing anything wrong?
If I'm parsing the question correctly, you're asking about how to handle gracefully the situation where the connecting client isn't sending data in a timely manner -- i.e. it has sent the first part of a message, but (due to a network problem or a client-side bug or whatever) never sends the rest, leaving your server-side I/O thread blocked inside a recv() call for a long/indefinite time.
If so, the first question to ask is: is this really a problem? If each connection gets its own thread, then having one particular thread/connection blocked shouldn't cause any issues to the other threads, since they all execute independently of each other. So maybe you can just ignore the problem entirely?
However, the more likely answer is that ignoring the problem isn't quite good enough, because of a couple of subsequent problems that aren't easily ignorable: (a) what if too many client connections "freeze up" at the same time? One or two stalled TCP connections/threads isn't a big deal, but if the same problem keeps happening, eventually you'll run out of resources to spawn more threads or TCP connections, and then your server can no longer function. And (b) what if the server process wants to quit now? (i.e. because the server's user has sent it a SIGINT interrupt or similar) If one or more threads are blocked indefinitely, then it is impossible for the server to exit in a timely-and-controlled manner, because the main thread needs to wait for all the TCP threads to exit first before it can clean up its process-wide resources, and any blocked threads will not exit for a long time, if ever.
So, assuming that the problem does need to be addressed, the most reliable way I've found to address it is to never block in recv() (or send()) in the first place. Instead, make sure to put each socket in non-blocking mode, and have the thread's while-loop block only in a select() call instead. Doing it this way makes your state machine a bit more complex (since it will now have to handle partial-sends as well as partial-receives), but the compensating benefit is that the thread is now in better control of its own blocking behavior. In particular, you can tell select() to always return after a certain amount of time, no matter what, and (better yet) you can tell select() to return whenever any of a number of sockets has bytes ready to be read on it. That means that if your main thread wants to exit, it can use a pipe() or socketpair() to send a dummy-byte to each TCP thread, and the TCP thread (which is presumably blocked inside select(), waiting for either data from its client or from the pipe/socketpair socket) will immediately return from select(), see that the main thread has sent it a byte, and respond by exiting immediately.
That should be sufficient -- in my experience it is better not to impose fixed timeouts if you can avoid it, since it's hard to predict what network performance will be like in all cases, and any rule-of-thumb you might come up with (like "a client that doesn't send the whole message in 5 seconds must be broken") is likely to be wrong, and you'll end up with false-positive problems if you try to enforce that rule. Better to just let each client take as long as it wants/needs to, while also having a mechanism by which the main thread can request that a particular client thread exit immediately if/when that becomes necessary (e.g. during server-process shutdown, or if there are too many TCP threads active and you want to prune some of the old/inactive ones before spawning more)

Problem with writing from kernel to user space - linux device driver

I'm trying to write a simple Raspberry Pi GPIO driver, with four switches connected to four of the GPIO pins, that reads each switch state. The problem is, I'm not sure how to write from kernel to user space, I'm not getting anything when I insert my device kernel module and try to read the device file with cat command.
The device_read function is as follows:
static ssize_t gpio_driver_read(struct file *filp, char *buf, size_t len, loff_t *f_pos)
{
/* Size of valid data in gpio_driver - data to send in user space. */
int data_size = 0;
/* Counter for 'for' loop. */
int i;
/* Print to kernel space. */
printk(KERN_INFO "Reading active Switch state...\n");
for (i = 0; i < 4; i = i+1)
{
printk(KERN_INFO "Loop number %d...\n", i);
/* TODO: fill gpio_driver_buffer here. */
if (i == 0 && mySwitches[0])
sprintf(gpio_driver_buffer, "gpio_driver: gpio12 value: %d\n", GetGpioPinValue(GPIO_12));
else if (i == 1 && mySwitches[1])
sprintf(gpio_driver_buffer, "gpio_driver: gpio16 value: %d\n", GetGpioPinValue(GPIO_16));
else if (i == 2 && mySwitches[2])
sprintf(gpio_driver_buffer, "gpio_driver: gpio20 value: %d\n", GetGpioPinValue(GPIO_20));
else if (i == 3 && mySwitches[3])
sprintf(gpio_driver_buffer, "gpio_driver: gpio21 value: %d\n", GetGpioPinValue(GPIO_21));
printk(KERN_INFO "%s\n", gpio_driver_buffer);
/* Get size of valid data. */
data_size = strlen(gpio_driver_buffer);
printk(KERN_INFO "%d\n", data_size);
/* Send data to user space. */
if (copy_to_user(buf, gpio_driver_buffer, data_size) != 0)
{
return -EFAULT;
}
}
return 0;
}
gpio_driver_buffer is an array of some default size (I put it to 80).
mySwitches is an array of 4 elements, each one with value 0 or 1 (I'm passing that as an argument when inserting the kernel module, 1 meaning I want to watch the state of the switch and 0 meaning I'm not watching the switch).
GetGpioPinValue is a function that returns switch state.
The problem is, when I try to read the device file with cat command, I'm not getting anything. However, as you can see, I kind of debugged the program with printk commands and everything is written correctly in kernel space. Where could the problem be?
It doesn't look like you are ever writing to the actual file. Since you don't mention how you are generating the file, I'm assuming you are writing to an arbitrary file, not one created by the driver for /proc or something.
Review the post here: Read/write files within a Linux kernel module
You can try this:
int file_write(struct file *file, unsigned long long offset, unsigned char *data, unsigned int size)
{
mm_segment_t oldfs;
int ret;
oldfs = get_fs();
set_fs(get_ds());
ret = vfs_write(file, data, size, &offset);
set_fs(oldfs);
return ret;
}
Then call it instead of 'copy_to_user':
/* Send data to user space. */
if (file_write(filep, 0, gpio_driver_buffer, data_size) != 0)
{
return -EFAULT;
}
Have a look at the sample code here.

Problems reading\writing to devices for embedded Linux project

This project consists of web server (using libwebsockets library) and a motor control plugin for the web server that writes out and reads in packets to a motor control board as well as communicating with the server.
I have two file descriptors (fd) to a COM port on an embedded Linux board, one for writing and one for reading. They are both working individually, but I can't get them to work at the same time.
To test the read(), I hook the receive line for the COM port to a computer and send a packet of 16 bytes repeatedly from a terminal program. The bytes are received to the byte array and passed to the GUI via the web server. For this test, write is not disabled and writes out the COM port back to the terminal. The motors are not connected to the COM port for this test.
To test the write(), read is disabled and the web server is run. I can control the motors using the GUI and motor positions are reported to the server.
When both read and write are enabled, something appears to block when the web server is run. The motor cannot be enabled, nor run. As soon as I press Ctrl-C to stop the server, one or more packets from the motor appear to be processed.
The motors start to move and motor packet data is transferred to the GUI.
Is the read blocking for some reason? It works alone.
I have gotten the following outputs at various times by checking the return of the the read() call:
EINTR - Interrupted system call
EIO - Input/Output error
EBADF - Bad file descriptor
After the last tests of the read and write (separately), the only error I get is the latter. After quitting out of the program, I get a string of about 50 bad reads (I am printing perror to the terminal).
The read/write code (below) is running in a separate thread, with the packets being processed in a continuous loop. I was trying to isolate the timing for writing from fluctuations in the scheduler. The write routine is driven by a Linux timer at 8.333ms (120Hz) and SIGALRM, thus the "gotAlarm" variable.
Is this a problem with processes? I do not fork any processes and I want the web server to be the main process.
Is it a threading problem? Both the read and write have been working within the spawned thread.
Thank you for your input! Code is below.
Call to initialize fd's:
mc_tx_fd = InitPort("/dev/ttyS1", "COM2", O_WRONLY | O_NOCTTY, B115200);
mc_rx_fd = InitPort("/dev/ttyS1", "COM2", O_RDONLY | O_NOCTTY, B115200);
InitPort function:
int InitPort( char *port, char *name, int oflags, speed_t baudRate )
{
int fd, rg, rs; // File descriptor
fd = open(port, oflags); // Open the port like a file
assert(fd > 0); // Open returns -1 on error
struct termios options; // Initialize a termios struct
rg = tcgetattr(fd, &options); // Populate with current attributes
if( rg < 0 ) {
printf("Failed to get attr: %d, %s\n", fd, strerror(errno));
}
cfsetospeed (&options, baudRate); // Set baud rate out
cfsetispeed (&options, baudRate); // Set baud rate in (same as baud rate out)
options.c_cflag &= ~CSIZE; // Clear bit-length flag so it can be set
//8N1 Serial Mode
options.c_cflag &= ~CSTOPB; // Set stop bit: 1
options.c_cflag &= ~CRTSCTS; // Set flow control: none
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 2;
cfmakeraw(&options);
options.c_cflag |= (CLOCAL | CREAD); // Enable receiver, and set local mode
rs = tcsetattr(fd, TCSANOW, &options); // Set new attributes to hardware
if( rs < 0 ) {
printf("Failed to set attr: %d, %s\n", fd, strerror(errno));
}
return fd;
}
Here is the SendMotorPacket function called at the creation of a new thread:
void *SendMotorPacket( void *arg )
{
char timez, checksum;
static unsigned short rcvTimer = 0;
int flags, rcvAzPos, bytesRead, wc, wo, r, t;
while( 1 ) {
// receive byte from motor
r = read( mc_rx_fd, inByte, 1 );
if( r == -1 ) {
if( errno == EINTR ) {
continue;
} else {
perror("read");
}
}
inChar = inByte[0];
// FSM to receive packet
switch( receiveState ) {
case MOTOR_HEAD_1:
if( inChar == 0xA5 ) {
receiveState = MOTOR_HEAD_2;
checksum = 0;
}
break;
case MOTOR_HEAD_2:
if( inChar == 0x52 ) {
receiveState = MOTOR_DATA;
idx = 0;
checksum = 0xA5 + 0x52;
} else {
receiveState = MOTOR_HEAD_1;
}
break;
case MOTOR_DATA:
readPacket[idx++] = inChar;
checksum += inChar;
if( idx >= 16 ) {
receiveState = MOTOR_CHECKSUM;
}
break;
case MOTOR_CHECKSUM:
if( checksum == inChar ) {
// Check status bits and set global variables
mc.az_status = *(signed short *)&readPacket[2];
mc.el_status = *(signed short *)&readPacket[10];
// If motor is disabled, set global variable for feedback to GUI
if( !az.enable ) {
mc.az_position = *(signed int *)&readPacket[4];
}
if( !el.enable ) {
mc.el_position = *(signed int *)&readPacket[12];
}
}
receiveState = MOTOR_HEAD_1;
break;
default:
break;
}
// TRANSMIT
if( gotAlarm ) {
mc.checkSum = 0;
gotAlarm = 0;
// Call both motor position functions to update position
AzMotorPos();
ElMotorPos();
if ( az.enable ) {
mc.az_control |= 0x0001;
} else {
mc.az_control &= ~0x0001;
}
if ( el.enable ) {
mc.el_control |= 0x0001;
} else {
mc.el_control &= ~0x0001;
}
*( unsigned short * )&writePacket = mc.header;
*( signed short * )&writePacket[2] = mc.az_control;
*( signed short * )&writePacket[4] = mc.az_status;
*( signed int * )&writePacket[6] = mc.az_position;
*( signed short * )&writePacket[10] = mc.el_control;
*( signed short * )&writePacket[12] = mc.el_status;
*( signed int * )&writePacket[14] = mc.el_position;
// Calculate checksum for all bytes in packet
for( int i = 0; i < 18; i++ ) {
mc.checkSum += writePacket[i];
}
*(unsigned char * )&writePacket[18] = mc.checkSum;
write( mc_tx_fd, writePacket, MC_PACKET_SIZE );
}
}
}
Here is thread creation:
void CreatePacketThread ( void )
{
int err = -1;
err = pthread_create( &packetThread, NULL, &SendMotorPacket, NULL );
if (err != 0)
printf("\ncan't create MC SEND thread :[%s]\n\n", strerror(err));
else
printf("\nMC SEND THREAD created successfully\n\n");
}
UPDATE:
Running the web server (lwsws) and then quitting results in one EIO error:
read: Input/output error
and multiple EBADF errors:
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
About 50 of them.
I solved the problem by moving the receive routine to a separate thread. To recap, the web server (LWSWS program from libwebsockets library) is running in headless (auto start and run) mode on a Linux single board computer(SBC). The motor control code is a plugin of LWSWS and it now spawns two threads: one for sending packets out to the motor control board, and one for receiving packets.
Originally, I started with this two-thread design, but apparently had other problems at that point. Before asking this question, I had moved the receive routine into the send thread to try to get read() and write() both working.
The working combination seems to be two threads with each having its own file descriptor to the COM port. I am surprised that a mutex is not needed, as #thekamilz suggested in the comments, but pleased that it works. If I need to expand the project, I am hopeful that this design will work; with each peripheral operating on its own thread for communication.

Establishing Serial Connection With RS232 using C

I am trying to establish a serial connection with a device via RS232 and the C code. The purpose is to connect arduino to computer later and show the ip adress retrieved from the device on the LED screen.
Usually I connect the device to the computer via RS232, open PuTTY and establish the serial connection at 115200 baud rate. Then I press enter, type login, press enter, type password, press enter, type 'ip show' and then retrieve the ip adress.
The problem is I am not good at C programming (studied it only for 1 year in University). The code I come up with (copy-pasted and edited) is attached below. The questions are:
1) How do I get the information printed on the terminal screen. For example, after I type login and then press enter, there is a sentence saying type your password. How do I retrieve that to IDE's console?
2) On the final step (retrieving the ip), how do I retrieve the ip? It is in text format, after it's shown I need to copy it and paste it into another document).
As for now, my limited amount of knowledge about C prohibits me to go further.
Any kind of help (even the name of the helpful function) is appreciated!
//
// serial.c / serial.cpp
// A simple serial port writing example
// Written by Ted Burke - last updated 13-2-2013
//
// To compile with MinGW:
//
// gcc -o serial.exe serial.c
//
// To compile with cl, the Microsoft compiler:
//
// cl serial.cpp
//
// To run:
//
// serial.exe
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
// Define the five bytes to send ("hello")
char bytes_to_send[15];
bytes_to_send[0] = '\n';
bytes_to_send[1] = 'a';
bytes_to_send[2] = 'd';
bytes_to_send[3] = 'm';
bytes_to_send[4] = 'i';
bytes_to_send[5] = 'n';
bytes_to_send[6] = '\n';
bytes_to_send[7] = 's';
bytes_to_send[8] = 'h';
bytes_to_send[9] = 'o';
bytes_to_send[10] = 'w';
bytes_to_send[11] = ' ';
bytes_to_send[12] = 'i';
bytes_to_send[13] = 'p';
bytes_to_send[14] = '\n';
// Declare variables and structures
HANDLE hSerial;
DCB dcbSerialParams = {0};
COMMTIMEOUTS timeouts = {0};
// Open the highest available serial port number
fprintf(stderr, "Opening serial port...");
hSerial = CreateFile(
"\\\\.\\COM6", GENERIC_READ|GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if (hSerial == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "Error\n");
return 1;
}
else fprintf(stderr, "OK\n");
// Set device parameters (38400 baud, 1 start bit,
// 1 stop bit, no parity)
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if (GetCommState(hSerial, &dcbSerialParams) == 0)
{
fprintf(stderr, "Error getting device state\n");
CloseHandle(hSerial);
return 1;
}
dcbSerialParams.BaudRate = CBR_115200;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
if(SetCommState(hSerial, &dcbSerialParams) == 0)
{
fprintf(stderr, "Error setting device parameters\n");
CloseHandle(hSerial);
return 1;
}
// Set COM port timeout settings
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if(SetCommTimeouts(hSerial, &timeouts) == 0)
{
fprintf(stderr, "Error setting timeouts\n");
CloseHandle(hSerial);
return 1;
}
// Send specified text (remaining command line arguments)
DWORD bytes_written, total_bytes_written = 0;
fprintf(stderr, "Sending bytes...");
if(!WriteFile(hSerial, bytes_to_send, sizeof(bytes_to_send), &bytes_written, NULL))
{
fprintf(stderr, "Error\n");
CloseHandle(hSerial);
return 1;
}
fprintf(stderr, "%d bytes written\n", bytes_written);
// Close serial port
fprintf(stderr, "Closing serial port...");
if (CloseHandle(hSerial) == 0)
{
fprintf(stderr, "Error\n");
return 1;
}
fprintf(stderr, "OK\n");
fprintf(stderr, "the sent sentence is: ");
for(int i=0;i<sizeof(bytes_to_send);i++){
fprintf(stderr,"%c",bytes_to_send[i]);
}
// exit normally
return 0;
}
For setting up the port and read/write commands you can use the code from this answer.
Then you only have to worry about what you send and what you receive.
Putty actually reads the serial port in the background and then prints it on the console so you can see it. You will have to do the same thing in your app. After you read the data, you have to print it.
For example, for login, as you described it:
write (fd, "login\r\n", 7); // send 7 character login command
// (similar to writing login + enter in Putty)
usleep ((7 + 25) * 100); // allow time for sending & receiving
// (sleep enough to transmit the 7 plus
// receive 25: approx 100 uS per char transmit)
char buf [100];
int n = read (fd, buf, sizeof(buf)); // read rx characters (Putty does this automatically)
// expect to have "the sentence saying type your password" in this buffer
// 100 characters are read, adjust it if you expect a longer answer
printf("Rx:"); // display the rx data (Putty does this automatically)
for (int i = 0; i < sizeof(buf); i++)
printf("%c",buf[i]);
printf("\n");
// continue with sending the password + reading the response
// then sending the ip command + reading the answer

Interacting with a serial port using the C functions fopen, fread, and fwrite?

I have been attempting to communicate with a device of mine via an RS232 serial port(in my case it is COM6). My code is supposed to write a string of ascii values to the device and then read the response, however I cannot seem to get any response. The program seems to work relatively fine when I have it write and read to a file in my computer, but not for when I designate COM6. Here is the latest edition of my code:
using namespace std;
const char ASCII[ ]= "0123456789ABCDEF";
char *Checksum (char *buffer)
{
static char Hex[10];
static int a1, a2;
register unsigned int i;
int sum;
printf("%s \n", buffer);
sum = 256;
for ( i=0 ; i<strlen(buffer) ; i++ )
{
sum-=buffer[i];
if ( sum<0 )
sum+= 256;
}
a1 = (sum & 0xF0) >> 4;
a2 = sum & 0x0F;
Hex[0] = ASCII[a1];
Hex[1] = ASCII[a2];
Hex[2] = 0;
printf("the checksum is %s \n",Hex);
return(Hex);
}
int main()
{
char data[80], input[80], *data2;
char *response;
DCB dcb;
bool retVal;
DWORD dwBytesTransferred;
DWORD byteswritten;
printf("the variable response is initially: %d\n", response);
dcb.BaudRate = CBR_19200; //19200 Baud
dcb.ByteSize = 8; //8 data bits
dcb.Parity = NOPARITY; //no parity
dcb.StopBits = ONESTOPBIT; //1 stop
//New open port area
HANDLE hPort;
if ((hPort = CreateFile ( "\\\\.\\COM6",
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)) != INVALID_HANDLE_VALUE)
{
printf("SUCCESS opening the port\n");// success
}
//GetCommState
DCB Dcb;
GetCommState (hPort, &Dcb);
Dcb.BaudRate = CBR_19200;
Dcb.StopBits = ONESTOPBIT;
Dcb.ByteSize = 8;
Dcb.Parity = NOPARITY;
Dcb.fParity = 0;
Dcb.fOutxCtsFlow = 0;
Dcb.fOutxDsrFlow = 0;
Dcb.fDsrSensitivity = 0;
Dcb.fTXContinueOnXoff = TRUE;
Dcb.fOutX = 0;
Dcb.fInX = 0;
Dcb.fNull = 0;
Dcb.fErrorChar = 0;
Dcb.fAbortOnError = 0;
Dcb.fRtsControl = RTS_CONTROL_DISABLE;
Dcb.fDtrControl = DTR_CONTROL_DISABLE;
//Flushing
FlushFileBuffers( hPort );
PurgeComm (hPort, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
COMSTAT comStat;
DWORD dwErrorFlags;
ClearCommError ( hPort, &dwErrorFlags, &comStat );
//NEW commtimeouts area
COMMTIMEOUTS CommTimeOuts;
DWORD dwTimeout = 3000; // <- set timeout in milliseconds
if(!dwTimeout)
{ // Don't use timeout -> Read the bytes already in input buffer and return immediately
CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
} else
{ // Use given timeout, wait until the requested number of bytes are read - or timeout
CommTimeOuts.ReadIntervalTimeout = 0;
CommTimeOuts.ReadTotalTimeoutConstant = dwTimeout;
}
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (hPort, &CommTimeOuts);
printf("insert ASCII code string you wish to send:");
scanf("%s", input);
strcpy(data, "{0x02}");
strcat(data, input);
printf("%s \n", data);
data2=Checksum(data);
strcat(data, data2);
strcat(data, "{0x03}");
printf("the final sent message will be: %s \n",data);
retVal = WriteFile(hPort,data, strlen(data), &byteswritten, NULL);
printf("Number of bytes written: %d\n", byteswritten);
printf("Write Success? %d\n", retVal);
retVal=ReadFile (hPort, &response, 20, &dwBytesTransferred, NULL);
printf("Read Success? %d\n", retVal);
printf("Port Response: %d\n", response);
free(response);
return 0;
}
Summary of latest discoveries: Using the Free Serial Port Monitor that Habi suggested I now know for sure that WriteFile is functioning correctly and COM6 is in receiving the message. I'm still looking for a crossover cable to double check that the message is being transferred across the line. I figure while I try to figure that out if someone could look at this new edition and tell me if there is anything wrong, particularly in relation to the ReadFile function, it would be much appreciated. It bothers me that the Free Serial Port software is only showing the data passed from my computer and not a response from the device at all. =\
Instead of
"COM6"
try
"\\\\.\\COM6"
And I would recommend to use CreateFile(), ReadFile(), WriteFile().
To open the COM port try this:
HANDLE hComDev;
if ((hComDev = CreateFile ( "\\\\.\\COM6",
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)) != INVALID_HANDLE_VALUE)
{
// success
}
It seems that calling GetCommState() is missing in your code. Try this to configure the COM port:
DCB Dcb;
GetCommState (hComDev, &Dcb);
Dcb.BaudRate = CBR_19200;
Dcb.StopBits = ONESTOPBIT;
Dcb.ByteSize = 8;
Dcb.Parity = NOPARITY;
Dcb.fParity = 0;
Dcb.fOutxCtsFlow = 0;
Dcb.fOutxDsrFlow = 0;
Dcb.fDsrSensitivity = 0;
Dcb.fTXContinueOnXoff = TRUE;
Dcb.fOutX = 0;
Dcb.fInX = 0;
Dcb.fNull = 0;
Dcb.fErrorChar = 0;
Dcb.fAbortOnError = 0;
Dcb.fRtsControl = RTS_CONTROL_DISABLE;
Dcb.fDtrControl = DTR_CONTROL_DISABLE;
And to initially clear the COM port, I would do a reset like this before starting to send and receive bytes:
FlushFileBuffers( hComDev );
PurgeComm (hComDev, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
COMSTAT comStat;
DWORD dwErrorFlags;
ClearCommError ( hComDev, &dwErrorFlags, &comStat );
You ask for timeouts? To configure time outs try this:
COMMTIMEOUTS CommTimeOuts;
DWORD dwTimeout = .... // <- set timeout in milliseconds
if(!dwTimeout)
{ // Don't use timeout -> Read the bytes already in input buffer and return immediately
CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
}
else
{ // Use given timeout, wait until the requested number of bytes are read - or timeout
CommTimeOuts.ReadIntervalTimeout = 0;
CommTimeOuts.ReadTotalTimeoutConstant = dwTimeout;
}
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (hComDev, &CommTimeOuts);
My code is supposed to write a string of ascii values to the device
and then read the response, however I cannot seem to get any response.
Are you sure that the bytes are really sent to the device? If you have the option, take a oscilloscope and monitor the PC's Tx line. Send some bytes and check baudrate and start/stop bits. If you have no hardware to monitor this signal, take a software based serial monitor, e.g. Free Serial Port Monitor. I have no experience with that kind of software tools but they should show you at least that the Windows drivers try to send something via the COM port of your choice.
Greetings
Habi
If you have been trying for a month then I would recommend you look at the windows function CreateFile. I have used this for serial port comms and it works fine. You might also want to look at the DCB struct and GetCommState and SetCommState functions to configure the serial port. WriteFile can be used to write data to the serial port. I cannot post all of my code as it owned by my company, but this should help you get started.
Also ClearCommError may be of help to you to query if any data is on the serial port buffer when you are waiting to read from it.
I encountered a similar problem and found the other answers helpful, but I was not receiving characters.
Ultimately I found the problem was with RTS_CONTROL_DISABLE or DTR_CONTROL_DISABLE.
By having RTS and DTR held low, the program was indicating to the modem/device that the program was not ready to receive data, and the modem was respecting this signal and dutifully not sending.
By changing these to RTS_CONTROL_ENABLE and DTR_CONTROL_ENABLE, the program indicates to the modem that data may be sent, and I began receiving characters. Not all devices respect these signals so it may work in some configurations with DTR and RTS disabled -- the modem may send data anyway.
It's possible, but you need to config com port via cmd mode command,
and there is no way to config timeout value via cmd command.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
// use mode command to config port
// mode com2 dtr=off rts=off baud=9600 parity=N data=8 stop=1 to=on
FILE *f = fopen("\\\\.\\COM2", "wb+");
setvbuf(f, NULL, _IONBF, 0);
if(f == NULL) return -1;
while(1)
{
static char p[5];
int cnt = fread(&p, 1, sizeof(p), f);
printf("read[%d]:%s\n", cnt, p);
if(p[0] == '0') break;
memset(p, 0, sizeof(p));
}
fclose(f);
return 0;
}

Resources