Sie sind auf Seite 1von 24

The monitor now includes a flash file system, serial port communications, and Ethernet

connectivity. The next natural step is to provide a mechanism that allows file transfer to and
from the target. The file transfer protocols presented in this chapter are not new but remain
popular because of their simplicity. The monitor uses Xmodem and TFTP for RS-232 and
Ethernet implementations respectively. Data can be transferred as raw memory or as a file for
both protocols. The MicroMonitor package includes code for a PC-based TFTP client/server and
PC-based Xmodem transfer. I will limit this discussion to the target side. Complete
implementation details are beyond the scope of this discussion. (Refer to the CD for a full source
listing.)

Both Xmodem and TFTP are “lock step” protocols, meaning that when one end sends some data,
it doesn’t send any new data until it receives acknowledgment from the other end indicating that
the data was received. Although this design slows things down a bit, especially with respect to
TFTP, the lock step approach offers the benefit of simplicity.

Xmodem
I think Xmodem has been around since dirt. Xmodem is one of those things that just won’t go
away, which is good because Xmodem can be found just about anywhere and it’s not too
difficult to implement. Xmodem has spawned many variants, including derivatives that support
CRC versus checksum, 128- versus 1024-byte packets, and multiple files per transfer. I won’t
discuss these variants here, because my focus is MicroMonitor’s Xmodem support — not the
larger protocol. Keep in mind that MicroMonitor assumes that the target is communicating with
the host via an error-free connection, so consequently the Xmodem implementation doesn’t need
to be very robust. The point of the discussion is to give you a feel for the Xmodem protocol as it
is used in MicroMonitor and to let you experience a basic Xmodem implementation for a very
“blue sky” environment.

This implementation of Xmodem is primarily based on two important functions: Xup() and
Xdown(). These functions provide upload and download capability, respectively. Both functions
use the same data structures to describe and track a transfer. Listing 10.1 shows the declaration
for this structure.

Listing 10.1: The Xmodem Control Structure.

struct xinfo {
uchar sno; /* Sequence number.
*/
uchar pad; /* Unused, padding.
*/
int xfertot; /* Running total of transfer.
*/
int pktlen; /* Length of packet (128 or 1024).
*/
int pktcnt; /* Running tally of number of packets processed.
*/
int filcnt; /* Number of files transferred by ymodem.
*/
long size; /* Size of upload.
*/
ulong flags; /* Storage for various runtime flags.
*/
ulong base; /* Starting address for data transfer.
*/
ulong dataddr; /* Running address for data transfer.
*/
int errcnt; /* Keep track of errors (used in verify mode).
*/
char *firsterrat; /* Pointer to location of error detected when
*/
/* transfer is in verify mode.
*/
char fname[TFSNAMESIZE];
};

Regardless of whether the Xmodem transfer is an upload or a download, the transfer starts with a
command at the MicroMonitor CLI. Once the command is issued, the Xmodem code waits for
the other end (usually HyperTerminal in Windows) to connect and begin the actual transfer.

Listing 10.2 shows the Xup() function, which is called when the user requests a file transfer from
the target to the host.

Listing 10.2: Xup().

static int
Xup(struct xinfo *xip)
{
uchar c, buf[PKTLEN_128];
int done, pktlen;
long actualsize;

Mtrace("Xup starting");

actualsize = xip->size;

if (xip->size & 0x7f) {


xip->size += 128;
xip->size &= 0xffffff80L;
}

printf("Upload %ld bytes from 0x%lx\n",xip->size,(ulong)xip->base);

/* Startup synchronization... */
/* Wait to receive a NAK or 'C' from receiver. */
done = 0;
while(!done) {
c = (uchar)getchar();
switch(c) {
case NAK:
done = 1;
Mtrace("CSM");
break;
case 'C':
xip->flags |= USECRC;
done = 1;
Mtrace("CRC");
break;
default:
break;
}
}

done = 0;
xip->sno = 1;
xip->pktcnt = 0;
while(!done) {
c = (uchar)putPacket((uchar *)(xip->dataddr),xip);
switch(c) {
case ACK:
xip->sno++;
xip->pktcnt++;
xip->size -= xip->pktlen;
xip->dataddr += xip->pktlen;
Mtrace("A");
break;
case NAK:
Mtrace("N");
break;
case CAN:
done = -1;
Mtrace("C");
break;
case EOT:
done = -1;
Mtrace("E");
break;
default:
done = -1;
Mtrace("<%2x>",c);
break;
}
if (xip->size <= 0) {
rputchar(EOT);
getchar(); /* Flush the ACK */
break;
}
Mtrace("!");
}
Mtrace("Xup_done.");
return(0);
}

Note The Mtrace() function (see Listing 10.2) deserves a quick side note. The Xmodem code
uses Mtrace() to help with debugging. The Xmodem protocol is using the same serial port
as is usually used by printf(), so you can’t add printf statements to debug the code because
adding printf statements screws up the protocol. The Mtrace() (or memory trace) function
looks like printf(), but, instead of the output going to the console, it goes to an internal
RAM buffer so that after the protocol completes, you have the option to dump the contents
of that buffer to see a trace log of what was happening. Mtrace() is a useful tool to have for
any case where you are writing code in an area that does not allow printing through the
serial port. Now back to Xup().

