1157873Simp/*-
2157873Simp * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3157873Simp *
4157873Simp * Redistribution and use in source and binary forms, with or without
5157873Simp * modification, are permitted provided that the following conditions
6157873Simp * are met:
7157873Simp * 1. Redistributions of source code must retain the above copyright
8157873Simp *    notice, this list of conditions and the following disclaimer.
9157873Simp * 2. Redistributions in binary form must reproduce the above copyright
10157873Simp *    notice, this list of conditions and the following disclaimer in the
11157873Simp *    documentation and/or other materials provided with the distribution.
12157873Simp *
13157873Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14157873Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15157873Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16157873Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17157873Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18157873Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19157873Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20157873Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21157873Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22157873Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23157873Simp *
24157873Simp * This software is derived from software provide by Kwikbyte who specifically
25157873Simp * disclaimed copyright on the code.  This version of xmodem has been nearly
26157873Simp * completely rewritten, but the CRC is from the original.
27157873Simp *
28157873Simp * $FreeBSD$
29157873Simp */
30157873Simp
31157873Simp#include "lib.h"
32157873Simp
33157873Simp#define PACKET_SIZE	128
34157873Simp
35157873Simp/* Line control codes */
36157873Simp#define SOH			0x01	/* start of header */
37157873Simp#define ACK			0x06	/* Acknowledge */
38157873Simp#define NAK			0x15	/* Negative acknowledge */
39157873Simp#define CAN			0x18	/* Cancel */
40157873Simp#define EOT			0x04	/* end of text */
41157873Simp
42161190Simp#define TO	10
43157873Simp/*
44157873Simp * int GetRecord(char , char *)
45157873Simp *  This private function receives a x-modem record to the pointer and
46157873Simp * returns non-zero on success.
47157873Simp */
48157873Simpstatic int
49157873SimpGetRecord(char blocknum, char *dest)
50157873Simp{
51157873Simp	int		size;
52157873Simp	int		ch;
53157873Simp	unsigned	chk, j;
54157873Simp
55157873Simp	chk = 0;
56157873Simp
57161190Simp	if ((ch = getc(TO)) == -1)
58157873Simp		goto err;
59161190Simp	if (ch != blocknum)
60157873Simp		goto err;
61161190Simp	if ((ch = getc(TO)) == -1)
62157873Simp		goto err;
63157873Simp	if (ch != (~blocknum & 0xff))
64157873Simp		goto err;
65157873Simp
66157873Simp	for (size = 0; size < PACKET_SIZE; ++size) {
67161190Simp		if ((ch = getc(TO)) == -1)
68157873Simp			goto err;
69157873Simp		chk = chk ^ ch << 8;
70157873Simp		for (j = 0; j < 8; ++j) {
71157873Simp			if (chk & 0x8000)
72157873Simp				chk = chk << 1 ^ 0x1021;
73157873Simp			else
74157873Simp				chk = chk << 1;
75157873Simp		}
76157873Simp		*dest++ = ch;
77157873Simp	}
78157873Simp
79157873Simp	chk &= 0xFFFF;
80157873Simp
81161190Simp	if (((ch = getc(TO)) == -1) || ((ch & 0xff) != ((chk >> 8) & 0xFF)))
82161190Simp		goto err;
83161190Simp	if (((ch = getc(TO)) == -1) || ((ch & 0xff) != (chk & 0xFF)))
84161190Simp		goto err;
85157873Simp	putchar(ACK);
86157873Simp
87157873Simp	return (1);
88157873Simperr:;
89157873Simp	putchar(CAN);
90157873Simp	// We should allow for resend, but we don't.
91157873Simp	return (0);
92157873Simp}
93157873Simp
94157873Simp/*
95157873Simp * int xmodem_rx(char *)
96157873Simp *  This global function receives a x-modem transmission consisting of
97157873Simp * (potentially) several blocks.  Returns the number of bytes received or
98157873Simp * -1 on error.
99157873Simp */
100157873Simpint
101157873Simpxmodem_rx(char *dest)
102157873Simp{
103157873Simp	int		starting, ch;
104157873Simp	char		packetNumber, *startAddress = dest;
105157873Simp
106157873Simp	packetNumber = 1;
107157873Simp	starting = 1;
108157873Simp
109157873Simp	while (1) {
110157873Simp		if (starting)
111157873Simp			putchar('C');
112157873Simp		if (((ch = getc(1)) == -1) || (ch != SOH && ch != EOT))
113157873Simp			continue;
114157873Simp		if (ch == EOT) {
115157873Simp			putchar(ACK);
116157873Simp			return (dest - startAddress);
117157873Simp		}
118157873Simp		starting = 0;
119157873Simp		// Xmodem packets: SOH PKT# ~PKT# 128-bytes CRC16
120157873Simp		if (!GetRecord(packetNumber, dest))
121157873Simp			return (-1);
122157873Simp		dest += PACKET_SIZE;
123157873Simp		packetNumber++;
124157873Simp	}
125157873Simp
126157873Simp	// the loop above should return in all cases
127157873Simp	return (-1);
128157873Simp}
129