1190688Sweongyo/*-
2190688Sweongyo * Copyright (c) 2006 Sam Leffler, Errno Consulting
3190688Sweongyo * All rights reserved.
4190688Sweongyo *
5190688Sweongyo * Redistribution and use in source and binary forms, with or without
6190688Sweongyo * modification, are permitted provided that the following conditions
7190688Sweongyo * are met:
8190688Sweongyo * 1. Redistributions of source code must retain the above copyright
9190688Sweongyo *    notice, this list of conditions and the following disclaimer,
10190688Sweongyo *    without modification.
11190688Sweongyo * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12190688Sweongyo *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13190688Sweongyo *    redistribution must be conditioned upon including a substantially
14190688Sweongyo *    similar Disclaimer requirement for further binary redistribution.
15190688Sweongyo *
16190688Sweongyo * NO WARRANTY
17190688Sweongyo * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18190688Sweongyo * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19190688Sweongyo * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20190688Sweongyo * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21190688Sweongyo * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22190688Sweongyo * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23190688Sweongyo * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24190688Sweongyo * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25190688Sweongyo * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26190688Sweongyo * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27190688Sweongyo * THE POSSIBILITY OF SUCH DAMAGES.
28190688Sweongyo *
29190688Sweongyo * $FreeBSD: releng/11.0/usr.sbin/uathload/uathload.c 297205 2016-03-23 04:18:57Z imp $
30190688Sweongyo */
31190688Sweongyo
32190688Sweongyo/*
33190688Sweongyo * Atheros AR5523 USB Station Firmware downloader.
34190688Sweongyo *
35190688Sweongyo *    uathload -d ugen-device [firmware-file]
36190688Sweongyo *
37190688Sweongyo * Intended to be called from devd on device discovery.
38190688Sweongyo */
39190688Sweongyo#include <sys/types.h>
40190688Sweongyo#include <sys/stat.h>
41190688Sweongyo#include <sys/endian.h>
42190688Sweongyo#include <sys/mman.h>
43190688Sweongyo
44190688Sweongyo#include <sys/ioctl.h>
45190688Sweongyo#include <dev/usb/usb.h>
46190688Sweongyo#include <dev/usb/usb_ioctl.h>
47190688Sweongyo
48190688Sweongyo#include <err.h>
49190688Sweongyo#include <fcntl.h>
50190688Sweongyo#include <libgen.h>
51190688Sweongyo#include <paths.h>
52190688Sweongyo#include <stdio.h>
53190688Sweongyo#include <string.h>
54190688Sweongyo#include <strings.h>
55190688Sweongyo#include <unistd.h>
56190688Sweongyo
57190688Sweongyo/* all fields are big endian */
58190688Sweongyostruct uath_fwmsg {
59190688Sweongyo	uint32_t	flags;
60190688Sweongyo#define UATH_WRITE_BLOCK	(1 << 4)
61190688Sweongyo
62190688Sweongyo	uint32_t	len;
63190688Sweongyo#define UATH_MAX_FWBLOCK_SIZE	2048
64190688Sweongyo
65190688Sweongyo	uint32_t	total;
66190688Sweongyo	uint32_t	remain;
67190688Sweongyo	uint32_t	rxtotal;
68190688Sweongyo	uint32_t	pad[123];
69190688Sweongyo} __packed;
70190688Sweongyo
71190688Sweongyo#define UATH_DATA_TIMEOUT	10000
72190688Sweongyo#define UATH_CMD_TIMEOUT	1000
73190688Sweongyo
74190688Sweongyo#define	VERBOSE(_fmt, ...) do {			\
75190688Sweongyo	if (verbose) {				\
76190688Sweongyo		printf(_fmt, __VA_ARGS__);	\
77190688Sweongyo		fflush(stdout);			\
78190688Sweongyo	}					\
79190688Sweongyo} while (0)
80190688Sweongyo
81190688Sweongyoextern	uint8_t _binary_ar5523_bin_start;
82190688Sweongyoextern	uint8_t _binary_ar5523_bin_end;
83190688Sweongyo
84190688Sweongyostatic int
85190688Sweongyogetdevname(const char *devname, char *msgdev, char *datadev)
86190688Sweongyo{
87190688Sweongyo	char *bn, *dn;
88190688Sweongyo
89190688Sweongyo	dn = dirname(devname);
90190688Sweongyo	if (dn == NULL)
91190688Sweongyo		return (-1);
92190688Sweongyo	bn = basename(devname);
93190688Sweongyo	if (bn == NULL || strncmp(bn, "ugen", 4))
94190688Sweongyo		return (-1);
95190688Sweongyo	bn += 4;
96190688Sweongyo
97190688Sweongyo	/* NB: pipes are hardcoded */
98190688Sweongyo	snprintf(msgdev, 256, "%s/usb/%s.1", dn, bn);
99190688Sweongyo	snprintf(datadev, 256, "%s/usb/%s.2", dn, bn);
100190688Sweongyo	return (0);
101190688Sweongyo}
102190688Sweongyo
103190688Sweongyostatic void
104190688Sweongyousage(void)
105190688Sweongyo{
106190688Sweongyo	errx(-1, "usage: uathload [-v] -d devname [firmware]");
107190688Sweongyo}
108190688Sweongyo
109190688Sweongyoint
110190688Sweongyomain(int argc, char *argv[])
111190688Sweongyo{
112190688Sweongyo	const char *fwname, *devname;
113190688Sweongyo	char msgdev[256], datadev[256];
114190688Sweongyo	struct uath_fwmsg txmsg, rxmsg;
115190688Sweongyo	char *txdata;
116190688Sweongyo	struct stat sb;
117190688Sweongyo	int msg, data, fw, timeout, b, c;
118190688Sweongyo	int bufsize = 512, verbose = 0;
119190688Sweongyo	ssize_t len;
120190688Sweongyo
121190688Sweongyo	devname = NULL;
122190688Sweongyo	while ((c = getopt(argc, argv, "d:v")) != -1) {
123190688Sweongyo		switch (c) {
124190688Sweongyo		case 'd':
125190688Sweongyo			devname = optarg;
126190688Sweongyo			break;
127190688Sweongyo		case 'v':
128190688Sweongyo			verbose = 1;
129190688Sweongyo			break;
130190688Sweongyo		default:
131190688Sweongyo			usage();
132190688Sweongyo			/*NOTREACHED*/
133190688Sweongyo		}
134190688Sweongyo	}
135190688Sweongyo	argc -= optind;
136190688Sweongyo	argv += optind;
137190688Sweongyo
138190688Sweongyo	if (devname == NULL)
139190688Sweongyo		errx(-1, "No device name; use -d to specify the ugen device");
140190688Sweongyo	if (argc > 1)
141190688Sweongyo		usage();
142190688Sweongyo
143296889Simp	if (argc == 1)
144190688Sweongyo		fwname = argv[0];
145296889Simp	else
146297205Simp		fwname = _PATH_FIRMWARE "/ar5523.bin";
147296889Simp	fw = open(fwname, O_RDONLY, 0);
148296889Simp	if (fw < 0)
149296889Simp		err(-1, "open(%s)", fwname);
150296889Simp	if (fstat(fw, &sb) < 0)
151296889Simp		err(-1, "fstat(%s)", fwname);
152296889Simp	txdata = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fw, 0);
153296889Simp	if (txdata == MAP_FAILED)
154296889Simp		err(-1, "mmap(%s)", fwname);
155296889Simp	len = sb.st_size;
156190688Sweongyo	/* XXX verify device is an AR5005 part */
157190688Sweongyo	if (getdevname(devname, msgdev, datadev))
158190688Sweongyo		err(-1, "getdevname error");
159190688Sweongyo
160190688Sweongyo	msg = open(msgdev, O_RDWR, 0);
161190688Sweongyo	if (msg < 0)
162190688Sweongyo		err(-1, "open(%s)", msgdev);
163190688Sweongyo	timeout = UATH_DATA_TIMEOUT;
164190688Sweongyo	if (ioctl(msg, USB_SET_RX_TIMEOUT, &timeout) < 0)
165190688Sweongyo		err(-1, "%s: USB_SET_RX_TIMEOUT(%u)", msgdev, UATH_DATA_TIMEOUT);
166190688Sweongyo	if (ioctl(msg, USB_SET_RX_BUFFER_SIZE, &bufsize) < 0)
167190688Sweongyo		err(-1, "%s: USB_SET_RX_BUFFER_SIZE(%u)", msgdev, bufsize);
168190688Sweongyo
169190688Sweongyo	data = open(datadev, O_WRONLY, 0);
170190688Sweongyo	if (data < 0)
171190688Sweongyo		err(-1, "open(%s)", datadev);
172190688Sweongyo	timeout = UATH_DATA_TIMEOUT;
173190688Sweongyo	if (ioctl(data, USB_SET_TX_TIMEOUT, &timeout) < 0)
174190688Sweongyo		err(-1, "%s: USB_SET_TX_TIMEOUT(%u)", datadev,
175190688Sweongyo		    UATH_DATA_TIMEOUT);
176190688Sweongyo
177190688Sweongyo	VERBOSE("Load firmware %s to %s\n", fwname, devname);
178190688Sweongyo
179190688Sweongyo	bzero(&txmsg, sizeof (struct uath_fwmsg));
180190688Sweongyo	txmsg.flags = htobe32(UATH_WRITE_BLOCK);
181190688Sweongyo	txmsg.total = htobe32(len);
182190688Sweongyo
183190688Sweongyo	b = 0;
184190688Sweongyo	while (len > 0) {
185190688Sweongyo		int mlen;
186190688Sweongyo
187190688Sweongyo		mlen = len;
188190688Sweongyo		if (mlen > UATH_MAX_FWBLOCK_SIZE)
189190688Sweongyo			mlen = UATH_MAX_FWBLOCK_SIZE;
190190688Sweongyo		txmsg.remain = htobe32(len - mlen);
191190688Sweongyo		txmsg.len = htobe32(mlen);
192190688Sweongyo
193190688Sweongyo		/* send firmware block meta-data */
194190688Sweongyo		VERBOSE("send block %2u: %zd bytes remaining", b, len - mlen);
195190688Sweongyo		if (write(msg, &txmsg, sizeof(txmsg)) != sizeof(txmsg)) {
196190688Sweongyo			VERBOSE("%s", "\n");
197190688Sweongyo			err(-1, "error sending msg (%s)", msgdev);
198190688Sweongyo			break;
199190688Sweongyo		}
200190688Sweongyo
201190688Sweongyo		/* send firmware block data */
202190688Sweongyo		VERBOSE("%s", "\n             : data...");
203190688Sweongyo		if (write(data, txdata, mlen) != mlen) {
204190688Sweongyo			VERBOSE("%s", "\n");
205190688Sweongyo			err(-1, "error sending data (%s)", datadev);
206190688Sweongyo			break;
207190688Sweongyo		}
208190688Sweongyo
209190688Sweongyo		/* wait for ack from firmware */
210190688Sweongyo		VERBOSE("%s", "\n             : wait for ack...");
211190688Sweongyo		bzero(&rxmsg, sizeof(rxmsg));
212190688Sweongyo		if (read(msg, &rxmsg, sizeof(rxmsg)) != sizeof(rxmsg)) {
213190688Sweongyo			VERBOSE("%s", "\n");
214190688Sweongyo			err(-1, "error reading msg (%s)", msgdev);
215190688Sweongyo			break;
216190688Sweongyo		}
217190688Sweongyo
218190688Sweongyo		VERBOSE("flags=0x%x total=%d\n",
219190688Sweongyo		    be32toh(rxmsg.flags), be32toh(rxmsg.rxtotal));
220190688Sweongyo		len -= mlen;
221190688Sweongyo		txdata += mlen;
222190688Sweongyo		b++;
223190688Sweongyo	}
224190688Sweongyo	sleep(1);
225190688Sweongyo	close(fw);
226190688Sweongyo	close(msg);
227190688Sweongyo	close(data);
228190688Sweongyo	return 0;
229190688Sweongyo}
230