I have a small program that read barcodes from /dev/input/event4. This is the code:
#include <sys/file.h>
#include <stdio.h>
#include <string.h>
#include <linux/input.h>
int main (int argc, char *argv[])
{
struct input_event ev;
int fd, rd;
//Open Device
if ((fd = open ("/dev/input/event4", O_RDONLY|O_NONBLOCK)) == -1){
printf ("not a vaild device.\n");
return -1;
}
while (1){
memset((void*)&ev, 0, sizeof(ev));
rd = read (fd, (void*)&ev, sizeof(ev));
if (rd <= 0){
printf ("rd: %d\n", rd);
sleep(1);
}
if(rd>0 && ev.value==0 && ev.type==1){
printf("type: %d, code: %d, value: %d, rd: %d\n", ev.type, ev.code, ev.value, rd);
}
}
return 0;
}
I have now created some barcodes with an online-generator (http://www.barcode-generator.de/V2/de/index.jsp). The barcodes are: 123456789 and 1234567890
The output of my programm when scanning the barcodes is:
type: 1, code: 2, value: 0, rd: 16
type: 1, code: 3, value: 0, rd: 16
type: 1, code: 4, value: 0, rd: 16
type: 1, code: 5, value: 0, rd: 16
type: 1, code: 6, value: 0, rd: 16
type: 1, code: 7, value: 0, rd: 16
type: 1, code: 8, value: 0, rd: 16
type: 1, code: 9, value: 0, rd: 16
type: 1, code: 10, value: 0, rd: 16
type: 1, code: 28, value: 0, rd: 16
for the 123456789
and
type: 1, code: 28, value: 0, rd: 16
for the 1234567890
So, the 10-digit-barcodes is not recognised correctly.
The code: 28 means this is an RETURN/ENTER, this is the internal terminator for a barcode, so this comes directly from the scanner.
Does anyone can tell my why ? Maybe there is something wrong with the code ?
Goodbye, Andre
You should only consider the event when the read()
returns == sizeof ev
, because we're reading from an input device here. If it returns zero, it means no more events are forthcoming (maybe device detached?). If it returns -1
, check errno
. If read()
returns any other value, the kernel driver has gone bonkers and you could consider it a fatal error.
errno == EINTR
is normal (occurs when a signal is delivered), it is not an error per se. It shouldn't happen here, but ignoring it (treating it as just a hiccup, not an error) is quite safe.
errno == EAGAIN
occurs when you used O_NONBLOCK
in the open()
flags, and there is no new event available yet.
There is absolutely no reason to use O_NONBLOCK
here. All it does is causes your code to waste CPU cycles, returning tens of thousands of times per second from the read()
call just to return -1
with errno == EAGAIN
. Just drop it, so that the read()
will simply wait until a new event arrives, and returns it.
See my answer to the input_event structure description question.
In summary, for ev_type == 1 == EV_KEY
:
ev_value == 0
: key released (key up)ev_value == 1
: key pressed (key down)ev_value == 2
: autorepeat (key automatically repeated)ev_code == 1 == KEY_ESC
ev_code == 2 == KEY_1
ev_code == 3 == KEY_2
ev_code == 10 == KEY_9
ev_code == 11 == KEY_0
ev_code == 28 == KEY_ENTER
The keypresses the device provided are actually 1 2 3 4 5 6 7 8 9 Enter.
(Note that you only showed the key release events; you should actually see two, one with ev_value == 1
, followed by one with ev_value == 0
, for each ev_code
.)
The one Chinese one I've tried was very nice, although dirt cheap. It had a manual with a few barcodes, including some to switch between barcode formats (and number of digits). I vaguely remember using two barcodes to switch to another mode, and to use the minimum volume for the beeps. It seemed to retain the settings even after being detached.
Here is an example of what kind of implementation I'd use to read barcodes. I'd obviously split the barcode reading part to a separate file.
The below code is dedicated to public domain (licensed under CC0), so feel free to use it in any way you wish. There is no guarantees of any kind, so don't blame me for any breakage. (Any bug fixes are welcome; if reported, I will check and include in the below code. I recommend adding a comment below; I do read all comments to my answers every couple of days or so.)
The header file barcode.h
:
#ifndef BARCODE_H
#define BARCODE_H
#include <stdlib.h>
#include <signal.h>
/* This flags turns nonzero if any signal
* installed with install_done is caught.
*/
extern volatile sig_atomic_t done;
/* Install signals that set 'done'.
*/
int install_done(const int signum);
/* Barcode device description.
* Do not meddle with the internals;
* this is here only to allow you
* to allocate one statically.
*/
typedef struct {
int fd;
volatile int timeout;
timer_t timer;
} barcode_dev;
/* Close a barcode device.
* Returns 0 if success, nonzero errno error code otherwise.
*/
int barcode_close(barcode_dev *const dev);
/* Open a barcode device.
* Returns 0 if success, nonzero errno error code otherwise.
*/
int barcode_open(barcode_dev *const dev, const char *const device_path);
/* Read a barcode, but do not spend more than maximum_ms.
* Returns the length of the barcode read.
* (although at most length-1 characters are saved at the buffer,
* the total length of the barcode is returned.)
* errno is always set; 0 if success, error code otherwise.
* If the reading timed out, errno will be set to ETIMEDOUT.
*/
size_t barcode_read(barcode_dev *const dev,
char *const buffer, const size_t length,
const unsigned long maximum_ms);
#endif /* BARCODE_H */
The implementation in the following barcode.c
file currently accepts only digits (0 through 1), but it should be trivial to add any other necessary keys (like KEY_A
through KEY_Z
). The current one ignores shift, control, et cetera, as they are not provided by any scanners as far as I know. It uses SIGRTMAX-0
realtime signal and a custom timer per barcode device to read barcodes, so you'll need to link it against librt
(-lrt
):
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <linux/input.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Link against the rt library; -lrt. */
#define UNUSED __attribute__((unused))
#define TIMEOUT_SIGNAL (SIGRTMAX-0)
/*
* done - flag used to exit program at SIGINT, SIGTERM etc.
*/
volatile sig_atomic_t done = 0;
static void handle_done(int signum UNUSED)
{
done = 1;
}
int install_done(const int signum)
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
/*
* Barcode input event device, and associated timeout timer.
*/
typedef struct {
int fd;
volatile int timeout;
timer_t timer;
} barcode_dev;
static void handle_timeout(int signum UNUSED, siginfo_t *info, void *context UNUSED)
{
if (info && info->si_code == SI_TIMER && info->si_value.sival_ptr)
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
__atomic_add_fetch((int *)info->si_value.sival_ptr, 1, __ATOMIC_SEQ_CST);
#else
__sync_add_and_fetch((int *)info->si_value.sival_ptr, 1);
#endif
}
static int install_timeouts(void)
{
static int installed = 0;
if (!installed) {
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_sigaction = handle_timeout;
act.sa_flags = SA_SIGINFO;
if (sigaction(TIMEOUT_SIGNAL, &act, NULL) == -1)
return errno;
installed = 1;
}
return 0;
}
int barcode_close(barcode_dev *const dev)
{
int retval = 0;
if (!dev)
return 0;
if (dev->fd != -1)
if (close(dev->fd) == -1)
retval = errno;
dev->fd = -1;
if (dev->timer)
if (timer_delete(dev->timer) == -1)
if (!retval)
retval = errno;
dev->timer = (timer_t)0;
/* Handle all pending TIMEOUT_SIGNALs */
while (1) {
struct timespec t;
siginfo_t info;
sigset_t s;
t.tv_sec = (time_t)0;
t.tv_nsec = 0L;
sigemptyset(&s);
if (sigtimedwait(&s, &info, &t) != TIMEOUT_SIGNAL)
break;
if (info.si_code != SI_TIMER || !info.si_value.sival_ptr)
continue;
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
__atomic_add_fetch((int *)info.si_value.sival_ptr, 1, __ATOMIC_SEQ_CST);
#else
__sync_add_and_fetch((int *)info.si_value.sival_ptr, 1);
#endif
}
return errno = retval;
}
int barcode_open(barcode_dev *const dev, const char *const device_path)
{
struct sigevent event;
int fd;
if (!dev)
return errno = EINVAL;
dev->fd = -1;
dev->timeout = -1;
dev->timer = (timer_t)0;
if (!device_path || !*device_path)
return errno = EINVAL;
if (install_timeouts())
return errno;
do {
fd = open(device_path, O_RDONLY | O_NOCTTY | O_CLOEXEC);
} while (fd == -1 && errno == EINTR);
if (fd == -1)
return errno;
errno = 0;
if (ioctl(fd, EVIOCGRAB, 1)) {
const int saved_errno = errno;
close(fd);
return errno = (saved_errno) ? errno : EACCES;
}
dev->fd = fd;
memset(&event, 0, sizeof event);
event.sigev_notify = SIGEV_SIGNAL;
event.sigev_signo = TIMEOUT_SIGNAL;
event.sigev_value.sival_ptr = (void *)&(dev->timeout);
if (timer_create(CLOCK_REALTIME, &event, &dev->timer) == -1) {
const int saved_errno = errno;
close(fd);
return errno = (saved_errno) ? errno : EMFILE;
}
return errno = 0;
}
size_t barcode_read(barcode_dev *const dev,
char *const buffer, const size_t length,
const unsigned long maximum_ms)
{
struct itimerspec it;
size_t len = 0;
int status = ETIMEDOUT;
if (!dev || !buffer || length < 2 || maximum_ms < 1UL) {
errno = EINVAL;
return (size_t)0;
}
/* Initial timeout. */
it.it_value.tv_sec = maximum_ms / 1000UL;
it.it_value.tv_nsec = (maximum_ms % 1000UL) * 1000000L;
/* After elapsing, fire every 10 ms. */
it.it_interval.tv_sec = 0;
it.it_interval.tv_nsec = 10000000L;
if (timer_settime(dev->timer, 0, &it, NULL) == -1)
return (size_t)0;
/* Because of the repeated elapsing, it is safe to
* clear the timeout flag here. */
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 7)
__atomic_store_n((int *)&(dev->timeout), 0, __ATOMIC_SEQ_CST);
#else
__sync_fetch_and_and((int *)&(dev->timeout), 0);
#endif
while (!dev->timeout) {
struct input_event ev;
ssize_t n;
int digit;
n = read(dev->fd, &ev, sizeof ev);
if (n == (ssize_t)-1) {
if (errno == EINTR)
continue;
status = errno;
break;
} else
if (n == sizeof ev) {
/* We consider only key presses and autorepeats. */
if (ev.type != EV_KEY || (ev.value != 1 && ev.value != 2))
continue;
switch (ev.code) {
case KEY_0: digit = '0'; break;
case KEY_1: digit = '1'; break;
case KEY_2: digit = '2'; break;
case KEY_3: digit = '3'; break;
case KEY_4: digit = '4'; break;
case KEY_5: digit = '5'; break;
case KEY_6: digit = '6'; break;
case KEY_7: digit = '7'; break;
case KEY_8: digit = '8'; break;
case KEY_9: digit = '9'; break;
default: digit = '\0';
}
/* Non-digit key ends the code, except at beginning of code. */
if (digit == '\0') {
if (!len)
continue;
status = 0;
break;
}
if (len < length)
buffer[len] = digit;
len++;
continue;
} else
if (n == (ssize_t)0) {
status = ENOENT;
break;
} else {
status = EIO;
break;
}
}
/* Add terminator character to buffer. */
if (len + 1 < length)
buffer[len + 1] = '\0';
else
buffer[length - 1] = '\0';
/* Cancel timeout. */
it.it_value.tv_sec = 0;
it.it_value.tv_nsec = 0;
it.it_interval.tv_sec = 0;
it.it_interval.tv_nsec = 0L;
(void)timer_settime(dev->timer, 0, &it, NULL);
errno = status;
return len;
}
Here is an example program, example.c
. You supply the input event device (I suggest using a symlink in /dev/input/by-id/
or /dev/input/by-path/
if your udev
provides those, as event device indexes may not be stable across kernel versions and hardware boots), and the maximum duration you're willing to wait for/until next barcode.
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include "barcode.h"
#define BARCODE_MAXLEN 1023
int main(int argc, char *argv[])
{
barcode_dev dev;
unsigned long ms;
int status, exitcode;
if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s INPUT-EVENT-DEVICE IDLE-TIMEOUT\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program reads barcodes from INPUT-EVENT-DEVICE,\n");
fprintf(stderr, "waiting at most IDLE-TIMEOUT seconds for a new barcode.\n");
fprintf(stderr, "The INPUT-EVENT-DEVICE is grabbed, the digits do not appear as\n");
fprintf(stderr, "inputs in the machine.\n");
fprintf(stderr, "You can at any time end the program by sending it a\n");
fprintf(stderr, "SIGINT (Ctrl+C), SIGHUP, or SIGTERM signal.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
if (install_done(SIGINT) ||
install_done(SIGHUP) ||
install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
{
double value, check;
char dummy;
if (sscanf(argv[2], " %lf %c", &value, &dummy) != 1 || value < 0.001) {
fprintf(stderr, "%s: Invalid idle timeout value (in seconds).\n", argv[2]);
return EXIT_FAILURE;
}
ms = (unsigned long)(value * 1000.0);
check = (double)ms / 1000.0;
if (value < check - 0.001 || value > check + 0.001 || ms < 1UL) {
fprintf(stderr, "%s: Idle timeout is too long.\n", argv[2]);
return EXIT_FAILURE;
}
}
if (barcode_open(&dev, argv[1])) {
fprintf(stderr, "%s: Cannot open barcode input event device: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
while (1) {
char code[BARCODE_MAXLEN + 1];
size_t len;
if (done) {
status = EINTR;
break;
}
len = barcode_read(&dev, code, sizeof code, ms);
if (errno) {
status = errno;
break;
}
if (len < (size_t)1) {
status = ETIMEDOUT;
break;
}
printf("%zu-digit barcode: %s\n", len, code);
fflush(stdout);
}
if (status == EINTR) {
fprintf(stderr, "Signaled to exit. Complying.\n");
fflush(stderr);
exitcode = EXIT_SUCCESS;
} else
if (status == ETIMEDOUT) {
fprintf(stderr, "Timed out, no more barcodes.\n");
fflush(stderr);
exitcode = EXIT_SUCCESS;
} else {
fprintf(stderr, "Error reading input event device %s: %s.\n", argv[1], strerror(status));
fflush(stderr);
exitcode = EXIT_FAILURE;
}
if (barcode_close(&dev)) {
fprintf(stderr, "Warning: Error closing input event device %s: %s.\n", argv[1], strerror(errno));
fflush(stderr);
exitcode = EXIT_FAILURE;
}
return exitcode;
}
As you can see, it just prints the barcodes to standard output (and any error messages and warnings to standard error). To compile it, I recommend using the following Makefile
(indentation must be using Tab, not spaces):
CC := gcc
CFLAGS := -Wall -Wextra -O2
LDFLAGS := -lrt
.PHONY: all clean
all: clean example
clean:
rm -f example *.o
%.o: %.c
$(CC) $(CFLAGS) -c $^
example: example.o barcode.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o example
To compile, create the four files listed above, then run
make clean example
Running for example
./example /dev/input/event4 5.0
will read barcodes from /dev/input/event4
, but will exit at Ctrl+C (INT signal), HUP signal, TERM signal, or if no barcode appears within 5 seconds.
Note that if only a partial barcode is read within that 5 seconds, we do get that partial part (and could just try and read the rest of it), but the above example program ignores the partial, and only shows the timeout.
Questions?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With