I have a piece of code that reads in the keyboard input (used for debugging purposes), implemented in C on Ubuntu 18.04. Since other processes have to run on the same thread, it is initialised as non-blocking.
When I try to run my application on run level 3, it blocks when trying to read in a keyboard character. This behaviour does not occur when I run the application on run level 5.
Does anyone have any answer as to why the behaviour is inconsistent between these two run levels?
This is the code (not shown: where the read operation is called by the application's main loop):
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
static int fd;
int kbd_initModule()
{
fd = open("/dev/tty", O_NONBLOCK | O_NOCTTY);
if(fd < 0)
{
ERROR("Unable to open keyboard: %d", fd);
return fd;
}
else
{
return 0;
}
}
int kbd_deinitModule()
{
close(fd);
return 0;
}
int kbd_getEvent()
{
uint8_t buf[1];
int tmp = read(fd, buf, sizeof(buf));
if(tmp == -1)
{
ERROR("%s", strerror(errno));
return -1;
}
else
{
return buf[0];
}
}
I am available to answer any questions and provide additional details.
Additional details:
Launch app: run level 5: sudo ./app ; run level 3: sudo xinit ./app (there are GUI components in the app, so X server must be started on run level 3 - would be good if someone knew more about this).
Update: Turns out if you intialise to the current tty device on runlevel 3, it does not work. Initialising to a specific tty device (in this case tty3) resolves the issue.
Not really sure why this is the case (maybe the default tty on run level 3 is the X window?), would appreciate if someone could explain why this happens.
Related
I have created my own event loop in Tcl as below. When i run the below code using tclsh interactively, CPU Utilization is close to 0% and when i run the same run using bsub, CPU Utilization shoots up to 100%.
I have even tried making read call blocking using below and that doesn't help too.
int flag = fcntl(0, F_GETFL);
flag = flag & (~O_NONBLOCK);
(void) fcntl(0, F_SETFL, (long)flag);
What is the reason here and how do i solve this problem?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <tcl.h>
#include <errno.h>
#include <fcntl.h>
void fwStdinKeyHandler(ClientData clientData, int mask)
{
unsigned char c = 0;
int rc = read(STDIN_FILENO, &c, 1);
//printf("rc is : %d\n",rc);
while (rc < 1 && errno == EINTR) {}
}
static void MainLoop(void)
{
Tcl_CreateFileHandler(STDIN_FILENO, TCL_READABLE, fwStdinKeyHandler, NULL);
while (1) {
Tcl_DoOneEvent(0);
}
fprintf(stdout,"Exit MainLoop\n");
fflush(stdout);
}
static int Hello_Cmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
Tcl_SetMainLoop(MainLoop);
return TCL_OK;
}
/*
* Hello_Init -- Called when Tcl loads your extension.
*/
int DLLEXPORT Cmd_Init(Tcl_Interp *interp)
{
if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
return TCL_ERROR;
}
/* changed this to check for an error - GPS */
if (Tcl_PkgProvide(interp, "Hello", "1.0") == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_CreateObjCommand(interp, "doone_loop", Hello_Cmd, NULL, NULL);
return TCL_OK;
}
How to make?
1. File saved in,say, hello.c
2. gcc -fpic -c hello.c -I/usr/local/include
3. gcc -shared hello.o -o libcmd.so
How to run?
runme file contains:
load libcmd.so
doone_loop
/usr/bin/tclsh runme => CPU Utilization close to 0%
bsub -q interactive -m "/usr/bin/tclsh runme" => CPU Utilization close to 100%
I think the problem is that when the command is run inside (the system that) bsub (talks to), it is run non-interactively. In particular, stdin is probably coming from either a file or /dev/null, both of which are always readable to the notifier (which is a tamed version of a bunch of low level system calls). That means your code is called back into a lot, almost as if it is busy-looping, generating a lot of CPU usage.
Since this is an operating system level behaviour, your approach simply won't work. You'll need to detect if you are in this situation (perhaps with isatty()?) and not install the event handler if it won't work right.
For non-interactive shell, there is no terminal and hence, no stdin channel, so read call returns zero. So, we need to add below code in fwStdinkeyhandler after read returns 0.
if(rc==0) {
Tcl_DeleteFileHandler(0);
}
I am using sama5d27-som1-ek1 embedded board which i build for it Linux image operating system and a cross compiler with YOCTO project.
I wanted to test a C code on my board. This code creates a new userspace LED class device and monitors it. A timestamp and brightness value is printed each time the brightness changes. I compiled it with the corss compiler but when i tried to run it, it tells me:
Failed to open /dev/uleds: No such file or directory
When i check /dev directory I can't find uleds. I think that this is the problem. Do you have any suggestions ?
This is the code:
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <linux/uleds.h>
int main(int argc, char const *argv[])
{
struct uleds_user_dev uleds_dev;
int fd, ret;
int brightness;
struct timespec ts;
if (argc != 2) {
fprintf(stderr, "Requires <device-name> argument\n");
return 1;
}
strncpy(uleds_dev.name, argv[1], LED_MAX_NAME_SIZE);
uleds_dev.max_brightness = 100;
fd = open("/dev/uleds", O_RDWR);
if (fd == -1) {
perror("Failed to open /dev/uleds");
return 1;
}
ret = write(fd, &uleds_dev, sizeof(uleds_dev));
if (ret == -1) {
perror("Failed to write to /dev/uleds");
close(fd);
return 1;
}
while (1) {
ret = read(fd, &brightness, sizeof(brightness));
if (ret == -1) {
perror("Failed to read from /dev/uleds");
close(fd);
return 1;
}
clock_gettime(CLOCK_MONOTONIC, &ts);
printf("[%ld.%09ld] %u\n", ts.tv_sec, ts.tv_nsec, brightness);
}
close(fd);
return 0;
}
First of all check your kernel config for CONFIG_LEDS_USER it should be like built-in (compiled in) module y or loadable module m. You can check it on already compiled and running kernel
zcat /proc/config.gz | grep LEDS_USER
cat /boot/config | grep LEDS_USER
cat /boot/config-$(uname -r) | grep LEDS_USER
Enable this option in config and rebuild your kernel. If I not mistaken you can add this line to the kernel config and YOCTO should use it as is. Or another way is to make it like the patch for config and add this patch to .bb kernel rule and YOCTO applies it during the building of a project.
Then use insmod to insert your module if it configures like loadable module m. If you choose y option /dev/uleds should be present by default
After these steps /dev/uleds should appear.
I'm trying to write my own "keyboard driver" (without actually writing a kernel module),
by grabbing the keyboard at what I assume is the lowest level of abstraction in userland: /dev/input/event*.
The following code does the grabbing, provided you change the first ocurrence of ioctl(fd, EVIOCGRAB, UNGRAB)
to ioctl(fd, EVIOCGRAB, GRAB).
// gcc main.c -o main
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <linux/input.h>
#include <fcntl.h>
#include <errno.h>
#define EXIT_KEY KEY_ESC
#define UNGRAB 0
#define GRAB 1
const char* kbd_device = "/dev/input/event4";
// ------------------------------------------------------------------------------------------------
int main(void){
int fd = open(kbd_device, O_RDONLY);
if(fd == -1){
printf("Cannot open %s. %s.\n", kbd_device, strerror(errno));
return -1;
}
if(ioctl(fd, EVIOCGRAB, UNGRAB))
printf("Couldn't grab %s. %s.\n", kbd_device, strerror(errno));
else
printf("Grabbed %s!\n", kbd_device);
while(1){
struct input_event event;
read(fd, &event, sizeof(event));
if (event.type == EV_KEY && event.value >= 0 && event.value <= 2){
printf("%d %3d\n", event.value, event.code);
if(event.code == EXIT_KEY){
ioctl(fd, EVIOCGRAB, UNGRAB);
close(fd);
return 0;
}
}
}
}
Problem
If I run gcc main.c -o main && sudo ./main, everything works as expected.
If first compile and then I run sudo ./main, however, the terminal scrolls down nonstop, as if the RETURN key was held down.
Why does happen?
Notes
I'm running Ubuntu 14.04
On my platform, /dev/input/event4 happens to be the keyboard
Motivation
I'm trying to write a keyboard "driver" that works both on X and not on X (eg. a TTY).
I understand X11's keyboard library/extension is XKB. I think the TTY's keyboard library is linux/divers/tty/vt/keyboard.c (source),
the initial keyboard map it uses is in linux/drivers/tty/vt/defkeymap.map (source), and it can be modified by using loadkeys (source here). Do correct me if I'm wrong.
When you type
gcc main.c -o main && sudo ./main ↵
GCC takes some time, so the ↵ key has been released by the time ./main runs.
When you type
sudo ./main ↵
the terminal sends the shell a newline as soon as you push down ↵, and starts executing ./main. Then the ↵ released event is seen by your program, but not by your terminal, because your program has grabbed the input device. Thus, to the terminal it looks like ↵ is stuck down, so it continues to produce newlines.
This question has been answered already, but it still lacks an elegant solution to the problem.
I had the same issue with a driver I implemented some time ago that also required capturing the keyboard.
I could not find a way to force the the kernel to recognize a key release in the device before capturing it, so the solution consists in not grabbing the device until you detect that all keys have actually been released. This can be accomplished by monitoring the device with the EVIOCGKEY ioctl AFTER opening it and BEFORE grabbing it.
OBS: Please observe that the apparently dummy read function within the while loop is necessary in order to avoid a busy wait and so that the loop will iterate after each event from the input device. Also note that the file descriptor must be configured for blocking I/O (the default).
void waitReleaseAll(int fd) {
struct input_event evt;
unsigned char key_b[KEY_MAX/8 + 1];
int i, nothing;
while ( 1 ) {
memset(key_b, 0, sizeof(key_b));
ioctl(fd, EVIOCGKEY(sizeof(key_b)), key_b);
for ( nothing = 1 , i = 0 ; i < KEY_MAX/8 + 1 ; i++ ) {
if ( key_b[i] != 0 ) { nothing = 0; break; }
}
if ( nothing ) break;
read(fd, &evt, sizeof(evt));
}
printf("All keys are now released\n");
}
To fix your problem, you should use SIGINT in your code to identify Ctrl-C keystroke from the user.
Implement SIGNAL in your code:
static volatile sig_atomic_t stop = 0;
static void interrupt_handler(int sig)
{
stop = 1;
} // Outside of the main function.
int main(int argc, char *argv[])
{
signal(SIGINT, interrupt_handler);
while (!stop) {
//your code
}
exit(0);
}
My problem is to deal with sparse file reads and understand where the extents of the file are to perform some logic around it.
Since, there is no direct API call to figure these stuff out, I decided to use ioctl api to do this. I got the idea from how cp command deals with problems of copying over sparse files by going through their code and ended up seeing this.
https://github.com/coreutils/coreutils/blob/df88fce71651afb2c3456967a142db0ae4bf9906/src/extent-scan.c#L112
So, I tried to do the same thing in my sample program running in user space and it errors out with "Invalid argument". I am not sure what I am missing or if this is even possible from userspace. I am running on ubuntu 14.04 on an ext4 file system. Could this be a problem with device driver supporting these request modes underneath?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include "fiemap.h" //This is from https://github.com/coreutils/coreutils/blob/df88fce71651afb2c3456967a142db0ae4bf9906/src/fiemap.h
int main(int argc, char* argv[]) {
int input_fd;
if(argc != 2){
printf ("Usage: ioctl file1");
return 1;
}
/* Create input file descriptor */
input_fd = open (argv [1], O_RDWR);
if (input_fd < 0) {
perror ("open");
return 2;
}
union { struct fiemap f; char c[4096]; } fiemap_buf;
struct fiemap *fiemap = &fiemap_buf.f;
int s = ioctl(input_fd, FS_IOC_FIEMAP, fiemap);
if (s == 0) {
printf("ioctl success\n");
} else {
printf("ioctl failure\n");
char * errmsg = strerror(errno);
printf("error: %d %s\n", errno, errmsg);
}
/* Close file descriptors */
close (input_fd);
return s;
}
As you're not properly setting the fiemap_buf.f parameters before invoking ioctl(), it is likely that the EINVAL is coming from the fiemap invalid contents than from the FS_IOC_FIEMAP request identifier support itself.
For instance, the ioctl_fiemap() (from kernel) will evaluate the fiemap.fm_extent_count in order to determine if it is greater than FIEMAP_MAX_EXTENTS and return -EINVAL in that case. Since no memory reset nor parameterization is being performed on fiemap, this is very likely the root cause of the problem.
Note that from the coreutils code you referenced, it performs the correct parameterization of fiemap before calling ioctl():
fiemap->fm_start = scan->scan_start;
fiemap->fm_flags = scan->fm_flags;
fiemap->fm_extent_count = count;
fiemap->fm_length = FIEMAP_MAX_OFFSET - scan->scan_start;
Note fiemap is not recommended as you have to be sure to pass FIEMAP_FLAG_SYNC which has side effects. The lseek(), SEEK_DATA and SEEK_HOLE interface is the recommended one, though note that will, depending on file system, represent unwritten extents (allocated zeros) as holes.
I am trying to read data from a bluetooth barcode scanner (KDC300) using C. Here is the code I have so far, and the program successfully establishes a bluetooth connection to the scanner, but when a barcode is scanned, no input is displayed on the screen (Eventually more will be done with the data, but we have to get it working first, right).
Here is the program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>
int main (int argc, const char * argv[]) {
// define vars
int STOP = 0;
//char buf[255];
if(argv[1])
{
int fd = open("/dev/tty.KDC1", O_RDONLY);
if(fd == -1)
{
printf("%s", strcat("Unable to open /dev/tty.", argv[1]));
}
int res;
while(STOP == 0)
{
while((res = read(fd,buf,255)) == 0);
{
if(res > 0)
{
buf[res]=0;
printf("%s:%d\n", buf, res);
if(buf[sizeof(buf)]=='\n') break;
}
}
}
}
return 0;
}
If anyone has any ideas, I am at a loss on this so far. If it is any help, I can run screen /dev/tty.KDC1 and any barcodes scanned on the scanner appear in the terminal, I just can't do anything with the data.
Jud
This line:
while((res = read(fd,buf,255)) == 0);
Does not do what you think it does. That's a while loop with an empty body.
#tommieb75,
the strcat statement was from the first "go" at the program, I took a variable from argv[1] and appended it to the /dev/tty.* so you could select which device you wanted to monitor.
I am not sure why I had commented out buf, probably stems from looking at the code too much / trying different approaches and forgetting where I was (not much of a C programmer, which is how I can get lost in 30 LOC).
#caf, Good catch on the extra semi-colon after the while loop, unfortunately, even after correcting it, the program doesn't behave correctly.
I am researching the problem further. I can verify (with osx packetlogger) that the computer is getting the data, but the but the buffer never has any data placed in it.
-Jud
---------------Edit--------------
I solved the problem after a little trial and error. Adding the following code to setup the serial connection solved everything:
struct termios theTermios;
memset(&theTermios, 0, sizeof(struct termios));
cfmakeraw(&theTermios);
cfsetspeed(&theTermios, 115200);
theTermios.c_cflag = CREAD | CLOCAL; // turn on READ
theTermios.c_cflag |= CS8;
theTermios.c_cc[VMIN] = 0;
theTermios.c_cc[VTIME] = 10; // 1 sec timeout
ioctl(fileDescriptor, TIOCSETA, &theTermios);
Thanks to the other answers for getting me to this point.
Here is the best info I've found.
The C program on there using termios worked just by adding
#include<string.h>
And changing the baudrate to match my needs.
In your code
printf("%s", strcat("Unable to open /dev/tty.", argv[1]));
Why did you do that? It would be easier to do it this way:
printf("%s: Unable to open /dev/tty.KDC1", argv[0]);
Why the parameter referencing to the command line?
res = read(fd,buf,255)
Why did you have buf declaration commented out above?