-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
laurent: driver for KernelChip Laurent family of relays
Add support for the KernelChip Laurent-2 / Laurent-5 / Laurent-112 / Laurent-128 network-controlled relay arrays. Signed-off-by: Dmitry Baryshkov <[email protected]>
- Loading branch information
Showing
6 changed files
with
234 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
Check failure Code scanning / CodeQL Cleartext transmission of sensitive information High
This operation transmits '*... + ...', which may contain unencrypted sensitive data from
password Error loading related location Loading |
||
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); | ||
Check failure Code scanning / CodeQL Cleartext transmission of sensitive information High
This operation receives into '*buf', which may put unencrypted sensitive data into
password Error loading related location Loading |
||
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, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters