Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion src/app_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,16 @@ int app_config_save(void) {
fprintf(file, " enable: %s\n", app_config.night_mode_enable ? "true" : "false");
fprintf(file, " ir_sensor_pin: %d\n", app_config.ir_sensor_pin);
fprintf(file, " check_interval_s: %d\n", app_config.check_interval_s);
if (app_config.smartir_gain_night || app_config.smartir_gain_day) {
fprintf(file, " smartir_gain_night: %d\n", app_config.smartir_gain_night);
fprintf(file, " smartir_gain_day: %d\n", app_config.smartir_gain_day);
}
fprintf(file, " ir_cut_pin1: %d\n", app_config.ir_cut_pin1);
fprintf(file, " ir_cut_pin2: %d\n", app_config.ir_cut_pin2);
fprintf(file, " ir_led_pin: %d\n", app_config.ir_led_pin);
fprintf(file, " pin_switch_delay_us: %d\n", app_config.pin_switch_delay_us);
fprintf(file, " adc_device: %s\n", app_config.adc_device);
fprintf(file, " lamp: %s\n", app_config.night_lamp);
fprintf(file, " adc_threshold: %d\n", app_config.adc_threshold);

fprintf(file, "isp:\n");
Expand Down Expand Up @@ -277,6 +282,7 @@ enum ConfigError app_config_parse(void) {
app_config.pin_switch_delay_us = 250;
app_config.check_interval_s = 10;
app_config.adc_device[0] = 0;
strcpy(app_config.night_lamp, "ir");
app_config.adc_threshold = 128;

struct IniConfig ini;
Expand Down Expand Up @@ -344,11 +350,17 @@ enum ConfigError app_config_parse(void) {
#define PIN_MAX 95
if (app_config.night_mode_enable) {
parse_int(
&ini, "night_mode", "ir_sensor_pin", 0, PIN_MAX,
&ini, "night_mode", "ir_sensor_pin", 0, 999, /* 999 = no sensor (the default) */
&app_config.ir_sensor_pin);
parse_int(
&ini, "night_mode", "check_interval_s", 0, 600,
&app_config.check_interval_s);
parse_int(
&ini, "night_mode", "smartir_gain_night", 0, 65535,
&app_config.smartir_gain_night);
parse_int(
&ini, "night_mode", "smartir_gain_day", 0, 65535,
&app_config.smartir_gain_day);
parse_int(
&ini, "night_mode", "ir_cut_pin1", 0, PIN_MAX,
&app_config.ir_cut_pin1);
Expand All @@ -363,6 +375,17 @@ enum ConfigError app_config_parse(void) {
&app_config.pin_switch_delay_us);
parse_param_value(
&ini, "night_mode", "adc_device", app_config.adc_device);
{
/* parse_param_value() sprintf()s into the caller's buffer with no
* size, so read into a temporary the size of the largest config
* string in this file and accept only the lamps the HAL drives. */
char lamp[128] = {0};
if (parse_param_value(&ini, "night_mode", "lamp", lamp) == CONFIG_OK &&
(EQUALS(lamp, "ir") || EQUALS(lamp, "white") || EQUALS(lamp, "none"))) {
strncpy(app_config.night_lamp, lamp, sizeof(app_config.night_lamp) - 1);
app_config.night_lamp[sizeof(app_config.night_lamp) - 1] = 0;
}
}
parse_int(
&ini, "night_mode", "adc_threshold", INT_MIN, INT_MAX,
&app_config.adc_threshold);
Expand Down
3 changes: 3 additions & 0 deletions src/app_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ struct AppConfig {
unsigned int check_interval_s;
unsigned int pin_switch_delay_us;
char adc_device[128];
char night_lamp[8]; /* ir (default), white (colour night vision), none */
int adc_threshold;
int smartir_gain_night; /* Fullhan SmartIR: image gain above which it is night (0 = library default) */
int smartir_gain_day; /* ... and below which it is day again */

// [isp]
bool mirror;
Expand Down
35 changes: 25 additions & 10 deletions src/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,26 @@ int gpio_init(void) {
return EXIT_SUCCESS;
}

/* The exported line's sysfs directory is "gpio<n>" on most kernels but
* "GPIO<n>" on Fullhan (fh) kernels; probe both and cache the format. */
static const char *gpio_node(char pin) {
static char fmt[24];
static char dir[40];
if (!fmt[0]) {
char probe[40];
sprintf(probe, "/sys/class/gpio/GPIO%d", pin);
strcpy(fmt, access(probe, F_OK) == 0 ?
"/sys/class/gpio/GPIO%d" : "/sys/class/gpio/gpio%d");
}
sprintf(dir, fmt, pin);
return dir;
}

static inline int gpio_direction(char pin, char *mode) {
char path[40];
sprintf(path, "/sys/class/gpio/gpio%d/direction", pin);
char path[48];
sprintf(path, "%s/direction", gpio_node(pin));
int fd = open(path, O_WRONLY);
if (!fd)
if (fd < 0)
HAL_ERROR("gpio", "Unable to control the direction of GPIO pin %d!\n", pin);
if (write(fd, mode, strlen(mode)) < 0) {
close(fd);
Expand All @@ -91,7 +106,7 @@ static inline int gpio_export(char pin, bool create) {
char path[40];
int fd = open(create ? "/sys/class/gpio/export" :
"/sys/class/gpio/unexport", O_WRONLY);
if (!fd)
if (fd < 0)
HAL_ERROR("gpio", "Unable to (un)export a GPIO pin!\n");

char val[4];
Expand All @@ -110,10 +125,10 @@ int gpio_read(char pin, bool *value) {
gpio_export(pin, true);
if (gpio_direction(pin, "in")) return EXIT_FAILURE;

char path[40];
sprintf(path, "/sys/class/gpio/gpio%d/value", pin);
char path[48];
sprintf(path, "%s/value", gpio_node(pin));
int fd = open(path, O_RDONLY);
if (!fd)
if (fd < 0)
HAL_ERROR("gpio", "Unable to read from GPIO pin %d!\n", pin);

char val = 0;
Expand All @@ -136,10 +151,10 @@ int gpio_write(char pin, bool value) {
gpio_export(pin, true);
if (gpio_direction(pin, "out")) return EXIT_FAILURE;

char path[40];
sprintf(path, "/sys/class/gpio/gpio%d/value", pin);
char path[48];
sprintf(path, "%s/value", gpio_node(pin));
int fd = open(path, O_WRONLY);
if (!fd)
if (fd < 0)
HAL_ERROR("gpio", "Unable to write to GPIO pin %d!\n", pin);

char val = value ? '1' : '0';
Expand Down
77 changes: 77 additions & 0 deletions src/hal/fh/fh_aud.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#pragma once

#include "fh_common.h"

/* FH_AC_Set_Config() payload (audio codec on the ARC coprocessor, /dev/fh_audio) */
typedef struct {
unsigned int ioType; /* 0/1 = capture, 2/3 = playback */
unsigned int sampleRate;
unsigned int bitWidth; /* 16 or 24 */
unsigned int encFormat; /* 0 = PCM, 1..5 = other formats (forces 16 bit) */
unsigned int channels;
unsigned int frameSamples; /* >= 80; vendor uses 320 at 8 kHz */
unsigned int volume; /* 0..100 */
} fh_aud_cnf;

typedef struct {
unsigned int length; /* filled with the number of bytes read */
void *data;
} fh_aud_frm;

typedef struct {
void *handle;

int (*fnDeinit)(void);
int (*fnDisable)(void);
int (*fnEnable)(void);
int (*fnGetFrame)(fh_aud_frm *frame, unsigned long long *timestamp);
int (*fnInit)(void);
int (*fnSetConfig)(fh_aud_cnf *config);
int (*fnSetMicVolume)(unsigned int volume);
int (*fnSetVolume)(unsigned int volume);
} fh_aud_impl;

static int fh_aud_load(fh_aud_impl *aud_lib) {
if (!(aud_lib->handle = dlopen("libacw_mpi.so", RTLD_NOW | RTLD_GLOBAL)))
HAL_ERROR("fh_aud", "Failed to load library!\nError: %s\n", dlerror());

if (!(aud_lib->fnDeinit = (int(*)(void))
hal_symbol_load("fh_aud", aud_lib->handle, "FH_AC_DeInit")))
return EXIT_FAILURE;

if (!(aud_lib->fnDisable = (int(*)(void))
hal_symbol_load("fh_aud", aud_lib->handle, "FH_AC_AI_Disable")))
return EXIT_FAILURE;

if (!(aud_lib->fnEnable = (int(*)(void))
hal_symbol_load("fh_aud", aud_lib->handle, "FH_AC_AI_Enable")))
return EXIT_FAILURE;

if (!(aud_lib->fnGetFrame = (int(*)(fh_aud_frm *frame, unsigned long long *timestamp))
hal_symbol_load("fh_aud", aud_lib->handle, "FH_AC_AI_GetFrameWithPts")))
return EXIT_FAILURE;

if (!(aud_lib->fnInit = (int(*)(void))
hal_symbol_load("fh_aud", aud_lib->handle, "FH_AC_Init")))
return EXIT_FAILURE;

if (!(aud_lib->fnSetConfig = (int(*)(fh_aud_cnf *config))
hal_symbol_load("fh_aud", aud_lib->handle, "FH_AC_Set_Config")))
return EXIT_FAILURE;

if (!(aud_lib->fnSetMicVolume = (int(*)(unsigned int volume))
hal_symbol_load("fh_aud", aud_lib->handle, "FH_AC_AI_MICIN_SetVol")))
return EXIT_FAILURE;

if (!(aud_lib->fnSetVolume = (int(*)(unsigned int volume))
hal_symbol_load("fh_aud", aud_lib->handle, "FH_AC_AI_SetVol")))
return EXIT_FAILURE;

return EXIT_SUCCESS;
}

static void fh_aud_unload(fh_aud_impl *aud_lib) {
if (aud_lib->handle) dlclose(aud_lib->handle);
aud_lib->handle = NULL;
memset(aud_lib, 0, sizeof(*aud_lib));
}
47 changes: 47 additions & 0 deletions src/hal/fh/fh_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "../symbols.h"
#include "../types.h"

/*
* Fullhan FH8852/FH8856 (V100 generation, SDK V1.2.0 "OSDRV" libraries).
*
* The SDK ships as binary-only shared objects without headers; the interface
* below was recovered from the libraries and from a vendor application that
* statically links the same SDK. Only the subset divinus needs is declared.
*/

#define FH_VPSS_CHN_NUM 4
#define FH_VENC_CHN_NUM 8

/* Error codes returned by the MPI (negative) */
#define FH_ERR_NODEV (-0x3e9)
#define FH_ERR_PARAM (-0x3ed)
#define FH_ERR_CHN (-0x3f0)

typedef enum {
FH_VENC_TYPE_JPEG = 0x01,
FH_VENC_TYPE_MJPEG = 0x02,
FH_VENC_TYPE_H264 = 0x04,
FH_VENC_TYPE_H264B = 0x08, /* variant that also needs a BGM gop threshold */
FH_VENC_TYPE_H265 = 0x10,
FH_VENC_TYPE_H265B = 0x20
} fh_venc_type;

typedef enum {
FH_VENC_RC_H264_CBR = 3, /* reported as VBR by /proc/driver/enc but it is the working CBR path; mode 8 boot-loops */
FH_VENC_RC_H264_VBR = 4,
FH_VENC_RC_H264_FIXQP = 5,
FH_VENC_RC_H264_AVBR = 6,
FH_VENC_RC_H265_CBR = 7,
FH_VENC_RC_H265_VBR = 8
} fh_venc_rcmode;

typedef struct {
unsigned int width;
unsigned int height;
} fh_common_dim;
86 changes: 86 additions & 0 deletions src/hal/fh/fh_compat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#if defined(__arm__) && !defined(__ARM_PCS_VFP) && __ARM_ARCH == 6

/*
* The Fullhan 3.0.8 kernels oops in rtnl_fill_ifinfo() on any netlink
* interface dump, which kills the calling process. musl implements
* getifaddrs() over netlink, so replace it with the SIOCGIFCONF ioctl
* interface for this platform.
*/
#include <errno.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>

struct fh_ifaddr {
struct ifaddrs ifa;
char name[IFNAMSIZ];
struct sockaddr_in addr, netmask, broadcast;
};

int getifaddrs(struct ifaddrs **ifap)
{
struct ifconf ifc;
struct ifreq *reqs;
int fd, count, len = 32 * sizeof(struct ifreq);
struct fh_ifaddr *list = NULL, *last = NULL;

*ifap = NULL;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
return -1;
if (!(reqs = malloc(len))) {
close(fd);
return -1;
}
ifc.ifc_len = len;
ifc.ifc_req = reqs;
if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
free(reqs);
close(fd);
return -1;
}
count = ifc.ifc_len / sizeof(struct ifreq);

for (int i = 0; i < count; i++) {
struct ifreq req = reqs[i];
struct fh_ifaddr *entry = calloc(1, sizeof(*entry));
if (!entry) break;
strncpy(entry->name, req.ifr_name, IFNAMSIZ - 1);
entry->ifa.ifa_name = entry->name;
memcpy(&entry->addr, &req.ifr_addr, sizeof(entry->addr));
entry->ifa.ifa_addr = (struct sockaddr*)&entry->addr;
if (!ioctl(fd, SIOCGIFFLAGS, &req))
entry->ifa.ifa_flags = req.ifr_flags;
if (!ioctl(fd, SIOCGIFNETMASK, &req)) {
memcpy(&entry->netmask, &req.ifr_netmask, sizeof(entry->netmask));
entry->ifa.ifa_netmask = (struct sockaddr*)&entry->netmask;
}
if (!ioctl(fd, SIOCGIFBRDADDR, &req)) {
memcpy(&entry->broadcast, &req.ifr_broadaddr, sizeof(entry->broadcast));
entry->ifa.ifa_broadaddr = (struct sockaddr*)&entry->broadcast;
}
if (last) last->ifa.ifa_next = &entry->ifa;
else list = entry;
last = entry;
}

free(reqs);
close(fd);
*ifap = list ? &list->ifa : NULL;
return 0;
}

void freeifaddrs(struct ifaddrs *ifa)
{
while (ifa) {
struct ifaddrs *next = ifa->ifa_next;
free(ifa);
ifa = next;
}
}

#endif
Loading