UDP to Serial bridge example for access to DTPlug protocol.
authorNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 26 Apr 2016 23:05:29 +0000 (01:05 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Thu, 9 Feb 2023 23:36:07 +0000 (00:36 +0100)
14 files changed:
host/udpbridge/Makefile [new file with mode: 0644]
host/udpbridge/dtplug_protocol_defs.h [new symlink]
host/udpbridge/dtplug_protocol_host.c [new file with mode: 0644]
host/udpbridge/dtplug_protocol_host.h [new file with mode: 0644]
host/udpbridge/handlers.c [new file with mode: 0644]
host/udpbridge/handlers.h [new file with mode: 0644]
host/udpbridge/list.h [new file with mode: 0644]
host/udpbridge/main.c [new file with mode: 0644]
host/udpbridge/protocol_bridge [new file with mode: 0755]
host/udpbridge/questions.pl [new file with mode: 0755]
host/udpbridge/serial_utils.c [new file with mode: 0644]
host/udpbridge/serial_utils.h [new file with mode: 0644]
host/udpbridge/sock_utils.c [new file with mode: 0644]
host/udpbridge/sock_utils.h [new file with mode: 0644]

diff --git a/host/udpbridge/Makefile b/host/udpbridge/Makefile
new file mode 100644 (file)
index 0000000..e67cea7
--- /dev/null
@@ -0,0 +1,32 @@
+#CROSS_COMPILE ?= arm-linux-gnueabihf-
+CC = $(CROSS_COMPILE)gcc
+
+CFLAGS = -Wall -O2 -Wextra
+
+EXEC = protocol_bridge
+
+all: $(EXEC)
+
+
+OBJDIR = objs
+SRC = $(shell find . -name \*.c)
+OBJS = ${SRC:%.c=${OBJDIR}/%.o}
+INCLUDES = includes/
+
+${OBJDIR}/%.o: %.c
+       @mkdir -p $(dir $@)
+       @echo "-- compiling" $<
+       @$(CC) -MMD -MP -MF ${OBJDIR}/$*.d $(CFLAGS) $< -c -o $@ -I$(INCLUDES)
+
+$(EXEC): $(OBJS)
+       @echo "Linking $@ ..."
+       @$(CC) $(LDFLAGS) -o $@ $(OBJS)
+       @echo Done.
+
+
+clean:
+       find ${OBJDIR} -name "*.o" -exec rm {} \;
+       find ${OBJDIR} -name "*.d" -exec rm {} \;
+
+mrproper: clean
+       rm -f $(EXEC)
diff --git a/host/udpbridge/dtplug_protocol_defs.h b/host/udpbridge/dtplug_protocol_defs.h
new file mode 120000 (symlink)
index 0000000..1c863aa
--- /dev/null
@@ -0,0 +1 @@
+../../include/lib/protocols/dtplug/defs.h
\ No newline at end of file
diff --git a/host/udpbridge/dtplug_protocol_host.c b/host/udpbridge/dtplug_protocol_host.c
new file mode 100644 (file)
index 0000000..553f6a7
--- /dev/null
@@ -0,0 +1,215 @@
+/****************************************************************************
+ *   dtplug_protocol_host.c
+ *
+ *
+ * Copyright 2013-2015 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+
+/* Host side implementation of the DTPlug communication protocol */
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include "dtplug_protocol_host.h"
+
+
+/******************************************************************************/
+/* Handle packet reception, including checksums */
+/* 'sum' is used to sum all the received characters, and if the last byte of sum is 0 for each
+ *   part (header and data) then the packet is valid.
+ * 'full_size' is the size of the whole packet, including header, updated as soon as the header
+ *   is checked and valid
+ *
+ * This function must be called for every received character.
+ * If the character is part of a packet but the packet is being built, then the function returns 0.
+ * When the character is the last one of a valid packet, then the function returns the packet size
+ *   and the packet in rx_data->rx_packet is valid.
+ * If the character is the last one of a packet which has an invalid data checksum, this function
+ *   returns -2 and the data is lost.
+ * If the character is not part of a packet it returns -1. The character may then be part of
+ *   a debug message (and displayed by the host), or any other kind of communication.
+ * When a set of consecutive characters have been used to build a packet but the packet is
+ *   not valid due to header error, then the function returns -3 (checksum error) or -4 (data size
+ *   error). The data in rx_data->rx_packet is the received data but is not valid.
+ *   The corresponding data size is always sizeof(struct header).
+ */
+int dtplug_protocol_decode(uint8_t c, struct line_transceiver* rx_data)
+{
+       struct header* info = (struct header*)(&(rx_data->rx_packet));
+       int ret = 0;
+
+       /* Do not start reception before receiving the packet start character */
+       if ((rx_data->rx_ptr == 0) && (c != FIRST_PACKET_CHAR)) {
+               return -1;
+       }
+
+       /* Store the new byte in the packet */
+       ((uint8_t*)(&(rx_data->rx_packet)))[rx_data->rx_ptr++] = c;
+       rx_data->sum += c;
+
+       /* Is this packet valid ? (at end of header reception) */
+       if (rx_data->rx_ptr == sizeof(struct header)) {
+               /* Checksum OK ? */
+               if (rx_data->sum != 0) {
+                       rx_data->errors_count++;
+                       ret = -3;
+                       goto next_packet;
+               }
+               /* Start the new checksum for data (if any) */
+               rx_data->sum = 0;
+
+               rx_data->full_size = sizeof(struct header);
+               /* If the packet is not a quick data or error packet it has a data part. Get it's size */
+               if (!(info->seq_num & QUICK_DATA_PACKET) && !(info->seq_num & PACKET_IS_ERROR)) {
+                       rx_data->full_size += (info->data.size & PKT_SIZE_MASK); /* Don't care about big packets here */
+                       /* Make sure the packet will fit in the buffer */
+                       if (rx_data->full_size > sizeof(struct packet)) {
+                               rx_data->errors_count++;
+                               ret = -4;
+                               goto next_packet;
+                       }
+               }
+       }
+
+       /* Did we receive the whole packet ? */
+       if (rx_data->rx_ptr == rx_data->full_size) {
+               /* From here on, the packet is valid, we can provide some feedback */
+               /* Check data checksum if we have a normal data packet */
+               if (!(info->seq_num & QUICK_DATA_PACKET) && !(info->seq_num & PACKET_IS_ERROR)) {
+                       if (rx_data->sum != info->data.checksum) {
+                               rx_data->errors_count++;
+                               ret = -2;
+                               goto next_packet;
+                       }
+               }
+               /* Count received packets */
+               rx_data->packet_rx_count++;
+               ret = rx_data->full_size;
+               /* And get ready to receive the next packet */
+               goto next_packet;
+       }
+
+       return 0;
+
+next_packet:
+#ifdef DEBUG
+       printf("Current rx pointer: %d, packet full size: %d\n", rx_data->rx_ptr, rx_data->full_size);
+       printf("Packets received count: %d, packets errors: %d\n",
+                               rx_data->packet_rx_count, rx_data->errors_count);
+       if (rx_data->rx_ptr >= sizeof(struct header)) {
+               struct header* h = info;
+               printf("Pkt: type: %d, seq: %d, err: %d, quick: %d\n", h->type, h->seq_num,
+                                               (h->seq_num & PACKET_IS_ERROR), (h->seq_num & QUICK_DATA_PACKET));
+       }
+#endif
+       /* Wether the packet was OK or not doesn't matter, go on for a new one :) */
+       rx_data->full_size = 0;
+       rx_data->rx_ptr = 0;
+       rx_data->sum = 0;
+       return ret;
+}
+
+
+/******************************************************************************/
+/* This function handles sending packets to slave
+ * It returns the number of bytes of data sent.
+ */
+int host_send_packet(struct line_transceiver* slave, uint8_t type, uint32_t size, uint8_t* data, int need_reply)
+{
+       struct packet cmd;
+       struct header* cmd_info = &(cmd.info);
+       unsigned int i = 0, sent = 0, len = sizeof(struct header);
+       uint8_t sum = 0;
+       unsigned int data_send_size = size, data_sent = 0;
+
+       /* Loop to send all data, possibly split in several packets */
+       do {
+               if (data_send_size > PACKET_DATA_SIZE) {
+                       data_send_size = PACKET_DATA_SIZE;
+               }
+               /* Set packet header */
+               cmd_info->start = FIRST_PACKET_CHAR;
+               cmd_info->type = type;
+               cmd_info->seq_num = (slave->sequence_number++ & SEQUENCE_MASK);
+               /* Set the need reply bit only on the last packet */
+               if ((need_reply != 0) && ((data_sent + data_send_size) == size)) {
+                       cmd_info->seq_num |= PACKET_NEEDS_REPLY;
+               }
+               /* Setup data */
+               /* Only use quick data packet for short packet, not continued data */
+               if (size <= 2) {
+                       cmd_info->seq_num |= QUICK_DATA_PACKET;
+                       cmd_info->quick_data[0] = data[0];
+                       cmd_info->quick_data[1] = data[1];
+               } else {
+                       /* Copy data, compute checksum (also OK for a data_send_size of 0) */
+                       for (i = 0; i < data_send_size; i++) {
+                               cmd.data[i] = data[i];
+                               sum += data[i]; /* Build checksum */
+                       }
+                       /* And update header information */
+                       cmd_info->data.size = data_send_size;
+                       /* Will this packet be continued in the following one ? */
+                       if (data_send_size < (size - data_sent)) {
+                               cmd_info->data.size |= BIG_DATA_PKT;
+                       }
+                       cmd_info->data.checksum = sum;
+                       /* Update length of data to send on serial link */
+                       len += data_send_size;
+               }
+               /* Compute header checksum */
+               sum = 0;
+               cmd_info->checksum = 0;
+               for (i = 0; i < sizeof(struct header); i++) {
+                       sum += ((uint8_t*)cmd_info)[i];
+               }
+               cmd_info->checksum = ((uint8_t)(256 - sum));
+
+               /* And send the packet on the serial link */
+               while (sent < len) {
+                       int ret = write(slave->fd, (((char*)(&cmd)) + sent), (len - sent));
+                       if (ret >= 0) {
+                               sent += ret;
+                       } else {
+                               /* Sending error ... */
+                               /* FIXME : handle / report errors */
+                               return data_sent;
+                       }
+               }
+               data_sent += data_send_size;
+
+               /* Need to send more ? */
+               if (data_sent <= size) {
+                       /* Move data pointer */
+                       data += data_send_size;
+                       /* Update size to send. check against PACKET_DATA_SIZE is done at beginning of loop */
+                       data_send_size = (size - data_sent);
+                       /* Set packet type to continued data packet for following packets */
+                       type = PKT_TYPE_CONTINUED_DATA;
+                       /* And prepare sending of the next packet (reset internal loop counters) */
+                       sum = 0;
+                       len = sizeof(struct header);
+                       sent = 0;
+               }
+       } while (data_send_size != 0);
+
+       return data_sent;
+}
+
+
diff --git a/host/udpbridge/dtplug_protocol_host.h b/host/udpbridge/dtplug_protocol_host.h
new file mode 100644 (file)
index 0000000..9fe3a2c
--- /dev/null
@@ -0,0 +1,79 @@
+/****************************************************************************
+ *   dtplug_protocol_host.h
+ *
+ *
+ * Copyright 2013-2015 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+
+/* Host side implementation of the DTPlug communication protocol */
+
+#ifndef DTPLUG_PROTOCOL_HOST_H
+#define DTPLUG_PROTOCOL_HOST_H
+
+
+#include <stdint.h>
+#include "dtplug_protocol_defs.h"
+
+
+/******************************************************************************/
+/* Handle packet reception, including checksums */
+/* 'sum' is used to sum all the received characters, and if the last byte of sum is 0 for each
+ *   part (header and data) then the packet is valid.
+ * 'full_size' is the size of the whole packet, including header, updated as soon as the header
+ *   is checked and valid
+ */
+struct line_transceiver {
+       int fd;
+       /* The packet being built */
+       struct packet rx_packet;
+       /* Sequence number for this line */
+       uint8_t sequence_number;
+       /* Packet building data */
+       uint8_t rx_ptr;
+       uint8_t sum;
+       uint8_t full_size;
+       /* packet counts */
+       uint32_t packet_rx_count;
+       uint32_t packet_tx_count;
+       uint32_t errors_count;
+};
+
+/* This function must be called for every received character.
+ * If the character is part of a packet but the packet is being built, then the function returns 0.
+ * When the character is the last one of a valid packet, then the function returns the packet size
+ *   and the packet in rx_data->rx_packet is valid.
+ * If the character is the last one of a packet which has an invalid data checksum, this function
+ *   returns -2 and the data is lost.
+ * If the character is not part of a packet it returns -1. The character may then be part of
+ *   a debug message (and displayed by the host), or any other kind of communication.
+ * When a set of consecutive characters have been used to build a packet but the packet is
+ *   not valid due to header error, then the function returns -3 (checksum error) or -4 (data size
+ *   error). The data in rx_data->rx_packet is the received data but is not valid.
+ *   The corresponding data size is always sizeof(struct header).
+ */
+int dtplug_protocol_decode(uint8_t c, struct line_transceiver* rx_data);
+
+/* This function handles sending packets to slave
+ * It returns the number of bytes of data sent.
+ */
+int host_send_packet(struct line_transceiver* slave, uint8_t type, uint32_t size,  uint8_t* data, int need_reply);
+
+
+#endif /* DTPLUG_PROTOCOL_HOST_H */
+
diff --git a/host/udpbridge/handlers.c b/host/udpbridge/handlers.c
new file mode 100644 (file)
index 0000000..0f7af46
--- /dev/null
@@ -0,0 +1,259 @@
+/****************************************************************************
+ *  DTPlug serial communication protocol handler for tests.
+ *
+ *   handlers.c
+ *
+ *
+ * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+
+/* This protocol handler is designed to run on a host replacing the DTPlug,
+ * but should be easy to use as a source for the protocol handling code for
+ * the DTPlug itself.
+ */
+
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include "dtplug_protocol_host.h"
+
+
+
+#define PKT_TYPE_GET_DISTANCES    (PKT_TYPE_LAST + 40)
+#define PKT_TYPE_GET_SMOKE        (PKT_TYPE_LAST + 41)
+#define PKT_TYPE_GET_BATTERY      (PKT_TYPE_LAST + 42)
+#define PKT_TYPE_GET_IR           (PKT_TYPE_LAST + 43)
+
+#define PKT_TYPE_SET_SPEED        (PKT_TYPE_LAST + 50)
+#define PKT_TYPE_SET_DIRECTION    (PKT_TYPE_LAST + 51)
+
+
+
+int16_t temperature = 0;
+struct timeval temp_timestamp;
+
+uint16_t adc = 0;
+struct timeval adc_timestamp;
+
+uint16_t smoke = 0;
+struct timeval smoke_timestamp;
+
+#define NB_PULSE_SENSORS  6
+uint16_t distances[NB_PULSE_SENSORS] = {0};
+struct timeval distances_timestamp;
+
+uint8_t red = 0;
+uint8_t green = 0;
+uint8_t blue = 0;
+struct timeval led_timestamp;
+
+int handle_module_data(struct line_transceiver* slave)
+{
+       struct header* head = &(slave->rx_packet.info);
+       /* If this packet holds no valid data, it indicates that there are errors */
+       if (head->seq_num & PACKET_IS_ERROR) {
+               printf("Received a packet indicating errors (%d - %d)\n",
+                                        head->err.error_code, head->err.info);
+               /* Request the list of errors */
+               host_send_packet(slave, PKT_TYPE_GET_ERRORS, 0, NULL, 1);
+               return 0;
+       }
+       /* Move data from "QUICK_DATA_PACKET" packets to the data part of the packet for easy handling */
+       if (head->seq_num & QUICK_DATA_PACKET) {
+               slave->rx_packet.data[0] = head->quick_data[0];
+               slave->rx_packet.data[1] = head->quick_data[1];
+               if (head->seq_num & QUICK_DATA_PACKET_ONE_BYTE) {
+                       head->data.size = 1;
+               } else {
+                       head->data.size = 2;
+               }
+       }
+
+       switch (head->type) {
+               case PKT_TYPE_PING:
+                       break;
+
+               case PKT_TYPE_GET_BOARD_INFO:
+                       break;
+
+               case PKT_TYPE_GET_NUM_PACKETS:
+                       break;
+
+               case PKT_TYPE_GET_ERRORS:
+                       /* FIXME */
+                       printf("Received error list: ... implement error decoding.\n");
+                       break;
+
+               case PKT_TYPE_GET_NUM_ERRORS:
+                       /* FIXME */
+                       printf("Received number of errors: %d.\n", 0);
+                       break;
+       
+               case PKT_TYPE_CONTINUED_DATA:
+                       break;
+       
+               case PKT_TYPE_GET_TEMPERATURE:
+                       {
+                               uint16_t* tmp = (uint16_t*)(&(slave->rx_packet.data));
+                               gettimeofday(&temp_timestamp, NULL);
+                               temperature = (int16_t)ntohs(tmp[0]);
+                       }
+                       break;
+       
+               case PKT_TYPE_GET_ADC_VALUE:
+                       {
+                               uint16_t* tmp = (uint16_t*)(&(slave->rx_packet.data));
+                               gettimeofday(&adc_timestamp, NULL);
+                               adc = ntohs(tmp[0]);
+                       }
+                       break;
+
+               case PKT_TYPE_GET_SMOKE:
+                       {
+                               uint16_t* tmp = (uint16_t*)(&(slave->rx_packet.data));
+                               gettimeofday(&smoke_timestamp, NULL);
+                               smoke = ntohs(tmp[0]);
+                       }
+                       break;
+
+               case PKT_TYPE_GET_DISTANCES:
+                       {
+                               uint16_t* tmp = (uint16_t*)(&(slave->rx_packet.data));
+                               int i = 0;
+                               gettimeofday(&distances_timestamp, NULL);
+                               for (i = 0; i < NB_PULSE_SENSORS; i++) {
+                                       distances[i] = ntohs(tmp[i]);
+                               }
+                       }
+                       break;
+
+               case PKT_TYPE_GET_GPIO:
+                       break;
+       
+               case PKT_TYPE_GET_RGB_LED:
+                       gettimeofday(&led_timestamp, NULL);
+                       red = slave->rx_packet.data[1];
+                       green = slave->rx_packet.data[2];
+                       blue = slave->rx_packet.data[3];
+                       break;
+       
+               case PKT_TYPE_SEND_ON_BUS:
+                       break;
+       
+               default:
+                       printf("Received packet type %d. Not handled.\n", head->type);
+                       break;
+       }
+       return 0;
+}
+
+#define REP_SIZE_MAX  1024
+
+int handle_udp_request(char* buf, int len, struct sockaddr* addr, socklen_t addr_len,
+                                                int sock, struct line_transceiver* slave)
+{
+       if ((len == 0) || (buf == NULL)) {
+               return -1;
+       }
+       if (buf[0] == 'G') {
+               char obuf[REP_SIZE_MAX];
+               int len = 0;
+               /* GET command : Return the last updated value with the update timestamp */
+               switch (buf[1]) {
+                       case 'T':
+                               len = snprintf(obuf, REP_SIZE_MAX, "GT: %d at %ld.%03ld",
+                                                       temperature, temp_timestamp.tv_sec, (temp_timestamp.tv_usec / 1000));
+                               break;
+                       case 'L':
+                               len = snprintf(obuf, REP_SIZE_MAX, "GL: %d, %d, %d at %ld.%03ld",
+                                                       red, green, blue, led_timestamp.tv_sec, (led_timestamp.tv_usec / 1000));
+                               break;
+                       case 'A':
+                               len = snprintf(obuf, REP_SIZE_MAX, "GA: %d at %ld.%03ld",
+                                                       adc, adc_timestamp.tv_sec, (adc_timestamp.tv_usec / 1000));
+                               break;
+               }
+               sendto(sock, obuf, len, 0, addr, addr_len);
+               printf("Handled GET request type: %c\n", buf[1]);
+       } else if (buf[0] == 'U') {
+               uint8_t num = 0;
+               /* Request a value update */
+               switch (buf[1]) {
+                       case 'T':
+                               host_send_packet(slave, PKT_TYPE_START_TEMP_CONVERSION, 0, NULL, 0);
+                               usleep(200 * 1000); /* Temp conversion at 11 bits resolution is about 160ms */
+                               host_send_packet(slave, PKT_TYPE_GET_TEMPERATURE, 0, NULL, 1);
+                               break;
+                       case 'L':
+                               num = (uint8_t)(strtoul(&buf[2], NULL, 10) & 0xFF);
+                               host_send_packet(slave, PKT_TYPE_GET_RGB_LED, 1, &num, 1);
+                               break;
+                       case 'A':
+                               num = (uint8_t)(strtoul(&buf[2], NULL, 10) & 0xFF);
+                               host_send_packet(slave, PKT_TYPE_GET_ADC_VALUE, 1, &num, 1);
+                               break;
+                       case 'S':
+                               host_send_packet(slave, PKT_TYPE_GET_SMOKE, 0, NULL, 1);
+                               break;
+                       case 'D':
+                               host_send_packet(slave, PKT_TYPE_GET_DISTANCES, 0, NULL, 1);
+                               break;
+               }
+               printf("Handled UPDATE request type: %c\n", buf[1]);
+       } else if (buf[0] == 'S') {
+               /* Send a new value to the micro-controller */
+               uint8_t obuf[4] = {0, 0, 0, 0};
+               char* tmp = NULL;
+
+               switch (buf[1]) {
+                       case 'L':
+                               obuf[0] = (strtoul(&buf[2], &tmp, 10) & 0xFF);
+                               obuf[1] = (strtoul(tmp, &tmp, 10) & 0xFF);
+                               obuf[2] = (strtoul(tmp, &tmp, 10) & 0xFF);
+                               obuf[3] = (strtoul(tmp, &tmp, 10) & 0xFF);
+                               host_send_packet(slave, PKT_TYPE_SET_RGB_LED, 4, obuf, 0);
+                               break;
+                       case 'D': /* Direction */
+                               obuf[0] = (strtoul(&buf[2], NULL, 10) & 0xFF);
+                               host_send_packet(slave, PKT_TYPE_SET_DIRECTION, 1, obuf, 0);
+                               break;
+                       case 'S': /* Speed */
+                               obuf[0] = (strtoul(&buf[2], NULL, 10) & 0xFF);
+                               host_send_packet(slave, PKT_TYPE_SET_SPEED, 1, obuf, 0);
+                               break;
+               }
+               printf("Handled SET request type: %c\n", buf[1]);
+       } else {
+               printf("Unhandled request type: '0x%02x'\n", buf[0]);
+               return -2;
+       }
+
+       return 0;
+}
+
+
+
diff --git a/host/udpbridge/handlers.h b/host/udpbridge/handlers.h
new file mode 100644 (file)
index 0000000..fad21d7
--- /dev/null
@@ -0,0 +1,47 @@
+/****************************************************************************
+ *  DTPlug serial communication protocol handler for tests.
+ *
+ *   handlers.h
+ *
+ *
+ * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+
+/* This protocol handler is designed to run on a host replacing the DTPlug,
+ * but should be easy to use as a source for the protocol handling code for
+ * the DTPlug itself.
+ */
+
+#ifndef HANDLERS_H
+#define HANDLERS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include "dtplug_protocol_host.h"
+
+
+
+int handle_module_data(struct line_transceiver* slave);
+
+
+int handle_udp_request(char* buf, int len, struct sockaddr* addr, socklen_t addr_len,
+                                               int sock, struct line_transceiver* slave);
+
+
+#endif /* HANDLERS_H */
diff --git a/host/udpbridge/list.h b/host/udpbridge/list.h
new file mode 100644 (file)
index 0000000..1be8f9a
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * list.h
+ *
+ * This code is from the linux kernel. It is a very simple and powerfull doubly
+ *   linked list implementation.
+ * Not everything has been taken from the original file.
+ */
+
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+
+/* linux/kernel.h */
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr:    the pointer to the member.
+ * @type:   the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({          \
+    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+    (type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+
+/* linux/stddef.h */
+
+#ifdef __compiler_offsetof
+#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
+#else
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+
+
+/* linux/list.h */
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+       list->next = list;
+       list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+                             struct list_head *prev,
+                             struct list_head *next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void __list_del_entry(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+}
+
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = LIST_POISON1;
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+                               const struct list_head *head)
+{
+       return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+       return head->next == head;
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr:       the list head to take the element from.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+       list_entry((ptr)->next, type, member)
+
+/**
+ * list_last_entry - get the last element from a list
+ * @ptr:       the list head to take the element from.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_last_entry(ptr, type, member) \
+       list_entry((ptr)->prev, type, member)
+
+/**
+ * list_next_entry - get the next element in list
+ * @pos:       the type * to cursor
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_next_entry(pos, member) \
+       list_entry((pos)->member.next, typeof(*(pos)), member)
+
+/**
+ * list_for_each_entry -       iterate over list of given type
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)                         \
+       for (pos = list_first_entry(head, typeof(*pos), member);        \
+            &pos->member != (head);                                    \
+            pos = list_next_entry(pos, member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:    the type * to use as a loop cursor.
+ * @n:      another type * to use as temporary storage
+ * @head:   the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)          \
+       for (pos = list_first_entry(head, typeof(*pos), member),    \
+               n = list_next_entry(pos, member);           \
+                &pos->member != (head);                    \
+                pos = n, n = list_next_entry(n, member))
+
+#endif  /* _LINUX_LIST_H */
diff --git a/host/udpbridge/main.c b/host/udpbridge/main.c
new file mode 100644 (file)
index 0000000..65350a2
--- /dev/null
@@ -0,0 +1,271 @@
+/****************************************************************************
+ *  DTPlug serial communication protocol handler for tests.
+ *
+ *   main.c
+ *
+ *
+ * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+
+/* this protocol handler is designed to run on a host replacing the DTPlug,
+ * but should be easy to use as a source for the protocol handling code for
+ * the DTPlug itself.
+ */
+
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include "dtplug_protocol_host.h"
+#include "serial_utils.h"
+#include "sock_utils.h"
+#include "handlers.h"
+
+
+#define BUF_SIZE  1024
+
+#define PROG_NAME  "DTPlug protocol test bridge"
+#define VERSION  "0.1"
+
+
+void help(char *prog_name)
+{
+       fprintf(stderr, "---------------- "PROG_NAME" --------------------------------\n");
+       fprintf(stderr, "Usage: %s [options]\n" \
+               "  Available options:\n" \
+               "  \t -p | --port : Start listening for messages on this port\n" \
+               "  \t -d | --device : Serial device to use for serial communication with the module\n" \
+               "  \t -h | --help : Print this help\n" \
+               "  \t -v | --version : Print programm version\n" \
+               "  All other arguments are data for the command, handled depending on the command.\n", prog_name);
+       fprintf(stderr, "-----------------------------------------------------------------------\n");
+}
+
+
+int main(int argc, char* argv[])
+{
+       /* Serial */
+       char* device = NULL;
+       struct line_transceiver slave;
+       /* TCP Server */
+       int port = -1;
+       int server_socket = -1;
+       /* For select */
+       struct timeval next_periodic_round;
+       struct timeval period;
+       fd_set read_fds;
+       int max_fd = 0;
+
+       while(1) {
+               int option_index = 0;
+               int c = 0;
+
+               struct option long_options[] = {
+                       {"port", required_argument, 0, 'p'},
+                       {"device", required_argument, 0, 'd'},
+                       {"help", no_argument, 0, 'h'},
+                       {"version", no_argument, 0, 'v'},
+                       {0, 0, 0, 0}
+               };
+
+               c = getopt_long(argc, argv, "p:d:hv", long_options, &option_index);
+
+               /* no more options to parse */
+               if (c == -1) break;
+               switch (c) {
+                       /* a, board address */
+                       case 'p':
+                               port = strtoul(optarg, NULL, 0);
+                               break;
+
+                       /* c, command */
+                       case 'd':
+                               device = optarg;
+                               break;
+
+                       /* v, version */
+                       case 'v':
+                               printf("%s Version %s\n", PROG_NAME, VERSION);
+                               return 0;
+                               break;
+
+                       /* h, help */
+                       case 'h':
+                       default:
+                               help(argv[0]);
+                               return 0;
+               }
+       }
+
+
+       /* Need Serial port and destination file as parameter */
+       if ((port == -1) || (device == NULL)) {
+               printf("Error, need both serial (tty) device and IP port number\n");
+               help(argv[0]);
+               return -1;
+       }
+
+       /* Open tty */
+       memset(&slave, 0, sizeof(struct line_transceiver));
+       slave.fd = serial_setup(device);
+       if (slave.fd < 0) {
+               printf("Unable to open specified serial port %s\n", device);
+               return -2;
+       }
+
+       /* Create UDP server socket */
+       server_socket = create_bound_udp_socket(port, NULL);
+       if (server_socket <= 0) {
+               printf("Unable to open the server UDP socket on port %d\n", port);
+               return -3;
+       }
+
+       /* ************************************************* */
+       /* Initial FD set setup */
+       FD_ZERO(&read_fds);
+       FD_SET(STDIN_FILENO, &read_fds);   /* Add stdin */
+       FD_SET(slave.fd, &read_fds);  /* Serial link */
+       FD_SET(server_socket, &read_fds);  /* New connexions */
+       max_fd = server_socket + 1;  /* No close, this one is the last open, so it's the higest */
+
+       /* Periodic actions setup */
+       gettimeofday(&next_periodic_round, NULL);
+       period.tv_sec = 1; /* "Timer" granularitu is 1 second */
+       period.tv_usec = 0;
+       timeradd(&next_periodic_round, &period, &next_periodic_round);
+
+
+       /* ************************************************* */
+       /* And never stop handling data ! */
+       while (1) {
+               int nb = 0, len = 0, ret = 0;
+               struct timeval timeout;
+               struct timeval now;
+               fd_set tmp_read_fds = read_fds;
+               char buf[BUF_SIZE];
+
+               /* Send periodic requests for temperature */
+               gettimeofday(&now, NULL);
+               
+               if (timercmp(&now, &next_periodic_round, >=)) {
+                       timeout.tv_sec = 0;
+                       timeout.tv_usec = 0;
+               } else {
+                       timersub(&next_periodic_round, &now, &timeout);
+               }
+
+               /* select() call .... be kind to other processes */
+               nb = select(max_fd, &tmp_read_fds, NULL, NULL, &timeout);
+               /* Errors here are bad ones .. exit ?? */
+               if (nb < 0) {
+                       perror ("select failed");
+                       printf("Error: select failed, this is critical.\n");
+                       return -10;
+               }
+               /* In case of timeout ... perform periodic actions if a period has been defined */
+               if (nb == 0) {
+                       int misses = 0;
+                       /* Setup next periodic request date */
+                       timeradd(&next_periodic_round, &period, &next_periodic_round);
+                       gettimeofday(&now, NULL);
+                       /* Did we miss some rounds ? */
+                       while (timercmp(&now, &next_periodic_round, >=)) {
+                               /* Count misses (and catch up) */
+                               timeradd(&next_periodic_round, &period, &next_periodic_round);
+                               misses++;
+                       }
+                       /* FIXME : parse the list of periodic actions for new packets */
+                       continue;
+               }
+
+               /* Data from Ethernet side */
+               if (FD_ISSET(server_socket, &tmp_read_fds)) {
+                       struct sockaddr_in addr;
+                       socklen_t addr_len = sizeof(struct sockaddr_in);
+
+                       /* Receive the new data */
+                       memset(buf, 0, BUF_SIZE);
+                       len = recvfrom(server_socket, buf, BUF_SIZE, 0, (struct sockaddr *)&addr, &addr_len);
+                       if (len > 0) {
+                               handle_udp_request(buf, len, (struct sockaddr *)&addr, addr_len, server_socket, &slave);
+                       } else {
+                               /* Wait .. we received something but nothing came in ? */
+                               perror("UDP receive error");
+                               printf("\nError on UDP packet reception (ret: %d)\n", len);
+                       }
+               }
+
+               /* Read user input if any */
+        if (FD_ISSET(STDIN_FILENO, &tmp_read_fds)) {
+            memset(buf, 0, BUF_SIZE);
+            len = read(STDIN_FILENO, buf, BUF_SIZE);
+                       /* Do not know how to handle it yet, nothing defined. */
+               }
+
+               /* Handle module messages */
+               if (FD_ISSET(slave.fd, &tmp_read_fds)) {
+                       int idx = 0;
+                       memset(buf, 0, BUF_SIZE);
+                       /* Get serial data and try to build a packet */
+                       len = read(slave.fd, buf, BUF_SIZE);
+                       if (len < 0) {
+                               perror("serial read error");
+                               /* FIXME : handle errors */
+                       } else if (len == 0) {
+                               /* Wait .. we received something but nothing came in ? */
+                               printf("\nError, got activity on serial link, but no data ...\n");
+                       }
+                       while (idx < len) {
+                               ret = dtplug_protocol_decode(buf[idx], &slave);
+                               /* Check return code to know if we have a valid packet */
+                               if (ret == -1) {
+                                       /* Anything that's not part of a packet is printed as is (debug output) */
+                                       printf("%c", buf[idx]);
+                               } else if (ret < 0) {
+                                       printf("\nError in received packet. (ret: %d)\n", ret);
+                                       /* FIXME : dump packet for debugging */
+                               } else if (ret == 0) {
+                                       /* Packet is being built */
+                               } else {
+                                       /* Valid packet received */
+                                       handle_module_data(&slave);
+                               }
+                               idx++;
+                       }
+               }
+
+       } /* End of infinite loop */
+
+       close(slave.fd);
+       close(server_socket);
+       return 0;
+}
+
+
diff --git a/host/udpbridge/protocol_bridge b/host/udpbridge/protocol_bridge
new file mode 100755 (executable)
index 0000000..2ee784c
Binary files /dev/null and b/host/udpbridge/protocol_bridge differ
diff --git a/host/udpbridge/questions.pl b/host/udpbridge/questions.pl
new file mode 100755 (executable)
index 0000000..c146d50
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+
+# Author : Nathael Pajani
+# Copyright 2015 Nathael Pajani <nathael.pajani@techno-innov.fr>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+use IO::Socket::INET;
+use Scalar::Util qw(looks_like_number);
+
+
+#######################################
+# Base package parts
+my $server = 'localhost';
+
+
+# UDP Helper
+sub send_to {
+       my ($port, $request, $type) = @_;
+       my $socket = new IO::Socket::INET(Proto => 'udp');
+       my $servaddr = sockaddr_in($port, inet_aton($server));
+       $socket->send($request, 0, $servaddr);
+       unless ($type =~ /G/) {
+               print "Request sent.\n";
+               return;
+       }
+       my $data;
+       $socket->recv($data, 1024, 0);
+       print "Reply: $data\n";
+       $socket->close();
+}
+
+
+#######################################
+my ($port, $req, $type, @args) = @ARGV;
+
+unless (defined($port) && looks_like_number($port) && defined($req) && defined($type)) {
+       print "Usage: $0 port request type [red green blue] ...\n";
+       exit 1;
+}
+
+my $full = "$req$type ";
+my $sep = ' ';
+foreach (@args) {
+       $full .= "$sep";
+       $full .= "$_";
+       #$sep = ', ';
+}
+print "Req to $port : $full\n";
+
+send_to($port, $full, $type);
+
diff --git a/host/udpbridge/serial_utils.c b/host/udpbridge/serial_utils.c
new file mode 100644 (file)
index 0000000..f5cafd3
--- /dev/null
@@ -0,0 +1,59 @@
+/*********************************************************************
+ *
+ *   Serial utility functions
+ *
+ *
+ * Copyright 2012-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *********************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+
+#define SERIAL_BAUD  B115200
+
+
+int serial_setup(char* name)
+{
+       struct termios tio;
+       int fd = -1;
+
+       /* Open serial port */
+       fd = open(name, O_RDWR);
+       if (fd < 0) {
+               perror("Unable to open communication with companion chip");
+               return -1;
+       }
+       /* Setup serial port */
+       memset(&tio, 0, sizeof(tio));
+       tio.c_cflag = CS8 | CREAD | CLOCAL;     /* 8n1, see termios.h for more information */
+       tio.c_cc[VMIN] = 1;
+       tio.c_cc[VTIME] = 5;
+       cfsetospeed(&tio, SERIAL_BAUD);
+       cfsetispeed(&tio, SERIAL_BAUD);
+       tcsetattr(fd, TCSANOW, &tio);
+
+       return fd;
+}
+
diff --git a/host/udpbridge/serial_utils.h b/host/udpbridge/serial_utils.h
new file mode 100644 (file)
index 0000000..ebb1fa0
--- /dev/null
@@ -0,0 +1,32 @@
+/*********************************************************************
+ *
+ *   Serial utility functions
+ *
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *********************************************************************/
+#ifndef SERIAL_UTILS_H
+#define SERIAL_UTILS_H
+
+/* Setup serial comunication, using name if given or saved name if name is NULL
+ *  SERIAL_BAUD  B38400
+ *  c_cflag  (CS7 | PARENB | CREAD | CLOCAL) (7e1)
+ */
+int serial_setup(char* name);
+
+#endif /* SERIAL_UTILS_H */
+
diff --git a/host/udpbridge/sock_utils.c b/host/udpbridge/sock_utils.c
new file mode 100644 (file)
index 0000000..0f3bb7c
--- /dev/null
@@ -0,0 +1,200 @@
+/*****************************************************************************
+ *
+ * socket utils
+ *
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *****************************************************************************/
+
+#include <stdio.h>  /* perror */
+#include <string.h> /* memset */
+#include <unistd.h> /* close, fcntl */
+#include <fcntl.h>  /* F_SETFL and O_NONBLOCK for fcntl */
+
+/* For socket stuff */
+#include <sys/types.h> /* For portability for socket stuff */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h> /* For TCP_NODELAY to disable nagle algorithm */
+
+
+/* This is used to allow quicker sending of small packets on wires */
+static void sox_disable_nagle_algorithm(int socket)
+{
+       const int on = 1;
+       setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof( int ));
+}
+
+/* Creates a TCP socket on the specified "port", bind to port and setup to
+ * accept connections with listen, with up to "nb_pending" pending connections
+ * in the queue.
+ * Returns the socket, after disabling nagle algorithm.
+ * If "our" is not null, it will be used to store the listening socket information.
+ * If "port" is 0, then "our" must contain all requiered information for bind call.
+ */
+int socket_tcp_server(int port, int nb_pending, struct sockaddr_in* our)
+{
+       struct sockaddr_in local;
+       struct sockaddr_in* tmp;
+       int s;
+       int optval = 1;
+
+       if (our == NULL ) {
+               tmp = &local;
+               if (port == 0) {
+                       fprintf(stderr, "No port and no address information provided, won't be able to bind, aborting\n");
+                       return -1;
+               }
+       } else {
+               tmp = our;
+       }
+
+       if (port != 0) {
+               memset(tmp, 0, sizeof(struct sockaddr_in));
+               tmp->sin_family = AF_INET;
+               tmp->sin_addr.s_addr = htonl(INADDR_ANY);
+               tmp->sin_port = htons((short)port);
+       }
+
+       if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+               perror("Unable to create TCP socket");
+               return -1;
+       }
+
+       if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int)) == -1) {
+               perror("Unable to set reuseaddress on socket");
+               goto err_close;
+       }
+       if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&optval, sizeof(int)) == -1) {
+               perror("Unable to set keepalive on socket");
+               goto err_close;
+       }
+
+       if (bind(s, (struct sockaddr *)tmp, sizeof(struct sockaddr_in)) < 0) {
+               perror("Unable to bind TCP port");
+               goto err_close;
+       }
+
+       if (listen(s, nb_pending)) {
+               perror("Unable to listen to TCP port");
+               goto err_close;
+       }
+
+       sox_disable_nagle_algorithm(s);
+       return s;
+
+err_close:
+       close(s);
+       return -1;
+}
+
+
+/* Creates an UDP socket on the specified "port" and bind to port but no specific interface.
+ * Returns the socket.
+ * If "our" is not null, it will be used to store the listening socket information.
+ * "port" must not be 0..
+ */
+int create_bound_udp_socket(int port, struct sockaddr_in* our)
+{
+       struct sockaddr_in local;
+       struct sockaddr_in* tmp;
+       int s;
+       int optval = 1;
+
+       if (port == 0) {
+               return -1;
+       }
+
+       if (our == NULL ) {
+               tmp = &local;
+       } else {
+               tmp = our;
+       }
+
+       memset(tmp, 0, sizeof(struct sockaddr_in));
+       tmp->sin_family = AF_INET;
+       tmp->sin_addr.s_addr = htonl(INADDR_ANY);
+       tmp->sin_port = htons((short)port);
+
+       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+               perror("Unable to create UDP socket");
+               return -1;
+       }
+
+       if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int)) == -1) {
+               perror("Unable to set reuseaddress on socket");
+               goto err_close;
+       }
+
+       if (bind(s, (struct sockaddr *)tmp, sizeof(struct sockaddr_in)) < 0) {
+               perror("Unable to bind UDP port");
+               goto err_close;
+       }
+
+       return s;
+
+err_close:
+       close(s);
+       return -1;
+}
+
+
+
+/* Creates an UDP socket bound to no specific interface.
+ * The kernel choses the port to bind to.
+ * Returns the socket.
+ */
+int create_broadcast_udp_socket(void)
+{
+       struct sockaddr_in local;
+       struct sockaddr_in* tmp = &local;
+       int s;
+       int optval = 1;
+
+       memset(tmp, 0, sizeof(struct sockaddr_in));
+       tmp->sin_family = AF_INET;
+       tmp->sin_addr.s_addr = htonl(INADDR_ANY);
+       tmp->sin_port = 0;
+
+       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+               perror("Unable to create UDP socket");
+               return -1;
+       }
+
+       if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int)) == -1) {
+               perror("Unable to set reuseaddress on socket");
+               goto err_close;
+       }
+
+       if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (void *)&optval, sizeof(int)) == -1) {
+               perror("Unable to set broadcast on socket");
+               goto err_close;
+       }
+
+       if (bind(s, (struct sockaddr *)tmp, sizeof(struct sockaddr_in)) < 0) {
+               perror("Unable to bind UDP port");
+               goto err_close;
+       }
+
+       return s;
+
+err_close:
+       close(s);
+       return -1;
+}
+
+
diff --git a/host/udpbridge/sock_utils.h b/host/udpbridge/sock_utils.h
new file mode 100644 (file)
index 0000000..059cd17
--- /dev/null
@@ -0,0 +1,52 @@
+/*****************************************************************************
+ *
+ * socket utils
+ *
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *****************************************************************************/
+
+#ifndef SOCK_UTILS_H
+#define SOCK_UTILS_H
+
+#include <netinet/in.h> /* struct sockaddr_in */
+
+/* Creates a socket on the specified "port", bind to port and setup to
+   accept connections with listen, with up to "nb_pending" pending connections
+   in the queue.
+   Returns the socket, after disabling nagle algorithm.
+   If "our" is not null, it will be used to store the listening socket information.
+ */
+int socket_tcp_server(int port, int nb_pending, struct sockaddr_in* our);
+
+
+/* Creates an UDP socket on the specified "port" and bind to port but no specific interface.
+ * Returns the socket.
+ * If "our" is not null, it will be used to store the listening socket information.
+ * "port" must not be 0..
+ */
+int create_bound_udp_socket(int port, struct sockaddr_in* our);
+
+
+/* Creates an UDP socket bound to no specific interface.
+ * The kernel choses the port to bind to.
+ * Returns the socket.
+ */
+int create_broadcast_udp_socket(void);
+
+#endif /* SOCK_UTILS_H */
+