Bluetooth Core v5.3 Set Extended Advertising Data Command - bluetooth

I'm trying to re-implement HCITool functions in my application. Currently trying to get the command "cmd" working so that I can set extended advertising data which will hold manufacturer data.
My first approach was to see how HCITool registers the command and so I came up with this:
hcitool -i hci0 cmd 0x08 0x0037 01 03 01 0C FFFF 12 34
I also tried it this way:
hcitool -i hci0 cmd 0x08 0x0037 01 03 01 0C FF FF 12 34
These gave me error code 1, which is saying the command doesn't exist so I'm guessing I didn't write it correctly. My first request is if anyone can explain how to correctly send this command in HCITool.
The following is how I am trying to implment it in my code. This code is just a lightly altered version of open source HCITool's approach.
static void cmd_manufacturer(int dev_id)
{
unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf;
struct hci_filter flt;
hci_event_hdr *hdr;
int i, opt, len, dd;
uint16_t ocf;
uint8_t ogf;
char arr[][20] = {"0xFFFF", "0x12", "0x34"};
if (dev_id < 0)
dev_id = hci_get_route(NULL);
errno = 0;
ogf = strtol("0x08", NULL, 16);
ocf = strtol("0x0037", NULL, 16);
if (errno == ERANGE || (ogf > 0x3f) || (ocf > 0x3ff))
{
return;
}
for (i = 2, len = 0; i < 12 && len < (int) sizeof(buf); i++, len++)
{
*ptr++ = (uint8_t) strtol(arr[i], NULL, 16);
}
dd = hci_open_dev(dev_id);
if (dd < 0) {
perror("Device open failed");
exit(EXIT_FAILURE);
}
/* Setup filter */
hci_filter_clear(&flt);
hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
hci_filter_all_events(&flt);
if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
perror("HCI filter setup failed");
exit(EXIT_FAILURE);
}
printf("< HCI Command: ogf 0x%02x, ocf 0x%04x, plen %d\n", ogf, ocf, len);
if (hci_send_cmd(dd, ogf, ocf, len, buf) < 0) {
perror("Send failed");
exit(EXIT_FAILURE);
}
len = read(dd, buf, sizeof(buf));
if (len < 0) {
perror("Read failed");
exit(EXIT_FAILURE);
}
hdr = (void *)(buf + 1);
ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
len -= (1 + HCI_EVENT_HDR_SIZE);
printf("> HCI Event: 0x%02x plen %d\n", hdr->evt, hdr->plen);
hci_close_dev(dd);
}
I run my application, turn advertising on and am using btmon to see the HCI events/commands. Btmon doesn't show my extended advertising data being set.
Anyone have any idea on what I can do to make this work? I think I'm not providing the data correctly to the function.
Thank you for the help.

Related

Arduino to PC secure bluetooth connection

