Headphone or mic detection connected via 3.5 mm jack linux - linux

I have written a small program that informs when any headphone or mic is connected or not.
I provide a file path to identify, is there any way to know through a C program which file path is for mic or headphone?
Here is the program:
#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
int main()
{
int fd = -1;
char name[256]= {'\0'};
struct input_event event;
/*On my system
/dev/input/event6 for headphone
/dev/input/event5 for mic
*/
if ((fd = open("/dev/input/event6", O_RDONLY)) < 0) {
perror("evdev open");
exit(1);
}
if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) {
perror("evdev ioctl");
}
printf("The device name on path %s is === %s\n Now Plug in or Plug out the device\n",
argv[1],name);
// while(1)
{
read(fd, &event, sizeof(struct input_event));
printf("Event type is %d\n", event.type);
printf("Event code is %d\n", event.code);
printf("Event value is %d\n", event.value);
}
close(fd);
return 0;
}
Here i need to provide path in open, I want my program should identify the path of the mic or headphone. Is there anyway to do this?
It would be great to have some help.
thanks,

Related

ioctl() and file open mode

I wonder if I should open a file using mode O_RDONLY, O_WRONLY or O_RDWR before calling ioctl.
There are reading and writing ioctls. For example _IOC_DIR(VIDIOC_QUERYCAP) will return _IOC_READ.
So I thought when I am calling this ioctl I have to open the file O_RDONLY.
But surprisingly I can open the file with mode=0 and it's still working:
#include <stdio.h> // for printf()
#include <linux/videodev2.h> // for struct v4l2_capability
#include <fcntl.h> // for open()
#include <sys/ioctl.h> // for ioctl()
#include <unistd.h> // for close()
int main(int argc, char** argv)
{
int fd = open("/dev/video0", 0); // mode set to 0
if (fd == -1)
{
printf("open failed\n");
return 1;
}
struct v4l2_capability cap;
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1)
{
printf("ioctl failed\n");
close(fd);
return 1;
}
printf("%s\n", cap.card);
return 0;
}
So my question is how to set the mode properly. Maybe someone got a link to an official documentation.

How to detect if a driver not supports the ioctl command TIOCSSERIAL

I am using USB to RS-232 serial adapters and cannot set the line properties to use custom baud rates on linux (fedora 26 or fedora 32) using:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <termio.h>
#include <linux/serial.h>
#include <err.h>
#include "portutils.h"
static int rate_to_constant(int baudrate) {
#define B(x) case x: return B##x
switch(baudrate) {
B(50); B(75); B(110); B(134); B(150);
B(200); B(300); B(600); B(1200); B(1800);
B(2400); B(4800); B(9600); B(19200); B(38400);
B(57600); B(115200); B(230400); B(460800); B(500000);
B(576000); B(921600); B(1000000);B(1152000);B(1500000);
default: return 0;
}
#undef B
}
int main(int argc, char** argv) {
struct termios options;
struct serial_struct serinfo;
int fd;
int speed = 0;
int baudrate = 625000;
char * port_name;
// Check arguments for correct usage
if (argc != 3){
printf("\n Usage: %s port baudrate!\n\n", argv[0]);
return -1;
}
print_bar();
baudrate = atoi(argv[2]);
port_name = argv[1];
/* Open and configure serial port */
if ((fd = open(port_name, O_RDWR|O_NOCTTY)) == -1)
{
printf("\n Could not open port %s\n", port_name);
goto HELL;
}
printf(" Trying to set baud rate to %d\n On port %s\n", baudrate, port_name);
// if you've entered a standard baud the function below will return it
speed = rate_to_constant(baudrate);
if (speed == 0) {
/* Custom divisor */
serinfo.reserved_char[0] = 0;
if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0) {
printf("\n ioctl: Failed to get #1\n serial line information of %s\n", port_name);
goto HELL;
}
serinfo.flags &= ~ASYNC_SPD_MASK;
serinfo.flags |= ASYNC_SPD_CUST;
serinfo.custom_divisor = (serinfo.baud_base + (baudrate / 2)) / baudrate;
if (serinfo.custom_divisor < 1) {
serinfo.custom_divisor = 1;
}
printf(" Baud_base: %d\n", serinfo.baud_base);
printf(" Devisor: %d\n", serinfo.custom_divisor);
printf(" Resulting baudrate: %f\n", serinfo.baud_base/(1.0*serinfo.custom_divisor));
if (ioctl(fd, TIOCSSERIAL, &serinfo) < 0) {
printf("\n ioctl: Failed to set\n serial line information of %s\n", port_name);
goto HELL;
}
if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0) {
printf("\n ioctl: Failed to get #2\n serial line information of %s\n", port_name);
goto HELL;
}
if (serinfo.custom_divisor * baudrate != serinfo.baud_base) {
warnx("actual baudrate is %d / %d = %f",
serinfo.baud_base, serinfo.custom_divisor,
(float)serinfo.baud_base / serinfo.custom_divisor);
}
}
fcntl(fd, F_SETFL, 0);
tcgetattr(fd, &options);
cfsetispeed(&options, speed ?: B38400);
cfsetospeed(&options, speed ?: B38400);
cfmakeraw(&options);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~CRTSCTS;
if (tcsetattr(fd, TCSANOW, &options) != 0)
{
printf("\n Failed to set final attributes of %s\n\n", port_name);
goto HELL;
}
close(fd);
printf(" Success on port %s\n", port_name);
print_bar();
return 0;
HELL:
print_bar();
return -1;
}
I am working with an adapter using an ASIX chip set and one using an FTDI chip set, the FTDI based device has no problems but the other simply returns -1 from ioctl when i try to set it up with the first TIOCSSERIAL command. So is there a way to detect if the TIOCSSERIAL command not is supported by the used driver?
PS! I am using the label HELL: as a common return point on error of my test program ;-)
USB-to-serial adapters do not support nor need those setserial ioctls.
If you want to set a custom speed on a USB-to-serial adapter, you should use the new TCSETS2, TCSETSW2 and TCSETSF2 ioctls, which take a struct termios2 where you should set the BOTHER flag in .c_cflag and use the .c_ispeed and .c_ospeed fields directly. Look at /usr/include/asm-generic/termbits.h.
No divisor setting or other such thing is necessary.
The last time I did that I had to use some #define kludges in order to be able to include those headers (they conflict with the termios headers from glibc).
Example wrappers (from some old sources, UNTESTED):
#include <sys/ioctl.h>
#define termios termios_HIDE
#define termio termio_HIDE
#define winsize winsize_HIDE
#include <asm/termios.h>
#undef termios
#undef termio
#undef winsize
#define termios termios2
#define tcsetattr tcsetattr2
#define tcgetattr tcgetattr2
int tcsetattr2(int fd, int act, struct termios *ts){
return act == TCSANOW ? ioctl(fd, TCSETS2, ts) :
act == TCSADRAIN ? ioctl(fd, TCSETSW2, ts) :
ioctl(fd, TCSETSF2, ts);
}
int tcgetattr2(int fd, struct termios *ts){
return ioctl(fd, TCGETS2, ts);
}
int cfsetspeed(struct termios *ts, speed_t s){
ts->c_cflag |= BOTHER;
ts->c_ispeed = ts->c_ospeed = s;
return 0;
}

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.