Right off the bat this function must deal with an Xmodem “yukism.” The size of the transfer
must be converted to a mod-128 size. The smallest packet size for Xmodem is 128 bytes, so if
the actual transfer size is 129 bytes, Xmodem sends 256. After padding the data size, the
function attempts to synchronize with the receiver by listening for the other end to connect. The
program at the other end of the serial connection sends either a NAK (negative
acknowledgement) to initiate a transfer using checksums or the character C to initiate a transfer
using CCITT CRC16. After this synchronization is finished, the packets begin to flow. This
Xmodem implementation is about as simple as it can get. Function Xup() sends each packet and
then waits for a response using the putPacket() function. The putPacket() function (not shown)
sends the SOH (start of header) character, increments the sequence number by one, sends the
sequence number and its complement, sends the packet data and checksum or CRC, then waits
for the response, and returns to the calling Xup() function. If the response is a positive
acknowledgment (ACK), then Xup() continues with the next packet. Otherwise, it quits. No
retransmission, nothing fancy. If a transfer fails, then it is up to the user to try again.

Xdown()

The Xdown() function (see Listing 10.3) is called from the xmodem command when the user
requests a file transfer from the host to target.

Listing 10.3: Xdown().

/* Xdown():
* Called when a transfer from host to target is being made (considered
* a download).
*/

static int
Xdown(struct xinfo *xip)
{
long timeout;
char c, tmppkt[PKTLEN_1K];
int done;

xip->sno = 0x01;
xip->pktcnt = 0;
xip->errcnt = 0;
xip->xfertot = 0;
xip->firsterrat = 0;

/* Startup synchronization... */
/* Continuously send NAK or 'C' until sender responds. */
Mtrace("Xdown");
while(1) {
if (xip->flags & USECRC)
rputchar('C');
else
rputchar(NAK);
timeout = LoopsPerSecond;
while(!gotachar() && timeout)
timeout--;
if (timeout)
break;
}

done = 0;
Mtrace("Got response");
while(done == 0) {
c = (char)getchar();
switch(c) {
case SOH: /* 128-byte incoming packet */
Mtrace("O");
xip->pktlen = PKTLEN_128;
done = getPacket(tmppkt,xip);
break;
case STX: /* 1024-byte incoming packet */
Mtrace("T");
xip->pktlen = PKTLEN_1K;
done = getPacket(tmppkt,xip);
break;
case CAN:
Mtrace("C");
done = -1;
break;
case EOT:
Mtrace("E");
rputchar(ACK);
done = xip->xfertot;
printf("\nRcvd %d pkt%c (%d bytes)\n",xip->pktcnt,
xip->pktcnt > 1 ? 's' : ' ',xip->xfertot);
break;
case ESC: /* User-invoked abort */
Mtrace("X");
done = -1;
break;
default:
Mtrace("<%02x>",c);
done = -1;
break;
}
Mtrace("!");
}
if (xip->flags & VERIFY) {
if (xip->errcnt)
printf("%d errors, first at 0x%lx\n",
xip->errcnt,(ulong)(xip->firsterrat));
else
printf("verification passed\n");
}
return(done);
}

If Xdown() looks similar to Xup(), that is because it is. Xdown() is just the other end of the
protocol. At the top of Listing 10.3 is the synchronization step. This time this code is sending
either the C or NAK (depending on how the transfer was configured) and then waiting for the
response. Once the response is received, the packets once again begin to flow, but this time the
packets flow in the other direction (into target memory). In this direction, packets are announced
by either of two bytes: SOH and STX. SOH indicates an incoming packet of 128 bytes, and STX
indicates a 1024-byte inbound packet. The getPacket() function (not shown) does the real work
of pulling in the packets and sending a response character. (The getPacket() function is similar to
putPacket() on the Xup() side.) After the sender has completed the transmission, the sender sends
an end-of-transmission (EOT) character to indicate that the transfer is done. Receipt of the EOT
character causes the loop to terminate, completing the transaction. A few other situations can
abort the transfer, but these situations are the exception.

Notice the VERIFY flag check just after the loop. The VERIFY flag check is a useful option
added to MicroMonitor’s Xmodem implementation. This option allows a host to download and
verify (not overwrite, just compare) the downloaded data to data already resident on the target.

Xmodem in MicroMonitor

Xmodem gives MicroMonitor the ability to transfer files to and from the target. This file transfer
can be to/from files or to/from raw memory in the target. The verification option mentioned
above is useful for simple memory tests or to verify that code did not overwrite some block of
data. The actual implementation of Xmodem in MicroMonitor supports most of the options, such
as 128/1024-byte packets, checksum and CRC, Ymodem extensions, and so forth. Refer to the
CD for more detail.

One last note on MicroMonitor’s Xmodem implementation. The boot monitor resides in boot
flash memory and occasionally the boot flash memory might need to be updated. You could just
use a programmer to burn a new device and install it; however, MicroMonitor’s Xmodem hooks
up with the flash driver code to allow the user to download a binary image and automatically re-
burn the monitor code on board. This feature is a nice convenience, especially when you are
porting the monitor to a new target. After the serial port and flash drivers are in place, you can
use Xmodem to make quick boot flash updates while you are working on other aspects of the
monitor port.

