sysfs, ifreq, IOCTL or ??? to programatically monitor network status - c

We have an embedded SoC running BusyBox Linux (kernel 2.6.x), and we have a need to monitor or at least notice in a timely manner when the network connection goes down or comes up (catching other events would be good but not essential).
I've spent a long time googling & reading SO threads and there seems to be a ton of different answers depending on the exact task at hand on the particular OS and the phases of the moon etc.
Our specific criteria are:
We are looking from inside a C program, so C code is preferable to command line calls.
Although the interface is always there, we can't guarantee it is or has ever been up (I have seen comments on some examples that only work when the interface is up even if the link is down)
It would be nice not to have to poll, but rather to send/catch status change messages as and when they happen. I believe the kernel may already get such messages from the driver, but I'm not sure if there's anything we can hook into?
I've narrowed the likely seeming answers down to a few candidates but can't work out which is the nicest (least overhead, most reliable, least likely to break in future versions):
cat sys/class/net/eth0/operstate
cat sys/class/net/eth0/carrier (I can find no good explanation of the difference between these two)
Using ifreq or various sequences of ioctl calls to read the socket status (looks kinda messy to me) as per answers here and here (more tidy looking).
Somehow catch status change messages???

You can use inotify to keep check on the /sys/class/net/eth0/operstate. Inotify allows different events to be watched on specified file or directory e.g. CREATE, MODIFY, WRITE etc.
You can seen the working example here

Related

How to set the baud rate of non-serial ttys?

A bit of a strange question here, but I'm wondering if there's any standard way to set the baud rate of non-serial interfaces, e.g. an SSH session to a Linux machine?
There are lots of examples of setting the baud rate for serial ttys in Linux, such as this:
struct termios slow;
tcgetattr(STDOUT_FILENO, &slow);
cfsetispeed(&slow, B1200);
cfsetospeed(&slow, B1200);
tcsetattr(STDOUT_FILENO, TCSANOW, &slow);
However, I've never been able to get any to work for me when working with ttys like an SSH connection. I'm not sure if it's just not supposed to work in those contexts, or if there's something else/different that needs to be done here.
The example compiles and runs just fine, but everything printed out to the screen is still at full speed. I've confirmed none of the functions runs into any issues, tried different baud rates, tried STDIN and STDOUT, set different flags in some of the examples floating around - none of that has any effect.
The stty command itself also seems to have no effect.
Is it possible to set the baud rate of an SSH session in the first place? Maybe it's not, and that's why it doesn't work.
If not, is there any way to achieve this? Short of a brute force solution like:
for each char in str:
print char
sleep 0.03
In case it wasn't obvious from the question, no, there's not a super useful application of this, but sometimes it's useful to simulate different baud rates without actually using a physical serial connection or anything like that, and it would be useful to handle the connection speed at the terminal layer or in the kernel somehow, rather than doing something dumb like printing everything out character by character. Can this be accomplished?
This must be accomplished entirely server-side (the connected SSH client should not have to do anything special), and the speed must be able to be set on demand just like with cfsetospeed.
One solution that does work is to use a pseudoterminal, which relays output from the PTY master to the actual file descriptor (e.g. a socket) one character at a time, with usleep inbetween each call to write.
However, this results in doing dozens to hundreds of calls to write() per second, which uses quite a bit of CPU. I doubt there's any way to space out writing a buffer gradually in the kernel, so this kind of system call overhead might be unavoidable, but if there was a more efficient way to do this, that would be great.

What is the difference between reboot arguments RB_HALT_SYSTEM and RB_POWER_OFF?