Read data-link (MAC) layer packets in Linux

Which is the simplest/shortest/easiest way to read packets from data-link (MAC) layer on Linux?
Could someone give us a code snippet on how to do that?
Why do we need it?
We are developing a network camera in which the gigabit chip implements only the data-link layer. Since we don't have resources to implement the IP stack, we need to exchange packets using only the MAC address.
Here is the code snippet I was looking for:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
int main()
{
int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (s == -1)
{
printf("Error while creating socket. Aborting...\n");
return 1;
}
void* buffer = (void*)malloc(ETH_FRAME_LEN);
while(1)
{
int receivedBytes = recvfrom(s, buffer, ETH_FRAME_LEN, 0, NULL, NULL);
printf("%d bytes received\n", receivedBytes);
int i;
for (i = 0; i < receivedBytes; i++)
{
printf("%X ", ((unsigned char*)buffer)[i]);
}
printf("\n");
}
return 0;
}

Daemon program that talks to devices

I read about Daemon Programming and i think I'll be needing this to detect my devices if online or not eg(RS232, usb, ethernet). then fetch in a webservice PHP.
The code from this site. http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html
I added some parts to test if i can detect devices.
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
int devfd;
int main(int argc, char *argv[]) {
int c;
while((c=getopt(argc,argv,"s")) != -1)
switch(c){
case 's': devfd = open("/dev/ttyUSB0", O_RDWR);
if(devfd==-1){
printf("offline\n");
}
else{
printf("online\n");
}
break;
default: printf("Wrong command\n");
}
/* Our process ID and Session ID */
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Daemon-specific initialization goes here */
/* The Big Loop */
while (1) {
}
exit(EXIT_SUCCESS);
}
I added this code..
while((c=getopt(argc,argv,"s")) != -1)
switch(c){
case 's': devfd = open("/dev/ttyUSB0", O_RDWR);
if(devfd==-1){
printf("offline\n");
}
else{
printf("online\n");
}
break;
default: printf("Wrong command\n");
}
so doing it like this in the terminal.
./daemon -s //prints offline because the device USB0 is not connected.
Is there another way for me to detect my devices?
Thanks,
If it has udev, you can use libudev to detect and monitor your devices. Checkout nice tutorial from signal11.

Resources