I'm using an Arduino Uno to build a smoke detection system, which works. Since I have to do an IoT project, I have to establish a secure connection with a (for example) Web server, and I'm using a Bluetooth module HC-05 to do it. Now I want to encrypt and decrypt messages (which contain the values of the gas sensor) using an AES library.
That's my code on Arduino IDE:
#include <AES.h>
#include <AESLib.h>
#include <AES_config.h>
#include <xbase64.h>
#define VCC2 5
int smokeA0 = A0;
int buzzer = 11;
float sensorValue;
void setup() {
pinMode(buzzer, OUTPUT);
pinMode(smokeA0, INPUT);
pinMode(VCC2, OUTPUT);
digitalWrite(VCC2, HIGH);
Serial.begin(9600);
Serial.println("Gas sensor warning up!");
delay(2000); //allow the sensor to warm up
noTone(buzzer);
}
void loop() {
sensorValue = analogRead(smokeA0);
Serial.print("Sensor value: ");
Serial.print(sensorValue);
if(sensorValue > 300){
Serial.print(" | Smoke detected!");
tone(buzzer,1000,2000);
}
else {
noTone(buzzer);
}
Serial.println("");
delay(200); //wait 2s for next reading
}
How is the code through which I can encrypt the value sensor? And have I include it to the code above?
In the site file zip containing the informations of the project there's also this file called "Receiver", on Arduino IDE, too:
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>
#include <AES.h>
AES aes ;
byte key [N_BLOCK] ;
byte cipher [N_BLOCK] ;
byte check [N_BLOCK] ;
#define PIN_EN 6
#define BUTTON 13
SoftwareSerial btSerial(4, 5);
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
String msg = "";
bool isConnected = false;
String readString = "";
byte msg_received[16];
char final_str[16];
void setup( )
{
Serial.begin(9600);
Serial.print("Receiving Application\n\n");
lcd.begin(16, 2);
lcd.setCursor(0,0);
lcd.print("Receiving App");
int bits = 128;
set_bits (bits, key, 0) ; // all zero key
byte succ;
succ = aes.set_key (key, bits) ;
if (succ != SUCCESS) Serial.println ("Failure set_key") ;
checkConnection();
if(!isConnected) {
pinMode(PIN_EN, OUTPUT); // this pin will pull the HC-010 EN HIGH to switch module to AT mode
digitalWrite(PIN_EN, HIGH);
Serial.println("Going in AT mode for BLE HC-10");
btSerial.begin(38400);
delay(1000);
pinMode(A0, INPUT);
while(!isConnected) {
if (btSerial.available()) {
char c = btSerial.read();
Serial.write(c);
}
if (Serial.available()) {
char c = Serial.read();
btSerial.write(c);
}
checkConnection();
}
btSerial.flush();
btSerial.end();
btSerial.begin(9600);
digitalWrite(PIN_EN, LOW);
Serial.println("Exiting from AT mode for BLE HC-10");
}
}
void checkConnection() {
int x = analogRead(A0);
if(x > 700) {
Serial.println("HC-10 is connected");
isConnected = true;
}
}
void loop( )
{
if(isConnected) {
getBTReply();
if(msg.length() > 0) {
Serial.println("Received msg: " + msg);
lcd.clear();
lcd.begin(16, 2);
lcd.setCursor(0,0);
lcd.print("Receiving App");
lcd.setCursor(0, 1);
lcd.print("Temp. > ");
lcd.print(msg);
lcd.setCursor(13, 1);
lcd.print(" C");
} else {
Serial.println("Received no msg");
lcd.clear();
lcd.begin(16, 2);
lcd.setCursor(0,0);
lcd.print("Receiving App");
lcd.setCursor(0, 1);
lcd.print("no message received");
}
}
delay(4000);
}
void getBTReply() {
msg = "";
int i = 0;
while (btSerial.available()) {
msg_received[i] = btSerial.read();
i++;
}
if (i==16) {
byte succ;
succ = aes.decrypt (msg_received, check) ;
if (succ != SUCCESS) Serial.println ("Failure decrypt") ;
String x = prs_byte_hex(check, 128);
textFromHexString(x.c_str(), final_str);
msg = String(final_str);
memset(final_str,0,strlen(final_str));
}
}
void set_bits (int bits, byte * a, int count)
{
bits >>= 3 ;
byte bcount = count >> 3 ;
for (byte i = 0 ; i < bcount ; i++)
a [i] = 0xFF ;
if ((count & 7) != 0)
a [bcount++] = 0xFF & (0xFF00 >> (count & 7)) ;
for (byte i = bcount ; i < bits ; i++)
a [i] = 0x00 ;
}
char * hex = "0123456789abcdef" ;
void print_value (byte * a, int bits)
{
bits >>= 3 ;
for (int i = 0 ; i < bits ; i++)
{
byte b = a[i] ;
// test purpose only
Serial.print (hex [b >> 4]) ;
Serial.print (hex [b & 15]) ;
}
Serial.println () ;
}
String prs_byte_hex (byte * a, int bits)
{
bits >>= 3 ;
String str_toparse;
for (int i = 0 ; i < bits ; i++)
{
byte b = a[i] ;
str_toparse += hex [b >> 4];
str_toparse += hex [b & 15];
}
return str_toparse;
}
void textFromHexString(char *hex, char *result)
{
char temp[3];
int index = 0;
temp[2] = '\0';
while (hex[index])
{
strncpy(temp, &hex[index], 2);
*result = (char)strtol(temp, NULL, 16);
result++;
index += 2;
}
*result = '\0';
}
So I don't need to build a Web Server in VSC to establish a connection through two devices?
If yes, can you please share me the code?
Sorry, I'm not very practical in IoT and I'm completely alone to do the project.
If you're sending data using the HC-05 to another arduino, then here is how to encrypt the data:
uint8_t key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
char data[] = "0123456789012345"; //16 chars == 16 bytes - Your data you want to send
aes128_enc_single(key, data);
Then on the receiving arduino
aes128_dec_single(key, data);
You send that data using serial. The receiving Arduino would also need an HC-05 module, or if you're sending to your computer, you can use a USB bluetooth adapter. You don't need a webserver to establish a connection between 2 arduinos, no. If you're sending data online, then you'll need to have a way to connect to the internet from one of your devices.

