I'm trying to get the usb device serial number from a C code application in a embedded Linux platform (petalinux) using libusb.
I've seen a few similar questions which helped me to get some direction but unfortunately I didn't manage to solve it yet.
First I'll show what I'm trying to read, this is the dmesg of the device when is plugged in:
[ 9.632965] usb 1-1.4: New USB device found, idVendor=0403, idProduct=de37
[ 9.639837] usb 1-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 9.647146] usb 1-1.4: Product: PI CAM
[ 9.651153] usb 1-1.4: Manufacturer: MANUF
[ 9.655326] usb 1-1.4: SerialNumber: 20102130
[ 9.688519] uvcvideo: Found UVC 1.00 device PI IMAGER (0403:de37)
[ 9.695084] uvcvideo 1-1.4:1.0: Entity type for entity Processing 3 was not initialized!
[ 9.703177] uvcvideo 1-1.4:1.0: Entity type for entity Camera 1 was not initialized!
[ 9.711098] input: PI CAM: PI CAM as /devices/platform/amba/ff9d0000.usb0/fe200000.dwc3/xhci-hcd.0.auto/usb1/1-1/1-1.4/1-1.4:1.0/input/input0
What I'm trying to read is the serial number: 20102130
I've tried the next code:
libusb_device*** list=NULL;
libusb_context *context = NULL;
libusb_init(&context);
unsigned char serial[200];
int status = libusb_get_string_descriptor_ascii(device,desc.iSerialNumber, serial,200);
if(status < 0)
{
printf("Index: %d Error: %d\n",desc.iSerialNumber, status);
}else{
printf("Themal camera serial: %s\n",serial);
}
But it gives a -1 error, it prints: "Index: 3 Error: -1" . If I remove the error checking I've got the serial: bA6` which it doesn't make any sense...
Does anyone know how could I retrieve/read that serial number from my application?
Thanks in advance.
#include <iostream>
#include <libusb-1.0/libusb.h>
#include <cstdio>
#include <unordered_map>
using namespace std;
int main() {
libusb_device **devs;
libusb_context *ctx = nullptr; // libusb session context
int r; // for results
ssize_t cnt; // number of usb devices found
r = libusb_init(&ctx); // open session
if (r < 0) {
cerr << "Error: initialization failed: " << r << endl;
return 1;
}
// set the verbosity level of debug messages
libusb_set_debug(ctx, 3);
// get a list of all found USB devices
cnt = libusb_get_device_list(ctx, &devs);
if (cnt < 0) {
cerr << "Error: USB device list not received." << endl;
return 1;
}
libusb_device_descriptor desc{};
for (int i = 0; i < cnt; i++) {
libusb_device_handle *handle;
auto *data = new uint8_t[33]();
r = libusb_get_device_descriptor(devs[i], &desc);
if (r < 0) {
cerr << "Error: Device handle not received, code: " << r << endl;
}
printf("%02X:%02X \t", desc.idVendor, desc.idProduct);
try {
libusb_open(devs[i], &handle);
if (handle != nullptr) {
if (libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, data, 31) >= 0) {
data[32] = '\0';
cout << "Serial Number: \"" << data << "\"";
}
}
libusb_close(handle);
} catch (libusb_error &e) {
cerr << e << endl;
}
cout << endl;
}
// release the memory allocated by the get device list function
libusb_free_device_list(devs, 1);
libusb_exit(ctx); // close session,
return 0;
}
// https://www.keil.com/pack/doc/mw/USB/html/_u_s_b__interface__descriptor.html
// https://libusb.sourceforge.io/api-1.0
Gives me an output:
...
1D6B:02 Serial Number: "0000:05:00.3"
...
Related
#include <unistd.h> //Needed for I2C port
#include <fcntl.h> //Needed for I2C port
#include <sys/ioctl.h> //Needed for I2C port
#include <linux/i2c-dev.h> //Needed for I2C port
#include <stdio.h>
#include <time.h>
int file_i2c;
int length;
unsigned char buffer[60] = {0};
unsigned char cmdbuffer[60] = {0};
//PRINTS (SENSOR STATUS) IN BINARY
void printBin(unsigned char value)
{
for (int i = sizeof(char) * 7; i >= 0; i--) {
printf("%d", (value & (1 << i )) >> i);
}
putc('\n', stdout);
}
//CREATES DELAY IN MS
void delay(int milli)
{
long pause;
clock_t now,then;
pause = milli * (CLOCKS_PER_SEC / 1000);
now = then = clock();
while ((now-then) < pause) {
now = clock();
}
}
//TIMESTAMPS OUTPUT
void timestamp()
{
time_t ltime;
ltime=time(NULL);
printf("%s", asctime(localtime(<ime)));
}
//PORT SELECT FOR TCA9548A Addresses range from 0x70-0x77
void portSelect(int port, int addressTCA)
{
if (port > 7 || port < 0)
return;
if (ioctl(file_i2c, I2C_SLAVE, addressTCA) < 0) {
printf("Failed to acquire bus access and/or talk to slave.\n");
return;
}
cmdbuffer[0] = 1 << port;
length = 1;
write(file_i2c, cmdbuffer, length);
}
int main()
{
//----- OPEN THE I2C BUS -----
char *filename = (char*)"/dev/i2c-1";
if ((file_i2c = open(filename, O_RDWR)) < 0) {
//ERROR HANDLING: you can check errno to see what went wrong
printf("Failed to open the i2c bus");
return 0;
}
portSelect(1, 0x70);
//CONFIGURE SLAVE AND ATTEMPT CONNECTION
if (ioctl(file_i2c, I2C_SLAVE, 0x28) < 0) {
printf("Failed to acquire bus access and/or talk to slave.\n");
return 0;
}
flag:
cmdbuffer[0] = 0xAA;
cmdbuffer[1] = 0x00;
cmdbuffer[2] = 0x00;
length = 3;
if (write(file_i2c, cmdbuffer, length) != length)
//write() returns the number of bytes actually written, if it doesn't match then an error occurred (e.g. no response from the device)
{
printf("Failed to write to the i2c bus.\n"); //FAILS HERE
return 0;
}
length = 7; //<<< Number of bytes to read
if (read(file_i2c, buffer, length) != length)
//read() returns the number of bytes actually read, if it doesn't match then an error occurred (e.g. no response from the device)
{
printf("Failed to read from the i2c bus.\n");
} else {
//timestamp();
printf("Status:\n");
printBin(buffer[0]);
int pressure = (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
int temperature = (buffer[4] << 16) + (buffer[5] << 8) + buffer[6];
printf("Pressure : %d\n", pressure);
printf("Temperature : %d\n\n", temperature);
}
delay(5);
goto flag;
return 0;
}
Current code shown above. I am trying to read from 8 different Honeywell pressure sensors but for the moment I am just trying to get one working. I am able to read/write just fine without the multiplexer. Documentation is unhelpful as it only references Python or Arduino. I have scanned with console and confirmed the addresses of the sensors and mux.
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
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
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.
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.