-
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.
Merge pull request #72 from lumag/laurent
laurent: driver for KernelChip Laurent family of relays
- 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); | ||
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, | ||
}; |
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