Skip to content

Commit

Permalink
Merge pull request #72 from lumag/laurent
Browse files Browse the repository at this point in the history
laurent: driver for KernelChip Laurent family of relays
  • Loading branch information
lumag authored Apr 8, 2024
2 parents 798a76a + 8f668bd commit f4e95f6
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 0 deletions.
13 changes: 13 additions & 0 deletions config-samples/sample12.yaml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions device.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions device_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
197 changes: 197 additions & 0 deletions drivers/laurent.c
Original file line number Diff line number Diff line change
@@ -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,
};
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
Expand Down
17 changes: 17 additions & 0 deletions schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit f4e95f6

Please sign in to comment.