The monitor now includes a flash file system, serial port communications, and Ethernet
connectivity. The next natural step is to provide a mechanism that allows file transfer to and
from the target. The file transfer protocols presented in this chapter are not new but remain
popular because of their simplicity. The monitor uses Xmodem and TFTP for RS-232 and
Ethernet implementations respectively. Data can be transferred as raw memory or as a file for
both protocols. The MicroMonitor package includes code for a PC-based TFTP client/server and
PC-based Xmodem transfer. I will limit this discussion to the target side. Complete
implementation details are beyond the scope of this discussion. (Refer to the CD for a full source
listing.)

Both Xmodem and TFTP are “lock step” protocols, meaning that when one end sends some data,
it doesn’t send any new data until it receives acknowledgment from the other end indicating that
the data was received. Although this design slows things down a bit, especially with respect to
TFTP, the lock step approach offers the benefit of simplicity.

Xmodem
I think Xmodem has been around since dirt. Xmodem is one of those things that just won’t go
away, which is good because Xmodem can be found just about anywhere and it’s not too
difficult to implement. Xmodem has spawned many variants, including derivatives that support
CRC versus checksum, 128- versus 1024-byte packets, and multiple files per transfer. I won’t
discuss these variants here, because my focus is MicroMonitor’s Xmodem support — not the
larger protocol. Keep in mind that MicroMonitor assumes that the target is communicating with
the host via an error-free connection, so consequently the Xmodem implementation doesn’t need
to be very robust. The point of the discussion is to give you a feel for the Xmodem protocol as it
is used in MicroMonitor and to let you experience a basic Xmodem implementation for a very
“blue sky” environment.

This implementation of Xmodem is primarily based on two important functions: Xup() and
Xdown(). These functions provide upload and download capability, respectively. Both functions
use the same data structures to describe and track a transfer. Listing 10.1 shows the declaration
for this structure.

Listing 10.1: The Xmodem Control Structure.

struct xinfo {
uchar sno; /* Sequence number.
*/
uchar pad; /* Unused, padding.
*/
int xfertot; /* Running total of transfer.
*/
int pktlen; /* Length of packet (128 or 1024).
*/
int pktcnt; /* Running tally of number of packets processed.
*/
int filcnt; /* Number of files transferred by ymodem.
*/
long size; /* Size of upload.
*/
ulong flags; /* Storage for various runtime flags.
*/
ulong base; /* Starting address for data transfer.
*/
ulong dataddr; /* Running address for data transfer.
*/
int errcnt; /* Keep track of errors (used in verify mode).
*/
char *firsterrat; /* Pointer to location of error detected when
*/
/* transfer is in verify mode.
*/
char fname[TFSNAMESIZE];
};

Regardless of whether the Xmodem transfer is an upload or a download, the transfer starts with a
command at the MicroMonitor CLI. Once the command is issued, the Xmodem code waits for
the other end (usually HyperTerminal in Windows) to connect and begin the actual transfer.

Listing 10.2 shows the Xup() function, which is called when the user requests a file transfer from
the target to the host.

Listing 10.2: Xup().

static int
Xup(struct xinfo *xip)
{
uchar c, buf[PKTLEN_128];
int done, pktlen;
long actualsize;

Mtrace("Xup starting");

actualsize = xip->size;

if (xip->size & 0x7f) {


xip->size += 128;
xip->size &= 0xffffff80L;
}

printf("Upload %ld bytes from 0x%lx\n",xip->size,(ulong)xip->base);

/* Startup synchronization... */
/* Wait to receive a NAK or 'C' from receiver. */
done = 0;
while(!done) {
c = (uchar)getchar();
switch(c) {
case NAK:
done = 1;
Mtrace("CSM");
break;
case 'C':
xip->flags |= USECRC;
done = 1;
Mtrace("CRC");
break;
default:
break;
}
}

done = 0;
xip->sno = 1;
xip->pktcnt = 0;
while(!done) {
c = (uchar)putPacket((uchar *)(xip->dataddr),xip);
switch(c) {
case ACK:
xip->sno++;
xip->pktcnt++;
xip->size -= xip->pktlen;
xip->dataddr += xip->pktlen;
Mtrace("A");
break;
case NAK:
Mtrace("N");
break;
case CAN:
done = -1;
Mtrace("C");
break;
case EOT:
done = -1;
Mtrace("E");
break;
default:
done = -1;
Mtrace("<%2x>",c);
break;
}
if (xip->size <= 0) {
rputchar(EOT);
getchar(); /* Flush the ACK */
break;
}
Mtrace("!");
}
Mtrace("Xup_done.");
return(0);
}

Note The Mtrace() function (see Listing 10.2) deserves a quick side note. The Xmodem code
uses Mtrace() to help with debugging. The Xmodem protocol is using the same serial port
as is usually used by printf(), so you can’t add printf statements to debug the code because
adding printf statements screws up the protocol. The Mtrace() (or memory trace) function
looks like printf(), but, instead of the output going to the console, it goes to an internal
RAM buffer so that after the protocol completes, you have the option to dump the contents
of that buffer to see a trace log of what was happening. Mtrace() is a useful tool to have for
any case where you are writing code in an area that does not allow printing through the
serial port. Now back to Xup().
Right off the bat this function must deal with an Xmodem “yukism.” The size of the transfer
must be converted to a mod-128 size. The smallest packet size for Xmodem is 128 bytes, so if
the actual transfer size is 129 bytes, Xmodem sends 256. After padding the data size, the
function attempts to synchronize with the receiver by listening for the other end to connect. The
program at the other end of the serial connection sends either a NAK (negative
acknowledgement) to initiate a transfer using checksums or the character C to initiate a transfer
using CCITT CRC16. After this synchronization is finished, the packets begin to flow. This
Xmodem implementation is about as simple as it can get. Function Xup() sends each packet and
then waits for a response using the putPacket() function. The putPacket() function (not shown)
sends the SOH (start of header) character, increments the sequence number by one, sends the
sequence number and its complement, sends the packet data and checksum or CRC, then waits
for the response, and returns to the calling Xup() function. If the response is a positive
acknowledgment (ACK), then Xup() continues with the next packet. Otherwise, it quits. No
retransmission, nothing fancy. If a transfer fails, then it is up to the user to try again.

Xdown()

The Xdown() function (see Listing 10.3) is called from the xmodem command when the user
requests a file transfer from the host to target.

Listing 10.3: Xdown().

/* Xdown():
* Called when a transfer from host to target is being made (considered
* a download).
*/

static int
Xdown(struct xinfo *xip)
{
long timeout;
char c, tmppkt[PKTLEN_1K];
int done;

xip->sno = 0x01;
xip->pktcnt = 0;
xip->errcnt = 0;
xip->xfertot = 0;
xip->firsterrat = 0;

/* Startup synchronization... */
/* Continuously send NAK or 'C' until sender responds. */
Mtrace("Xdown");
while(1) {
if (xip->flags & USECRC)
rputchar('C');
else
rputchar(NAK);
timeout = LoopsPerSecond;
while(!gotachar() && timeout)
timeout--;
if (timeout)
break;
}

done = 0;
Mtrace("Got response");
while(done == 0) {
c = (char)getchar();
switch(c) {
case SOH: /* 128-byte incoming packet */
Mtrace("O");
xip->pktlen = PKTLEN_128;
done = getPacket(tmppkt,xip);
break;
case STX: /* 1024-byte incoming packet */
Mtrace("T");
xip->pktlen = PKTLEN_1K;
done = getPacket(tmppkt,xip);
break;
case CAN:
Mtrace("C");
done = -1;
break;
case EOT:
Mtrace("E");
rputchar(ACK);
done = xip->xfertot;
printf("\nRcvd %d pkt%c (%d bytes)\n",xip->pktcnt,
xip->pktcnt > 1 ? 's' : ' ',xip->xfertot);
break;
case ESC: /* User-invoked abort */
Mtrace("X");
done = -1;
break;
default:
Mtrace("<%02x>",c);
done = -1;
break;
}
Mtrace("!");
}
if (xip->flags & VERIFY) {
if (xip->errcnt)
printf("%d errors, first at 0x%lx\n",
xip->errcnt,(ulong)(xip->firsterrat));
else
printf("verification passed\n");
}
return(done);
}

If Xdown() looks similar to Xup(), that is because it is. Xdown() is just the other end of the
protocol. At the top of Listing 10.3 is the synchronization step. This time this code is sending
either the C or NAK (depending on how the transfer was configured) and then waiting for the
response. Once the response is received, the packets once again begin to flow, but this time the
packets flow in the other direction (into target memory). In this direction, packets are announced
by either of two bytes: SOH and STX. SOH indicates an incoming packet of 128 bytes, and STX
indicates a 1024-byte inbound packet. The getPacket() function (not shown) does the real work
of pulling in the packets and sending a response character. (The getPacket() function is similar to
putPacket() on the Xup() side.) After the sender has completed the transmission, the sender sends
an end-of-transmission (EOT) character to indicate that the transfer is done. Receipt of the EOT
character causes the loop to terminate, completing the transaction. A few other situations can
abort the transfer, but these situations are the exception.

Notice the VERIFY flag check just after the loop. The VERIFY flag check is a useful option
added to MicroMonitor’s Xmodem implementation. This option allows a host to download and
verify (not overwrite, just compare) the downloaded data to data already resident on the target.

Xmodem in MicroMonitor

Xmodem gives MicroMonitor the ability to transfer files to and from the target. This file transfer
can be to/from files or to/from raw memory in the target. The verification option mentioned
above is useful for simple memory tests or to verify that code did not overwrite some block of
data. The actual implementation of Xmodem in MicroMonitor supports most of the options, such
as 128/1024-byte packets, checksum and CRC, Ymodem extensions, and so forth. Refer to the
CD for more detail.

One last note on MicroMonitor’s Xmodem implementation. The boot monitor resides in boot
flash memory and occasionally the boot flash memory might need to be updated. You could just
use a programmer to burn a new device and install it; however, MicroMonitor’s Xmodem hooks
up with the flash driver code to allow the user to download a binary image and automatically re-
burn the monitor code on board. This feature is a nice convenience, especially when you are
porting the monitor to a new target. After the serial port and flash drivers are in place, you can
use Xmodem to make quick boot flash updates while you are working on other aspects of the
monitor port.

