changeset 10:36c4cda4b6c4

Add extension support with the PINGPONG extension Add extension support. There are two ways to add extensions: 1. as a shared object which will be loaded with ext=<filename> as a parameter to lwwire. See the lwwire_pingpong.c file for details. 2. By doing basically the same thing as a shared object but linking it into the main binary and calling lwwire_register_extension() appropriately.
author William Astle <lost@l-w.ca>
date Sat, 30 Jul 2016 13:16:39 -0600
parents a11b330771e0
children 38184ef1296d
files docs/extension-pingpong.txt docs/protocol.txt src/Makefile src/lwwire-pingpong.c src/lwwire.c src/lwwire.h
diffstat 6 files changed, 270 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/extension-pingpong.txt	Sat Jul 30 13:16:39 2016 -0600
@@ -0,0 +1,53 @@
+This document describes an extension to the LWWire protocol which provides
+a "PINGPONG" facility. It does not provide a generally useful service.
+However, it does serve to show how a basic extension can be defined. It also
+gives clients an option to enable an extension and use that to determine if
+the server has reset or otherwise got into a mixed up state. For instance,
+the client could negotiate the PINGPONG extension and then periodically send
+a PINGPONG_PING operation. If it doesn't get the appropriate response, it
+can then assume that the server has reset and it should re-initialize its
+drivers.
+
+LWWire Protocol Extension
+=========================
+
+This extension is called PINGPONG and is assigned extension number DF. It
+defines the following operations in its extension space.
+
+00 PINGPONG_PING
+
+This does just what it's name implies. It does nothing except reply to the
+client.
+
+Request:
+
+Octet	Meaning
+-----	-------
+0-2	F3 DF 00
+
+Response:
+
+Octet	Meaning
+-----	-------
+0	42
+
+The response will always be 42.
+
+01 PINGPONG_PONG
+
+This is exactly like PINGPONG_PING. It's provided for humour value.
+
+Request:
+
+Octet	Meaning
+-----	-------
+0-2	F3 DF 01
+
+Response:
+
+Octet	Meaning
+-----	-------
+0	42
+
+The response will always be 42.
+
--- a/docs/protocol.txt	Sat Jul 30 10:35:14 2016 -0600
+++ b/docs/protocol.txt	Sat Jul 30 13:16:39 2016 -0600
@@ -425,5 +425,6 @@
 
 00	VPORT
 01	PACKET
+DF	PINGPONG
 E0-EF	reserved for future extension code expansion
 F0-FF	reserved for private extensions and will never be assigned
--- a/src/Makefile	Sat Jul 30 10:35:14 2016 -0600
+++ b/src/Makefile	Sat Jul 30 13:16:39 2016 -0600
@@ -1,3 +1,7 @@
 CFLAGS += -Wall
+LDLIBS += -ldl -rdynamic
 
-all: lwwire lwwire-serial
+all: lwwire lwwire-serial lwwire-pingpong.so
+
+%.so: %.c
+	$(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -shared -o $@ $<
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lwwire-pingpong.c	Sat Jul 30 13:16:39 2016 -0600
@@ -0,0 +1,62 @@
+/*
+PINGPONG extension for lwwire
+*/
+
+#include <stdio.h>
+#include "lwwire.h"
+
+/*
+
+NOTE: the __attribute__((used)) tags are there to prevent the functions
+from being optimized out as would often be the case with functions declared
+static. This could be avoided by removing the static declarations. However,
+by declaring everything static except for the lwwire_register() function,
+we avoid polluting the global namespace and possibly having conflicts with
+other extensions.
+*/
+
+
+// this is called to handle an operation for this extension
+// it should return nonzero if the operation code (op) is not
+// supported. Otherwise, it should implement the operation codes.
+__attribute__((used)) static int pingpong_handler(int op)
+{
+    unsigned char buf[1];
+    
+    if (op > 1 || op < 0)
+        return -1;
+    buf[1] = 0x42;
+    lwwire_write(buf, 1);
+    return 0;
+}
+
+// this will be called when the extension is enabled
+// return nonzero if enabling the extension cannot happen for some reason
+__attribute__((used)) static int pingpong_enable(void)
+{
+    return 0;
+}
+
+// this will be called when an extension is disabled
+// if this extension cannot be disabled after it is enabled, it should return
+// nonzero; otherwise, this MUST succeed
+__attribute__((used)) static int pingpong_disable(void)
+{
+    return 0;
+}
+
+// this call should do anything needed to disable the extension
+// and anything special that might be needed on a protocol reset
+// it will only be called if the extension is enabled when the
+// reset occurs. Even if the extension isn't disablable, this call
+// MUST disable it and reset things as if the extension was never
+// enabled.
+__attribute__((used)) static void pingpong_reset(void)
+{
+}
+
+int lwwire_register(void)
+{
+    fprintf(stderr, "Registering PINGPONG extension\n");
+    return lwwire_register_extension(0xdf, pingpong_handler, pingpong_enable, pingpong_disable, pingpong_reset);
+}
--- a/src/lwwire.c	Sat Jul 30 10:35:14 2016 -0600
+++ b/src/lwwire.c	Sat Jul 30 13:16:39 2016 -0600
@@ -39,6 +39,7 @@
 // for nanosleep
 #define _POSIX_C_SOURCE 199309L
 
+#include <dlfcn.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
@@ -61,6 +62,17 @@
 	int isconst;
 };
 
