Bluetooth pairing in C blueZ on Linux - c

I cannot find any reference on how to pair a bluetooth device on linux in a program written in C using the BlueZ Bluetooth libraries. I already managed to do a HCI level query to get devices along with thier RSSI levels (during the device discovery) but currently I am stuck with this. I saw a suggestion to use the DBUS api for the blueZ-simple-agent - but is there any way to avoid this and just use some C level methods from BlueZ?

Authentication code from hcitool (original source code can see at http://git.kernel.org/cgit/bluetooth/bluez.git/tree/tools/hcitool.c)
/* Request authentication */
static void cmd_auth(int dev_id, int argc, char **argv)
{
struct hci_conn_info_req *cr;
bdaddr_t bdaddr;
int opt, dd;
for_each_opt(opt, auth_options, NULL) {
switch (opt) {
default:
printf("%s", auth_help);
return;
}
}
helper_arg(1, 1, &argc, &argv, auth_help);
str2ba(argv[0], &bdaddr);
if (dev_id < 0) {
dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
if (dev_id < 0) {
fprintf(stderr, "Not connected.\n");
exit(1);
}
}
dd = hci_open_dev(dev_id);
if (dd < 0) {
perror("HCI device open failed");
exit(1);
}
cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
if (!cr) {
perror("Can't allocate memory");
exit(1);
}
bacpy(&cr->bdaddr, &bdaddr);
cr->type = ACL_LINK;
if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
perror("Get connection info failed");
exit(1);
}
if (hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000) < 0) {
perror("HCI authentication request failed");
exit(1);
}
free(cr);
hci_close_dev(dd);
}
And setting up PIN
/* Activate encryption */
static void cmd_enc(int dev_id, int argc, char **argv)
{
struct hci_conn_info_req *cr;
bdaddr_t bdaddr;
uint8_t encrypt;
int opt, dd;
for_each_opt(opt, enc_options, NULL) {
switch (opt) {
default:
printf("%s", enc_help);
return;
}
}
helper_arg(1, 2, &argc, &argv, enc_help);
str2ba(argv[0], &bdaddr);
if (dev_id < 0) {
dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
if (dev_id < 0) {
fprintf(stderr, "Not connected.\n");
exit(1);
}
}
dd = hci_open_dev(dev_id);
if (dd < 0) {
perror("HCI device open failed");
exit(1);
}
cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
if (!cr) {
perror("Can't allocate memory");
exit(1);
}
bacpy(&cr->bdaddr, &bdaddr);
cr->type = ACL_LINK;
if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
perror("Get connection info failed");
exit(1);
}
encrypt = (argc > 1) ? atoi(argv[1]) : 1;
if (hci_encrypt_link(dd, htobs(cr->conn_info->handle), encrypt, 25000) < 0) {
perror("HCI set encryption request failed");
exit(1);
}
free(cr);
hci_close_dev(dd);
}

You can download the newest version of the source code here: http://www.bluez.org/
There ist the tool "btmgmt" and also the bluez-simple-agent that can be used for pairing. The code is all in the sources and there is also some documentation (in docs folder). Maybe you can use code of one of these tools for your desires or maybe it helps you understand the pairing.
I want to pair 2 device with the bluez bluetooth library in the first place but I happend to find helpful code in the source for the bluez-tools.
There is the file "btmgmt.c" and some files that are include in it which implement the pairing.
For me unfortunately it is not working and I can't understand why. But maybe you have more success with it. Here is how you can test it.
If you haven't already, download the newest version of the source code here: http://www.bluez.org/
Extract it and open a terminal in the bluez folder.
Then run the following in the terminal:
./configure --prefix=/usr \
--sysconfdir=/etc \
--localstatedir=/var \
--enable-tools \
--disable-test \
--disable-systemd
I don't remember all packages you need to install but you can run this command and check why it fails, then install the package and rerun it till it works. Ask google if you don't know which package you need to install.
Afterwards:
make
Now you can switch into tools folder from terminal and type ./btmgmt to see how to use it.
You can also intall it to be able to use it by just typing "btmgmt" regardless of your location.
sudo /usr/bin/install -c tools/btmgmt /usr/bin/btmgmt
You need sudo rights to use it.