The monitor now includes a flash file system, serial port communications, and Ethernet
connectivity. The next natural step is to provide a mechanism that allows file transfer to and
from the target. The file transfer protocols presented in this chapter are not new but remain
popular because of their simplicity. The monitor uses Xmodem and TFTP for RS-232 and
Ethernet implementations respectively. Data can be transferred as raw memory or as a file for
both protocols. The MicroMonitor package includes code for a PC-based TFTP client/server and
PC-based Xmodem transfer. I will limit this discussion to the target side. Complete
implementation details are beyond the scope of this discussion. (Refer to the CD for a full source
listing.)

Both Xmodem and TFTP are “lock step” protocols, meaning that when one end sends some data,
it doesn’t send any new data until it receives acknowledgment from the other end indicating that
the data was received. Although this design slows things down a bit, especially with respect to
TFTP, the lock step approach offers the benefit of simplicity.
Xmodem
I think Xmodem has been around since dirt. Xmodem is one of those things that just won’t go
away, which is good because Xmodem can be found just about anywhere and it’s not too
difficult to implement. Xmodem has spawned many variants, including derivatives that support
CRC versus checksum, 128- versus 1024-byte packets, and multiple files per transfer. I won’t
discuss these variants here, because my focus is MicroMonitor’s Xmodem support — not the
larger protocol. Keep in mind that MicroMonitor assumes that the target is communicating with
the host via an error-free connection, so consequently the Xmodem implementation doesn’t need
to be very robust. The point of the discussion is to give you a feel for the Xmodem protocol as it
is used in MicroMonitor and to let you experience a basic Xmodem implementation for a very
“blue sky” environment.

This implementation of Xmodem is primarily based on two important functions: Xup() and
Xdown(). These functions provide upload and download capability, respectively. Both functions
use the same data structures to describe and track a transfer. Listing 10.1 shows the declaration
for this structure.

Listing 10.1: The Xmodem Control Structure.

struct xinfo {
uchar sno; /* Sequence number.
*/
uchar pad; /* Unused, padding.
*/
int xfertot; /* Running total of transfer.
*/
int pktlen; /* Length of packet (128 or 1024).
*/
int pktcnt; /* Running tally of number of packets processed.
*/
int filcnt; /* Number of files transferred by ymodem.
*/
long size; /* Size of upload.
*/
ulong flags; /* Storage for various runtime flags.
*/
ulong base; /* Starting address for data transfer.
*/
ulong dataddr; /* Running address for data transfer.
*/
int errcnt; /* Keep track of errors (used in verify mode).
*/
char *firsterrat; /* Pointer to location of error detected when
*/
/* transfer is in verify mode.
*/
char fname[TFSNAMESIZE];
};
Regardless of whether the Xmodem transfer is an upload or a download, the transfer starts with a
command at the MicroMonitor CLI. Once the command is issued, the Xmodem code waits for
the other end (usually HyperTerminal in Windows) to connect and begin the actual transfer.

Listing 10.2 shows the Xup() function, which is called when the user requests a file transfer from
the target to the host.

Listing 10.2: Xup().

static int
Xup(struct xinfo *xip)
{
uchar c, buf[PKTLEN_128];
int done, pktlen;
long actualsize;

Mtrace("Xup starting");

actualsize = xip->size;

if (xip->size & 0x7f) {


xip->size += 128;
xip->size &= 0xffffff80L;
}

printf("Upload %ld bytes from 0x%lx\n",xip->size,(ulong)xip->base);

/* Startup synchronization... */
/* Wait to receive a NAK or 'C' from receiver. */
done = 0;
while(!done) {
c = (uchar)getchar();
switch(c) {
case NAK:
done = 1;
Mtrace("CSM");
break;
case 'C':
xip->flags |= USECRC;
done = 1;
Mtrace("CRC");
break;
default:
break;
}
}

done = 0;
xip->sno = 1;
xip->pktcnt = 0;
while(!done) {
c = (uchar)putPacket((uchar *)(xip->dataddr),xip);
switch(c) {
case ACK:
xip->sno++;
xip->pktcnt++;
xip->size -= xip->pktlen;
xip->dataddr += xip->pktlen;
Mtrace("A");
break;
case NAK:
Mtrace("N");
break;
case CAN:
done = -1;
Mtrace("C");
break;
case EOT:
done = -1;
Mtrace("E");
break;
default:
done = -1;
Mtrace("<%2x>",c);
break;
}
if (xip->size <= 0) {
rputchar(EOT);
getchar(); /* Flush the ACK */
break;
}
Mtrace("!");
}
Mtrace("Xup_done.");
return(0);
}

Note The Mtrace() function (see Listing 10.2) deserves a quick side note. The Xmodem code
uses Mtrace() to help with debugging. The Xmodem protocol is using the same serial port
as is usually used by printf(), so you can’t add printf statements to debug the code because
adding printf statements screws up the protocol. The Mtrace() (or memory trace) function
looks like printf(), but, instead of the output going to the console, it goes to an internal
RAM buffer so that after the protocol completes, you have the option to dump the contents
of that buffer to see a trace log of what was happening. Mtrace() is a useful tool to have for
any case where you are writing code in an area that does not allow printing through the
serial port. Now back to Xup().

