Change remote device name and class via HCI - linux

I have a bluetooth audio interface which I want to change the name and the CoD (Class of Device) parameter. I have tried doing this using hcitool on Ubuntu, with: sudo hcitool cmd 0x03 0x0024 0x260420 which is supposed to set the CoD to 0x260420 (it's currently 0x260404), but I've had no luck. From what I now understand, I take id that you cannot use hcitool on a Linux computer to send cmd commands to a bluetooth device connected to that computer via bluetooth. Is there a way to achieve this?
Is it at all possible to change a bluetooth devices' configuration remotely in any way?

You can try it with a small C script. We will use these two methods you can find in the hci_lib.h header file.
int hci_read_class_of_dev(int dd, uint8_t *cls, int to);
int hci_write_class_of_dev(int dd, uint32_t cls, int to);
Be aware that a Class of Device (CoD) has strict requirements you can find in the official Bluetooth documentation (here). That's why you need sudo to execute.
// Simple program to read and write Class of Device.
// To compile: `gcc -o rw_cod rw_cod.c -lbluetooth`
// To install lbluetooth: `apt-get install libbluetooth-dev`
// Execute with `sudo ./rw_cod`
// Note the original CoD first because it has strict rules, you might need to write it back later.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // for close()
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
int main(void){
int dd = hci_open_dev(hci_devid("DC:A6:32:E4:9C:05"));
int timeout = 1000;
uint8_t cls[3] = {0};
int class;
int ret;
uint32_t new_class = 0x260404;
// Read CoD
ret = hci_read_class_of_dev(dd, cls, timeout);
class = cls[0] | cls[1] << 8 | cls[2] << 16;
printf("Class of Device = 0x%06x [%d]\n", class, ret);
// Set CoD
ret = hci_write_class_of_dev(dd, new_class, 2000);
printf("Required new Class of Device = 0x%06x [%d]\n", new_class, ret);
// Check read CoD
ret = hci_read_class_of_dev(dd, cls, timeout);
class = cls[0] | cls[1] << 8 | cls[2] << 16;
printf("Actual new Class of Device = 0x%06x [%d]\n", class, ret);
close(dd);
return 0;
}
Console returns:
pi#raspberrypi:~/Work_Bluetooth $ sudo ./rw_cod
Class of Device = 0x260404 [0]
Required new Class of Device = 0x260420 [0]
Actual new Class of Device = 0x260420 [0]
pi#raspberrypi:~/Work_Bluetooth $

Related

UART reply includes previous command?

I am trying to read from a UART device in a Linux environment using a C program, but I experience different results respect to communicating to the UART using screen.
The C code I use to test the UART communication is the following:
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <getopt.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <signal.h>
#include <ctype.h>
bool loop;
void sigHandler(int32_t sig)
{
if(sig == SIGINT)
{
printf("Catched SIGINT");
loop = false;
}
}
int main(int argc, char *argv[])
{
char *devname = argv[1];
int fd = -1;
int nread = -1;
int nwrite = -1;
int ret;
struct termios t_new = {0};
struct termios t_old = {0};
signal(SIGINT, sigHandler);
fd = open(devname, O_RDWR | O_NOCTTY |O_NONBLOCK);
if(fd > 0)
{
printf("TTY open ! Configuring TTY");
}
else
{
fd = -1;
return 1;
}
ret = tcgetattr(fd, &t_old);
if(ret < 0)
{
perror("tcgetattr ");
close(fd);
fd = -1;
return 1;
}
t_new = t_old;
t_new.c_cflag = (B9600 | CS8 | CREAD );
t_new.c_oflag = 0;
t_new.c_iflag = 0;
t_new.c_lflag = 0;
ret = tcsetattr(fd, TCSANOW, &t_new);
loop = true;
while(loop)
{
char s[] = "at+gmi=?\r\n";
nwrite = write(fd, s, strlen(s));
if(nwrite == strlen(s))
{
fd_set rfd;
struct timeval tm = {.tv_sec = 0, .tv_usec = 500000};
FD_ZERO(&rfd);
FD_SET(fd, &rfd);
char buffer[64] = {0};
if(select(fd + 1, &rfd, NULL, NULL, &tm) > 0)
nread = read(fd, buffer, sizeof(buffer));
if(nread > 0)
printf("Reply is: %s\n", buffer);
}
usleep(500000);
}
}
But when I read the reply, it always includes the string I have sent.
I don't experience this problem using screen.
What is the best way to read from an UART in C using Linux ?
Could the multiplexed way (using select) causing the problems ?
EDIT
For completeness, the output is:
Reply is: at+gmi=?
OK
Also, sometimes I don't read anything.
But when I read the reply, it always includes the string I have sent.
Since your termios configuration obliterated the local echo attributes and you're sending an AT modem command, you should try sending an ATE0 command to disable echoing by the modem.
I don't experience this problem using screen.
This observation confirms that the connected modem has its echoing enabled.
The AT command is echoed (by the modem) as you type, but you don't object to this received data in this situation (because you want to see what you type).
If the modem did not have echoing enabled, then you would be complaining that what you type in screen was not visible.
IOW echo is desired when using a terminal emulator program (such as screen), but echoing needs to be disabled when sending data by a program.
What is the best way to read from an UART in C using Linux ?
(Technically you are not reading from a "UART", but rather from a serial terminal that fully buffers all input and output.)
Code that conforms to POSIX standard as described in Setting Terminal Modes Properly
and Serial Programming Guide for POSIX Operating Systems would be far better that what you have now.
I'm surprised that it works at all (e.g. CREAD is not enabled).
Could the multiplexed way (using select) causing the problems ?
Not the echo "problem".
Your program does not do anything that requires using select() and nonblocking mode.
Also, sometimes I don't read anything.
When you write code that is not POSIX compliant, you should not expect reliable program behavior.

Transmitting binary data throught ttyACM

Good afternoon,
I have a peripheral device which communicates over usb over virtual serial port. Everything works well under Windows with generic ACM serial driver, for example with: https://www.kernel.org/doc/Documentation/usb/linux-cdc-acm.inf
Under Linux, it uses CDC ACM drivers. Everything in sys logs seems to work ok, but communication is behaving strangely. When I connect the device, about 10 bytes at the begining of communication are lost. Next, just each second command is received ok.
My questions are:
1) Communication protocol of this device doesn't use ASCII, it is binary (it can randomly contain control characters etc...). Should I use stty for configuring just speed, data bits, stop bits and parity, or something more is necessary to be set up for binary communication? (To ignore control bits in kernel and transmit every byte - raw data.)
2) Any idea, how to test if linux ACM drivers works properly, or which another drivers should i try for my CDC ACM device?
Thanks for any idea!
Linux will often mangle things like line-ending characters (0x0A and 0x0D) when you try to send them over a serial port, which can cause issues if they are actually binary data and not intended as line-ending characters.
Here is a snippet from Pololu that shows how to configure your serial port correctly and then send and receive a few bytes. Pay attention to the part that calls tcsetattr in particular.
#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()
{
const char * device = "/dev/ttyACM0"; // Linux
int fd = open(device, O_RDWR | O_NOCTTY);
if (fd == -1)
{
perror(device);
return 1;
}
#ifdef _WIN32
_setmode(fd, _O_BINARY);
#else
struct termios options;
tcgetattr(fd, &options);
options.c_iflag &= ~(INLCR | IGNCR | ICRNL | IXON | IXOFF);
options.c_oflag &= ~(ONLCR | OCRNL);
options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
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;
}
You might be able to do the same thing with the stty command line utility.