Interrupt Handler Stops Working After Kernel Module Reload (Xilinx FPGA / PCIe)

I am currently working on a PCI driver for the Xilinx Kintex 7 board using the Xilinx PCI IP core (AXI Memory Mapped to PCIe).
One problem is, that the interrupt handler stops working when I reload the kernel module. In more detail:
Fresh boot of my machine
Load the kernel module and monitor the kernel messages with dmesg
/proc/interrupts shows the expected interrupt ids
I trigger the HW interrupt and everything works as expected; I can see the interrupt handler working.
rmmod my_module
/proc/interrupts removed the interrupt ids as expected
insmod my_module and trigger interrupt
Now the interrupt handler is silent and /proc/interrupts does not increase the counter
I reboot my machine and everything works again. The fact that I do not have to restart the FPGA lets me assume that I do something wrong in the kernel module and its probably not an HW problem.
I've already used /sys/pci/devices/.../reset, /sys/bus/pci/devices/.../remove and /sys/bus/pci/rescan to try to reach a state which is equivalent to freshly booted machine. But nothing worked.
Relevant module code:
#define VENDOR_ID 0x10EE
#define DEVICE_ID 0x7024
static dev_t pci_dev_number;
static struct cdev * driver_object;
static struct class * pci_class;
static struct device * pci_prc;
static struct device * pci_irq_0;
static struct device * pci_irq_1;
static int msi_vec_num = 2; // Number of requested MSI interrupts
static int msi_0 = -1;
static int msi_1 = -1;
// Used for poll and select
static DECLARE_WAIT_QUEUE_HEAD(queue_vs0);
static DECLARE_WAIT_QUEUE_HEAD(queue_vs1);
static irqreturn_t pci_isr_0(int irq, void * dev_id) {
printk(KERN_NOTICE "codec IRQ: interrupt handler 0. IRQ: %d\n", irq);
wake_up_interruptible(&queue_vs0);
return IRQ_HANDLED;
}
static irqreturn_t pci_isr_1(int irq, void * dev_id) {
printk(KERN_NOTICE "codec IRQ: interrupt handler 1. IRQ: %d\n", irq);
wake_up_interruptible(&queue_vs1);
return IRQ_HANDLED;
}
static void* bars[PCIE_BARS] = {0};
static int device_init(struct pci_dev * pdev, const struct pci_device_id * id) {
int i = 0; // loop var
if (pci_enable_device(pdev))
return -EIO;
// Request memory regions for bar 0 to 2
for (i = 0; i < PCIE_BARS; i++) {
if (pci_request_region(pdev, i, "codec_pci") != 0) {
dev_err( & pdev - > dev, "Bar %d - I/O address conflict for device \"%s\"\n", i, pdev - > dev.kobj.name);
return -EIO;
}
}
// DEBUG: Check if we are in memory space (which we should) or io space
if ((pci_resource_flags(pdev, 0) & IORESOURCE_IO)) {
printk(KERN_NOTICE "codec INIT: in io space\n");
} else if ((pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
printk(KERN_NOTICE "codec INIT: in mem_space\n");
}
// This request enables MSI_enable in the hardware
msi_vec_num = pci_alloc_irq_vectors(pdev, 1, msi_vec_num, PCI_IRQ_MSI);
// msi_N will contain the IRQ number - see /proc/interrupts
msi_0 = pci_irq_vector(pdev, 0);
msi_1 = pci_irq_vector(pdev, 1);
printk(KERN_NOTICE "codec INIT: nvec: %d\n", msi_vec_num);
printk(KERN_NOTICE "codec INIT: msi_0: %d\n", msi_0);
printk(KERN_NOTICE "codec INIT: msi_1: %d\n", msi_1);
if (request_irq(msi_0, pci_isr_0, IRQF_SHARED, "codec_pci", pdev)) {
dev_err( & pdev - > dev, "codec INIT: IRQ MSI %d not free.\n", msi_0);
goto cleanup;
};
if (request_irq(msi_1, pci_isr_1, IRQF_SHARED, "codec_pci", pdev)) {
dev_err( & pdev - > dev, "codec INIT: IRQ MSI %d not free.\n", msi_1);
goto cleanup;
};
for (i = 0; i < PCIE_BARS; i++) {
// Last parameter is the address space/length of each bar. Defined in the PCIe core.
bars[i] = pci_iomap(pdev, i, pci_resource_len(pdev, i));
if (bars[i] == NULL) {
printk(KERN_ERR "codec INIT: bar %d allocation failed\n", i);
goto cleanup;
}
printk(KERN_NOTICE "codec INIT: bar %d pointer: %p\n", i, bars[i]);
}
printk(KERN_NOTICE "codec INIT: loaded\n");
return 0;
cleanup:
for (i = 0; i < PCIE_BARS; i++) {
if (bars[i] != NULL)
pci_iounmap(pdev, bars[i]);
pci_release_region(pdev, i);
}
return -EIO;
}
static void device_deinit(struct pci_dev * pdev) {
int i = 0; // loop var
if (msi_0 >= 0)
free_irq(msi_0, pdev);
if (msi_1 >= 0)
free_irq(msi_1, pdev);
pci_free_irq_vectors(pdev);
// release bar regions
for (i = 0; i < PCIE_BARS; i++)
pci_release_region(pdev, i);
for (i = 0; i < PCIE_BARS; i++) {
if (bars[i] != NULL)
pci_iounmap(pdev, bars[i]);
}
pci_disable_device(pdev);
}
// File operations not in this snipped
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = device_open,
.read = device_read,
.write = device_write,
.poll = device_poll
};
static struct pci_device_id pci_drv_tbl[] = {
{
VENDOR_ID,
DEVICE_ID,
PCI_ANY_ID,
PCI_ANY_ID,
0,
0,
0
},
{
0,
}
};
static struct pci_driver pci_drv = {
.name = "codec_pci",
.id_table = pci_drv_tbl,
.probe = device_init,
.remove = device_deinit
};
static int __init mod_init(void) {
int i = 0;
if (alloc_chrdev_region( & pci_dev_number, 0, MAX_DEVICES, "codec_pci") < 0)
return -EIO;
driver_object = cdev_alloc();
if (driver_object == NULL)
goto free_dev_number;
driver_object - > owner = THIS_MODULE;
driver_object - > ops = & fops;
if (cdev_add(driver_object, pci_dev_number, MAX_DEVICES))
goto free_cdev;
pci_class = class_create(THIS_MODULE, "codec_pci");
if (IS_ERR(pci_class)) {
pr_err("codec MOD_INIT: no udev support available\n");
goto free_cdev;
}
pci_prc = device_create(pci_class, NULL, MKDEV(MAJOR(pci_dev_number), MINOR(pci_dev_number) + 0), NULL, "%s", "codec_prc");
pci_irq_0 = device_create(pci_class, NULL, MKDEV(MAJOR(pci_dev_number), MINOR(pci_dev_number) + 1), NULL, "codec_irq_%d", 0);
pci_irq_1 = device_create(pci_class, NULL, MKDEV(MAJOR(pci_dev_number), MINOR(pci_dev_number) + 2), NULL, "codec_irq_%d", 1);
if (pci_register_driver( & pci_drv) < 0) {
for (i = 0; i < MAX_DEVICES; i++)
device_destroy(pci_class, MKDEV(pci_dev_number, i));
goto free_dev_number;
}
return 0;
free_cdev:
kobject_put( & driver_object - > kobj);
free_dev_number:
unregister_chrdev_region(pci_dev_number, MAX_DEVICES);
return -EIO;
}
static void __exit mod_exit(void) {
int i = 0;
pci_unregister_driver( & pci_drv);
device_unregister(pci_prc);
device_unregister(pci_irq_0);
device_unregister(pci_irq_1);
for (i = 0; i < MAX_DEVICES; i++) {
device_destroy(pci_class, MKDEV(pci_dev_number, i));
}
class_destroy(pci_class);
cdev_del(driver_object);
unregister_chrdev_region(pci_dev_number, MAX_DEVICES);
}
module_init(mod_init);
module_exit(mod_exit);
Error handling routines could be better, but they don't get triggered anyway.
I guess I found the cause of my problem. I took a look at the PCI configuration space while executing each of the steps of my original post. The configuration space when interrupts are working:
# lspci -xxx | grep Xilinx
23:00.0 Memory controller: Xilinx Corporation Device 7024
00: ee 10 24 70 *07* 04 10 00 00 00 80 05 10 00 00 00
. . .
And when it's broken:
# lspci -xxx | grep Xilinx
23:00.0 Memory controller: Xilinx Corporation Device 7024
00: ee 10 24 70 *03* 04 10 00 00 00 80 05 10 00 00 00
. . .
What I found is that the command register value changes after the kernel module reload (marked with *). When the interrupt works the command register value is 0x0407, after the module reload it is 0x0403. Why? I don't know. It is probably just the way the Xilinx AXI Memory Mapped to PCIe core is implemented.
Anyway, you can set the values of the PCI configuration space using setpci(8).
The wanted value of the command register is 0407 so you execute:
# setpci -d <vendor_id>:<device_id> command=0407
#read back to check if it worked
# sudo setpci -d <vendor_id>:<device_id> command
0407
Afterwards the interrupts are working again, and I do not need to reboot.
Within the kernel module you can e.g. use pci_write_config_byte(...) to set the command register (or any other) to the required value. The corresponding functions to access the configuration space can be found here: Linux Device Drivers - Accessing the Configuration Space