Right off the bat this function must deal with an Xmodem “yukism.” The size of the transfer
must be converted to a mod-128 size. The smallest packet size for Xmodem is 128 bytes, so if
the actual transfer size is 129 bytes, Xmodem sends 256. After padding the data size, the
function attempts to synchronize with the receiver by listening for the other end to connect. The
program at the other end of the serial connection sends either a NAK (negative
acknowledgement) to initiate a transfer using checksums or the character C to initiate a transfer
using CCITT CRC16. After this synchronization is finished, the packets begin to flow. This
Xmodem implementation is about as simple as it can get. Function Xup() sends each packet and
then waits for a response using the putPacket() function. The putPacket() function (not shown)
sends the SOH (start of header) character, increments the sequence number by one, sends the
sequence number and its complement, sends the packet data and checksum or CRC, then waits
for the response, and returns to the calling Xup() function. If the response is a positive
acknowledgment (ACK), then Xup() continues with the next packet. Otherwise, it quits. No
retransmission, nothing fancy. If a transfer fails, then it is up to the user to try again.

Xdown()

The Xdown() function (see Listing 10.3) is called from the xmodem command when the user
requests a file transfer from the host to target.

Listing 10.3: Xdown().

/* Xdown():
* Called when a transfer from host to target is being made (considered
* a download).
*/

static int
Xdown(struct xinfo *xip)
{
long timeout;
char c, tmppkt[PKTLEN_1K];
int done;

xip->sno = 0x01;
xip->pktcnt = 0;
xip->errcnt = 0;
xip->xfertot = 0;
xip->firsterrat = 0;

/* Startup synchronization... */
/* Continuously send NAK or 'C' until sender responds. */
Mtrace("Xdown");
while(1) {
if (xip->flags & USECRC)
rputchar('C');
else
rputchar(NAK);
timeout = LoopsPerSecond;
while(!gotachar() && timeout)
timeout--;
if (timeout)
break;
}

done = 0;
Mtrace("Got response");
while(done == 0) {
c = (char)getchar();
switch(c) {
case SOH: /* 128-byte incoming packet */
Mtrace("O");
xip->pktlen = PKTLEN_128;
done = getPacket(tmppkt,xip);
break;
case STX: /* 1024-byte incoming packet */
Mtrace("T");
xip->pktlen = PKTLEN_1K;
done = getPacket(tmppkt,xip);
break;
case CAN:
Mtrace("C");
done = -1;
break;
case EOT:
Mtrace("E");
rputchar(ACK);
done = xip->xfertot;
printf("\nRcvd %d pkt%c (%d bytes)\n",xip->pktcnt,
xip->pktcnt > 1 ? 's' : ' ',xip->xfertot);
break;
case ESC: /* User-invoked abort */
Mtrace("X");
done = -1;
break;
default:
Mtrace("<%02x>",c);
done = -1;
break;
}
Mtrace("!");
}
if (xip->flags & VERIFY) {
if (xip->errcnt)
printf("%d errors, first at 0x%lx\n",
xip->errcnt,(ulong)(xip->firsterrat));
else
printf("verification passed\n");
}
return(done);
}

If Xdown() looks similar to Xup(), that is because it is. Xdown() is just the other end of the
protocol. At the top of Listing 10.3 is the synchronization step. This time this code is sending
either the C or NAK (depending on how the transfer was configured) and then waiting for the
response. Once the response is received, the packets once again begin to flow, but this time the
packets flow in the other direction (into target memory). In this direction, packets are announced
by either of two bytes: SOH and STX. SOH indicates an incoming packet of 128 bytes, and STX
indicates a 1024-byte inbound packet. The getPacket() function (not shown) does the real work
of pulling in the packets and sending a response character. (The getPacket() function is similar to
putPacket() on the Xup() side.) After the sender has completed the transmission, the sender sends
an end-of-transmission (EOT) character to indicate that the transfer is done. Receipt of the EOT
character causes the loop to terminate, completing the transaction. A few other situations can
abort the transfer, but these situations are the exception.

Notice the VERIFY flag check just after the loop. The VERIFY flag check is a useful option
added to MicroMonitor’s Xmodem implementation. This option allows a host to download and
verify (not overwrite, just compare) the downloaded data to data already resident on the target.

Xmodem in MicroMonitor

Xmodem gives MicroMonitor the ability to transfer files to and from the target. This file transfer
can be to/from files or to/from raw memory in the target. The verification option mentioned
above is useful for simple memory tests or to verify that code did not overwrite some block of
data. The actual implementation of Xmodem in MicroMonitor supports most of the options, such
as 128/1024-byte packets, checksum and CRC, Ymodem extensions, and so forth. Refer to the
CD for more detail.

One last note on MicroMonitor’s Xmodem implementation. The boot monitor resides in boot
flash memory and occasionally the boot flash memory might need to be updated. You could just
use a programmer to burn a new device and install it; however, MicroMonitor’s Xmodem hooks
up with the flash driver code to allow the user to download a binary image and automatically re-
burn the monitor code on board. This feature is a nice convenience, especially when you are
porting the monitor to a new target. After the serial port and flash drivers are in place, you can
use Xmodem to make quick boot flash updates while you are working on other aspects of the
monitor port.