USB device detection - Windows & Linux (FT232R)

How can I enumerate and identify (get pid, vid and serial) FT232R chips in windows and linux. I need to:
1. at start of program enumerate already plugged devices
2. detect plugging of usb device
3. get PID, VID, Serial
And I need to do that in windows and linux. I know there is libusb for windows as well as for linux, but I don't have that much experience with USB. Code example would be best.
You should use SetupAPIs in Windows for getting device information like hardware id(contains vid and pid both) and to detect the plugging/unplugging see example in this link
Registering for Device Notification
To use SetupAPI you can use below code as reference and add/modify according to your requirement.
#include "stdafx.h"
#include <stdlib.h>
#include <Windows.h>
// Link to setapi.lib
#include <setupapi.h>
void GetDeviceInfo()
{
GUID gUSBGuid;
DWORD ClassGuidListSize = 1;
DWORD RequiredSize = NULL;
//if device shown under "USB" node in Devmgr, else see inf for classname
BOOL bres = SetupDiClassGuidsFromName((PCTSTR)"USB",
&gUSBGuid,//GUID will get populated
ClassGuidListSize,
&RequiredSize);
HDEVINFO hDevInfo = SetupDiGetClassDevs(&gUSBGuid,NULL,NULL,DIGCF_PRESENT);
if (INVALID_HANDLE_VALUE != hDevInfo)
{
BOOL bResult = FALSE;
SP_DEVICE_INTERFACE_DATA tDeviceInterfaceData;
tDeviceInterfaceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);
for (int nMemberIndex = 0; TRUE ; nMemberIndex++)
{
SP_DEVINFO_DATA tSpDevInfoData;
tSpDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
//Get the tSpDevInfoData for the instance ID
bResult = SetupDiEnumDeviceInfo(hDevInfo,nMemberIndex,&tSpDevInfoData);
if(bResult)
{
TCHAR *szHardwareId = new TCHAR[128] ;
DWORD dwtype = REG_SZ;
SetupDiGetDeviceRegistryProperty(hDevInfo,&tSpDevInfoData,SPDRP_HARDWAREID
,&dwtype,(PBYTE szHardwareId,256,NULL);
//code to process szHardwareId
delete szHardwareId;
break;
}
}
}
}
I dont have idea for Linux..:(
Hope this helps..

Serial Communication under Linux ttyUSBX

I'm using Linux Ubuntu and try to get serial communication to work.
Ok, what am I using...
I use a Raspberry Pi and connected it via USB/Serial-Adapter with an Inserial Measurement Unit (mulifunction sensor).
Just to clarify what i'm trying to do:
Establishing a connection betwenn Raspberry Pi and IMU.
To run IMU there are given steps i have to follow.
Power-on Sequence:
(a) power-on.
(b) Wait 800ms.
(c) Wait until NOT_READY bit goes to 0. NOT_READY is GLOB_CMD[3Eh]'s bit[10].
TXdata={0x3E,0x00,0x0d}. /* GLOB_CMD read command */
TXdata={0x3E,MSByte,LSByte,0x0d}. /* get response */
Confirm NOT_READY bit.
When NOT_READY becomes 0, it ends. Otherwise , please repeat (c).
(d) Confirm HARD_ERR bits. HARD_ERR is DIAG_STAT[3Ch]'s bit[6:5].
TXdata={0x3C,0x00,0x0d}. /* DIAG_STAT read command */
TXdata={0x3C,MSByte,LSByte,0x0d}. /* get response */
Confirm HARD_ERR is 00.
If HARD_ERR is 00, the IMU is OK. Otherwise, the IMU is faulty.
Register read and write:
[Read Example]
To read a 16bit-data from a register(addr=0x38).
TXdata={0x38,0x00,0x0d}. /* command */
RXdata={0x38,0x04,0x04,0x0d} /* response */
0x04 in 2nd byte of RXdata is Configuration mode.
0x04 in 3rd byte of RXdata is TAP=16.
Please note that read data unit is 16bit, and Most Significant Byte first.
-------------------------------------------------------------
[Write Example]
To write a 8bit-data into a register(addr=0x39).
TXdata={0xB9,0x01,0x0d}. /* command */
RXdata= w/o response
By sending this command, the IMU moves to Sampling mode.
Please note that write data unit is 8bit.
On my Linux Ubuntu there is a ttyUSB0 device given after connecting IMU.
So i tried to set Baudrate, Databits, Stopbits, Parity, flowcontrol.
First via stty-command, later with a simple c++-code.
I'm using this c++-code:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <cstdlib>
void SleepMs(int);
int main(int argc, char** argv)
{
int fd; // port file descriptor
char port[20] = "/dev/ttyUSB0"; // port to connect to
fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY); // connect to port
if(fd == -1)
{
printf("Error while opening the port.\n");
return 1;
}
printf("Port opened successfully.\n");
fcntl(fd, F_SETOWN, getpid());
struct termios settings;
tcgetattr(fd, &settings);
settings.c_cflag &= ~(CBAUD | CSIZE | CREAD);
settings.c_cflag |= B230400;
settings.c_cflag |= CS8;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &settings); // apply the settings
int len = 7;
unsigned char bytes[len];
bytes[0] = 0x3E;
bytes[1] = 0x00;
bytes[2] = 0x0D;
bytes[3] = 0x3E;
bytes[4] = 0x00;
bytes[5] = 0x00;
bytes[6] = 0x0D;
int wr = write(fd, bytes, len);
unsigned char answer[32];
SleepMs(350);
int rd = -1;
int i;
while (rd==-1)
{
if(wr != 7)
{
printf("Error while sending!\n");
}
for(i=0; i<len; i++)
{
printf("%X sent\n", (unsigned int)bytes[i]);
SleepMs(350);
}
printf("\n");
printf("%d bytes sent.\n", wr);
printf("\n");
printf("Trying to read...\n");
printf("\n");
rd = read(fd, answer, 32);
SleepMs(350);
printf("%d\n", rd);
for(i=0; i<rd; i++)
{
printf("%X ", (unsigned int)answer[i]);
}
printf("\n\n");
}
close(fd);
return 0;
}
void SleepMs(int ms) {
usleep(ms*1000); //convert to microseconds
return;
}
If i start program, it tells me "Port open successfully" and writes given bytes in program.
But it receives no data.
I transmit 0x3E 0x00 0x0D to activate GLOB_CMD read command.
I have to confirm "Not Ready"-Bit is 0 but i dont get an answer with my serial connection.
So this is where i need your help, maybe someone got a hint for me.
How can i communicate with my IMU or via serialcommunication properly with Linux?
int wr = write(fd, bytes, len);
Your bytes array only needs to be 3 bytes long, so len should be 3. (The 0x3e is what the IMU should respond with, so it shouldn't be in your program except when checking the response.) When you read, you should only read the expected size of the answer (len=4). You don't need to sleep after writing, and probably not after reading.

Finding all the devices I can use to play PCM with ALSA

I use ALSA to play PCM samples. I open the PCM stream with this function:
int snd_pcm_open(snd_pcm_t** pcmp,
const char* name,
snd_pcm_stream_t stream,
int mode);
I'm currently using "default" as the name parameter. I would like to be able to choose other devices. What I cannot understand is how I can determine what are the names of the other available devices.
I attached a USB microphone to my system and aplay and amixer seems to detect the new device. How do I determine the name of that device? Is there any ALSA function to get a list of available devices with their respective names?
I think you can use snd_device_name_hint for enumerating devices.
Here is an example. Beware that I haven't compiled it !
char **hints;
/* Enumerate sound devices */
int err = snd_device_name_hint(-1, "pcm", (void***)&hints);
if (err != 0)
return;//Error! Just return
char** n = hints;
while (*n != NULL) {
char *name = snd_device_name_get_hint(*n, "NAME");
if (name != NULL && 0 != strcmp("null", name)) {
//Copy name to another buffer and then free it
free(name);
}
n++;
}//End of while
//Free hint buffer too
snd_device_name_free_hint((void**)hints);
It was my first requirements to a linux/unix projects where I need to know about all of the available audio devices capability and name. Then I need to use these devices to capture and plaback the audio. What I have done is pretty simple. There is a linux/unix command which is used to find the devices through alsa utility in linux.
It is:
aplay -l
Now what I did is just make a programme to give the out as like as this by alsa.
For everyone's help I have made a (.so) library and a sample Application demonstrating the use of this library in c++.
The output of my library is like-
[root#~]# ./IdeaAudioEngineTest
HDA Intel plughw:0,0
HDA Intel plughw:0,2
USB Audio Device plughw:1,0
This library can also capture and playback the real-time audio data.
It is available with documentation in IdeaAudio library with Duplex Alsa Audio
Just for grins, your program reformatted:
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <alsa/asoundlib.h>
void listdev(char *devname)
{
char** hints;
int err;
char** n;
char* name;
char* desc;
char* ioid;
/* Enumerate sound devices */
err = snd_device_name_hint(-1, devname, (void***)&hints);
if (err != 0) {
fprintf(stderr, "*** Cannot get device names\n");
exit(1);
}
n = hints;
while (*n != NULL) {
name = snd_device_name_get_hint(*n, "NAME");
desc = snd_device_name_get_hint(*n, "DESC");
ioid = snd_device_name_get_hint(*n, "IOID");
printf("Name of device: %s\n", name);
printf("Description of device: %s\n", desc);
printf("I/O type of device: %s\n", ioid);
printf("\n");
if (name && strcmp("null", name)) free(name);
if (desc && strcmp("null", desc)) free(desc);
if (ioid && strcmp("null", ioid)) free(ioid);
n++;
}
//Free hint buffer too
snd_device_name_free_hint((void**)hints);
}
int main(void)
{
printf("PCM devices:\n");
printf("\n");
listdev("pcm");
printf("MIDI devices:\n");
printf("\n");
listdev("rawmidi");
return 0;
}

Resources