# HG changeset patch # User William Astle # Date 1469906199 21600 # Node ID 36c4cda4b6c47e54c844223ad069cd7b37ecf8ca # Parent a11b330771e0fcdabf4b27efdb54d1d5e259a60e 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= 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. diff -r a11b330771e0 -r 36c4cda4b6c4 docs/extension-pingpong.txt --- /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. + diff -r a11b330771e0 -r 36c4cda4b6c4 docs/protocol.txt --- 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 diff -r a11b330771e0 -r 36c4cda4b6c4 src/Makefile --- 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 $@ $< diff -r a11b330771e0 -r 36c4cda4b6c4 src/lwwire-pingpong.c --- /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 +#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); +} diff -r a11b330771e0 -r 36c4cda4b6c4 src/lwwire.c --- 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 #include #include #include @@ -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; +} diff -r a11b330771e0 -r 36c4cda4b6c4 src/lwwire.h --- /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___