summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/devices/kermit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mess/devices/kermit.c')
-rw-r--r--src/mess/devices/kermit.c710
1 files changed, 710 insertions, 0 deletions
diff --git a/src/mess/devices/kermit.c b/src/mess/devices/kermit.c
new file mode 100644
index 00000000000..839e435e952
--- /dev/null
+++ b/src/mess/devices/kermit.c
@@ -0,0 +1,710 @@
+/* Kermit protocol implementation.
+
+ Transfer between an emulated machine and an image using the kermit protocol.
+
+ Used in the HP48 S/SX/G/GX emulation.
+
+ Based on the 'KERMIT PROTOCOL MANUAL', Sixth Edition, by Frank da Cruz,
+ Columbia University Center for Computing Activities, New York, June 1986.
+ Available at: http://www-vs.informatik.uni-ulm.de/teach/ws05/rn1/Kermit%20Protocol.pdf
+
+ Used in the HP48 S/SX/G/GX emulation.
+ For HP48 specific kermit notes, see http://www.columbia.edu/kermit/hp48.html
+
+ Only the basic protocol is implemented.
+ The following features are NOT supported:
+ - 8-bit quoting (QBIN)
+ - 2- or 3-byte checksums (only 1-byte supported)
+ - repeat count
+ - file attribute packets
+ - sliding window
+ - extended length packets
+ - server mode
+
+ Author: Antoine Mine'
+ Date: 29/03/2008
+ */
+
+
+#include "emu.h"
+#include "kermit.h"
+
+
+/* debugging */
+#define VERBOSE 0
+
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+/* protocol bytes */
+#define KERMIT_SOH 0x01 /* (default) start of header */
+
+/* packet types */
+#define KERMIT_DATA 'D' /* data packet */
+#define KERMIT_ACK 'Y' /* acknowledge */
+#define KERMIT_NAK 'N' /* negative acknowledge */
+#define KERMIT_SEND 'S' /* send initiate (exchange parameters) */
+#define KERMIT_EOT 'B' /* break transmission */
+#define KERMIT_FILE 'F' /* file header */
+#define KERMIT_EOF 'Z' /* end of file */
+#define KERMIT_ERR 'E' /* error */
+
+#define KERMIT_QCTL '#' /* default escape character */
+#define KERMIT_EOL 15 /* default terminator: carriage-return */
+#define KERMIT_MAXL 80 /* default maximum packet length */
+
+/* protocol state, to drive action */
+#define KERMIT_IDLE 0 /* nothing to do */
+#define KERMIT_RESET 1 /* reset after sending the last packet */
+#define KERMIT_RECEIVE 2 /* receiving */
+#define KERMIT_SEND_DATA 3 /* sending data */
+#define KERMIT_SEND_EOF 4 /* sending EOF */
+#define KERMIT_SEND_EOT 5 /* sendinf EOT */
+
+/* packet retry parameters */
+#define KERMIT_MAX_RETRIES 5
+#define KERMIT_RETRY_DELAY attotime::from_seconds( 10 )
+
+
+/* packet format is:
+
+ <mark> <len> <seq> <type> <data0> ... <datan> <check>
+
+ - <mark> KERMIT_SOH
+ - <len> number of bytes after <len> (max=94), plus 32
+ - <seq> sequence number, modulo 63, plus 32
+ - <type> one of KERMIT_ packet types
+ - <data> payload, with control characters escaped with KERMIT_QCTL
+ - <check> checksum, from <len> to <datan>, excluding <mark>
+
+ the packet may be followed by arbitrary (non KERMIT_SOH) data, not counted
+ in <len>, <check>, and not interpreted
+ there is generally at least one KERMIT_EOL
+*/
+
+typedef struct {
+
+ UINT8 pin[1024]; /* packet received */
+ UINT8 pout[1024]; /* packet sent */
+ UINT32 seq; /* sequence number, starting at 0 */
+ UINT16 posin; /* position in pin */
+ UINT16 posout; /* position in pout */
+ UINT16 nbout; /* size of pout */
+
+ UINT8 state; /* current protocol state */
+ UINT8 retries; /* packet retries remaining */
+
+ /* (relevant) configuration information, sent in S and D packet */
+ UINT8 maxl; /* max packet length (value of <len>) */
+ UINT8 npad; /* number of padding characters before packet */
+ UINT8 padc; /* padding character */
+ UINT8 eol; /* character to add after packet */
+ UINT8 qctl; /* escape character */
+
+ emu_timer* resend; /* auto-resend packet */
+
+ device_image_interface* image; /* underlying image */
+
+ running_machine *machine;
+ kermit_config* conf;
+
+} kermit;
+
+
+INLINE kermit *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == KERMIT);
+
+ return (kermit*)downcast<legacy_device_base *>(device)->token();
+}
+
+static int kermit_is_char( UINT8 data )
+{
+ return data >= 32 && data <= 126;
+}
+
+
+static UINT8 kermit_tochar( UINT8 data )
+{
+ if ( data > 94 )
+ {
+ logerror( "kermit: tochar: %i not in the range 0-94\n", data );
+ }
+ return data + 32;
+}
+
+static UINT8 kermit_unchar( UINT8 data )
+{
+ if ( data < 32 || data > 126 )
+ {
+ logerror( "kermit: unchar: %i not in the range 32-126\n", data );
+ }
+ return data - 32;
+}
+
+#define kermit_ctl(x) ((x) ^ 0x40)
+
+static int kermit_is_ctl( UINT8 x )
+{
+ x &= 0x7f;
+ return (x < 32) || (x == 127);
+}
+
+static UINT8 kermit_checksum( UINT8* packet )
+{
+ int i, len = kermit_unchar( packet[1] );
+ UINT8 sum = 0;
+ for ( i = 0; i < len; i++ )
+ sum += packet[ i + 1 ];
+ sum = kermit_tochar( (sum + (sum >> 6)) & 63 );
+ return sum;
+}
+
+/* starts sending a new packet */
+static void kermit_start_sending( kermit* state )
+{
+ int i;
+
+ for (i=1;i<state->nbout;i++)
+ if (kermit_is_ctl(state->pout[i]))
+ logerror( "send: not char %i at pos %i\n", state->pout[i], i );
+
+ if ( state->nbout <= 0 ) return;
+
+ /* prepend padding and append eol */
+ if ( state->npad > 0 )
+ {
+ memmove( state->pout + state->npad, state->pout, state->nbout );
+ memset( state->pout, state->padc, state->npad );
+ state->nbout += state->npad;
+ }
+ state->pout[ state->nbout ] = state->eol;
+ state->nbout++;
+
+ if ( state->conf && state->conf->send )
+ {
+ state->conf->send( *state->machine, state->pout[ 0 ] );
+ }
+ state->posout = 1;
+
+ state->retries = KERMIT_MAX_RETRIES;
+ state->resend->adjust( KERMIT_RETRY_DELAY, 0, KERMIT_RETRY_DELAY );
+}
+
+static void kermit_reset( kermit* state );
+
+static void kermit_resend( kermit* state )
+{
+ if ( state->conf && state->conf->send )
+ {
+ /* retry */
+ if ( state->nbout == 0 ) return;
+ if ( state->retries <= 0 )
+ {
+ kermit_reset( state );
+ return;
+ }
+
+ state->conf->send( *state->machine, state->pout[ 0 ] );
+
+ state->posout = 1;
+ state->retries--;
+ LOG(( "kermit: resend packet (%i tries left)\n", state->retries ));
+ }
+}
+
+/* resend packet periodically */
+static TIMER_CALLBACK( kermit_resend_cb )
+{
+ kermit* state = (kermit*) ptr;
+ if ( state->posout >= state->nbout)
+ {
+ kermit_resend( state );
+ }
+}
+
+/* fetches image data and wrap it into a kermit data packet */
+static void kermit_send_data_packet( kermit* state )
+{
+ int len;
+ LOG(( "kermit: send data packet\n" ));
+ for ( len = 2; len < state->maxl-2; len++ )
+ {
+ UINT8 c;
+ if ( state->image->image_feof() ) break;
+ c = state->image->fgetc();
+ if ( kermit_is_ctl( c ) )
+ {
+ /* escape control char */
+ state->pout[ 2 + len ] = state->qctl;
+ len++;
+ state->pout[ 2 + len ] = kermit_ctl( c );
+ }
+ else if ( (c & 0x7f) == state->qctl )
+ {
+ /* escape escape char */
+ state->pout[ 2 + len ] = state->qctl;
+ len++;
+ state->pout[ 2 + len ] = c;
+ }
+ else
+ {
+ state->pout[ 2 + len ] = c;
+ }
+ }
+ state->pout[ 0 ] = KERMIT_SOH;
+ state->pout[ 1 ] = kermit_tochar( len + 1 );
+ state->pout[ 2 ] = kermit_tochar( state->seq & 63 );
+ state->pout[ 3 ] = KERMIT_DATA;
+ state->pout[ 2 + len ] = kermit_checksum( state->pout );
+ state->nbout = len + 3;
+ kermit_start_sending( state );
+}
+
+/* write data from packet to the image */
+static void kermit_write_data_packet( kermit* state )
+{
+ int i, len = kermit_unchar( state->pin[1] );
+ LOG(( "kermit: received data pack, len=%i\n", len ));
+ for ( i = 4; i <= len; i++ )
+ {
+ UINT8 c = state->pin[i];
+ if ( (c /*& 0x7f*/) == state->qctl )
+ {
+ /* unescape */
+ i++;
+ c = state->pin[i];
+ if ( kermit_is_ctl( kermit_ctl( c ) ) )
+ {
+ c = kermit_ctl( c );
+ }
+ }
+ state->image->fwrite(&c, 1 );
+ }
+}
+
+/* validate received packet and acquire seq number, <0 if error */
+static int kermit_validate_packet( kermit* state )
+{
+ int len;
+ if ( state->pin[ 0 ] != KERMIT_SOH ) return -1;
+ if ( !kermit_is_char( state->pin[1] ) ) return -1;
+ if ( !kermit_is_char( state->pin[2] ) ) return -1;
+ len = kermit_unchar( state->pin[1] );
+ if ( state->pin[ 1 + len ] != kermit_checksum( state->pin ) ) return -1;
+ return 0;
+}
+
+/* interpret configuration packet, <0 if error */
+static int kermit_get_conf( kermit* state )
+{
+ int len = kermit_unchar( state->pin[1] );
+ if ( len >= 4 ) state->maxl = kermit_unchar( state->pin[ 4 ] );
+ if ( len >= 6 ) state->npad = kermit_unchar( state->pin[ 6 ] );
+ if ( len >= 7 ) state->padc = kermit_ctl( state->pin[ 7 ] );
+ if ( len >= 8 ) state->eol = kermit_unchar( state->pin[ 8 ] );
+ if ( len >= 9 ) state->qctl = state->pin[ 9 ];
+
+ LOG(( "kermit: get conf: len=%i, maxl=%i, npad=%i, padc=%i, eol=%i, qctl=%i\n",
+ len, state->maxl, state->npad, state->padc, state->eol, state->qctl ));
+
+ /* validation */
+ if ( state->maxl < 10 || state->maxl > 94 ) return -1;
+ return 0;
+}
+
+/* create a configuration packet, with header h */
+static void kermit_send_conf_packet( kermit* state, UINT8 h )
+{
+ LOG(( "kermit: send conf packet of type %c\n", h ));
+ state->seq = 0;
+ state->pout[ 0 ] = KERMIT_SOH;
+ state->pout[ 1 ] = kermit_tochar( 13 );
+ state->pout[ 2 ] = kermit_tochar( state->seq & 63 );
+ state->pout[ 3 ] = h;
+ state->pout[ 4 ] = kermit_tochar( state->maxl ); /* maxl */
+ state->pout[ 5 ] = kermit_tochar( 1 ); /* time: 1 sec */
+ state->pout[ 6 ] = kermit_tochar( state->npad ); /* npad */
+ state->pout[ 7 ] = kermit_ctl( state->padc ); /* padc */
+ state->pout[ 8 ] = kermit_tochar( state->eol ); /* eol */
+ state->pout[ 9 ] = state->qctl; /* qtcl */
+ state->pout[ 10 ] = 'N'; /* qbin: no 8-bit quoting */
+ state->pout[ 11 ] = '1'; /* chkt: single character checksum */
+ state->pout[ 12 ] = ' '; /* rept: no repeat count */
+ state->pout[ 13 ] = kermit_tochar( 0 ); /* capas: no capabilities */
+ state->pout[ 14 ] = kermit_checksum( state->pout );
+ state->nbout = 15;
+ kermit_start_sending( state );
+}
+
+/* sends a packet with no data */
+static void kermit_send_simple_packet( kermit* state, UINT8 h )
+{
+ LOG(( "kermit: send empty packet of type %c, seq=%i\n", h, state->seq & 63 ));
+ state->pout[ 0 ] = KERMIT_SOH;
+ state->pout[ 1 ] = kermit_tochar( 3 );
+ state->pout[ 2 ] = kermit_tochar( state->seq & 63 );
+ state->pout[ 3 ] = h;
+ state->pout[ 4 ] = kermit_checksum( state->pout );
+ state->nbout = 5;
+ kermit_start_sending( state );
+}
+
+/* sends a packet with string data */
+static void kermit_send_string_packet( kermit* state, UINT8 h, const char* data )
+{
+ int i, len;
+ LOG(( "kermit: send string packet of type %c, data=%s, seq=%i\n", h, data, state->seq & 63 ));
+ for ( len = i = 0; (len < state->maxl - 5) && data[i]; i++ )
+ {
+ char c = data[i];
+ if ( kermit_is_char( c ) && ( c != '_' ) && ( c != ' ' ) )
+ {
+ state->pout[ 4 + len ] = c;
+ len++;
+ }
+ }
+ state->pout[ 0 ] = KERMIT_SOH;
+ state->pout[ 1 ] = kermit_tochar( len + 3 );
+ state->pout[ 2 ] = kermit_tochar( state->seq & 63 );
+ state->pout[ 3 ] = h;
+ state->pout[ 4 + len ] = kermit_checksum( state->pout );
+ state->nbout = len + 5;
+ kermit_start_sending( state );
+}
+
+/* extract the string contained in received packet */
+static char* kermit_string_in_packet( kermit* state )
+{
+ int len = kermit_unchar( state->pin[1] );
+ state->pin[ len + 1 ] = 0;
+ return (char*) state->pin + 4;
+}
+
+static void kermit_reset( kermit* state )
+{
+ /* default config */
+ state->maxl = KERMIT_MAXL;
+ state->npad = 0;
+ state->padc = 0;
+ state->eol = KERMIT_EOL;
+ state->qctl = KERMIT_QCTL;
+
+ state->retries = KERMIT_MAX_RETRIES;
+ state->posin = 0;
+ state->posout = 0;
+ state->nbout = 0;
+ state->state = KERMIT_IDLE;
+ state->seq = 0;
+
+ if ( state->image )
+ {
+ state->image->fseek( SEEK_SET, 0 );
+ }
+
+ state->resend->adjust( attotime::never, 0, attotime::never );
+}
+
+
+/* emulated machine sends a byte to the outside (us) */
+void kermit_receive_byte( device_t *device, UINT8 data )
+{
+ kermit* state = get_safe_token(device);
+
+ LOG(( "get %i %2x %c (%i)\n", data, data, data, state->posin ));
+
+ /* get SOH */
+ if ( (data == KERMIT_SOH) && (state->posin <= 1) )
+ {
+ state->pin[ 0 ] = data;
+ state->posin = 1;
+ }
+
+ /* get packet contents */
+ else if ( state->posin > 0 )
+ {
+ if ( state->posin >= sizeof( state->pout ) )
+ {
+ LOG(( "kermit: too many bytes received\n" ));
+ state->posin = 0;
+ kermit_send_simple_packet( state, KERMIT_NAK );
+ return;
+ }
+
+ if (kermit_is_ctl(data))
+ logerror( "received: not char %i at pos %i\n", data, state->posin );
+
+ state->pin[ state->posin ] = data;
+ state->posin++;
+
+ if ( state->posin > 5 )
+ {
+ int len, seq, typ;
+ if ( !kermit_is_char( state->pin[1] ) ||
+ (kermit_unchar( state->pin[1] ) < 3) ||
+ (kermit_unchar( state->pin[1] ) > state->maxl) )
+ {
+ LOG(( "kermit: invalid packet size %i-32, not in 3-%i\n", state->pin[1], state->maxl ));
+ kermit_resend( state );
+ return;
+ }
+
+ len = kermit_unchar( state->pin[1] );
+
+ if ( state->posin >= len + 2 )
+ {
+ /* got packet! */
+ state->posin = 0;
+ if ( kermit_validate_packet( state ) )
+ {
+ LOG(( "kermit: invalid packet\n" ));
+ kermit_resend( state );
+ return;
+ }
+
+ typ = state->pin[3];
+ seq = kermit_unchar( state->pin[2] );
+
+ LOG(( "kermit: received packet type=%c, seq=%i, len=%i\n", typ, seq, len ));
+
+ if ( !state->image )
+ {
+ kermit_send_string_packet( state, KERMIT_ERR, "NO IMAGE" );
+ return;
+ }
+
+
+ switch ( typ )
+ {
+
+ /* receiving */
+
+ case KERMIT_SEND:
+ kermit_get_conf( state );
+ if ( state->maxl >= KERMIT_MAXL )
+ {
+ state->maxl = KERMIT_MAXL;
+ }
+ kermit_send_conf_packet( state, KERMIT_ACK );
+ state->state = KERMIT_RECEIVE;
+ break;
+
+ case KERMIT_FILE:
+ LOG(( "kermit: got file name '%s'\n", kermit_string_in_packet( state ) ));
+ state->seq = seq;
+ kermit_send_simple_packet( state, KERMIT_ACK );
+ break;
+
+ case KERMIT_DATA:
+ case KERMIT_EOF:
+ if ( seq == ((state->seq + 1) & 63) )
+ {
+ /* next packet */
+ if ( typ == KERMIT_DATA )
+ {
+ kermit_write_data_packet( state );
+ }
+ state->seq = seq;
+ kermit_send_simple_packet( state, KERMIT_ACK );
+ }
+ else if ( seq == (state->seq & 63) )
+ {
+ /* same packet */
+ kermit_send_simple_packet( state, KERMIT_ACK );
+ }
+ else
+ {
+ /* unexpected sequence number */
+ LOG(( "kermit: got seq=%i, expected %i\n", seq, (state->seq + 1) & 63 ));
+ state->seq++;
+ kermit_send_simple_packet( state, KERMIT_NAK );
+ state->seq--;
+ }
+ break;
+
+ case KERMIT_EOT:
+ /* send ack and reset */
+ state->seq = seq;
+ kermit_send_simple_packet( state, KERMIT_ACK );
+ state->state = KERMIT_RESET;
+ break;
+
+
+ /* sending */
+
+ case KERMIT_NAK:
+ case KERMIT_ACK:
+
+ if ( (typ == KERMIT_NAK) && (seq == 0) && (state->state == KERMIT_IDLE) )
+ {
+ /* transfer start */
+ kermit_send_conf_packet( state, KERMIT_SEND );
+ break;
+ }
+
+ if ( ((seq == (state->seq & 63)) && (typ == KERMIT_ACK)) ||
+ ((seq == ((state->seq + 1) & 63)) && (typ == KERMIT_NAK)) )
+ {
+ /* send next packet */
+
+ state->seq = state->seq + 1;
+
+ switch ( state->state )
+ {
+ case KERMIT_IDLE:
+ /* get conf & send file name */
+ kermit_get_conf( state );
+ kermit_send_string_packet( state, KERMIT_FILE, state->image->basename() );
+ state->state = KERMIT_SEND_DATA;
+ break;
+
+ case KERMIT_SEND_DATA:
+ /* send next data packet or EOF */
+ if ( state->image->image_feof() )
+ {
+ kermit_send_simple_packet( state, KERMIT_EOF );
+ state->state = KERMIT_SEND_EOF;
+ }
+ else
+ {
+ kermit_send_data_packet( state );
+ }
+ break;
+
+ case KERMIT_SEND_EOF:
+ /* send EOT */
+ kermit_send_simple_packet( state, KERMIT_EOT );
+ state->state = KERMIT_SEND_EOT;
+ break;
+
+ case KERMIT_SEND_EOT:
+ /* reset */
+ kermit_reset( state );
+ state->state = KERMIT_RESET;
+ break;
+
+ }
+ }
+
+ else if ( (seq == (state->seq & 63)) && (typ == KERMIT_NAK) )
+ {
+ /* resend last packet */
+ kermit_resend( state );
+ }
+ else
+ {
+ /* bad */
+ LOG(( "kermit: expected to send %i, got seq=%i\n", (state->seq + 1) & 63, seq ));
+ kermit_reset( state );
+ }
+ break;
+
+ /* errors */
+
+ case KERMIT_ERR:
+ LOG(( "kermit: got error '%s'\n", kermit_string_in_packet( state ) ));
+ state->seq = seq;
+ kermit_reset( state );
+ break;
+
+ default:
+ LOG(( "kermit: ignoring packet type %c\n", state->pin[ 3 ] ));
+ /* no abort, just ignoring */
+ }
+ }
+ }
+ }
+}
+
+void kermit_byte_transmitted( device_t *device )
+{
+ kermit* state = get_safe_token(device);
+
+ LOG(( "transmitted %i %2x %c, %i / %i\n", state->pout[ state->posout-1 ], state->pout[ state->posout-1 ], state->pout[ state->posout-1 ], state->posout - 1, state->nbout - 1 ));
+
+ /* at end of packet */
+ if ( state->posout >= state->nbout )
+ {
+ if ( state->state == KERMIT_RESET )
+ {
+ kermit_reset( state );
+ }
+ return;
+ }
+
+ if ( state->conf && state->conf->send )
+ {
+ state->conf->send(*state->machine, state->pout[ state->posout ] );
+ }
+ state->posout++;
+}
+
+static DEVICE_START( kermit )
+{
+ kermit* state = get_safe_token(device);
+ LOG(( "kermit: start\n" ));
+ state->image = NULL;
+ state->conf = (kermit_config*) device->static_config();
+ state->machine = &device->machine();
+ state->resend = device->machine().scheduler().timer_alloc(FUNC(kermit_resend_cb), state );
+ kermit_reset( state );
+}
+
+static DEVICE_RESET( kermit )
+{
+ kermit* state = get_safe_token(device);
+ LOG(( "kermit: reset\n" ));
+ kermit_reset( state );
+}
+
+static DEVICE_IMAGE_LOAD( kermit )
+{
+ kermit* state = get_safe_token(&image.device());
+ LOG(( "kermit: image load\n" ));
+ state->image = &image;
+ kermit_reset( state );
+ return IMAGE_INIT_PASS;
+}
+
+static DEVICE_IMAGE_CREATE( kermit )
+{
+ kermit* state = get_safe_token(&image.device());
+ LOG(( "kermit: image create\n" ));
+ state->image = &image;
+ kermit_reset( state );
+ return IMAGE_INIT_PASS;
+}
+
+static DEVICE_IMAGE_UNLOAD( kermit )
+{
+ kermit* state = get_safe_token(&image.device());
+ LOG(( "kermit: image unload\n" ));
+ state->image = NULL;
+ kermit_reset( state );
+}
+
+DEVICE_GET_INFO( kermit )
+
+{
+ switch ( state ) {
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof( kermit ); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+ case DEVINFO_INT_IMAGE_TYPE: info->i = IO_SERIAL; break;
+ case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break;
+ case DEVINFO_INT_IMAGE_CREATABLE: info->i = 1; break;
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( kermit ); break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( kermit ); break;
+ case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME( kermit ); break;
+ case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME( kermit ); break;
+ case DEVINFO_FCT_IMAGE_CREATE: info->f = (genf *) DEVICE_IMAGE_CREATE_NAME( kermit ); break;
+ case DEVINFO_STR_IMAGE_BRIEF_INSTANCE_NAME: strcpy(info->s, "k"); break;
+ case DEVINFO_STR_IMAGE_INSTANCE_NAME:
+ case DEVINFO_STR_NAME: strcpy(info->s, "Kermit"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Serial protocol"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, ""); break;
+ }
+}
+
+DEFINE_LEGACY_IMAGE_DEVICE(KERMIT, kermit);