Merge pull request #72 from lumag/laurent

laurent: driver for KernelChip Laurent family of relays
This commit is contained in:
Dmitry Baryshkov
2024-04-08 23:09:06 +03:00
committed by GitHub
6 changed files with 234 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
---
devices:
- board: myboard
name: "My Board"
description: |
My Awesome board
console: /dev/ttyABC0
fastboot: cacafada
fastboot_set_active: true
fastboot_key_timeout: 2
laurent:
server: laurent.lan
relay: 5

View File

@@ -105,6 +105,7 @@ extern const struct control_ops ftdi_gpio_ops;
extern const struct control_ops local_gpio_ops;
extern const struct control_ops external_ops;
extern const struct control_ops qcomlt_dbg_ops;
extern const struct control_ops laurent_ops;
extern const struct console_ops conmux_console_ops;
extern const struct console_ops console_ops;

View File

@@ -115,6 +115,11 @@ static void parse_board(struct device_parser *dp)
if (dev->control_options)
set_control_ops(dev, &ftdi_gpio_ops);
continue;
} else if (!strcmp(key, "laurent")) {
dev->control_options = laurent_ops.parse_options(dp);
if (dev->control_options)
set_control_ops(dev, &laurent_ops);
continue;
}
device_parser_expect(dp, YAML_SCALAR_EVENT, value, TOKEN_LENGTH);

197
drivers/laurent.c Normal file
View File

@@ -0,0 +1,197 @@
/*
* Copyright (c) 2024, Linaro Ltd.
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Driver for KernelChip Laurent family of Ethernet-controlled relay arrays.
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#include <err.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <yaml.h>
#include "cdba-server.h"
#include "device.h"
#include "device_parser.h"
struct laurent_options {
const char *server;
const char *password;
unsigned int relay;
};
struct laurent {
struct laurent_options *options;
struct addrinfo addr;
};
#define DEFAULT_PASSWORD "Laurent"
#define TOKEN_LENGTH 128
void *laurent_parse_options(struct device_parser *dp)
{
struct laurent_options *options;
char value[TOKEN_LENGTH];
char key[TOKEN_LENGTH];
options = calloc(1, sizeof(*options));
options->password = DEFAULT_PASSWORD;
device_parser_accept(dp, YAML_MAPPING_START_EVENT, NULL, 0);
while (device_parser_accept(dp, YAML_SCALAR_EVENT, key, TOKEN_LENGTH)) {
if (!device_parser_accept(dp, YAML_SCALAR_EVENT, value, TOKEN_LENGTH))
errx(1, "%s: expected value for \"%s\"", __func__, key);
if (!strcmp(key, "server"))
options->server = strdup(value);
else if (!strcmp(key, "password"))
options->password = strdup(value);
else if (!strcmp(key, "relay"))
options->relay = strtoul(value, NULL, 0);
else
errx(1, "%s: unknown option \"%s\"", __func__, key);
}
device_parser_expect(dp, YAML_MAPPING_END_EVENT, NULL, 0);
if (!options->server)
errx(1, "%s: server hostname not specified", __func__);
return options;
}
static void laurent_resolve(struct laurent *laurent)
{
struct addrinfo hints = {};
struct addrinfo *result, *rp;
int ret;
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
ret = getaddrinfo(laurent->options->server, "80", &hints, &result);
if (ret != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
exit(EXIT_FAILURE);
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
int fd = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (fd == -1)
continue;
if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) {
close(fd);
break;
}
close(fd);
}
if (rp == NULL)
errx(1, "Could not resolve / connect to the controller\n");
laurent->addr = *rp;
laurent->addr.ai_addr = malloc(rp->ai_addrlen);
memcpy(laurent->addr.ai_addr, rp->ai_addr,rp->ai_addrlen);
freeaddrinfo(result); /* No longer needed */
}
static void *laurent_open(struct device *dev)
{
struct laurent *laurent;
laurent = calloc(1, sizeof(*laurent));
laurent->options = dev->control_options;
laurent_resolve(laurent);
return laurent;
}
static int laurent_power(struct device *dev, bool on)
{
struct laurent *laurent = dev->cdb;
char buf[BUFSIZ];
int fd, ret, len, off;
fd = socket(laurent->addr.ai_family, laurent->addr.ai_socktype,
laurent->addr.ai_protocol);
if (fd == -1) {
warn("failed to open socket\n");
return -1;
}
ret = connect(fd, laurent->addr.ai_addr, laurent->addr.ai_addrlen);
if (ret == -1) {
warn("failed to connect\n");
goto err;
}
len = snprintf(buf, sizeof(buf), "GET /cmd.cgi?psw=%s&cmd=REL,%u,%d HTTP/1.0\r\n\r\n",
laurent->options->password,
laurent->options->relay,
on);
if (len < 0) {
warn("asprintf failed\n");
goto err;
}
for (off = 0; off != len; ) {
ret = send(fd, buf + off, len - off, 0);
if (ret == -1) {
warn("failed to send\n");
goto err;
}
off += ret;
}
/* Dump controller response to stderr */
while (true) {
ret = recv(fd, buf, sizeof(buf), 0);
if (ret == -1) {
warn("failed to recv\n");
goto err;
}
if (!ret)
break;
write(STDERR_FILENO, buf, ret);
}
write(STDERR_FILENO, "\n", 1);
shutdown(fd, SHUT_RDWR);
close(fd);
return 0;
err:
shutdown(fd, SHUT_RDWR);
close(fd);
return -1;
}
const struct control_ops laurent_ops = {
.parse_options = laurent_parse_options,
.open = laurent_open,
.power = laurent_power,
};

View File

@@ -70,6 +70,7 @@ drivers_srcs = ['drivers/alpaca.c',
'drivers/conmux.c',
'drivers/external.c',
'drivers/ftdi-gpio.c',
'drivers/laurent.c',
'drivers/local-gpio.c',
'drivers/qcomlt_dbg.c',
]

View File

@@ -142,6 +142,23 @@ properties:
patternProperties:
"^power|fastboot_key|power_key|usb_disconnect$":
$ref: "#/$defs/local_gpio"
laurent:
description: KernelChip Laurent relays
type: object
unevaluatedItems: false
properties:
server:
type: string
relay:
type: integer
password:
description: password to access the relays, defaults to 'Laurent'
type: string
required:
- server
- relay
required:
- board
- name