From e8a7f91c44d7c2d600ab56b1a2d1a0cc9adbfb2a Mon Sep 17 00:00:00 2001
From: guowenxue <guowenxue@gmail.com>
Date: Mon, 06 Nov 2023 11:02:27 +0800
Subject: [PATCH] Add driver user space test code
---
kernel/drivers/uapi/makefile | 76 ++++++
kernel/drivers/uapi/test_led.c | 75 ++++++
kernel/drivers/uapi/test_key.c | 173 ++++++++++++++
kernel/drivers/uapi/fbtest.c | 395 ++++++++++++++++++++++++++++++++
4 files changed, 719 insertions(+), 0 deletions(-)
diff --git a/kernel/drivers/uapi/fbtest.c b/kernel/drivers/uapi/fbtest.c
new file mode 100644
index 0000000..91b9d55
--- /dev/null
+++ b/kernel/drivers/uapi/fbtest.c
@@ -0,0 +1,395 @@
+/********************************************************************************
+ * Copyright: (C) 2021 Guo Wenxue<Email:guowenxue@gmail.com QQ:281143292>
+ * All rights reserved.
+ *
+ * Filename: fbtest.c
+ * Description: This program used to:
+ * 1, Show framebuffer device information
+ * 2, Show RGB color on LCD full screen
+ * 3, Show BMP file on LCD;
+ *
+ * Version: 1.0.0(08/26/22)
+ * Author: Guo Wenxue <wenxue.guo@avnet.com>
+ * ChangeLog: 1, Release initial version on "08/26/22 12:56:31"
+ *
+ ********************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/fb.h>
+#include <sys/mman.h>
+
+#define PROG_VERSION "1.0.0"
+
+enum
+{
+ CMD_SHOW_INFO, /* show LCD info */
+ CMD_SHOW_RGB, /* show RGB color */
+ CMD_SHOW_BMP, /* show BMP file */
+};
+
+typedef struct fb_ctx_s
+{
+ int fd; /* framebuffer file descriptor */
+ char dev[64]; /* framebuffer device name */
+ long fb_size; /* framebuffer size */
+ int pix_size; /* lcd screen pix size */
+ struct fb_var_screeninfo vinfo; /* framebuffer var information */
+ char *fbp; /* framebuffer mmap() address */
+} fb_ctx_t;
+
+
+int fb_init(fb_ctx_t *fb_ctx);
+int fb_term(fb_ctx_t *fb_ctx);
+int show_rgb(fb_ctx_t *fb_ctx, int times);
+int show_bmp(fb_ctx_t *fb_ctx, char *bmp_file);
+
+static void program_usage(char *progname)
+{
+ printf("Usage: %s [OPTION]...\n", progname);
+ printf(" %s is a program to show RGB color or BMP file on LCD screen\n", progname);
+
+ printf("\nMandatory arguments to long options are mandatory for short options too:\n");
+ printf(" -d[device ] Specify framebuffer device, such as: /dev/fb0\n");
+ printf(" -c[color ] Display RGB corlor on LCD screen for some times, such as: -c 3\n");
+ printf(" -b[bmp ] Display BMP file, such as: -b bg.bmp\n");
+ printf(" -h[help ] Display this help information\n");
+ printf(" -v[version ] Display the program version\n");
+
+ printf("\n%s version %s\n", progname, PROG_VERSION);
+ return;
+}
+
+int main (int argc, char **argv)
+{
+ fb_ctx_t fb_ctx;
+ char *progname=NULL;
+ char *bmp_file=NULL;
+ char *fb_dev="/dev/fb0";
+ int cmd = CMD_SHOW_INFO;
+ int opt, times;
+
+ struct option long_options[] = {
+ {"device", required_argument, NULL, 'd'},
+ {"color", required_argument, NULL, 'c'},
+ {"bmp", required_argument, NULL, 'b'},
+ {"version", no_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ progname = (char *)basename(argv[0]);
+
+ /* Parser the command line parameters */
+ while ((opt = getopt_long(argc, argv, "d:c:b:vh", long_options, NULL)) != -1)
+ {
+ switch (opt)
+ {
+ case 'd': /* Set framebuffer device */
+ fb_dev = optarg;
+ break;
+
+ case 'b': /* Set BMP file */
+ bmp_file = optarg;
+ cmd = CMD_SHOW_BMP;
+ break;
+
+ case 'c': /* Show RGB color */
+ times = atoi(optarg);
+ cmd = CMD_SHOW_RGB;
+ break;
+
+ case 'v': /* Get software version */
+ printf("%s version %s\n", progname, PROG_VERSION);
+ return 0;
+
+ case 'h': /* Get help information */
+ program_usage(progname);
+ return 0;
+
+ default:
+ break;
+ }
+
+ }
+
+ memset(&fb_ctx, 0, sizeof(fb_ctx));
+ strncpy(fb_ctx.dev, fb_dev, sizeof(fb_ctx.dev));
+
+ if( fb_init(&fb_ctx) < 0 )
+ {
+ printf("ERROR: Initial framebuffer device '%s' failure.\n", fb_ctx.dev);
+ return 1;
+ }
+
+ switch( cmd )
+ {
+ case CMD_SHOW_RGB:
+ show_rgb(&fb_ctx, times);
+ break;
+
+ case CMD_SHOW_BMP:
+ show_bmp(&fb_ctx, bmp_file);
+ break;
+
+ default:
+ break;
+ }
+
+ fb_term(&fb_ctx);
+
+ return 0;
+}
+
+
+int fb_get_var_screeninfo(int fd, struct fb_var_screeninfo *vinfo)
+{
+ if( fd<0 || !vinfo )
+ {
+ printf("ERROR: Invalid input arguments\n");
+ return -1;
+ }
+
+ if(ioctl(fd, FBIOGET_VSCREENINFO, vinfo))
+ {
+ printf("ERROR: ioctl() get variable info failure: %s\n", strerror(errno));
+ return -2;
+ }
+
+ printf("LCD information : %dx%d, bpp:%d rgba:%d/%d,%d/%d,%d/%d,%d/%d\n", vinfo->xres, vinfo->yres, vinfo->bits_per_pixel,
+ vinfo->red.length, vinfo->red.offset, vinfo->green.length, vinfo->green.offset,
+ vinfo->blue.length,vinfo->blue.offset, vinfo->transp.length, vinfo->transp.offset);
+
+ return 0;
+}
+
+int fb_init(fb_ctx_t *fb_ctx)
+{
+ struct fb_var_screeninfo *vinfo;
+
+ if( !fb_ctx || !strlen(fb_ctx->dev) )
+ {
+ printf("ERROR: Invalid input arguments\n");
+ return -1;
+ }
+
+ if( (fb_ctx->fd=open(fb_ctx->dev, O_RDWR)) < 0 )
+ {
+ printf("ERROR: Open framebuffer device '%s' failure: %s\n", fb_ctx->dev, strerror(errno));
+ return -2;
+ }
+
+ fb_get_var_screeninfo(fb_ctx->fd, &fb_ctx->vinfo);
+
+ vinfo = &fb_ctx->vinfo;
+ fb_ctx->fb_size = vinfo->xres * vinfo->yres * vinfo->bits_per_pixel / 8;
+ fb_ctx->pix_size = vinfo->xres * vinfo->yres;
+
+ fb_ctx->fbp =(char *)mmap(0, fb_ctx->fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_ctx->fd ,0);
+ if ( !fb_ctx->fbp )
+ {
+ printf ("ERROR: Framebuffer mmap() failure: %s\n", strerror(errno));
+ return -2;
+ }
+
+ vinfo->xoffset = 0;
+ vinfo->yoffset = 0;
+
+ return 0;
+}
+
+int fb_term(fb_ctx_t *fb_ctx)
+{
+ if( !fb_ctx )
+ {
+ printf("ERROR: Invalid input arguments\n");
+ return -1;
+ }
+
+ munmap(fb_ctx->fbp, fb_ctx->fb_size);
+ close (fb_ctx->fd);
+
+ return 0;
+}
+
+int show_rgb_screen(fb_ctx_t *fb_ctx)
+{
+ struct fb_var_screeninfo *vinfo;
+ char *fb_addr;
+ int interval = 1;
+ int i, color;
+ int bpp_bytes;
+
+ if( !fb_ctx )
+ {
+ printf("ERROR: Invalid input arguments\n");
+ return -1;
+ }
+
+ vinfo = &fb_ctx->vinfo;
+ bpp_bytes = vinfo->bits_per_pixel / 8;
+
+ printf("show full screen on red\n");
+ color = (1<<vinfo->red.length-1) << vinfo->red.offset;
+ fb_addr = fb_ctx->fbp;
+ for(i=0; i<fb_ctx->pix_size; i++)
+ {
+ memcpy(fb_addr, &color, bpp_bytes);
+ fb_addr += bpp_bytes;
+ }
+ sleep(interval);
+
+ printf("show full screen on green\n");
+ color = (1<<vinfo->green.length-1) << vinfo->green.offset;
+ fb_addr = fb_ctx->fbp;
+ for(i=0; i<fb_ctx->pix_size; i++)
+ {
+ memcpy(fb_addr, &color, bpp_bytes);
+ fb_addr += bpp_bytes;
+ }
+ sleep(interval);
+
+ printf("show full screen on blue\n");
+ color = (1<<vinfo->blue.length-1) << vinfo->blue.offset;
+ fb_addr = fb_ctx->fbp;
+ for(i=0; i<fb_ctx->pix_size; i++)
+ {
+ memcpy(fb_addr, &color, bpp_bytes);
+ fb_addr += bpp_bytes;
+ }
+ sleep(interval);
+
+ return 0;
+}
+
+int show_rgb(fb_ctx_t *fb_ctx, int times)
+{
+ int i;
+
+ if( !fb_ctx )
+ {
+ printf("ERROR: Invalid input arguments\n");
+ return -1;
+ }
+
+ for(i=0; i<times; i++)
+ {
+ show_rgb_screen(fb_ctx);
+ }
+
+ return 0;
+}
+
+
+/* BMP format: https://blog.csdn.net/weixin_39756273/article/details/110585675 */
+typedef struct bmp_file_head_s
+{
+ uint8_t bfType[2]; /* BMP file type: "BM" */
+ int32_t bfSize; /* BMP file size */
+ int32_t bfReserved; /* reserved */
+ int32_t bfoffBits; /* image data offset */
+}__attribute__((packed)) bmp_file_head_t;
+
+
+typedef struct bmp_bitmap_info_s
+{
+ int32_t biSize; /* this struture size */
+ int32_t biWidth; /* image width in pix */
+ int32_t biHeight; /* image height in pix */
+ uint16_t biPlanes; /* display planes, always be 1 */
+ uint16_t biBitCount; /* bpp: 1,4,8,16,24,32 */
+ int32_t biCompress; /* compress type */
+ int32_t biSizeImage; /* image size in byte */
+ int32_t biXPelsPerMeter; /* x-res in pix/m */
+ int32_t biYPelsPerMeter; /* y-res in pix/m */
+ int32_t biClrUsed;
+ int32_t biClrImportant;
+}__attribute__((packed)) bmp_bitmap_info_t;
+
+/*
+ * BMP image file format:
+ * +----------------+------------------+--------------------+---------------+
+ * | File Head(14B) | Bitmap Info(40B) | color palette(OPT) | bitmap data |
+ * +----------------+------------------+--------------------+---------------+
+ */
+int show_bmp(fb_ctx_t *fb_ctx, char *bmp_file)
+{
+ struct fb_var_screeninfo *vinfo;
+ char *fb_addr;
+ bmp_file_head_t *file_head;
+ bmp_bitmap_info_t *bitmap_info;
+ struct stat statbuf;
+ char *f_addr;
+ char *d_addr;
+ char *p_addr;
+ int fd;
+ int bpp_bytes;
+ int i, j;
+ int width, height;
+
+ if( !fb_ctx || !bmp_file )
+ {
+ printf("ERROR: Invalid input arguments\n");
+ return -1;
+ }
+
+ if( (fd=open(bmp_file, O_RDONLY)) < 0 )
+ {
+ printf("ERROR: Open file '%s' failure: %s\n", bmp_file, strerror(errno));
+ return -2;
+ }
+
+ fstat(fd, &statbuf);
+ f_addr =(char *)mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd ,0);
+ if ( !f_addr )
+ {
+ printf ("ERROR: BMP file mmap() failure: %s\n", strerror(errno));
+ return -2;
+ }
+
+ /* BMP file header */
+ file_head = (bmp_file_head_t *)f_addr;
+ if( memcmp(file_head->bfType, "BM", 2) != 0)
+ {
+ printf("ERROR: It's not a BMP file\n");
+ return -3;
+ }
+
+ /* BMP bitmap information header */
+ bitmap_info = (bmp_bitmap_info_t *)(f_addr+sizeof(bmp_file_head_t));
+
+ /* BMP bitmap data */
+ d_addr = f_addr + file_head->bfoffBits;
+
+ vinfo = &fb_ctx->vinfo;
+ fb_addr = fb_ctx->fbp;
+ bpp_bytes = vinfo->bits_per_pixel / 8;
+
+ width = bitmap_info->biWidth>vinfo->xres ? vinfo->xres : bitmap_info->biWidth;
+ height = bitmap_info->biHeight>vinfo->yres ? vinfo->yres : bitmap_info->biHeight;
+
+ printf("BMP file '%s': %dx%d and display %dx%d\n", bmp_file, bitmap_info->biWidth, bitmap_info->biHeight, width, height);
+
+ /* if biHeight is positive, the bitmap is a bottom-up DIB */
+ for(i=0; i<height; i++)
+ {
+ p_addr=d_addr+(height-1-i)*bitmap_info->biWidth*bpp_bytes;
+ memcpy(fb_addr, p_addr, bpp_bytes*width);
+ fb_addr += bpp_bytes * vinfo->xres;
+ }
+
+ munmap(f_addr, statbuf.st_size);
+ close (fd);
+
+ return 0;
+}
+
diff --git a/kernel/drivers/uapi/makefile b/kernel/drivers/uapi/makefile
new file mode 100644
index 0000000..af2d750
--- /dev/null
+++ b/kernel/drivers/uapi/makefile
@@ -0,0 +1,76 @@
+#*********************************************************************************
+# Copyright: (C) 2021 Guo Wenxue <guowenxue@gmail.com>
+# All rights reserved.
+#
+# Filename: Makefile
+# Description: This Makefile used to compile all the C source code file in current
+# folder to respective excutable binary files.
+#
+# Version: 1.0.0(11/17/2021~)
+# Author: Guo Wenxue <guowenxue@gmail.com>
+# ChangeLog: 1, Release initial version on "11/17/2021 01:29:33 PM"
+#
+#********************************************************************************/
+
+PWD=$(shell pwd)
+INSTPATH=/tftp
+
+CROSS_COMPILE=/opt/gcc-aarch32-10.3-2021.07/bin/arm-none-linux-gnueabihf-
+
+export CC=${CROSS_COMPILE}gcc
+export CXX=${CROSS_COMPILE}g++
+export AR=${CROSS_COMPILE}ar
+export AS=${CROSS_COMPILE}as
+export RANLIB=${CROSS_COMPILE}ranlib
+export STRIP=${CROSS_COMPILE}strip
+
+LIB_PATH=$(shell dirname ${PWD})
+LIB_NAME=$(shell basename ${LIB_PATH})
+
+CFLAGS+=-I${LIB_PATH}
+
+#LDFLAGS+=-L${LIB_PATH} -l${LIB_NAME}
+#LDFLAGS+=-static
+
+SRCS = $(wildcard ${VPATH}/*.c)
+OBJS = $(patsubst %.c,%.o,$(SRCS))
+
+SRCFILES = $(wildcard *.c)
+BINARIES=$(SRCFILES:%.c=%)
+
+all: entry binaries install
+
+libs:
+ make -C ..
+
+entry:
+ @echo " ";
+ @echo " =========================================================";
+ @echo " ** Cross compile \"${BINARIES}\" ";
+ @echo " =========================================================";
+ @make clean
+
+binaries: ${BINARIES}
+ @echo " Compile over"
+
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+
+tag:
+ @ctags --c-kinds=+defglmnstuvx --langmap=c:.c.h.ho.hem.het.hec.hev.him.hit.hic.hiv -R .
+ @cscope -Rbq
+
+install:
+ cp $(BINARIES) ${INSTPATH}
+
+clean:
+ @rm -f version.h
+ @rm -f *.o *.lo $(BINARIES)
+ @rm -rf *.gdb *.a *.so *.elf*
+ @rm -f *.log
+
+distclean: clean
+ @rm -f tags cscope*
+
+.PHONY: clean entry
+
diff --git a/kernel/drivers/uapi/test_key.c b/kernel/drivers/uapi/test_key.c
new file mode 100644
index 0000000..95b8aff
--- /dev/null
+++ b/kernel/drivers/uapi/test_key.c
@@ -0,0 +1,173 @@
+/*********************************************************************************
+ * Copyright: (C) 2021 Guo Wenxue<Email:guowenxue@gmail.com QQ:281143292>
+ * All rights reserved.
+ *
+ * Filename: test_key.c
+ * Description: This file used to test GPIO button driver builtin Linux kernel on ARM board
+ *
+ * Version: 1.0.0(11/17/2021~)
+ * Author: Guo Wenxue <guowenxue@gmail.com>
+ * ChangeLog: 1, Release initial version on "11/17/2021 02:46:18 PM"
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <linux/input.h>
+#include <linux/kd.h>
+#include <linux/keyboard.h>
+
+#if 0 /* Just for comment here, Reference to linux-3.3/include/linux/input.h */
+struct input_event
+{
+ struct timeval time;
+ __u16 type; /* 0x00:EV_SYN 0x01:EV_KEY 0x04:EV_MSC 0x11:EV_LED*/
+ __u16 code; /* key value, which key */
+ __s32 value; /* 1: Pressed 0:Not pressed 2:Always Pressed */
+};
+#endif
+
+#define EV_RELEASED 0
+#define EV_PRESSED 1
+
+#define BUTTON_CNT 10
+
+
+void usage(char *name);
+
+void display_button_event(struct input_event *ev, int cnt);
+
+int main(int argc, char **argv)
+{
+ char *kbd_dev = "/dev/input/event2";
+ char kbd_name[256] = "Unknown";
+ int kbd_fd = -1;
+
+ int rv, opt;
+ int size = sizeof (struct input_event);
+ fd_set rds;
+
+ struct input_event ev[BUTTON_CNT];
+
+ struct option long_options[] = {
+ {"device", required_argument, NULL, 'd'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ while ((opt = getopt_long(argc, argv, "d:h", long_options, NULL)) != -1)
+ {
+ switch (opt)
+ {
+ case 'd':
+ kbd_dev = optarg;
+ break;
+
+ case 'h':
+ usage(argv[0]);
+ return 0;
+
+ default:
+ break;
+ }
+ }
+
+ if(NULL == kbd_dev)
+ {
+ usage(argv[0]);
+ return -1;
+ }
+
+ if ((getuid ()) != 0)
+ printf ("You are not root! This may not work...\n");
+
+
+ if ((kbd_fd = open(kbd_dev, O_RDONLY)) < 0)
+ {
+ printf("Open %s failure: %s", kbd_dev, strerror(errno));
+ return -1;
+ }
+
+ ioctl (kbd_fd, EVIOCGNAME (sizeof (kbd_name)), kbd_name);
+ printf ("Monitor input device %s (%s) event on poll mode:\n", kbd_dev, kbd_name);
+
+
+ while (1)
+ {
+ FD_ZERO(&rds);
+ FD_SET(kbd_fd, &rds);
+
+ rv = select(kbd_fd + 1, &rds, NULL, NULL, NULL);
+ if (rv < 0)
+ {
+ printf("Select() system call failure: %s\n", strerror(errno));
+ goto CleanUp;
+ }
+ else if (FD_ISSET(kbd_fd, &rds))
+ {
+ if ((rv = read (kbd_fd, ev, size*BUTTON_CNT )) < size)
+ {
+ printf("Reading data from kbd_fd failure: %s\n", strerror(errno));
+ break;
+ }
+ else
+ {
+ display_button_event(ev, rv/size);
+ }
+ }
+ }
+
+CleanUp:
+ close(kbd_fd);
+
+ return 0;
+}
+
+void usage(char *name)
+{
+ char *progname = NULL;
+ char *ptr = NULL;
+
+ ptr = strdup(name);
+ progname = basename(ptr);
+
+ printf("Usage: %s [-p] -d <device>\n", progname);
+ printf(" -d[device ] button device name\n");
+ printf(" -p[poll ] Use poll mode, or default use infinit loop.\n");
+ printf(" -h[help ] Display this help information\n");
+
+ free(ptr);
+
+ return;
+}
+
+void display_button_event(struct input_event *ev, int cnt)
+{
+ int i;
+ struct timeval pressed_time, duration_time;
+
+ for(i=0; i<cnt; i++)
+ {
+
+ if(EV_KEY==ev[i].type && EV_PRESSED==ev[i].value)
+ {
+ pressed_time = ev[i].time;
+ printf("Keypad[%d] pressed time: %ld.%ld\n", ev[i].code, pressed_time.tv_sec, pressed_time.tv_usec);
+ }
+ if(EV_KEY==ev[i].type && EV_RELEASED==ev[i].value)
+ {
+ timersub(&ev[i].time, &pressed_time, &duration_time);
+ printf("keypad[%d] released time: %ld.%ld\n", ev[i].code, ev[i].time.tv_sec, ev[i].time.tv_usec);
+ printf("keypad[%d] duration time: %ld.%ld\n", ev[i].code, duration_time.tv_sec, duration_time.tv_usec);
+ }
+ } /* for(i=0; i<cnt; i++) */
+}
diff --git a/kernel/drivers/uapi/test_led.c b/kernel/drivers/uapi/test_led.c
new file mode 100644
index 0000000..4163077
--- /dev/null
+++ b/kernel/drivers/uapi/test_led.c
@@ -0,0 +1,75 @@
+/*********************************************************************************
+ * Copyright: (C) 2021 LingYun IoT System Studio
+ * All rights reserved.
+ *
+ * Filename: test_led.c
+ * Description: This file is imx6ull LED test program
+ *
+ * Version: 1.0.0(2021年11月17日)
+ * Author: Guo Wenxue <guowenxue@gmail.com>
+ * ChangeLog: 1, Release initial version on "2021年11月17日 22时59分31秒"
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#define CLASS_LED "/sys/class/leds"
+
+int turn_led(char *which, unsigned char brightness);
+
+int main (int argc, char **argv)
+{
+ while(1)
+ {
+ turn_led("sysled", 255);
+ sleep(1);
+ turn_led("sysled", 0);
+ sleep(1);
+ }
+
+
+ return 0;
+}
+
+
+int turn_led(char *which, unsigned char brightness)
+{
+ char led_path[64] = {0x0};
+ char buf[5] = {0x0};
+ int fd = -1;
+ int rv = 0;
+
+ if( !which )
+ {
+ printf("%s() Invalid input arguments\n", __func__);
+ return -1;
+ }
+
+ snprintf(led_path, sizeof(led_path), "%s/%s/brightness", CLASS_LED, which);
+ if( (fd=open(led_path, O_WRONLY)) < 0 )
+ {
+ printf("Open led file '%s' failure: %s\n", strerror(errno));
+ return -2;
+ }
+
+
+ snprintf(buf, sizeof(buf), "%d", brightness);
+ if( write(fd, buf, strlen(buf)) < 0 )
+ {
+ printf("Open led file '%s' failure: %s\n", strerror(errno));
+ rv = -3;
+ }
+
+ close(fd);
+
+ return rv;
+}
+
--
Gitblit v1.9.1