What is the real difference between the LINUX_REBOOT_CMD_HALT and LINUX_REBOOT_CMD_POWER_OFF arguments to the reboot() system call (resp. the RB_HALT_SYSTEM and RB_POWER_OFF arguments given to its wrapper function)?
The reboot(2) manual page has the following descriptions (differences emphasized):
RB_HALT_SYSTEM
LINUX_REBOOT_CMD_HALT
(RB_HALT_SYSTEM, 0xcdef0123; since Linux 1.1.76). The message "System halted." is printed, and the system is halted. Control is given to the ROM monitor, if there is one. If not preceded by a sync(2), data will be lost.
LINUX_REBOOT_CMD_POWER_OFF
(RB_POWER_OFF, 0x4321fedc; since Linux 2.1.30).
The message "Power down." is printed, the system is stopped, and all power is removed from the system, if possible. If not preceded by a sync(2), data will be lost.
Reading the descriptions, a few questions come up:
What is the difference between halted and stopped?
Would a reboot(RB_HALT_SYSTEM) call not remove power from the
system?
Where would the "System halted." and "Power down." messages be printed?
I don't think there is a difference; those words are synonyms in common English and I think this documentation is just using their English meaning, not as specific technical terms.
correct, that's exactly what the docs are trying to tell you.
on the console and/or kernel log, duh. Where kernel messages are normally printed, like during bootup.
You can easily try these for yourself to see what they do; the user-space shutdown(8) command has -H (halt) and -P / -h (poweroff) options, as well as -r. Read the man page. I assume it eventually makes a reboot(2) system call, or causes init to make one, after a sync.
And yes, the traditional shutdown -h command is halt + power off, i.e. POWER_OFF. Back in the old days, computers didn't used to be able to power themselves off, but these days that's usually what people think of as a non-reboot shutdown. Especially on systems where the kernel can't "return" to a BIOS / firmware command interface.
On a PC, one of the few use-cases I could imagine for halt without poweroff would be to insert a USB drive or CD before pressing the reset button (or ctrl+alt+delete). But maybe you don't want the currently-booted Linux kernel to react to the new hardware at all, so you want to halt Linux first.
You could poweroff to do this, but you don't need to and there's no need to start/stop your rotating disks and put extra wear on their motors.

Glitchy audio output, no underruns

