# Sending a file via serial port



## balanga (Jun 20, 2018)

What is the recommended way of sending a file to a device via the serial port?

I've read various posts which mention things like screen, cu, minicom etc but would be interested to hear anyone's opinion on this forum.


----------



## Deleted member 54719 (Jun 20, 2018)

It depends.  There is no one size fits all answer.  It depends on the type of data and the preipheral device where it's being received.


----------



## balanga (Jun 20, 2018)

tempest766 said:


> It depends.  There is no one size fits all answer.  It depends on the type of data and the preipheral device where it's being received.



I want to send a 500kB binary boot file to a GoFlex Home docking station.


----------



## bds (Jun 20, 2018)

I haven't done this for a while but if I was connected over a serial TTY then I'd use the (X|Y|Z)modem protocol - see the comms/lrzsz port.


----------



## SirDice (Jun 20, 2018)

bds said:


> I haven't done this for a while but if I was connected over a serial TTY then I'd use the (X|Y|Z)modem protocol


This will only work if the other end is capable of receiving it. And this depends on the capabilities of the firmware of the device.


----------



## balanga (Jun 20, 2018)

What's the best way to use `lsx`? Can I use it from within `cu`?


----------



## Deleted member 54719 (Jun 20, 2018)

I'd suggest backing up a bit.  If you can boot over ethernet then I'd suggest that instead: bootp/dhcp.


----------



## trev (Jun 21, 2018)

The GoFlex Home has a firmware recovery mechanism built into the bootloader.

Firmware can be loaded onto a FAT32 formatted USB stick and inserted into the rear USB host port.

The hard disk should be removed from the dock, otherwise the hard disk may come up as /dev/sda rather than the USB stick and prevent the *.ubi file being found.

Hold the reset pin while power is applied. The yellow light will blink immediately followed by the white LED. At this stage uBoot will have entered the USB firmware installation mode and you can release the reset pin.

Source and further details: https://wiki.beyondlogic.org/index.php/Seagate_FreeAgent_GoFlex_Home_Firmare_Recovery


----------



## balanga (Jun 21, 2018)

trev said:


> The GoFlex Home has a firmware recovery mechanism built into the bootloader.



Thanks, I am aware of this, but my aim is to boot using a different u-Boot rather than the original firmware. This is normally done by sending a new u-Boot over the serial port using a program called kwboot, which works fine on Linux, but I can't get a FreeBSD compiled version to work. That's why I'm looking for an alternative means of sending the file.


----------



## bds (Jun 21, 2018)

It sounds like kwboot does a little more than just sending the file:


> Following power-up or a system reset, system BootROM code  polls  the  UART  for  a  brief period  of time, sensing a handshake message which initiates an image upload. This
> sends this boot message until  it  receives  a  positive  acknowledgement.  The  image  is transferred using Xmodem.


So if you can't get kwboot to work it sounds like you'll need to figure out this initial interaction first by looking at kwboot's source. I think you can invoke lsx from cu using ~C, or Minicom can do it too once its set up.


----------



## balanga (Jun 21, 2018)

bds said:


> It sounds like kwboot does a little more than just sending the file:
> 
> So if you can't get kwboot to work it sounds like you'll need to figure out this initial interaction first by looking at kwboot's source. I think you can invoke lsx from cu using ~C, or Minicom can do it too once its set up.



Looking at the man page for kwboot  it does, as you say, sense a handshake message, which I hadn't noticed before....

I've spent quite a while looking at the source code, but it looks like gobbledegook to me. I guess I need to focus on this 'handshake message' if only I can find it...


----------



## balanga (Jun 23, 2018)

Just found a useful link for sending a binary file over serial line...

http://www.denx.de/wiki/view/DULG/UBootCmdGroupDownload

This uses comms/kermit, although I haven't tried it myself yet.


----------



## bds (Jun 25, 2018)

Thats for uploading and booting a kernel from uboot itself.
Have you looked at alternatives to kwboot? https://www.solinno.co.uk/public/kwuartboot/ looks portable enough.


----------



## balanga (Jun 25, 2018)

Many thanks, I'll give it a try...

kwboot.c seems portable - I just can't get it to work 

.......

Just tried it and got...

unable to open '/dev/cuaU0': m

cu has no problem openning the port. Will have to delve into the code....

After further attempts.... I get...

Sending file..../
Finishing... failed


----------



## balanga (Jun 26, 2018)

This is the function I can't get to work...

```
xmodem_send(int tty_fd, int in_fd)
{
    unsigned char packet[134];
    static const int buflen = 128;
    int packetno, lastpacket;
    int c;
    int retry;
    int res;

    printf("Sending file...");

    /* We have already received a NAK, but wait for another to be sure */
    if (wait_for_nak(tty_fd) == -1)
    return -1;

    lastpacket = 0;
    packetno   = 1;
    for (retry = 0; retry < MAX_RETRANS; ++retry) {
    rotator();

    if (packetno != lastpacket) {
        res = build_packet(in_fd, packetno, packet, buflen);
        if (res == -1) return -1;
        if (res ==  1) break;
        lastpacket = packetno;
    }
    else
        printf("*%d*.", retry);

    c = send_packet(tty_fd, packet, buflen + 4);
    if (c == -1) {
        if (errno == ETIMEDOUT)
        continue; /* retry if timed out */

        fprintf(stderr,
            "%s: error writing to serial port: %m\n", argv0);
        break;
    }

    switch (c) {
        case ACK:
        retry = 0;
        ++packetno;
        break;
        case CAN:
        printf("cancelled by remote\n");
        if ((c = read_byte(tty_fd, 1)) == CAN) {
            write_byte(tty_fd, ACK);
            tcdrain(tty_fd);
            return -1;
        }
        break;
        default:
        printf("unexpected character %02x\n", c);
        case NAK:
        break;
    }
    }
    if (retry == MAX_RETRANS) {
    fprintf(stderr, "Too many retries, cancelling send\n");
    cancel_send(tty_fd);
    return -1; /* xmit error */
    }

    printf("\nFinishing...");
    for (retry = 0; retry < MAX_RETRANS; ++retry) {
        write_byte(tty_fd, EOT);
        tcdrain(tty_fd);
        if ((c = read_byte(tty_fd, 1)) == ACK)
        break;
    }

    if (c != ACK) {
    puts("failed\n");
    return -1;
    }
    puts("done\n");
    return 0;
}
```


How do I figure out why it prints 'failed' ?


----------



## bds (Jun 27, 2018)

After transferring the data, it sends an End Of Transmission (EOT) byte, and expect an ACKnowlegement in return. After retrying this a few times, it gives up and assumes it has failed.
Earlier on in the code it sends the handshake message before calling this. You could either manually push the handshake then run the Xmodem send from a script, or modify this to run the Xmodem transfer as a child process (using system()). Or add some debugging in to this to see what it is giving you back.


----------



## balanga (Jun 27, 2018)

Do you mean ignoring the check for an ACK like this?

```
/*
if (c != ACK) {
    puts("failed\n");
    return -1;
    }
    */
```

I'm not much of a coder so don't know how to run Xmodem using system() although I suspect it is only a few lines code...


----------