Handsfree device not able to receive any audio from sco socket

I am developing a handsfree module, in which after completing service level connection I am setting up sco connection with phone's audio gateway and receive audio data as below...
void audio_connection_setup(char *bluetooth_addr)
{
struct sockaddr_sco addr;
pthread_t tid;
int scoSock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
memset(&addr, 0, sizeof(addr));
addr.sco_family = AF_BLUETOOTH;
addr.sco_bdaddr = *BDADDR_ANY;
if (bind(scoSock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
{
printf( "Can't bind socket: %s (%d)",strerror(errno), errno);
}
hci_read_voice_setting(scoSock, &voice, 5000);
hci_write_voice_setting(scoSock, BT_VOICE_CVSD_16BIT, 5000);
str2ba(bluetooth_addr, &addr.sco_bdaddr);
if (connect(scoSock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
{
printf( "\nCan't connect: %s (%d)", strerror(errno), errno);
}
pthread_create(&tid, NULL, &read_data, &scoSock);
}
Here is the read_data thread
gBuff[16284];
void* read_data(int *scoSock)
{
int fd = *scoSock;
int len = -1;
char buff[48];
int numBytesRead;
while (1)
{
numBytesRead = 0;
while(numBytesRead < 16284)
{
memset(buff, 0x0, 48);
len = recv(fd, buff, 48, MSG_DONTWAIT);
usleep(10);
memcpy(gBuff + numBytesRead, buff + 2, len - 2);
numBytesRead = numBytesRead + len - 2;
}
printf("Number of bytes received = %d errno = %d\n", numBytesRead, errno);
memset(gBuff, 0x0, numBytesRead);
}
}
This code is working fine if I run it on linux PC, But when i run on arm board the recv system call returns errno EAGAIN in continuous loop and never comes out. On PC the recv system call returns number of bytes properly. What may be possible cause to this issue?
I think the reason should be when your code runs on the pc, you may get the audio data from HCI link, i.e. the HF sco over HCI and then you transfer it to PC driver. However when you switch to board, I wonder whether the board's hardware Bluetooth interface lets say UART, whether it has high throughput to transfer the sco data, and most importantly, you need check whether the sco data was routed to PCM interface i.e. does not sending to HCI.

beaglebone black gpio select is not working

I'm trying to detect when a gpio pin goes from low to high and am having trouble. From what I've read I should be able to configure the pin as input this way:
# echo in > /sys/class/gpio/gpio51/direction
# echo rising > /sys/class/gpio/gpio51/edge
Next I try running a c program that waits for the rising edge using select. The code looks like this (notice I commented out an attempt to just read the file, since reading is supposed to block if you don't set O_NONBLOCK):
#include<stdio.h>
#include<fcntl.h>
#include <sys/select.h>
int main(void) {
int fd = open("/sys/class/gpio/gpio51/value", O_RDONLY & ~O_NONBLOCK);
//int fd = open("/sys/class/gpio/gpio51/value", O_RDONLY | O_NONBLOCK);
//unsigned char buf[2];
//int x = read(fd, &buf, 2);
//printf("%d %d: %s\n", fd, x, buf);
fd_set exceptfds;
int res;
FD_ZERO(&exceptfds);
FD_SET(fd, &exceptfds);
//printf("waiting for %d: %s\n", exceptfds);
res = select(fd+1,
NULL, // readfds - not needed
NULL, // writefds - not needed
&exceptfds,
NULL); // timeout (never)
if (res > 0 && FD_ISSET(fd, &exceptfds)) {
printf("finished\n");
}
return 0;
}
The program exits immediately no matter what the state of the pin (high or low). Can anyone see something wrong with the way I'm doing this?
PS. I have a python library that uses poll() to do just this, and the python works as expected. I pull the pin low, call the python, it blocks, pull the pin high and the code continues. So I don't think it is a problem with the linux gpio driver.
https://bitbucket.org/cswank/gadgets/src/590504d4a30b8a83143e06c44b1c32207339c097/gadgets/io/poller.py?at=master
I figured it out. You must read from the file descriptor before the select call returns. Here is an example that works:
#include<stdio.h>
#include<fcntl.h>
#include <sys/select.h>
#define MAX_BUF 64
int main(void) {
int len;
char *buf[MAX_BUF];
int fd = open("/sys/class/gpio/gpio51/value", O_RDONLY);
fd_set exceptfds;
int res;
FD_ZERO(&exceptfds);
FD_SET(fd, &exceptfds);
len = read(fd, buf, MAX_BUF); //won't work without this read.
res = select(fd+1,
NULL, // readfds - not needed
NULL, // writefds - not needed
&exceptfds,
NULL); // timeout (never)
if (res > 0 && FD_ISSET(fd, &exceptfds)) {
printf("finished\n");
}
return 0;
}

Reading from serial port fails

I have the following C program:
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
int main()
{
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK);
if(fd < 0)
{
perror("Could not open device");
}
printf("Device opened\n");
struct termios options;
tcgetattr(fd, &options);
cfmakeraw(&options);
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
tcsetattr(fd, TCSANOW, &options);
char txpacket[] = {0x23, 0x06, 0x00, 0x00, 0xdd, 0xf9};
ssize_t written = write(fd, txpacket, sizeof(txpacket));
printf("Written %d bytes\n", written);
printf("Starting to wait for target to respond\n");
while(1)
{
fd_set readset;
FD_ZERO(&readset);
FD_SET(fd, &readset);
int nCount = select(fd + 1, &readset, NULL, NULL, NULL);
if(nCount > 0)
{
if(FD_ISSET(fd, &readset))
{
int i;
char buffer[128];
ssize_t bytesread = read(fd, buffer, sizeof(buffer));
printf("Received %d bytes\n", bytesread);
for(i = 0; i < bytesread; i++)
{
printf(" %02x", buffer[i]);
}
}
}
}
}
This program opens the serial device /dev/ttyS0, writes a sequence of data to it and starts listening for a response. I get the following output:
Device opened
Written 6 bytes
Starting to wait for target to respond
Received 0 bytes
Received 0 bytes
Received 0 bytes
Received 0 bytes
Received 0 bytes
Received 0 bytes
...
And the application consumes 100% CPU. I'm not able to receive any data, even though the target hardware actually transmits it.
What is wrong?
read() returning 0 indicates the end-of-file condition. You should check for that and break out of the loop if it occurs.
As to what's causing that - end-of-file on a serial port indicates it has detected a hangup, meaning that the DCD line has been dropped.
You can set the CLOCAL flag in options.c_cflag to ignore the modem control lines, if your device doesn't set them properly.
You should try without the O_NONBLOCK flag. in raw mode, if the settings of c_cc[VMIN] and c_cc[VTIME] is 0, the serial port behave like this (according to man cfmakeraw) :
If data is available, read returns
immediately, with the lesser of the
number of bytes available, or the
number of bytes requested. If no data
is available, read returns 0
So what you should try is :
options->c_cc[VMIN]=1;

Resources