When using snd_pcm_writei() in non-blocking mode everything works perfect for a while but eventually the audio gets choppy. It sounds like the ring buffer pointers are getting out of sync (ie. sometimes I can tell that the audio is playing out of order). How long it takes for the problem to start it's hardware dependent. On a Gentoo box on real hardware it seldom happens, but on a buildroot system running on QEMU it happens after about 5 minutes. On both cases draining the pcm stream fixes the problem. I have verified that I'm writing the samples correctly by also writting them to a file and playing them with aplay.
Currently I'm setting avail_min to the period size (1024 frames) and calling snd_pcm_wait() before writting chunks of the period size. But I tried a number of different variations (different chunk sizes, checking avail myself and use pthread_cond_timedwait() instead of snd_pcm_wait(), etc). But the only thing that works fine is using blocking mode but I can not do that.
You can see the current source code here: https://bitbucket.org/frodzdev/mediabox/src/5a6471316c7ae481b329e7e0d4af1bb68a32e71d/src/audio.c?at=staging&fileviewer=file-view-default (it needs a little cleanup since I'm trying all kinds of things). The code that does the actual IO starts at line 375.
Edit:
I think I got a solution but I don't understand why it seems to work. It seems that it does not matter if I'm using non-blocking mode, the problem is when I wait to make sure there's room on the buffer (either through snd_pcm_wait(), pthread_cond_timedwait(), or usleep()).
The version that seems to work is here: https://bitbucket.org/frodzdev/mediabox/src/c3eb290087d9bbe0d5f37653a33a1ba88ef0628b/src/audio.c?fileviewer=file-view-default. I switched to blocking mode while still waiting before calling snd_pcm_writei() and it didn't made a difference. Then I added the call to snd_pcm_avail() before calling snd_pcm_status() on avbox_audiostream_gettime(). This function is called constantly by another thread to get the stream clock and it only uses snd_pcm_status() to get the timestamps. Now it seems to work (at least it is a lot less probable to happen) but I don't understand exactly why. I understand that snd_pcm_avail() will synchronize the pointers with the kernel but I don't really understand when it needs to be called and the difference between snd_pcm_state() et al and snd_pcm_status(). Does snd_pcm_status() also synchronize anything? It seems not because sometimes snd_pcm_status_get_state() will return RUNNING when snd_pcm_avail() returns -EPIPE. The ALSA documentation is really vague. Perhaps understanding these things will help me understand my problem?
Now, when I said that it seems to be working I mean that I cannot reproduce it on real hardware. It still happens on QEMU though way less often. But considering that on the next commit I switched to blocking mode without waiting (which I've used in the past and never had a problem with on real hardware) and it still happens in QEMU and also the fact that this is a common issue with QEMU I'm starting to think that I may have fixed the issue on my end and now it's just a QEMU problem. Is there any way to determine if the problem is a bug on my end that is easier to trigger on the emulator or if it's just an emulator problem?
Edit: I realize that I should fill the buffer before waiting but at this point my concern is not to prevent underruns but to make sure that my code can handle them when they happen. Besides the buffer is filling up after a few iterations. I confirmed this by outputing avail, buffer_size, etc before writing each packet and the numbers I get don't make perfect sense, they show an error of 1 or 2 periods about every 8th period. Also (and this is the main problem) I'm not detecting any underruns, the audio get choppy but all writes succeed. In fact, if the problem start happening and I trigger an underrun by overloading the CPU it will correct itself when the pcm is reset.
In line 505: You're using time as argument to malloc.
In line 568: Weren't you playing audio? In this case you should do wait only after you wrote the frames. Let's think ...
Audio device generates an interrupt when it terminates to process a period.
| period A | period B |
^ ^
irq irq
Before you start the pcm, audio device doesn't generate any interrupt. Notice here that you're waiting and you haven't started the pcm yet. You only starts it when you call snd_pcm_writei().
When you wait for audio data you'll be awake only when the current period has been fully processed -- in your first wait the first period wasn't even written -- so in a comfortable situation you should write the whole buffer, wait for the first interrupt, and then write the just processed period, and on and on.
Initially, buffer is empty:
| | |
write():
|############|############|
wait():
..............
When we wake up:
| |############|
write():
|############|############|
I found the problem is you're writing audio just before it be played, then sometimes it may arrive delayed in the buffer.

STM32 STM32CubeF4 USB CDC operation

I built the code from the STM32CubeF4 for the USB CDC example. I added the missing receive code for CDC_Receive_FS() in usbd_cdc_if.c.
I loaded this into my STM32F4 Discovery and it works. A character typed on Tera Term returns and is displayed on Tera Term.
I am hoping that someone here, could give me some knowledge about how this USB CDC firmware works, specifically, is this being driven by an interrupt that is generated when there is a level shift in voltage on the USB -D and +D pins, or is there an infinite while loop that was launched somewhere, and it's just polling waiting for some data to appear?
What prompted my question is that I see that one can blink the LEDs on this board by toggling the state of the GPIO pins within an infinite while loop in main.c. However, there is nothing within this while loop at all within main.c for USB. So how does this USB CDC firmware get and send a character from/to Tera Term.
I will take the 2 minutes to answer you instead of lecturing you. Receive is done through interrupts. Very, very simply, the hardware sees the voltage change on the D+/D- and flags an interrupt based on the intialization functions. The interrupt calls HAL_PCD_IRQHandler, which calls USBD_LL_DataInStage in the usbd_conf.c file. That ends up calling the function USBD_CDC_DataIn in the usbd_cdc.c file. There is your starting point, but it is not simple. To do what you want you might have to stop the output to UART and just handle it in the main loop.
This question is to broad for this forum and not an actual question for a specific problem. However, as some hints, you might
Read the USB-specs, at least some basic overview (just start at wikipedia). USB does not work by toogling a GPIO in software (see next point)
Read the STM32F4xx reference manual. This is quite comprehensive.
Read the source code of the demo. This should answer all questions.
To track execution paths, you should remember that C always starts with the main() function, so this is a good start to see what's going on. (disclaimer: I know pretty well, it starts with startup, but this might confuse a beginner even more).
If you want to work with USB, you will have to do this all anyway, so you might start with it as well right now. Yes, this will take some time; no surprise, engineers have learned all this for years before they start with larger projects.
All information is available legal and for free on the web.
And, yes, USB is most likely interrupt-driven and might also use DMA to transfer data.

Reliable method for detecting when a USB HID device is emulating a keyboard

I'm working on an embedded system, and I need to determine if the user has connected an HID device emulating a keyboard (i.e. a barcode scanner) or an actual keyboard.
I can inspect the iManufacturer, iProduct, iSerialNumber, and iInterface string descriptors to gain some clues. For example, "reader" or "scanner" has been observed to appear in the iProduct string when a barcode reader has been connected. Likewise, "emulation" has been seen in the iInterface string. However, I'm a bit reluctant to rely solely on these strings (string case is easy enough to handle, but handling things like abbreviations in key words/phrases quickly becomes onerous.)
I've reviewed the USB 1.10 and 1.11 specs and found that the Report descriptor offers some cues, as well. For example, the Global Usage item should be something like 0x0501 for Generic Desktop, and the Local Usage item should be 0x0906 (Keyboard). Is there anything else in the Report descriptor that might assist in detecting the difference?
I would like to avoid relying on the Vendor ID and Product ID lists since this is an embedded system with limited resources. Suggestions? Is there something I've missed?
There is no absolutely reliable method to do this. There cannot be, because there is no absolute definition of what is or is not a keyboard -- nor of what the difference is between "emulating a keyboard" and "being a keyboard". The boundary gets particularly fuzzy around assistive devices and things like chording keyboards, but even beyond that, you have the fundamental problem that the way to emulate a keyboard is to communicate across the USB cable in exactly the way that a keyboard communicates.
The methods you've suggested are reasonable heuristics for identifying a number of things that are clearly non-keyboards (with a faint risk of false positives in some cases), but heuristics that look for hints are the best solution that's going to be possible.

Resources