This dbus command can be used to initiate pairing
dbus-send --system --print-reply --dest=org.bluez /org/bluez/1301/hci0 org.bluez.Adapter.CreatePairedDevice string:"XX:XX:XX:XX:XX:XX" objpath:/org/bluez/agent_1317 string:"NoInputNoOutput"
Here 1301 is the process id of bluetoothd
/org/bluez/agent_1317 is the bluetooth pairing agent.The bluezagent that comes as agent.c in bluez/test can be used for this purpose.

I used to play around with Bluez in C/C++. As far as I can understand, C/C++ interface is not really welcome in BlueZ for users, it do prefers python.
So the main idea is to take a look at BlueZ repo, at directory /tools which implements some of required features in C.
Also, you can take a look at this article which shows some possibilities of BlueZ usage from plain C: https://people.csail.mit.edu/albert/bluez-intro/c404.html
Here is what i came up with (based on hcidump):
https://github.com/IGR2014/Alsa-BlueZ-Example (inspired by https://github.com/pauloborges/bluez, https://git.kernel.org/pub/scm/bluetooth/bluez.git)
Small example of possible implementation for connect function:
// Connect to device
bool btCore::connect(const char* address) {
std::cout << "Connecting to device\t" << address << " ..." << std::endl;
std::cout << std::endl;
uint16_t handle;
unsigned int ptype = HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5;
char addr[19] = {0};
bdaddr_t bdaddr;
str2ba(address, &bdaddr);
// Open local HCI device
int sk = hci_open_dev(dev_id);
if (sk < 0) {
std::cerr << "HCI open device:\t\t" << strerror(errno) << std::endl;
return false;
}
// Establish HCI connection with device
if (hci_create_connection(sk, &bdaddr, htobs(ptype), 0, 0, &handle, 0) < 0) {
std::cerr << "HCI create connection:\t" << strerror(errno) << std::endl;
close(sk);
return false;
} else {
std::cout << "Connection:\t\tOK" << std::endl;
}
// Authenticate HCI link (without pin)
if (hci_authenticate_link(sk, handle, 0) < 0) {
std::cerr << "HCI authenticate connection:\t" << strerror(errno) << std::endl;
close(sk);
return false;
} else {
std::cout << "Authentication:\t\tOK" << std::endl;
}
// Encrypt HCI link
if (hci_encrypt_link(sk, handle, 1, 0) < 0) {
std::cerr << "HCI encrypt connection:\t" << strerror(errno) << std::endl;
close(sk);
return false;
} else {
std::cout << "Encryption:\t\tOK" << std::endl;
}
close(sk);
return true;
}
Next thing you have to do is discover services (https://www.bluetooth.com/specifications/assigned-numbers/service-discovery)
Also here is my old question which can lead you to answer: C++ Bluetooth headphones under Linux over BlueZ
Please note: All of the API (if we can name those internal functions "API") is not guaranteed to be same for other versions of BlueZ. At least, there is no any warranty of such behavior.

Related

I want to ask about the use of the kea dhcp server, I hope someone can help me

I want to modify the dhcp4 source code to notify the ddns server to set the dynamic domain name when assigning the lease, but I added the nanomsg statement in dhcp4_srv.cc. When my nanomsg performs shutdown or close, the dhcp4 service will automatically close. This is why there is no other way to implement dynamic domain names (my dynamic domain name mainly sends the field set by the foreground and the mac address and IP address to the ddns server, or it may be the login account of the foreground).
Someone can help me? thank you very much.
if (lease) {
// We have a lease! Let's set it in the packet and send it back to
// the client.
// 我们有租约! 让我们在数据包中进行设置,然后将其发送回客户端。
if (fake_allocation) {
//租约建议
LOG_INFO(lease4_logger, DHCP4_LEASE_ADVERT)
.arg(query->getLabel())
.arg(lease->addr_.toText());
} else {
//成功授予租约
LOG_INFO(lease4_logger, DHCP4_LEASE_ALLOC)
.arg(query->getLabel())
.arg(lease->addr_.toText())
.arg(lease->valid_lft_);
int rc = 0;
int pair_socket = 0;
int str_len = 0;
char buf[256] = { 0 };
char buf1[256] = { 0 };
int timeo = 5000;
//计算长度
str_len = strlen(HELLOWORLD);
//初始化socket
pair_socket = nn_socket(1, NN_PAIR);
if (pair_socket == -1) {
printf("nn_socket failed! error: %s.\n", nn_err_strerror(errno));
//system("pause");
nn_err_abort();
//return 0;
}
//设置超时
rc = nn_setsockopt(pair_socket, 0, NN_SNDTIMEO, &timeo, sizeof(timeo));
rc = nn_setsockopt(pair_socket, 0, NN_RCVTIMEO, &timeo, sizeof(timeo));
//连接服务端
rc = nn_connect(pair_socket, SOCKET_ADDRESS2);
if (rc < 0) {
printf("bind failed! error: %s.\n", nn_err_strerror(errno));
//system("pause");
nn_err_abort();
//return 0;
}
//将hello world复制到buf中
memcpy(buf, HELLOWORLD, str_len);
//发送数据
rc = nn_send(pair_socket, buf, str_len, 0);
if (rc < 0) {
printf("nn_send failed! error: %s.rc = %d.\n", nn_err_strerror(errno), rc);
}
//打印
printf("send:%s\n", buf);
//这里主要是测试使用,平时项目不要使用标签
//接收数据
rc = nn_recv(pair_socket, buf1, 256, 0);
if (rc < 0) {
printf("nn_recv failed! error: %s.rc = %d.\n", nn_err_strerror(errno), rc);
}
//打印
printf("recv:%s\n", buf1);
memset(buf1, 0, 256);
//Sleep(1000);
//关闭套接字
rc = nn_shutdown(pair_socket, 1);
if (rc != 1) {
printf("nn_close failed! error: %s.\n", nn_err_strerror(errno));
//system("pause");
nn_err_abort();
//return 0;
}
std::cout << "testtttttttttttttttttttttttttttttttttt "
<< "hostnameeeeeeeeeeeeeeeeeeeeeeeeeeeeee:" << lease->hostname_ << std::endl;
std::cout << "testtttttttttttttttttttttttttttttttttt "
<< "ipppppppppppppppppppppppppppppppppppp:" << lease->addr_.toText() << std::endl;
std::cout << "lease ALLOC " << __LINE__ << " file name " << __FILE__
<< " HOST:" << lease->hostname_ << std::endl;
std::cout << "lease ALLOC " << __LINE__ << " file name " << __FILE__
<< " IP:" << lease->addr_.toText() << std::endl;
}
It sounds like you need some general information on the KEA software developed by Internet Systems Consortium aka: https://kea.isc.org/ I would read through the Kea docs, mailing lists and the general development wiki to try to hone in on the specific issue you are having with your code. Once you are able explain the issue you are having with your code you can edit your question with your details, and there is a much better chance that you will get more meaningful answers from this site.

Unable to claim USB interface with C + libusb on Mac OS X

I've got a compound USB + CDC device I built using a PIC32 microcontroller, and I'm trying to connect to the device and send some data to the CDC data interface endpoint from my Mac.
I know the circuit works 100%, as the device registers as both a HID joystick, and I'm able to connect to the device using Zoc terminal, on /dev/tty.usbmodemfa132. I can send commands with Zoc, and see my MCU responding to these commands by blinking some LEDs on the circuit.
I'm running this on Mac OS X Mavericks, but had the same problem with a similar example I gave up on, a few weeks ago on Mountain Lion.
My code looks like follows:
// Includes -----------------------------------------------------------------------------------------------------------
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
#include <unistd.h>
// Defines ------------------------------------------------------------------------------------------------------------
#define VID 0x04d8
#define PID 0x005e
#define CDC_DATA_INTERFACE_ID 2
// Function Declarations ----------------------------------------------------------------------------------------------
void print_device(libusb_device *device);
void send(libusb_context *usb_context, uint16_t vid, uint16_t pid);
// Function Definitions -----------------------------------------------------------------------------------------------
/**
* main
*/
int main(int argc, char **argv)
{
libusb_device **usb_devices = NULL;
libusb_context *usb_context = NULL;
ssize_t device_count = 0;
bool debug_enabled = false;
int c;
// Collect command line attributes
while ( (c = getopt(argc, argv, "d")) != -1) {
switch (c) {
case 'd':
debug_enabled = true;
break;
}
}
// Initialize USB context
int result = libusb_init(&usb_context);
if(result < 0) {
printf("Unable to initialise libusb!");
return EXIT_FAILURE;
}
// Turn debug mode on/off
if(debug_enabled) {
libusb_set_debug(usb_context, 3);
}
// Get USB device list
device_count = libusb_get_device_list(usb_context, &usb_devices);
if(device_count < 0) {
puts("Unable to retrieve USB device list!");
}
// Iterate and print devices
puts("VID PID Manufacturer Name\n------ ------ -------------------");
for (int i = 0; i < device_count; i++) {
print_device(usb_devices[i]);
}
// Attempt to send data
send(usb_context, VID, PID);
// Cleanup and exit
libusb_free_device_list(usb_devices, 1);
libusb_exit(usb_context);
return EXIT_SUCCESS;
}
/**
* print_device
*/
void print_device(libusb_device *device)
{
struct libusb_device_descriptor device_descriptor;
struct libusb_device_handle *device_handle = NULL;
// Get USB device descriptor
int result = libusb_get_device_descriptor(device, &device_descriptor);
if (result < 0) {
printf("Failed to get device descriptor!");
}
// Only print our devices
if(VID == device_descriptor.idVendor && PID == device_descriptor.idProduct) {
// Print VID & PID
printf("0x%04x 0x%04x", device_descriptor.idVendor, device_descriptor.idProduct);
} else {
return;
}
// Attempt to open the device
int open_result = libusb_open(device, &device_handle);
if (open_result < 0) {
libusb_close(device_handle);
return;
}
// Print the device manufacturer string
char manufacturer[256] = " ";
if (device_descriptor.iManufacturer) {
libusb_get_string_descriptor_ascii(device_handle, device_descriptor.iManufacturer,
(unsigned char *)manufacturer, sizeof(manufacturer));
printf(" %s", manufacturer);
}
puts("");
libusb_close(device_handle);
}
/**
* send
*/
void send(libusb_context *usb_context, uint16_t vid, uint16_t pid)
{
libusb_device_handle *device_handle;
device_handle = libusb_open_device_with_vid_pid(usb_context, vid, pid);
if (device_handle == NULL) {
puts("Unable to open device by VID & PID!");
return;
}
puts("Device successfully opened");
unsigned char *data = (unsigned char *)"test";
if (libusb_kernel_driver_active(device_handle, CDC_DATA_INTERFACE_ID)) {
puts("Kernel driver active");
if (libusb_detach_kernel_driver(device_handle, CDC_DATA_INTERFACE_ID)) {
puts("Kernel driver detached");
}
} else {
puts("Kernel driver doesn't appear to be active");
}
int result = libusb_claim_interface(device_handle, CDC_DATA_INTERFACE_ID);
if (result < 0) {
puts("Unable to claim interface!");
libusb_close(device_handle);
return;
}
puts("Interface claimed");
int written = 0;
result = libusb_bulk_transfer(device_handle, (3 | LIBUSB_ENDPOINT_OUT), data, 4, &written, 0);
if (result == 0 && written == 4) {
puts("Send success");
} else {
puts("Send failed!");
}
result = libusb_release_interface(device_handle, CDC_DATA_INTERFACE_ID);
if (result != 0) {
puts("Unable to release interface!");
}
libusb_close(device_handle);
}
I'm getting the following error output:
libusb: 0.828223 error [darwin_open] USBDeviceOpen: another process has device opened for exclusive access
libusb: 0.828241 info [darwin_open] device open for access
Device successfully opened
Kernel driver doesn't appear to be active
libusb: 0.828641 error [darwin_claim_interface] USBInterfaceOpen: another process has device opened for exclusive access
Unable to claim interface!
libusb: 0.828766 info [event_thread_main] thread exiting
Is there a way I can release the USB device from the other process, freeing it up so I can claim it?
Is there an alternative way I can connect to /dev/tty.usbmodemfa132 to send and receive data to the CDC interface on the USB device?
An alternative to libusb perhaps?
That's right. While libusb seems to be all-powerful in Linux, you cannot use it to connect to a USB CDC interface on Mac OS X because that interface is already claimed by the AppleUSBCDCACM driver.
What you should do is use the standard way that people connect to serial ports. This will be easier because you don't have to worry about endpoints and bulk transfers and such. Here is some example cross-platform C code I wrote for one of our CDC-based products that connects to a COM port to read and write some data (source). It uses the standard functions open, read, and write.
// Uses POSIX functions to send and receive data from a Maestro.
// NOTE: You must change the 'const char * device' line below.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#ifdef _WIN32
#define O_NOCTTY 0
#else
#include <termios.h>
#endif
// Gets the position of a Maestro channel.
// See the "Serial Servo Commands" section of the user's guide.
int maestroGetPosition(int fd, unsigned char channel)
{
unsigned char command[] = {0x90, channel};
if(write(fd, command, sizeof(command)) == -1)
{
perror("error writing");
return -1;
}
unsigned char response[2];
if(read(fd,response,2) != 2)
{
perror("error reading");
return -1;
}
return response[0] + 256*response[1];
}
// Sets the target of a Maestro channel.
// See the "Serial Servo Commands" section of the user's guide.
// The units of 'target' are quarter-microseconds.
int maestroSetTarget(int fd, unsigned char channel, unsigned short target)
{
unsigned char command[] = {0x84, channel, target & 0x7F, target >> 7 & 0x7F};
if (write(fd, command, sizeof(command)) == -1)
{
perror("error writing");
return -1;
}
return 0;
}
int main()
{
// Open the Maestro's virtual COM port.
const char * device = "\\\\.\\USBSER000"; // Windows, "\\\\.\\COM6" also works
//const char * device = "/dev/ttyACM0"; // Linux
//const char * device = "/dev/cu.usbmodem00034567"; // Mac OS X
int fd = open(device, O_RDWR | O_NOCTTY);
if (fd == -1)
{
perror(device);
return 1;
}
#ifndef _WIN32
struct termios options;
tcgetattr(fd, &options);
options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
options.c_oflag &= ~(ONLCR | OCRNL);
tcsetattr(fd, TCSANOW, &options);
#endif
int position = maestroGetPosition(fd, 0);
printf("Current position is %d.\n", position);
int target = (position < 6000) ? 7000 : 5000;
printf("Setting target to %d (%d us).\n", target, target/4);
maestroSetTarget(fd, 0, target);
close(fd);
return 0;
}
If you want to use some USB device that is also recognised by the Apple FTDI serial driver, you can first unload the driver:
sudo kextunload -b com.apple.driver.AppleUSBFTDI
After that you can use it through libusb normally.
For other devices that are recognised as serial devices, you probably need to unload some other driver.
The problem seems to be due to a conflict between different drivers, which use the same libraries, and in my case they were related to previous Samsung device installations. i had solved this way:
kextstat | grep -v apple
To have a return like this:
70 0 0x57574000 0x3000 0x2000 com.devguru.driver.SamsungComposite (1.2.4) <33 4 3>
72 0 0x57831000 0x7000 0x6000 com.devguru.driver.SamsungACMData (1.2.4) <71 33 5 4 3>
94 0 0x57674000 0x3000 0x2000 com.devguru.driver.SamsungACMControl (1.2.4) <33 4 3>
Then:
$ sudo kextunload -b com.devguru.driver.SamsungComposite
$ sudo kextunload -b com.devguru.driver.SamsungACMData
$ sudo kextunload -b com.devguru.driver.SamsungACMControl
Done. Enjoy

Generate elliptic curve key pairs (EC_KEY_generate_key) in multiple threads using OpenSSL C library

I want to generate many ec key pairs. Speeding up the process a bit, I rewrote my appication to use multiple threads for this job. Here is a code snippet of the way each thread wants to generate the keys:
(...)
EC_KEY* _ec_key = EC_KEY_new();
EC_GROUP* ec_group_new = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
const EC_GROUP* ec_group = ec_group_new;
if (!EC_KEY_set_group(ec_key,ec_group))
DieWithError("Error in initializeCrypto, EC_KEY_set_group failed!");
// Segfault at this position
if(!EC_KEY_generate_key(ec_key))
DieWithError ("Error in generateKeys, EC_KEY_generate_key failed!");
(...)
EC_GROUP_free(ec_group_new);
EC_KEY_free(ec_key);
Ok at the first glance, everything seemed to work fine. The applications ran twice as fast using four threads on my i5 520m. But then after 3-4 E6 key generations it suddenly segfaults. If I lock the EC_KEY_generate_key operation there is no segfault anymore, but the advantage of using multiple threads is gone. Now my questions. Is it possible split the creation of keys into multiple threads without corrupting memory? I didn't found any information using google. The SSL Docu doesn't mention anything about thread-safety, though. Any help is highly appreciated. thx
// Segfault at this position
if(!EC_KEY_generate_key(ec_key))
DieWithError ("Error in generateKeys, EC_KEY_generate_key failed!");
...
... But then after 3-4 E6 key generations it suddenly segfaults.
You are using OpenSSL's random number generator, and its not thread safe. Below is from cryptlib.c around line 125. Notice the random number generators and the elliptic curve gear make the list.
/* real #defines in crypto.h, keep these upto date */
static const char* const lock_names[CRYPTO_NUM_LOCKS] =
{
"<<ERROR>>",
"err",
"ex_data",
"x509",
"x509_info",
"x509_pkey",
"x509_crl",
"x509_req",
...
"ssl_ctx",
"ssl_session",
"ssl",
"ssl_method",
"rand",
"rand2",
...
"ecdsa",
"ec",
"ecdh",
"bn",
"ec_pre_comp",
...
};
You have to explicitly set the locks. See OpenSSL's threads(3).
Is it possible split the creation of keys into multiple threads without corrupting memory?
Yes, but you have to use OpenSSL's locking mechanism.
Here's what my OpenSSL initialization routine looks like in C++. It initializes the locks and sets the callbacks.
pthread_mutex_t s_locks[CRYPTO_NUM_LOCKS] = { };
void Initialize()
{
static once_flag init;
std::call_once(init, []() {
// Standard OpenSSL library init
OPENSSL_no_config();
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
// Lock setup
LOCK_setup();
CALLBACK_setup();
});
}
void LOCK_setup()
{
ASSERT(CRYPTO_NUM_LOCKS == CRYPTO_num_locks());
if(CRYPTO_NUM_LOCKS != CRYPTO_num_locks())
throw runtime_error("CRYPTO_NUM_LOCKS mismatch");
for(unsigned i = 0; i < CRYPTO_NUM_LOCKS; ++i)
{
int rc = pthread_mutex_init(&s_locks[i], NULL);
ASSERT(rc == 0);
if(!(rc == 0))
throw runtime_error("pthread_mutex_init");
}
}
void CALLBACK_setup()
{
CRYPTO_set_id_callback(&ThreadIdFnc);
CRYPTO_set_locking_callback(&LockingFnc);
}
void LockingFnc(int mode, int idx, const char* file, int line)
{
ASSERT(mode == CRYPTO_LOCK || mode == CRYPTO_UNLOCK);
ASSERT(CRYPTO_NUM_LOCKS == CRYPTO_num_locks());
ASSERT(idx >= 0 && idx < CRYPTO_NUM_LOCKS);
if(!(idx >= 0 && idx < CRYPTO_NUM_LOCKS))
{
ostringstream oss;
oss << "LockingFnc: lock failed with bad index ";
oss << idx << ". File: " << (file ? file : "Unknown");
oss << ", line: " << line;
// Log oss.str()
return;
}
if((mode & CRYPTO_LOCK) == CRYPTO_LOCK)
{
int rc = pthread_mutex_lock(&s_locks[idx]);
int err = errno;
ASSERT(rc == 0);
if(!(rc == 0))
{
ostringstream oss;
oss << "LockingFnc: lock failed with error ";
oss << err << ". File: " << (file ? file : "Unknown");
oss << ", line: " << line;
throw runtime_error(oss.str());
}
}
else if((mode & CRYPTO_UNLOCK) == CRYPTO_UNLOCK)
{
int rc = pthread_mutex_unlock(&s_locks[idx]);
int err = errno;
ASSERT(rc == 0);
if(!(rc == 0))
{
ostringstream oss;
oss << "LockingFnc: unlock failed with error ";
oss << err << ". File: " << (file ? file : "Unknown");
oss << ", line: " << line;
throw runtime_error(oss.str());
}
}
}
unsigned long ThreadIdFnc()
{
#if defined(AC_OS_APPLE)
ASSERT(sizeof(unsigned long) >= sizeof(pid_t));
return static_cast<unsigned long>(pthread_mach_thread_np(pthread_self()));
#elif defined(AC_OS_STARNIX)
ASSERT(sizeof(unsigned long) >= sizeof(pid_t));
return static_cast<unsigned long>(gettid());
#else
# error "Unsupported platform"
#endif
}
If you are not using libssl, then forgo the call to SSL_library_init. All libcrypto needs is the call to OpenSSL_add_all_algorithms to initialize.
The SSL Documentation doesn't mention anything about thread-safety, though.
Yeah, the docs leave something to be desired at times. I know a bunch of folks are working on improving it through a wiki run by the OpenSSL Foundation. Matt Caswell has done a lot of work in simply documenting the elliptic curve stuff at http://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography. He's also responsible for the POD files and MAN pages. Keep in mind that Matt did not write any of the code - he's just documenting it for others.
There's a page on initialization, but it does not have the code for the locks. Its on my TODO list. See http://wiki.openssl.org/index.php/Library_Initialization.

Linux C Serial Program Freezes

I'm working on a small Linux server (Ubuntu Server 13.04 running on a Beagleboard xM ARM computer) that will be communicating between a laptop wirelessly and an Arduino. The issue I seem to be having is regarding the communication between the Arduino and the Beagleboard. The program will run just fine for a certain amount of time, ~30 seconds or so, then it will halt. The program will stay running but the port apparently freezes.
The program I'm running is currently just a test program that will sweep a servo over a certain range. The code for the functions used to set up the ports was found here.
My program code is as follows, with exception of the code found in the separate thread:
#include <errno.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
using namespace std;
...
int main (int argc, const char* argv[]) {
cout << "TestIO running...\n";
char* portname = "/dev/ttyACM0";
// Open serial port
int fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
// Return error if port cannot be opened
if (fd < 0)
{
cout << "error " << errno << " opening " << portname << ": " << strerror (errno) << "\n";
return -1;
}
set_interface_attribs (fd, B9600, 0); // set speed to 9600 bps, 8n1 (no parity)
set_blocking (fd, 0); // set no blocking
// Read from serial port
//char inputBuffer[64];
//int inputLength = read(fd, inputBuffer, sizeof inputBuffer);
double output = 575;
char outputString[255];
char outputLength;
int incrimentor = 1;
char inChar;
for(;;) {
if (output >= 675 )
incrimentor = -1;
else if (output <= 375)
incrimentor = 1;
output += incrimentor;
// Sweep wheels on car, set drive motor to 0
outputLength = sprintf(outputString, "%0.2f", output);
write(fd, outputString, outputLength);
write(fd, ",", 1);
write(fd, "0", 1);
write(fd, "\n", 1);
cout << outputString << "\n";
// Sleep thread for 5000 uS (5 ms)
usleep(5000);
}
close(fd);
return 0;
}
On a slightly different note, when the program freezes I must force it to quit the code to close the port is never reached and thus I cannot run the program again to test it. I'm curious if anyone might know how to close a serial port through a Linux command run in the terminal.
Thanks!
Referring your second issues on how to quit the hanging program:
Adding tests for the return value to all system calls is a good idea in general!
Be aware that read()/write() do not necessarily read in/write out as much data as the were told to.
Also read()/write() return if the process received a signal.
Here in particular add testing the result to the calls that might block (write()):
ssize_t writen(int fd, char * buffer, size_t size)
{
ssize_t written_total = 0;
ssize_t written = 0;
while (outputLength > written_total)
{
written = write(fd, buffer + written_total, size - written_total);
if (-1 == written)
{
if (EINTR == errno)
{
/* interupted by signal -> break and leave */
break;
}
elseif ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
continue; /* try again */
}
/* another error occured -> log, break and leave */
break;
}
written_total += written;
}
if (outputLength > written_total)
{
if (-1 = written)
{
/* handle error */
}
else
{
/* notify of interruption */
}
}
else
{
/* log succesfully transmission of all data */
}
return written_total;
}
int main()
{
...
do
{
if (outputLength != writen(fd, outputString, outputLength))
{
fprintf(stderr, "writen(fd, outputString, outputLength) failed");
break;
}
if (1 != writen(fd, ",", 1))
{
fprintf(stderr, "writen(fd, ",", 1)) failed");
break;
}
if (1 != writen(fd, "0", 1))
{
fprintf(stderr, "writen(fd, "0", 1)) failed");
break;
}
if (1 != writen(fd, "\n", 1))
{
fprintf(stderr, "writen(fd, "\n", 1)) failed");
break;
}
} while (0);
if (-1 == close(fd))
{
perror("close() failed");
}
...
}
Note that the program also needs to have a signal handler registered (for SIGUSR1 for example) that does nothing, but "eating" the signal.
Then from the command line you could easily un-block the program by doing:
$ kill <program-pid> -SIGUSR1

(Linux) Get /dev/input/eventX from an attached USB device with PID:VID

So my daemon will sit there and listen to udev, waiting for connect/disconnect events so it can notify the other thread to attach or stop reading from the /dev/input/eventX file.
Essentially, it's listening to a USB RFID scanner attached to the local system (which emulates a HID Keyboard).
Now I've got the /dev/input/eventX reading code going - but since I threaded it the UDEV thread crashes.
What is the best way of obtaining the correct /dev/input/eventX device from a known USB device (Like VID:PID)?
You could add a udev rule that either runs a script to notify your program, or gives you a symlink to the device with a predictable name. A quick search turned up this page explaining how to create rules.
Well the code crashing was a result of something else completely (vfprintf vs. fprintf) - anyway libudev as of version 172 has a nifty little function, that when enumerating devices it auto-binds the search (enumeration) to a single parent and returns only it's children:
udev_enumerate_add_match_parent()
I've already written the code that finds the hidraw device by VID/PID:
/sys/devices/pci000xyz/000.000.XYZ/usbX/X-Y
And I'm just waiting for that udev version to become streamlined with Ubuntu Natty, because then I'll just create a new enumeration and hand it the udev_device I found in the previous enum and get all it's children; Including the child device I'm after:
/sys/devices/pci000xyz/000.000.XYZ/usbX/X-Y/X-Y:A.B/input/inputX/eventY
In the mean time, I'll do as suggested and create a symlink - cheers Dmitri.
Look at this file: /proc/bus/input/devices
Example line from the file:
I: Bus=0003 Vendor=1a2c Product=0c23 Version=0110
N: Name="USB USB Keyboard"
P: Phys=usb-0000:00:14.0-3/input0
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0/0003:1A2C:0C23.0015/input/input30
U: Uniq=
H: Handlers=sysrq kbd event10
B: PROP=0
B: EV=120013
B: KEY=1000000000007 ff800000000007ff febeffdff3cfffff fffffffffffffffe
B: MSC=10
B: LED=7
This function gets the event number from the device with the matching VID:PID:
#include <string>
#include <iostream>
#include <fstream>
void open_device (std::string device_vid, std::string device_pid)
{
try
{
std::ifstream file_input;
std::size_t pos;
std::string device_path, current_line, search_str, event_str;
std::string device_list_file = "/proc/bus/input/devices";
bool vid_pid_found = false;
int fd = 0;
bool debug = true;
// 1. open device list file
file_input.open(device_list_file.c_str());
if (!file_input.is_open())
{
std::cerr << "file_input.open >> " << std::strerror(errno) << std::endl;
throw -2;
}
// 2. search for first VID:PID and get event number
search_str = "Vendor=" + device_vid + " Product=" + device_pid;
while (getline(file_input, current_line))
{
if (!vid_pid_found)
{
pos = current_line.find(search_str, 0);
if (pos != std::string::npos)
{
vid_pid_found = true;
search_str = "event";
}
}
else
{
pos = current_line.find(search_str, 0);
if (pos != std::string::npos)
{
event_str = current_line.substr(pos);
// remove spaces from string
event_str.erase(std::remove(event_str.begin(), event_str.end(), ' '), event_str.end());
break;
}
}
}
// 3. build device path
device_path = "/dev/input/" + event_str;
if (debug) std::cout << "device_path = " << device_path << std::endl;
// 4. connect to device
fd = open (device_path.c_str(), O_RDONLY);
if (fd < 0)
{
std::cerr << "open >> errno = " << std::strerror(errno) << std::endl;
throw -3;
}
}
catch (const std::exception &e)
{
std::cerr << "e.what() = " << e.what() << std::endl;
throw -1;
}
return;
}
The events are enumerated as they are plugged in. ls /dev/input before and after unplugging a USB device will show different results.

Resources