+struct lwwire_extension_data
+{
+	int enabled;
+	int (*handler)(int op);	// handle op, return -1 if not supported
+	int (*disable)(void);	// disable extension negotiated
+	int (*enable)(void);	// enable extension negotiated (return -1 if failed to init)
+	void (*reset)(void);	// on server reset opcode; extension will be disabled automatically here
+};
+
+struct lwwire_extension_data lwwire_extension_list[256];
+
 struct lwwire_driveinfo drivedata[256];
 
 void lwwire_protoerror(void);
@@ -74,7 +86,7 @@
 int lwwire_save_sector(int dn, int lsn, void *);
 int nonblock(int);
 int lwwire_drive_readononly(int dn);
-
+int lwwire_register_extension(int, int (*handler)(int), int (*enable)(void), int (*disable)(void), void (*reset)(void));
 void lwwire_proto_read(void);
 void lwwire_proto_write(void);
 void lwwire_proto_readex(void);
@@ -103,10 +115,43 @@
 	}
 
 	memset(&drivedata, 0, sizeof(drivedata));
+	memset(lwwire_extension_list, 0, sizeof(struct lwwire_extension_data) * 256);
 
+	// call lwwire_register_extension() for each extension you define
+	// statically. Add those calls here, or calls to the equivalent
+	// of the "lwwire_register" function from a DSO type extension.
+	
 	for (i = 1; i < argc; i++)
 	{
-		if (strncmp("drive=", argv[i], 6) == 0)
+		if (strncmp("ext=", argv[i], 4) == 0)
+		{
+			// SO name is at argv[i]+4
+			void *dlhandle;
+			void *initfn;
+			int (*rinitfn)(void);
+			
+			dlhandle = dlopen(argv[i] + 4, RTLD_NOW | RTLD_LOCAL);
+			if (!dlhandle)
+			{
+				fprintf(stderr, "Cannot load extension %s: %s\n", argv[i] + 4, dlerror());
+				continue;
+			}
+			initfn = dlsym(dlhandle, "lwwire_register");
+			if (!initfn)
+			{
+				dlclose(dlhandle);
+				fprintf(stderr, "Extension '%s' is not valid.\n", argv[i] + 4);
+				continue;
+			}
+			rinitfn = initfn;
+			if ((*rinitfn)() != 0)
+			{
+				dlclose(dlhandle);
+				fprintf(stderr, "Initialization of extension '%s' failed.\n", argv[i] + 4);
+				continue;
+			}
+		}
+		else if (strncmp("drive=", argv[i], 6) == 0)
 		{
 			int dn=0;
 			int isconst = 0;
@@ -331,31 +376,68 @@
 void lwwire_proto_requestextension(void)
 {
 	unsigned char buf[1];
-	
+	int ext;
+		
 	if (lwwire_read(buf, 1) < 0)
 		return;
-	// NAK the request
-	buf[0] = 0x55;
+	ext = buf[0];
+	buf[0] = 0x55;	// default to NAK
+	if (lwwire_extension_list[ext].enable)
+	{
+		if ((*(lwwire_extension_list[ext].enable))() == 0)
+		{
+			// enable succeeded; enable it
+			buf[0] = 0x42;
+			lwwire_extension_list[ext].enabled = 1;
+		}
+	}
 	lwwire_write(buf, 1);
 }
 
 void lwwire_proto_disableextension(void)
 {
 	unsigned char buf[1];
+	int ext;
 	
 	if (lwwire_read(buf, 1) < 0)
 		return;
-	// ACK disabling any unsupported extensions
-	buf[0] = 0x42;
+	ext = buf[0];
+	
+	buf[0] = 0x42;	// default to ACK
+	if (lwwire_extension_list[ext].disable)
+	{
+		if ((*(lwwire_extension_list[ext].disable))() != 0)
+		{
+			// extension says it can't be disabled; NAK response
+			buf[0] = 0x55;
+		}
+	}
+	if (buf[0] == 0x42)
+		lwwire_extension_list[ext].enabled = 0;
 	lwwire_write(buf, 1);
 }
 
 void lwwire_proto_extensionop(void)
 {
-	unsigned char buf[1];
-	if (lwwire_read(buf, 1) < 0)
+	unsigned char buf[2];
+	int ext;
+	int op;
+	
+	if (lwwire_read(buf, 2) < 0)
 		return;
-	// we don't currently support any extensions so treat as unknown
+	ext = buf[0];
+	op = buf[1];
+	if (lwwire_extension_list[ext].enabled == 1)
+	{
+		if (lwwire_extension_list[ext].handler)
+		{
+			if ((*(lwwire_extension_list[ext].handler))(op) == 0)
+				return;
+		}
+		lwwire_protoerror();
+		return;
+	}
+	// extension not enabled; do a protocol error
 	lwwire_protoerror();
 }
 
@@ -675,4 +757,34 @@
 */
 void lwwire_reset(void)
 {
+	int i;
+	
+	// tell all extensions to reset and disable them
+	for (i = 0; i < 256; i++)
+	{
+		if (lwwire_extension_list[i].handler)
+		{
+			(*(lwwire_extension_list[i].reset))();
+		}
+		lwwire_extension_list[i].enabled = 0;
+	}
 }
+
+/*
+Register an extension. This will replace the registration if the same
+extension number is registered twice. Returns -1 on parameter error.
+ALL function arguments are required. num must be between 0 and 255 inclusive
+currently.  Returns 0 if the extension is registered.
+*/
+int lwwire_register_extension(int num, int (*handler)(int), int (*enable)(void), int (*disable)(void), void (*reset)(void))
+{
+	if (!handler || !enable || !disable || !reset)
+		return -1;
+	if (num < 0 || num > 255)
+		return -1;
+	lwwire_extension_list[num].handler = handler;
+	lwwire_extension_list[num].enable = enable;
+	lwwire_extension_list[num].disable = disable;
+	lwwire_extension_list[num].reset = reset;
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lwwire.h	Sat Jul 30 13:16:39 2016 -0600
@@ -0,0 +1,27 @@
+/*
+lwwire.h
+
+*/
+#ifndef lwwire_h_seen___
+#define lwwire_h_seen___ 
+
+void lwwire_protoerror(void);
+int lwwire_readdata(void *, int, int);
+int lwwire_writedata(void *, int);
+void lwwire_write(void *, int);
+int lwwire_read(void *, int);
+int lwwire_read2(void *, int, int);
+void lwwire_reset(void);
+int lwwire_fetch_sector(int dn, int lsn, void *);
+int lwwire_save_sector(int dn, int lsn, void *);
+int nonblock(int);
+int lwwire_drive_readononly(int dn);
+int lwwire_register_extension(int, int (*handler)(int), int (*enable)(void), int (*disable)(void), void (*reset)(void));
+void lwwire_proto_read(void);
+void lwwire_proto_write(void);
+void lwwire_proto_readex(void);
+void lwwire_proto_requestextension(void);
+void lwwire_proto_disableextension(void);
+void lwwire_proto_extensionop(void);
+
+#endif // lwwire_h_seen___