You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
405 lines
9.8 KiB
405 lines
9.8 KiB
/* |
|
* drcNet.h: |
|
* Extend wiringPi with the DRC Network protocol (e.g. to another Pi) |
|
* Copyright (c) 2016-2017 Gordon Henderson |
|
*********************************************************************** |
|
* This file is part of wiringPi: |
|
* https://projects.drogon.net/raspberry-pi/wiringpi/ |
|
* |
|
* wiringPi is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU Lesser General Public License as |
|
* published by the Free Software Foundation, either version 3 of the |
|
* License, or (at your option) any later version. |
|
* |
|
* wiringPi is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU Lesser General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU Lesser General Public |
|
* License along with wiringPi. |
|
* If not, see <http://www.gnu.org/licenses/>. |
|
*********************************************************************** |
|
*/ |
|
|
|
#include <stdio.h> |
|
#include <stdint.h> |
|
#include <unistd.h> |
|
#include <sys/types.h> |
|
#include <sys/socket.h> |
|
#include <arpa/inet.h> |
|
#include <netdb.h> |
|
#include <string.h> |
|
#include <errno.h> |
|
#include <crypt.h> |
|
|
|
|
|
#include "wiringPi.h" |
|
#include "drcNet.h" |
|
#include "../wiringPiD/drcNetCmd.h" |
|
|
|
|
|
/* |
|
* remoteReadline: |
|
* Read in a line of data from the remote server, ending with a newline |
|
* character which is not stored. Returns the length or < 0 on |
|
* any sort of failure. |
|
********************************************************************************* |
|
*/ |
|
|
|
static int remoteReadline (int fd, char *buf, int max) |
|
{ |
|
int len = 0 ; |
|
char c ; |
|
|
|
for (;;) |
|
{ |
|
if (read (fd, &c, 1) < 1) |
|
return -1 ; |
|
|
|
if (c == '\n') |
|
return len ; |
|
|
|
*buf++ = c ; |
|
if (++len == max) |
|
return len ; |
|
} |
|
} |
|
|
|
|
|
/* |
|
* getChallenge: |
|
* Read in lines from the remote site until we get one identified |
|
* as the challenge. This line contains the password salt. |
|
********************************************************************************* |
|
*/ |
|
|
|
static char *getChallenge (int fd) |
|
{ |
|
static char buf [1024] ; |
|
int num ; |
|
|
|
for (;;) |
|
{ |
|
if ((num = remoteReadline (fd, buf, 1023)) < 0) |
|
return NULL ; |
|
buf [num] = 0 ; |
|
|
|
if (strncmp (buf, "Challenge ", 10) == 0) |
|
return &buf [10] ; |
|
} |
|
} |
|
|
|
|
|
/* |
|
* authenticate: |
|
* Read in the challenge from the server, use it to encrypt our password |
|
* and send it back to the server. Wait for a reply back from the server |
|
* to say that we're good to go. |
|
* The server will simply disconnect on a bad response. No 3 chances here. |
|
********************************************************************************* |
|
*/ |
|
|
|
static int authenticate (int fd, const char *pass) |
|
{ |
|
char *challenge ; |
|
char *encrypted ; |
|
char salted [1034] ; |
|
|
|
if ((challenge = getChallenge (fd)) == NULL) |
|
return -1 ; |
|
|
|
sprintf (salted, "$6$%s$", challenge) ; |
|
encrypted = crypt (pass, salted) ; |
|
|
|
// This is an assertion, or sanity check on my part... |
|
// The '20' comes from the $6$ then the 16 characters of the salt, |
|
// then the terminating $. |
|
|
|
if (strncmp (encrypted, salted, 20) != 0) |
|
{ |
|
errno = EBADE ; |
|
return -1 ; |
|
} |
|
|
|
// 86 characters is the length of the SHA-256 hash |
|
|
|
if (write (fd, encrypted + 20, 86) == 86) |
|
return 0 ; |
|
else |
|
return -1 ; |
|
} |
|
|
|
|
|
/* |
|
* _drcSetupNet: |
|
* Do the hard work of establishing a network connection and authenticating |
|
* the password. |
|
********************************************************************************* |
|
*/ |
|
|
|
int _drcSetupNet (const char *ipAddress, const char *port, const char *password) |
|
{ |
|
struct addrinfo hints; |
|
struct addrinfo *result, *rp ; |
|
struct in6_addr serveraddr ; |
|
int remoteFd ; |
|
|
|
// Start by seeing if we've been given a (textual) numeric IP address |
|
// which will save lookups in getaddrinfo() |
|
|
|
memset (&hints, 0, sizeof (hints)) ; |
|
hints.ai_flags = AI_NUMERICSERV ; |
|
hints.ai_family = AF_UNSPEC ; |
|
hints.ai_socktype = SOCK_STREAM ; |
|
hints.ai_protocol = 0 ; |
|
|
|
if (inet_pton (AF_INET, ipAddress, &serveraddr) == 1) // Valid IPv4 |
|
{ |
|
hints.ai_family = AF_INET ; |
|
hints.ai_flags |= AI_NUMERICHOST ; |
|
} |
|
else |
|
{ |
|
if (inet_pton (AF_INET6, ipAddress, &serveraddr) == 1) // Valid IPv6 |
|
{ |
|
hints.ai_family = AF_INET6 ; |
|
hints.ai_flags |= AI_NUMERICHOST ; |
|
} |
|
} |
|
|
|
// Now use getaddrinfo() with the newly supplied hints |
|
|
|
if (getaddrinfo (ipAddress, port, &hints, &result) != 0) |
|
return -1 ; |
|
|
|
// Now try each address in-turn until we get one that connects... |
|
|
|
for (rp = result; rp != NULL; rp = rp->ai_next) |
|
{ |
|
if ((remoteFd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) |
|
continue ; |
|
|
|
if (connect (remoteFd, rp->ai_addr, rp->ai_addrlen) < 0) |
|
continue ; |
|
|
|
if (authenticate (remoteFd, password) < 0) |
|
{ |
|
close (remoteFd) ; |
|
errno = EACCES ; // Permission denied |
|
return -1 ; |
|
} |
|
else |
|
return remoteFd ; |
|
} |
|
|
|
errno = EHOSTUNREACH ; // Host unreachable - may not be right, but good enough |
|
return -1 ; // Nothing connected |
|
} |
|
|
|
|
|
/* |
|
* myPinMode: |
|
* Change the pin mode on the remote DRC device |
|
********************************************************************************* |
|
*/ |
|
|
|
static void myPinMode (struct wiringPiNodeStruct *node, int pin, int mode) |
|
{ |
|
struct drcNetComStruct cmd ; |
|
|
|
cmd.pin = pin - node->pinBase ; |
|
cmd.cmd = DRCN_PIN_MODE ; |
|
cmd.data = mode ; |
|
|
|
(void)send (node->fd, &cmd, sizeof (cmd), 0) ; |
|
(void)recv (node->fd, &cmd, sizeof (cmd), 0) ; |
|
} |
|
|
|
|
|
/* |
|
* myPullUpDnControl: |
|
********************************************************************************* |
|
*/ |
|
|
|
static void myPullUpDnControl (struct wiringPiNodeStruct *node, int pin, int mode) |
|
{ |
|
struct drcNetComStruct cmd ; |
|
|
|
cmd.pin = pin - node->pinBase ; |
|
cmd.cmd = DRCN_PULL_UP_DN ; |
|
cmd.data = mode ; |
|
|
|
(void)send (node->fd, &cmd, sizeof (cmd), 0) ; |
|
(void)recv (node->fd, &cmd, sizeof (cmd), 0) ; |
|
} |
|
|
|
|
|
/* |
|
* myDigitalWrite: |
|
********************************************************************************* |
|
*/ |
|
|
|
static void myDigitalWrite (struct wiringPiNodeStruct *node, int pin, int value) |
|
{ |
|
struct drcNetComStruct cmd ; |
|
|
|
cmd.pin = pin - node->pinBase ; |
|
cmd.cmd = DRCN_DIGITAL_WRITE ; |
|
cmd.data = value ; |
|
|
|
(void)send (node->fd, &cmd, sizeof (cmd), 0) ; |
|
(void)recv (node->fd, &cmd, sizeof (cmd), 0) ; |
|
} |
|
|
|
|
|
/* |
|
* myDigitalWrite8: |
|
********************************************************************************* |
|
|
|
static void myDigitalWrite8 (struct wiringPiNodeStruct *node, int pin, int value) |
|
{ |
|
struct drcNetComStruct cmd ; |
|
|
|
cmd.pin = pin - node->pinBase ; |
|
cmd.cmd = DRCN_DIGITAL_WRITE8 ; |
|
cmd.data = value ; |
|
|
|
(void)send (node->fd, &cmd, sizeof (cmd), 0) ; |
|
(void)recv (node->fd, &cmd, sizeof (cmd), 0) ; |
|
} |
|
*/ |
|
|
|
|
|
/* |
|
* myAnalogWrite: |
|
********************************************************************************* |
|
*/ |
|
|
|
static void myAnalogWrite (struct wiringPiNodeStruct *node, int pin, int value) |
|
{ |
|
struct drcNetComStruct cmd ; |
|
|
|
cmd.pin = pin - node->pinBase ; |
|
cmd.cmd = DRCN_ANALOG_WRITE ; |
|
cmd.data = value ; |
|
|
|
(void)send (node->fd, &cmd, sizeof (cmd), 0) ; |
|
(void)recv (node->fd, &cmd, sizeof (cmd), 0) ; |
|
} |
|
|
|
|
|
/* |
|
* myPwmWrite: |
|
********************************************************************************* |
|
*/ |
|
|
|
static void myPwmWrite (struct wiringPiNodeStruct *node, int pin, int value) |
|
{ |
|
struct drcNetComStruct cmd ; |
|
|
|
cmd.pin = pin - node->pinBase ; |
|
cmd.cmd = DRCN_PWM_WRITE ; |
|
cmd.data = value ; |
|
|
|
(void)send (node->fd, &cmd, sizeof (cmd), 0) ; |
|
(void)recv (node->fd, &cmd, sizeof (cmd), 0) ; |
|
} |
|
|
|
|
|
/* |
|
* myAnalogRead: |
|
********************************************************************************* |
|
*/ |
|
|
|
static int myAnalogRead (struct wiringPiNodeStruct *node, int pin) |
|
{ |
|
struct drcNetComStruct cmd ; |
|
|
|
cmd.pin = pin - node->pinBase ; |
|
cmd.cmd = DRCN_ANALOG_READ ; |
|
cmd.data = 0 ; |
|
|
|
(void)send (node->fd, &cmd, sizeof (cmd), 0) ; |
|
(void)recv (node->fd, &cmd, sizeof (cmd), 0) ; |
|
|
|
return cmd.data ; |
|
} |
|
|
|
|
|
/* |
|
* myDigitalRead: |
|
********************************************************************************* |
|
*/ |
|
|
|
static int myDigitalRead (struct wiringPiNodeStruct *node, int pin) |
|
{ |
|
struct drcNetComStruct cmd ; |
|
|
|
cmd.pin = pin - node->pinBase ; |
|
cmd.cmd = DRCN_DIGITAL_READ ; |
|
cmd.data = 0 ; |
|
|
|
(void)send (node->fd, &cmd, sizeof (cmd), 0) ; |
|
(void)recv (node->fd, &cmd, sizeof (cmd), 0) ; |
|
|
|
return cmd.data ; |
|
} |
|
|
|
|
|
/* |
|
* myDigitalRead8: |
|
********************************************************************************* |
|
|
|
static unsigned int myDigitalRead8 (struct wiringPiNodeStruct *node, int pin) |
|
{ |
|
struct drcNetComStruct cmd ; |
|
|
|
cmd.pin = pin - node->pinBase ; |
|
cmd.cmd = DRCN_DIGITAL_READ8 ; |
|
cmd.data = 0 ; |
|
|
|
(void)send (node->fd, &cmd, sizeof (cmd), 0) ; |
|
(void)recv (node->fd, &cmd, sizeof (cmd), 0) ; |
|
|
|
return cmd.data ; |
|
} |
|
*/ |
|
|
|
|
|
/* |
|
* drcNet: |
|
* Create a new instance of an DRC GPIO interface. |
|
* Could be a variable nunber of pins here - we might not know in advance. |
|
********************************************************************************* |
|
*/ |
|
|
|
int drcSetupNet (const int pinBase, const int numPins, const char *ipAddress, const char *port, const char *password) |
|
{ |
|
int fd, len ; |
|
struct wiringPiNodeStruct *node ; |
|
|
|
if ((fd = _drcSetupNet (ipAddress, port, password)) < 0) |
|
return FALSE ; |
|
|
|
len = sizeof (struct drcNetComStruct) ; |
|
|
|
if (setsockopt (fd, SOL_SOCKET, SO_RCVLOWAT, (void *)&len, sizeof (len)) < 0) |
|
return FALSE ; |
|
|
|
node = wiringPiNewNode (pinBase, numPins) ; |
|
|
|
node->fd = fd ; |
|
node->pinMode = myPinMode ; |
|
node->pullUpDnControl = myPullUpDnControl ; |
|
node->analogRead = myAnalogRead ; |
|
node->analogRead = myAnalogRead ; |
|
node->analogWrite = myAnalogWrite ; |
|
node->digitalRead = myDigitalRead ; |
|
node->digitalWrite = myDigitalWrite ; |
|
//node->digitalRead8 = myDigitalRead8 ; |
|
//node->digitalWrite8 = myDigitalWrite8 ; |
|
node->pwmWrite = myPwmWrite ; |
|
|
|
return TRUE ; |
|
}
|
|
|