Cannot communicate serial port with different COM - c

I`m having a problem communicating with the serial port of Windows.
I have a Windows Service written in C.
This service is ready to listen requests from an application to communicate with a Pinpad. Pretty Simple.
The problem comes when the Pinpad's Com is assigned, sometimes it is assigned COM4,COM5, COM9 (these coms are working properly), so when the Pinpad's comm gets the COM10 or later, I get an error, and I cannot communicate with the pinpad. It sends me an error, this error is already defined, but I cannot figure what is the problem, cause in the function CreateFileA. Everything works perfect, I mean, it returns a handle, but in the next function: GetCommState, I get the error.
int srlOpen(char * szCOM)
{
DCB dcbSrlParms;
COMMTIMEOUTS timeouts;
int inRetVal = P_SUCCESS;
memset(&dcbSrlParms, 0x00, sizeof(dcbSrlParms));
memset(&timeouts, 0x00, sizeof(timeouts));
if(inRetVal > P_ERROR)
{
hSerial = CreateFileA(szCOM, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,0);
//hSerial = CreateFileA("COM21", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_ALWAYS,
// FILE_ATTRIBUTE_NORMAL,0);
if(hSerial == INVALID_HANDLE_VALUE)
{
if(GetLastError() == ERROR_FILE_NOT_FOUND)
{
inRetVal = ERR_PORT_NOT_FOUND;
}
}
}
if(inRetVal > P_ERROR)
{
dcbSrlParms.DCBlength = sizeof(dcbSrlParms);
if(!GetCommState(hSerial, &dcbSrlParms))
{
inRetVal = ERR_GET_PORT_CONFIG;
}
}
if(inRetVal > P_ERROR)
{
dcbSrlParms.BaudRate = CBR_19200;
dcbSrlParms.ByteSize = 8;
dcbSrlParms.Parity = NOPARITY;
dcbSrlParms.StopBits = ONESTOPBIT;
if(!SetCommState(hSerial, &dcbSrlParms))
{
inRetVal = ERR_SET_PORT_CONFIG;
}
}
if(inRetVal > P_ERROR)
{
timeouts.ReadIntervalTimeout = COMM_READ_INT_TMEOUT;
timeouts.ReadTotalTimeoutConstant = COMM_READ_TOTAL_TIMEOUT;
timeouts.ReadTotalTimeoutMultiplier = COMM_READ_TOTAL_MULTI;
timeouts.WriteTotalTimeoutConstant = COMM_WRITE_TOTAL_TIMEOUT;
timeouts.WriteTotalTimeoutMultiplier = COMM_WRITE_TOTAL_MULTI;
if(!SetCommTimeouts(hSerial, &timeouts))
{
inRetVal = ERR_SET_TIMEOUT_CONFIG;
}
}
pdebug (("inRetVal=%x", inRetVal));
return inRetVal;
}

Invalid com port name.
A com port past "COM9" needs a different string format.
See Specify Serial Ports Larger than COM9
srlOpen("COM9"); //OK
srlOpen("COM10"); //Not OK
srlOpen("\\\\.\\COM9"); //OK
srlOpen("\\\\.\\COM10"); //OK

Related

Profiling XDP eBPF packet loss and poor performance

I have created a small project (https://github.com/NHAS/wag) that uses XDP & eBPF to allow connections based on time over a wireguard VPN.
I have attached the XDP eBPF program to the wireguard TUN device, and am experiencing poor throughput (speedtest of down ~20 Mbps wireguard + eBPF, vs wireguard - eBPF ~100 Mbps). Additionally, pings to the wireguard server itself have inconsistent latency, and are dropped at a rate of 1 ICMP packet/~600 pings.
Please note that this occurs during unloaded periods. Where traffic will be less than 100 Mbps total.
The code below is loaded into the kernel with cilium.
// Kernel load
...
xdpLink, err = link.AttachXDP(link.XDPOptions{
Program: xdpObjects.XdpProgFunc,
Interface: iface.Index,
})
...
eBPF kernel:
// +build ignore
#include "bpf_endian.h"
#include "common.h"
char __license[] SEC("license") = "Dual MIT/GPL";
// One /24
#define MAX_MAP_ENTRIES 256
// Inner map is a LPM tri, so we use this as the key
struct ip4_trie_key
{
__u32 prefixlen; // first member must be u32
__u32 addr; // rest can are arbitrary
};
// Map of users (ipv4) to BOOTTIME uint64 timestamp denoting authorization status
struct bpf_map_def SEC("maps") sessions = {
.type = BPF_MAP_TYPE_HASH,
.max_entries = MAX_MAP_ENTRIES,
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
.map_flags = 0,
};
// Map of users (ipv4) to BOOTTIME uint64 timestamp denoting when the last packet was recieved
struct bpf_map_def SEC("maps") last_packet_time = {
.type = BPF_MAP_TYPE_HASH,
.max_entries = MAX_MAP_ENTRIES,
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
.map_flags = 0,
};
// A single variable in nano seconds
struct bpf_map_def SEC("maps") inactivity_timeout_minutes = {
.type = BPF_MAP_TYPE_ARRAY,
.max_entries = 1,
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
.map_flags = 0,
};
// Two tables of the same construction
// IP to LPM trie
struct bpf_map_def SEC("maps") mfa_table = {
.type = BPF_MAP_TYPE_HASH_OF_MAPS,
.max_entries = MAX_MAP_ENTRIES,
.key_size = sizeof(__u32),
.value_size = sizeof(__u32),
.map_flags = 0,
};
struct bpf_map_def SEC("maps") public_table = {
.type = BPF_MAP_TYPE_HASH_OF_MAPS,
.max_entries = MAX_MAP_ENTRIES,
.key_size = sizeof(__u32),
.value_size = sizeof(__u32),
.map_flags = 0,
};
/*
Attempt to parse the IPv4 source address from the packet.
Returns 0 if there is no IPv4 header field; otherwise returns non-zero.
*/
static int parse_ip_src_dst_addr(struct xdp_md *ctx, __u32 *ip_src_addr, __u32 *ip_dst_addr)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
// As this is being attached to a wireguard interface (tun device), we dont get layer 2 frames
// Just happy little ip packets
// Then parse the IP header.
struct iphdr *ip = data;
if ((void *)(ip + 1) > data_end)
{
return 0;
}
// We dont support ipv6
if (ip->version != 4)
{
return 0;
}
// Return the source IP address in network byte order.
*ip_src_addr = (__u32)(ip->saddr);
*ip_dst_addr = (__u32)(ip->daddr);
return 1;
}
static int conntrack(__u32 *src_ip, __u32 *dst_ip)
{
// Max lifetime of the session.
__u64 *session_expiry = bpf_map_lookup_elem(&sessions, src_ip);
if (!session_expiry)
{
return 0;
}
// The most recent time a valid packet was received from our a user src_ip
__u64 *lastpacket = bpf_map_lookup_elem(&last_packet_time, src_ip);
if (!lastpacket)
{
return 0;
}
// Our userland defined inactivity timeout
u32 index = 0;
__u64 *inactivity_timeout = bpf_map_lookup_elem(&inactivity_timeout_minutes, &index);
if (!inactivity_timeout)
{
return 0;
}
__u64 currentTime = bpf_ktime_get_boot_ns();
// The inner map must be a LPM trie
struct ip4_trie_key key = {
.prefixlen = 32,
.addr = *dst_ip,
};
// If the inactivity timeout is not disabled and users session has timed out
u8 isTimedOut = (*inactivity_timeout != __UINT64_MAX__ && ((currentTime - *lastpacket) >= *inactivity_timeout));
if (isTimedOut)
{
u64 locked = 0;
bpf_map_update_elem(&sessions, src_ip, &locked, BPF_EXIST);
}
// Order of preference is MFA -> Public, just in case someone adds multiple entries for the same route to make sure accidental exposure is less likely
// If the key is a match for the LPM in the public table
void *user_restricted_routes = bpf_map_lookup_elem(&mfa_table, src_ip);
if (user_restricted_routes)
{
if (bpf_map_lookup_elem(user_restricted_routes, &key) &&
// 0 indicates invalid session
*session_expiry != 0 &&
// If max session lifetime is disabled, or we are before the max lifetime of the session
(*session_expiry == __UINT64_MAX__ || *session_expiry > currentTime) &&
!isTimedOut)
{
// Doesnt matter if the value is not atomically set
*lastpacket = currentTime;
return 1;
}
}
void *user_public_routes = bpf_map_lookup_elem(&public_table, src_ip);
if (user_public_routes && bpf_map_lookup_elem(user_public_routes, &key))
{
// Only update the lastpacket time if we're not expired
if (!isTimedOut)
{
*lastpacket = currentTime;
}
return 1;
}
return 0;
}
SEC("xdp")
int xdp_prog_func(struct xdp_md *ctx)
{
__u32 src_ip, dst_ip;
if (!parse_ip_src_dst_addr(ctx, &src_ip, &dst_ip))
{
return XDP_DROP;
}
if (conntrack(&src_ip, &dst_ip) || conntrack(&dst_ip, &src_ip))
{
return XDP_PASS;
}
return XDP_DROP;
}
The questions I'm looking to answer are:
How do I profile which areas (if any) of the eBPF program are intensive?
Is this a processing time limit for XDP, or an optimal time to keep in mind?
Is my eBPF program sane?
Thanks.
For the BPF XDP hook, the most common sources of huge per-packet overhead are:
JIT compiler is disabled. You can check the value of /proc/sys/net/core/bpf_jit_enable for that.
The driver doesn't support XDP. You need to check this for the specific driver and kernel versions you are using.
As discussed in comments, you're in the second case. Your program is attached to the TUN device which doesn't support the XDP driver mode. That means your BPF program runs after the skb allocation and performance won't be much better than at the tc hook.

How can I receive Ethernet frames with ibverbs?

I want to write a simple test program to receive Ethernet frames using the ibverbs API.
The code below compiles and runs but never receives any packets. I'm using Mellanox ConnectX-3 hardware on Ubuntu 18.
Questions:
If, while running this RX program, I ping the Inifiniband interface from another machine, then ping receives responses. I would not expect that because the ping requests should be grabbed by the RX program and the Linux IP stack should not see them and therefore should not respond. What should happen?
Is there anything obvious wrong with my code?
Do I need a steering rule? If I remove the call of ibv_create_flow() should I just receive all packets the interface sees?
#include <infiniband/verbs.h>
#include <stdio.h>
#include <stdlib.h>
#define PORT_NUM 1
#define MAX_MSG_SIZE 1500 // The maximum size of each received packet.
#define RQ_NUM_DESC 512 // Max packets that can be received without processing.
// The MAC of the interface we are listening on.
#define DEST_MAC { 0x00, 0x0d, 0x3a, 0x47, 0x1c, 0x2e }
#define FATAL_ERROR(msg, ...) { fprintf(stderr, "ERROR: " msg "\n", ##__VA_ARGS__); exit(-1); }
int main() {
// Get the list of devices.
int num_devices = 0;
struct ibv_device **dev_list = ibv_get_device_list(&num_devices);
if (!dev_list)
FATAL_ERROR("Failed to get IB devices list.");
// Choose the first device.
struct ibv_device *ib_dev = dev_list[0];
if (!ib_dev)
FATAL_ERROR("IB device not found.");
printf("Found %i Infiniband device(s).\n", num_devices);
printf("Using device '%s'.\n", ibv_get_device_name(ib_dev));
// Get the device context.
struct ibv_context *context = ibv_open_device(ib_dev);
if (!context)
FATAL_ERROR("Couldn't get context for device.");
// Allocate a protection domain (PD) that will group memory
// regions (MR) and rings.
struct ibv_pd *pd = ibv_alloc_pd(context);
if (!pd)
FATAL_ERROR("Couldn't allocate protection domain.");
// Create Complition Queue (CQ).
struct ibv_cq *cq = ibv_create_cq(context, RQ_NUM_DESC, NULL, NULL, 0);
if (!cq)
FATAL_ERROR("Couldn't create completion queue. errno = %d.", errno);
// Create Queue Pair (QP).
struct ibv_qp_init_attr qp_init_attr = {
.qp_context = NULL,
.send_cq = cq, // Report receive completion to CQ.
.recv_cq = cq,
.cap = {
.max_send_wr = 0, // No send ring.
.max_recv_wr = RQ_NUM_DESC, // Max num packets in ring.
.max_recv_sge = 1, // Only one pointer per descriptor.
},
.qp_type = IBV_QPT_RAW_PACKET, // Use Ethernet packets.
};
struct ibv_qp *qp = ibv_create_qp(pd, &qp_init_attr);
if (!qp)
FATAL_ERROR("Couldn't create queue pair.");
// Initialize the QP (receive ring) and assign a port.
struct ibv_qp_attr qp_attr = { 0 };
qp_attr.qp_state = IBV_QPS_INIT;
qp_attr.port_num = PORT_NUM;
int qp_flags = IBV_QP_STATE | IBV_QP_PORT;
if (ibv_modify_qp(qp, &qp_attr, qp_flags) < 0)
FATAL_ERROR("Failed to initialize queue pair.");
// Move ring state to ready-to-receive. This is needed in
// order to be able to receive packets.
memset(&qp_attr, 0, sizeof(qp_attr));
qp_flags = IBV_QP_STATE;
qp_attr.qp_state = IBV_QPS_RTR;
if (ibv_modify_qp(qp, &qp_attr, qp_flags) < 0)
FATAL_ERROR("Failed to put queue pair into ready-to-receive state.");
// Allocate memory for packet buffer.
int buf_size = MAX_MSG_SIZE * RQ_NUM_DESC; // Maximum size of data to be accessed by hardware.
void *buf = malloc(buf_size);
if (!buf)
FATAL_ERROR("Couldn't allocate memory.");
// Register the user memory so it can be accessed by the HW directly.
struct ibv_mr *mr = ibv_reg_mr(pd, buf, buf_size, IBV_ACCESS_LOCAL_WRITE);
if (!mr)
FATAL_ERROR("Couldn't register memory region.");
// Create a scatter/gather entry.
struct ibv_sge sg_entry;
sg_entry.length = MAX_MSG_SIZE;
sg_entry.lkey = mr->lkey;
// Create a receive work request.
struct ibv_recv_wr wr;
wr.num_sge = 1;
wr.sg_list = &sg_entry;
wr.next = NULL;
// Post a load of receive work requests onto the receive queue.
struct ibv_recv_wr *bad_wr;
for (int n = 0; n < RQ_NUM_DESC; n++) {
// Each descriptor points to max MTU size buffer.
sg_entry.addr = (uint64_t)buf + MAX_MSG_SIZE * n;
// When a packet is received, a work completion will be created
// corresponding to this work request. It will contain this field.
wr.wr_id = n;
// Post the receive buffer to the ring.
int rv = ibv_post_recv(qp, &wr, &bad_wr);
if (rv != 0) {
FATAL_ERROR("Posting recv failed with error code %i.", rv);
}
}
// Create steering rule.
struct raw_eth_flow_attr {
struct ibv_flow_attr attr;
struct ibv_flow_spec_eth spec_eth;
} __attribute__((packed)) flow_attr = {
.attr = {
.comp_mask = 0,
.type = IBV_FLOW_ATTR_NORMAL,
.size = sizeof(flow_attr),
.priority = 0,
.num_of_specs = 1,
.port = PORT_NUM,
.flags = 0,
},
.spec_eth = {
.type = IBV_FLOW_SPEC_ETH,
.size = sizeof(struct ibv_flow_spec_eth),
.val = {
.dst_mac = DEST_MAC,
.src_mac = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
.ether_type = 0,
.vlan_tag = 0,
},
.mask = {
.dst_mac = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
.src_mac = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
.ether_type = 0,
.vlan_tag = 0,
}
}
};
// Register steering rule to intercept packet to DEST_MAC and place packet in
// ring pointed by qp.
struct ibv_flow *eth_flow = ibv_create_flow(qp, &flow_attr.attr);
if (!eth_flow)
FATAL_ERROR("Couldn't attach steering flow. Does DEST_MAC match that of the local NIC?");
printf("Receiving.\n");
while (1) {
// Wait for CQ event upon message received, and print a message
struct ibv_wc wc;
int msgs_completed = ibv_poll_cq(cq, 1, &wc);
if (msgs_completed > 0) {
printf("Message %ld received size %d\n", wc.wr_id, wc.byte_len);
sg_entry.addr = (uint64_t)buf + wc.wr_id * MAX_MSG_SIZE;
wr.wr_id = wc.wr_id;
// After processed need to post back the buffer.
int rv = ibv_post_recv(qp, &wr, &bad_wr);
if (rv != 0) {
FATAL_ERROR("Re-posting recv failed with error code %i.", rv);
}
}
else if (msgs_completed < 0) {
FATAL_ERROR("Polling error.");
}
}
}
Take a look at this example from Mellanox: https://community.mellanox.com/s/article/raw-ethernet-programming--basic-introduction---code-example
To receive everything the interface sees, you can use the experimental api #include <infiniband/verbs_exp.h>, then when creating the steering rule, use ibv_exp_flow_attr and set the type to IBV_EXP_FLOW_ATTR_SNIFFER.
Please refer to https://github.com/Mellanox/libvma/wiki/Architecture
VMA implements native RDMA verbs API. The native RDMA verbs have been extended into the Ethernet RDMA-capable NICs, enabling the packets to pass directly between the user application and the InfiniBand HCA or Ethernet NIC, bypassing the kernel and its TCP/UDP handling network stack.

poll the status of CTS line in windows

Hi stackoverflow users!
I need to poll the CTS line of my serial port in Windows environment,
I have opened successfully the COM port,
HANDLE hSerialIn;
const char* pcCommPort = TEXT("COM3");
hSerialIn = CreateFile(pcCommPort, GENERIC_READ | GENERIC_WRITE, \
0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
Then I want to have something like this
DCB dcb = { 0 };
while (GetCommState(hSerialIn, &dcb)) {
if (dcb.fOutxCtsFlow)
;
else
;
}
The background of my interest in COM port is that there, I have a USB->UART convertor which in connected to the trigger output of the measuring device, this device triggers the output each second, and I want to have it in my program. When I connect to the COM port via Hercules(Terminal app) it works, I see that my CTS line is changing each second. So how to check the state of the CTS line?
Thanks in advance.
DWORD dwModemStatus;
BOOL fCTS = 0;
if (!SetCommMask(hSerialIn, EV_CTS))
{
DWORD err = GetLastError();
printf("\nHandle creation error code: %x\n", err);
}
DWORD dwCommEvent;
while(1)
{
if (!WaitCommEvent(hSerialIn, &dwCommEvent, NULL)) // An error occurred waiting for the event.
printf("");
else
{
if (!GetCommModemStatus(hSerialIn, &dwModemStatus)) // Error in GetCommModemStatus;
return;
fCTS = MS_CTS_ON & dwModemStatus;
if(fCTS)
printf("%x ", fCTS);
}
}

Extra bytes sent when communicating at 115200 baud over USB serial (WinAPI)

I'm attempting to send serial messages over USB to an Arduino Uno, using raw WinAPI commands. When using baud rates less than 115200, it works perfectly fine. However, when I send at 115200 baud, two extra bytes are sent prefixing the data I sent, but ONLY for the first message after connecting to the Arduino. For example, if I connect to the Arduino and send two bytes, "Hi", the Arduino receives "ððHi". If I send "Hi" again, the Arduino receives "Hi" like it should. (The extra bytes are usually ð (0xF0), but not always.)
I know that my computer and the Arduino are capable of communicating at 115200 baud, because other programs such as avrdude and the Arduino IDE's serial monitor can do it fine.
I have tried clearing the RX and TX buffers on both sides and also messing the DCB settings, with no effect. Does anyone know what might be causing this?
Thanks!
Here is my code to reproduce the problem:
Computer side:
#include <Windows.h>
int main()
{
// Open device as non-overlapped
HANDLE device = CreateFile(L"COM6",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
// Make sure the device is valid
if(device == INVALID_HANDLE_VALUE)
return 0;
DCB dcb;
if(!GetCommState(device, &dcb))
return 0;
dcb.fOutX = 0;
dcb.fInX = 0;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fNull = 0;
dcb.BaudRate = CBR_115200;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
if(!SetCommState(device, &dcb))
return 0;
COMMTIMEOUTS Timeouts = { 0 };
Timeouts.ReadTotalTimeoutConstant = 1000;
Timeouts.WriteTotalTimeoutConstant = 1000;
if(!SetCommTimeouts(device, &Timeouts))
return 0;
char *buf = "abcdef";
DWORD written;
WriteFile(device, buf, 6, &written, NULL);
DWORD read;
char inbuf[100];
ReadFile(device, inbuf, 100, &read, NULL);
// When I get the result inbuf, it has 8 bytes: {0xF0, 0xF0, a, b, c, d, e, f}
// Doing a 2nd set of Write/ReadFile, with the same message, gives the correct response
return 0;
}
Arduino side:
void setup()
{
Serial.begin(115200);
}
void loop()
{
if(Serial.available())
Serial.write(Serial.read());
}

How to detect if a Bluetooth HID device was disconnected?

I'm using CreateFile to open an asynchronous file handle to a Bluetooth HID device on the system. The device will then start streaming data, and I use ReadFile to read data from the device. The problem is, that if the Bluetooth connection is dropped, ReadFile just keeps giving ERROR_IO_PENDING instead of reporting a failure.
I cannot rely on timeouts, because the device doesn't send any data if there is nothing to report. I do not want it to time out if the connection is still alive, but there is simply no data for a while.
Still, the Bluetooth manager (both the Windows one and the Toshiba one) do immediately notice that the connection was lost. So this information is somewhere inside the system; it's just not getting through to ReadFile.
I have available:
the file handle (HANDLE value) to the device,
the path that was used to open that handle (but I don't want to attempt to open it another time, creating a new connection...)
an OVERLAPPED struct used for asynchronous ReadFile.
I am not sure if this issue is Bluetooth-specific, HID-specific, or occurs with devices in general. Is there any way that I can either
get ReadFile to return an error when the connection was dropped, or
detect quickly upon a timeout from ReadFile whether the connection is still alive (it needs to be fast because ReadFile is called at least 100 times per second), or
solve this problem in another way I haven't thought of?
You are going to have to have some sort of polling to check. Unless there is a event you can attach (I'm not familiar with the driver), the simplest way is to poll your COM port by doing a ReadFile and check is dwBytesRead > 0 when you send a command. There should be some status command you can send or you can check if you can write to the port and copy those bytes written to dwBytesWrite using WriteFile, for instance, and check if that is equal to the length of bytes you are sending. For instance:
WriteFile(bcPort, cmd, len, &dwBytesWrite, NULL);
if (len == dwBytesWrite) {
// Good! Return true
} else
// Bad! Return false
}
This is how I do it in my application. Below may seem like a bunch of boilerplate code, but I think it will help you get to the root of your problem. I first open the Comm Port in the beginning.
I have an array of COM ports that I maintain and check to see if they are open before writing to a particular COM port. For instance, they are opened in the beginning.
int j;
DWORD dwBytesRead;
if (systemDetect == SYS_DEMO)
return;
if (port <= 0 || port >= MAX_PORT)
return;
if (hComm[port]) {
ShowPortMessage(true, 20, port, "Serial port already open:");
return;
}
wsprintf(buff, "COM%d", port);
hComm[port] = CreateFile(buff,
GENERIC_READ | GENERIC_WRITE,
0, //Set of bit flags that specifies how the object can be shared
0, //Security Attributes
OPEN_EXISTING,
0, //Specifies the file attributes and flags for the file
0); //access to a template file
if (hComm[port] != INVALID_HANDLE_VALUE) {
if (GetCommState(hComm[port], &dcbCommPort)) {
if(baudrate == 9600) {
dcbCommPort.BaudRate = CBR_9600;//current baud rate
} else {
if(baudrate == 115200) {
dcbCommPort.BaudRate = CBR_115200;
}
}
dcbCommPort.fBinary = 1; //binary mode, no EOF check
dcbCommPort.fParity = 0; //enable parity checking
dcbCommPort.fOutxCtsFlow = 0; //CTS output flow control
dcbCommPort.fOutxDsrFlow = 0; //DSR output flow control
// dcbCommPort.fDtrControl = 1; //DTR flow control type
dcbCommPort.fDtrControl = 0; //DTR flow control type
dcbCommPort.fDsrSensitivity = 0;//DSR sensitivity
dcbCommPort.fTXContinueOnXoff = 0; //XOFF continues Tx
dcbCommPort.fOutX = 0; //XON/XOFF out flow control
dcbCommPort.fInX = 0; //XON/XOFF in flow control
dcbCommPort.fErrorChar = 0; //enable error replacement
dcbCommPort.fNull = 0; //enable null stripping
//dcbCommPort.fRtsControl = 1; //RTS flow control
dcbCommPort.fRtsControl = 0; //RTS flow control
dcbCommPort.fAbortOnError = 0; //abort reads/writes on error
dcbCommPort.fDummy2 = 0; //reserved
dcbCommPort.XonLim = 2048; //transmit XON threshold
dcbCommPort.XoffLim = 512; //transmit XOFF threshold
dcbCommPort.ByteSize = 8; //number of bits/byte, 4-8
dcbCommPort.Parity = 0; //0-4=no,odd,even,mark,space
dcbCommPort.StopBits = 0; //0,1,2 = 1, 1.5, 2
dcbCommPort.XonChar = 0x11; //Tx and Rx XON character
dcbCommPort.XoffChar = 0x13; //Tx and Rx XOFF character
dcbCommPort.ErrorChar = 0; //error replacement character
dcbCommPort.EofChar = 0; //end of input character
dcbCommPort.EvtChar = 0; //received event character
if (!SetCommState(hComm[port], &dcbCommPort)) {
setBit(SystemState, SYSTEM_PORT_ERROR);
//ShowPortMessage(true, 21, port, "Cannot set serial port state information:");
if (!CloseHandle(hComm[port])) {
//ShowPortMessage(true, 22, port, "Cannot close serial port:");
}
hComm[port] = NULL;
return;
}
} else {
setBit(SystemState, SYSTEM_PORT_ERROR);
//ShowPortMessage(true, 29, port, "Cannot get serial port state information:");
if (!CloseHandle(hComm[port])) {
//ShowPortMessage(true, 22, port, "Cannot close serial port:");
}
hComm[port] = NULL;
return;
}
if (!SetupComm(hComm[port], 1024*4, 1024*2)) {
setBit(SystemState, SYSTEM_PORT_ERROR);
//ShowPortMessage(true, 23, port, "Cannot set serial port I/O buffer size:");
if (!CloseHandle(hComm[port])) {
//ShowPortMessage(true, 22, port, "Cannot close serial port:");
}
hComm[port] = NULL;
return;
}
if (GetCommTimeouts(hComm[port], &ctmoOld)) {
memmove(&ctmoNew, &ctmoOld, sizeof(ctmoNew));
//default setting
ctmoNew.ReadTotalTimeoutConstant = 100;
ctmoNew.ReadTotalTimeoutMultiplier = 0;
ctmoNew.WriteTotalTimeoutMultiplier = 0;
ctmoNew.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts(hComm[port], &ctmoNew)) {
setBit(SystemState, SYSTEM_PORT_ERROR);
//ShowPortMessage(true, 24, port, "Cannot set serial port timeout information:");
if (!CloseHandle(hComm[port])) {
//ShowPortMessage(true, 22, port, "Cannot close serial port:");
}
hComm[port] = NULL;
return;
}
} else {
setBit(SystemState, SYSTEM_PORT_ERROR);
//ShowPortMessage(true, 25, port, "Cannot get serial port timeout information:");
if (!CloseHandle(hComm[port])) {
//ShowPortMessage(true, 22, port, "Cannot close serial port:");
}
hComm[port] = NULL;
return;
}
for (j = 0; j < 255; j++) {
if (!ReadFile(hComm[port], buff, sizeof(buff), &dwBytesRead, NULL)) {
setBit(SystemState, SYSTEM_PORT_ERROR);
//ShowPortMessage(true, 26, port, "Cannot read serial port:");
j = 999; //read error
break;
}
if (dwBytesRead == 0) //No data in COM buffer
break;
Sleep(10); //Have to sleep certain time to let hardware flush buffer
}
if (j != 999) {
setBit(pcState[port], PORT_OPEN);
}
} else {
setBit(SystemState, SYSTEM_PORT_ERROR);
//ShowPortMessage(true, 28, port, "Cannot open serial port:");
hComm[port] = NULL;
}
HANDLE TCommPorts::OpenCommPort(void) {
// OPEN THE COMM PORT.
if (hComm)
return NULL; // if already open, don't bother
if (systemDetect == SYS_DEMO)
return NULL;
hComm = CreateFile(port,
GENERIC_READ | GENERIC_WRITE,
0, //Set of bit flags that specifies how the object can be shared
0, //Security Attributes
OPEN_EXISTING,
0, //Specifies the file attributes and flags for the file
0);//access to a template file
// If CreateFile fails, throw an exception. CreateFile will fail if the
// port is already open, or if the com port does not exist.
// If the function fails, the return value is INVALID_HANDLE_VALUE.
// To get extended error information, call GetLastError.
if (hComm == INVALID_HANDLE_VALUE) {
// throw ECommError(ECommError::OPEN_ERROR);
return INVALID_HANDLE_VALUE;
}
// GET THE DCB PROPERTIES OF THE PORT WE JUST OPENED
if (GetCommState(hComm, &dcbCommPort)) {
// set the properties of the port we want to use
dcbCommPort.BaudRate = CBR_9600;// current baud rate
//dcbCommPort.BaudRate = CBR_115200;
dcbCommPort.fBinary = 1; // binary mode, no EOF check
dcbCommPort.fParity = 0; // enable parity checking
dcbCommPort.fOutxCtsFlow = 0; // CTS output flow control
dcbCommPort.fOutxDsrFlow = 0; // DSR output flow control
//dcbCommPort.fDtrControl = 1; // DTR flow control type
dcbCommPort.fDtrControl = 0; // DTR flow control type
dcbCommPort.fDsrSensitivity = 0;// DSR sensitivity
dcbCommPort.fTXContinueOnXoff = 0; // XOFF continues Tx
dcbCommPort.fOutX = 0; // XON/XOFF out flow control
dcbCommPort.fInX = 0; // XON/XOFF in flow control
dcbCommPort.fErrorChar = 0; // enable error replacement
dcbCommPort.fNull = 0; // enable null stripping
//dcbCommPort.fRtsControl = 1; // RTS flow control
dcbCommPort.fRtsControl = 0; // RTS flow control
dcbCommPort.fAbortOnError = 0; // abort reads/writes on error
dcbCommPort.fDummy2 = 0; // reserved
dcbCommPort.XonLim = 2048; // transmit XON threshold
dcbCommPort.XoffLim = 512; // transmit XOFF threshold
dcbCommPort.ByteSize = 8; // number of bits/byte, 4-8
dcbCommPort.Parity = 0; // 0-4=no,odd,even,mark,space
dcbCommPort.StopBits = 0; // 0,1,2 = 1, 1.5, 2
dcbCommPort.XonChar = 0x11; // Tx and Rx XON character
dcbCommPort.XoffChar = 0x13; // Tx and Rx XOFF character
dcbCommPort.ErrorChar = 0; // error replacement character
dcbCommPort.EofChar = 0; // end of input character
dcbCommPort.EvtChar = 0; // received event character
}
else
{
// something is way wrong, close the port and return
CloseHandle(hComm);
throw ECommError(ECommError::GETCOMMSTATE);
}
// SET BAUD RATE, PARITY, WORD SIZE, AND STOP BITS TO OUR SETTINGS.
// REMEMBERTHAT THE ARGUMENT FOR BuildCommDCB MUST BE A POINTER TO A STRING.
// ALSO NOTE THAT BuildCommDCB() DEFAULTS TO NO HANDSHAKING.
// wsprintf(portSetting, "%s,%c,%c,%c", baud, parity, databits, stopbits);
dcbCommPort.DCBlength = sizeof(DCB);
// BuildCommDCB(portSetting, &dcbCommPort);
if (!SetCommState(hComm, &dcbCommPort)) {
// something is way wrong, close the port and return
CloseHandle(hComm);
throw ECommError(ECommError::SETCOMMSTATE);
}
// set the intial size of the transmit and receive queues.
// I set the receive buffer to 32k, and the transmit buffer
// to 9k (a default).
if (!SetupComm(hComm, 1024*32, 1024*9))
{
// something is hay wire, close the port and return
CloseHandle(hComm);
throw ECommError(ECommError::SETUPCOMM);
}
// SET THE COMM TIMEOUTS.
if (GetCommTimeouts(hComm,&ctmoOld)) {
memmove(&ctmoNew, &ctmoOld, sizeof(ctmoNew));
//default settings
ctmoNew.ReadTotalTimeoutConstant = 100;
ctmoNew.ReadTotalTimeoutMultiplier = 0;
ctmoNew.WriteTotalTimeoutMultiplier = 0;
ctmoNew.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts(hComm, &ctmoNew)) {
// something is way wrong, close the port and return
CloseHandle(hComm);
throw ECommError(ECommError::SETCOMMTIMEOUTS);
}
} else {
CloseHandle(hComm);
throw ECommError(ECommError::GETCOMMTIMEOUTS);
}
return hComm;
}

Resources