The monitor now includes a flash file system, serial port communications, and Ethernet
connectivity. The next natural step is to provide a mechanism that allows file transfer to and
from the target. The file transfer protocols presented in this chapter are not new but remain
popular because of their simplicity. The monitor uses Xmodem and TFTP for RS-232 and
Ethernet implementations respectively. Data can be transferred as raw memory or as a file for
both protocols. The MicroMonitor package includes code for a PC-based TFTP client/server and
PC-based Xmodem transfer. I will limit this discussion to the target side. Complete
implementation details are beyond the scope of this discussion. (Refer to the CD for a full source
listing.)

Both Xmodem and TFTP are “lock step” protocols, meaning that when one end sends some data,
it doesn’t send any new data until it receives acknowledgment from the other end indicating that
the data was received. Although this design slows things down a bit, especially with respect to
TFTP, the lock step approach offers the benefit of simplicity.

Xmodem
I think Xmodem has been around since dirt. Xmodem is one of those things that just won’t go
away, which is good because Xmodem can be found just about anywhere and it’s not too
difficult to implement. Xmodem has spawned many variants, including derivatives that support
CRC versus checksum, 128- versus 1024-byte packets, and multiple files per transfer. I won’t
discuss these variants here, because my focus is MicroMonitor’s Xmodem support — not the
larger protocol. Keep in mind that MicroMonitor assumes that the target is communicating with
the host via an error-free connection, so consequently the Xmodem implementation doesn’t need
to be very robust. The point of the discussion is to give you a feel for the Xmodem protocol as it
is used in MicroMonitor and to let you experience a basic Xmodem implementation for a very
“blue sky” environment.

This implementation of Xmodem is primarily based on two important functions: Xup() and
Xdown(). These functions provide upload and download capability, respectively. Both functions
use the same data structures to describe and track a transfer. Listing 10.1 shows the declaration
for this structure.

Listing 10.1: The Xmodem Control Structure.

struct xinfo {
uchar sno; /* Sequence number.
*/
uchar pad; /* Unused, padding.
*/
int xfertot; /* Running total of transfer.
*/
int pktlen; /* Length of packet (128 or 1024).
*/
int pktcnt; /* Running tally of number of packets processed.
*/
int filcnt; /* Number of files transferred by ymodem.
*/
long size; /* Size of upload.
*/
ulong flags; /* Storage for various runtime flags.
*/
ulong base; /* Starting address for data transfer.
*/
ulong dataddr; /* Running address for data transfer.
*/
int errcnt; /* Keep track of errors (used in verify mode).
*/
char *firsterrat; /* Pointer to location of error detected when
*/
/* transfer is in verify mode.
*/
char fname[TFSNAMESIZE];
};

Regardless of whether the Xmodem transfer is an upload or a download, the transfer starts with a
command at the MicroMonitor CLI. Once the command is issued, the Xmodem code waits for
the other end (usually HyperTerminal in Windows) to connect and begin the actual transfer.
Listing 10.2 shows the Xup() function, which is called when the user requests a file transfer from
the target to the host.

Listing 10.2: Xup().

static int
Xup(struct xinfo *xip)
{
uchar c, buf[PKTLEN_128];
int done, pktlen;
long actualsize;

Mtrace("Xup starting");

actualsize = xip->size;

if (xip->size & 0x7f) {


xip->size += 128;
xip->size &= 0xffffff80L;
}

printf("Upload %ld bytes from 0x%lx\n",xip->size,(ulong)xip->base);

/* Startup synchronization... */
/* Wait to receive a NAK or 'C' from receiver. */
done = 0;
while(!done) {
c = (uchar)getchar();
switch(c) {
case NAK:
done = 1;
Mtrace("CSM");
break;
case 'C':
xip->flags |= USECRC;
done = 1;
Mtrace("CRC");
break;
default:
break;
}
}

done = 0;
xip->sno = 1;
xip->pktcnt = 0;
while(!done) {
c = (uchar)putPacket((uchar *)(xip->dataddr),xip);
switch(c) {
case ACK:
xip->sno++;
xip->pktcnt++;
xip->size -= xip->pktlen;
xip->dataddr += xip->pktlen;
Mtrace("A");
break;
case NAK:
Mtrace("N");
break;
case CAN:
done = -1;
Mtrace("C");
break;
case EOT:
done = -1;
Mtrace("E");
break;
default:
done = -1;
Mtrace("<%2x>",c);
break;
}
if (xip->size <= 0) {
rputchar(EOT);
getchar(); /* Flush the ACK */
break;
}
Mtrace("!");
}
Mtrace("Xup_done.");
return(0);
}

Note The Mtrace() function (see Listing 10.2) deserves a quick side note. The Xmodem code
uses Mtrace() to help with debugging. The Xmodem protocol is using the same serial port
as is usually used by printf(), so you can’t add printf statements to debug the code because
adding printf statements screws up the protocol. The Mtrace() (or memory trace) function
looks like printf(), but, instead of the output going to the console, it goes to an internal
RAM buffer so that after the protocol completes, you have the option to dump the contents
of that buffer to see a trace log of what was happening. Mtrace() is a useful tool to have for
any case where you are writing code in an area that does not allow printing through the
serial port. Now back to Xup().

