Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>

enum {
        Soh=    0x1,
        Eot=    0x4,
        Ack=    0x6,
        Nak=    0x15,
        Cancel= 0x18,
};

int notifyf(void*, char*);
int readupto(uchar*, int);
int receive(int, uchar);
void send(int);

int debug, dfd;

void
main(int argc, char **argv)
{
        int fd;
        uchar seqno;
        ulong bytes;

        ARGBEGIN {
        case 'd':
                dfd = 2;
                debug = 1;
                break;
        } ARGEND

        if(argc != 1){
                fprint(2, "usage: xmr filename\n");
                exits("usage");
        }
        fd = open("/dev/consctl", OWRITE);
        if(fd >= 0)
                write(fd, "rawon", 5);
        fd = create(argv[0], ORDWR, 0666);
        if(fd < 0){
                perror("xmr: create");
                exits("create");
        }

        atnotify(notifyf, 1);
        send(Nak);

        /*
         *  keep receiving till the other side gives up
         */
        bytes = 0;
        for(seqno = 1; ; seqno++){
                if(receive(fd, seqno) == -1)
                        break;
                bytes += 128;
        }
        fprint(2, "xmr: received %ld bytes\n", bytes);
        exits(0);
}

void
send(int byte)
{
        uchar c;

        c = byte;
        if(write(1, &c, 1) != 1){
                fprint(2, "xmr: hungup\n");
                exits("hangup");
        }
}

int
readupto(uchar *a, int len)
{
        int n;
        int sofar;

        for(sofar = 0; sofar < len; sofar += n){
                n = read(0, a, len-sofar);
                if(n <= 0){
                        send(Nak);
                        return sofar;
                }
                if(*a == Eot || *a == Cancel)
                        return sofar + n;
                a += n;
        }
        return sofar;

}

int
receive(int fd, uchar seqno)
{
        uchar buf[128+4];
        uchar sum;
        uchar *p;
        int n;
        int tries;
        int have;

        for(have = 0, tries = 0;; tries++){
                if(debug)
                        fprint(dfd, "have == %d\n", have);
                if(tries > 10){
                        fprint(2, "xmr: timed out\n");
                        if(debug)
                                close(dfd);
                        exits("timeout");
                }

                /* try to gather up a block */
                alarm(15*1000);
                n = readupto(&buf[have], 132-have);
                alarm(0);
                have += n;
                if(have){
                        switch(buf[0]){
                        case Eot:
                                send(Ack);
                                return -1;
                        case Cancel:
                                fprint(2, "xmr: transfer aborted by sender\n");
                                exits("cancel");
                        }
                }
                if(have != 132)
                        continue;

                /* checksum */
                for(p = buf, sum = 0; p < &buf[128+3]; p++)
                        sum += *p;

                /* If invalid block, resynchronize */
                if(buf[0] != Soh || buf[2] != (255-buf[1]) || sum != buf[131]){
                        if(debug){
                                fprint(dfd, "resync %2.2ux %d %d %ux %ux\n", buf[0],
                                        buf[1], buf[2], sum, buf[131]);
                                write(dfd, (char*)buf+3, 128);
                                fprint(dfd, "\n");
                        }
                        p = memchr(buf+1, Soh, 131);
                        if(p){
                                have = 132-(p-buf);
                                memmove(buf, p, have);
                        } else
                                have = 0;
                        continue;
                }

                /* it's probably a real block, so dump it if there's an error */
                have = 0;

                /* if this is the last block, ack */
                if(buf[1] == seqno-1){
                        tries = 0;
                        send(Ack);
                }else if(buf[1] == seqno){
                        if(debug)
                                fprint(dfd, "Ack\n");
                        send(Ack);
                        if(write(fd, buf+3, 128) != 128){
                                fprint(2, "xmr: abort, error writing file\n");
                                exits("write");
                        }
                        return 0;
                } else {
                        send(Nak);
                }
        }
}

int
notifyf(void *a, char *msg)
{
        USED(a);
        if(strcmp(msg, "alarm") == 0)
                return 1;
        return 0;
}