1/*-
2 * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * This software is derived from software provide by Kwikbyte who specifically
25 * disclaimed copyright on the code.  This version of xmodem has been nearly
26 * completely rewritten, but the CRC is from the original.
27 *
28 * $FreeBSD$
29 */
30
31#include "lib.h"
32
33#define PACKET_SIZE	128
34
35/* Line control codes */
36#define SOH			0x01	/* start of header */
37#define ACK			0x06	/* Acknowledge */
38#define NAK			0x15	/* Negative acknowledge */
39#define CAN			0x18	/* Cancel */
40#define EOT			0x04	/* end of text */
41
42#define TO	10
43/*
44 * int GetRecord(char , char *)
45 *  This private function receives a x-modem record to the pointer and
46 * returns non-zero on success.
47 */
48static int
49GetRecord(char blocknum, char *dest)
50{
51	int		size;
52	int		ch;
53	unsigned	chk, j;
54
55	chk = 0;
56
57	if ((ch = getc(TO)) == -1)
58		goto err;
59	if (ch != blocknum)
60		goto err;
61	if ((ch = getc(TO)) == -1)
62		goto err;
63	if (ch != (~blocknum & 0xff))
64		goto err;
65
66	for (size = 0; size < PACKET_SIZE; ++size) {
67		if ((ch = getc(TO)) == -1)
68			goto err;
69		chk = chk ^ ch << 8;
70		for (j = 0; j < 8; ++j) {
71			if (chk & 0x8000)
72				chk = chk << 1 ^ 0x1021;
73			else
74				chk = chk << 1;
75		}
76		*dest++ = ch;
77	}
78
79	chk &= 0xFFFF;
80
81	if (((ch = getc(TO)) == -1) || ((ch & 0xff) != ((chk >> 8) & 0xFF)))
82		goto err;
83	if (((ch = getc(TO)) == -1) || ((ch & 0xff) != (chk & 0xFF)))
84		goto err;
85	putchar(ACK);
86
87	return (1);
88err:;
89	putchar(CAN);
90	// We should allow for resend, but we don't.
91	return (0);
92}
93
94/*
95 * int xmodem_rx(char *)
96 *  This global function receives a x-modem transmission consisting of
97 * (potentially) several blocks.  Returns the number of bytes received or
98 * -1 on error.
99 */
100int
101xmodem_rx(char *dest)
102{
103	int		starting, ch;
104	char		packetNumber, *startAddress = dest;
105
106	packetNumber = 1;
107	starting = 1;
108
109	while (1) {
110		if (starting)
111			putchar('C');
112		if (((ch = getc(1)) == -1) || (ch != SOH && ch != EOT))
113			continue;
114		if (ch == EOT) {
115			putchar(ACK);
116			return (dest - startAddress);
117		}
118		starting = 0;
119		// Xmodem packets: SOH PKT# ~PKT# 128-bytes CRC16
120		if (!GetRecord(packetNumber, dest))
121			return (-1);
122		dest += PACKET_SIZE;
123		packetNumber++;
124	}
125
126	// the loop above should return in all cases
127	return (-1);
128}
129