Right off the bat this function must deal with an Xmodem “yukism.” The size of the transfer
must be converted to a mod-128 size. The smallest packet size for Xmodem is 128 bytes, so if
the actual transfer size is 129 bytes, Xmodem sends 256. After padding the data size, the
function attempts to synchronize with the receiver by listening for the other end to connect. The
program at the other end of the serial connection sends either a NAK (negative
acknowledgement) to initiate a transfer using checksums or the character C to initiate a transfer
using CCITT CRC16. After this synchronization is finished, the packets begin to flow. This
Xmodem implementation is about as simple as it can get. Function Xup() sends each packet and
then waits for a response using the putPacket() function. The putPacket() function (not shown)
sends the SOH (start of header) character, increments the sequence number by one, sends the
sequence number and its complement, sends the packet data and checksum or CRC, then waits
for the response, and returns to the calling Xup() function. If the response is a positive
acknowledgment (ACK), then Xup() continues with the next packet. Otherwise, it quits. No
retransmission, nothing fancy. If a transfer fails, then it is up to the user to try again.

Xdown()

The Xdown() function (see Listing 10.3) is called from the xmodem command when the user
requests a file transfer from the host to target.

Listing 10.3: Xdown().

/* Xdown():
* Called when a transfer from host to target is being made (considered
* a download).
*/

static int
Xdown(struct xinfo *xip)
{
long timeout;
char c, tmppkt[PKTLEN_1K];
int done;

xip->sno = 0x01;
xip->pktcnt = 0;
xip->errcnt = 0;
xip->xfertot = 0;
xip->firsterrat = 0;

/* Startup synchronization... */
/* Continuously send NAK or 'C' until sender responds. */
Mtrace("Xdown");
while(1) {
if (xip->flags & USECRC)
rputchar('C');
else
rputchar(NAK);
timeout = LoopsPerSecond;
while(!gotachar() && timeout)
timeout--;
if (timeout)
break;
}

done = 0;
Mtrace("Got response");
while(done == 0) {
c = (char)getchar();
switch(c) {
case SOH: /* 128-byte incoming packet */
Mtrace("O");
xip->pktlen = PKTLEN_128;
done = getPacket(tmppkt,xip);
break;
case STX: /* 1024-byte incoming packet */
Mtrace("T");
xip->pktlen = PKTLEN_1K;
done = getPacket(tmppkt,xip);
break;
case CAN:
Mtrace("C");
done = -1;
break;
case EOT:
Mtrace("E");
rputchar(ACK);
done = xip->xfertot;
printf("\nRcvd %d pkt%c (%d bytes)\n",xip->pktcnt,
xip->pktcnt > 1 ? 's' : ' ',xip->xfertot);
break;
case ESC: /* User-invoked abort */
Mtrace("X");
done = -1;
break;
default:
Mtrace("<%02x>",c);
done = -1;
break;
}
Mtrace("!");
}
if (xip->flags & VERIFY) {
if (xip->errcnt)
printf("%d errors, first at 0x%lx\n",
xip->errcnt,(ulong)(xip->firsterrat));
else
printf("verification passed\n");
}
return(done);
}

If Xdown() looks similar to Xup(), that is because it is. Xdown() is just the other end of the
protocol. At the top of Listing 10.3 is the synchronization step. This time this code is sending
either the C or NAK (depending on how the transfer was configured) and then waiting for the
response. Once the response is received, the packets once again begin to flow, but this time the
packets flow in the other direction (into target memory). In this direction, packets are announced
by either of two bytes: SOH and STX. SOH indicates an incoming packet of 128 bytes, and STX
indicates a 1024-byte inbound packet. The getPacket() function (not shown) does the real work
of pulling in the packets and sending a response character. (The getPacket() function is similar to
putPacket() on the Xup() side.) After the sender has completed the transmission, the sender sends
an end-of-transmission (EOT) character to indicate that the transfer is done. Receipt of the EOT
character causes the loop to terminate, completing the transaction. A few other situations can
abort the transfer, but these situations are the exception.
Notice the VERIFY flag check just after the loop. The VERIFY flag check is a useful option
added to MicroMonitor’s Xmodem implementation. This option allows a host to download and
verify (not overwrite, just compare) the downloaded data to data already resident on the target.

Xmodem in MicroMonitor

Xmodem gives MicroMonitor the ability to transfer files to and from the target. This file transfer
can be to/from files or to/from raw memory in the target. The verification option mentioned
above is useful for simple memory tests or to verify that code did not overwrite some block of
data. The actual implementation of Xmodem in MicroMonitor supports most of the options, such
as 128/1024-byte packets, checksum and CRC, Ymodem extensions, and so forth. Refer to the
CD for more detail.

One last note on MicroMonitor’s Xmodem implementation. The boot monitor resides in boot
flash memory and occasionally the boot flash memory might need to be updated. You could just
use a programmer to burn a new device and install it; however, MicroMonitor’s Xmodem hooks
up with the flash driver code to allow the user to download a binary image and automatically re-
burn the monitor code on board. This feature is a nice convenience, especially when you are
porting the monitor to a new target. After the serial port and flash drivers are in place, you can
use Xmodem to make quick boot flash updates while you are working on other aspects of the
monitor port.

Das könnte Ihnen auch gefallen