13229Spst/*-
23229Spst * Copyright (c) 2006 Sam Leffler, Errno Consulting
33229Spst * All rights reserved.
43229Spst *
53229Spst * Redistribution and use in source and binary forms, with or without
63229Spst * modification, are permitted provided that the following conditions
73229Spst * are met:
83229Spst * 1. Redistributions of source code must retain the above copyright
93229Spst *    notice, this list of conditions and the following disclaimer,
103229Spst *    without modification.
113229Spst * 2. Redistributions in binary form must reproduce at minimum a disclaimer
123229Spst *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
133229Spst *    redistribution must be conditioned upon including a substantially
143229Spst *    similar Disclaimer requirement for further binary redistribution.
153229Spst *
163229Spst * NO WARRANTY
173229Spst * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
183229Spst * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
193229Spst * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
203229Spst * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
2118471Swosch * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
2250476Speter * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2318471Swosch * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
243229Spst * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
253229Spst * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
263229Spst * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
273229Spst * THE POSSIBILITY OF SUCH DAMAGES.
283229Spst *
293229Spst * $FreeBSD$
303229Spst */
313229Spst
323229Spst/*
333229Spst * Atheros AR5523 USB Station Firmware downloader.
343229Spst *
353229Spst *    uathload -d ugen-device [firmware-file]
363229Spst *
373229Spst * Intended to be called from devd on device discovery.
383229Spst */
393229Spst#include <sys/types.h>
403229Spst#include <sys/stat.h>
413229Spst#include <sys/endian.h>
423229Spst#include <sys/mman.h>
433229Spst
443229Spst#include <sys/ioctl.h>
453229Spst#include <dev/usb/usb.h>
463229Spst#include <dev/usb/usb_ioctl.h>
473229Spst
483229Spst#include <err.h>
493229Spst#include <fcntl.h>
503229Spst#include <libgen.h>
513229Spst#include <paths.h>
523229Spst#include <stdio.h>
533229Spst#include <string.h>
543229Spst#include <strings.h>
553229Spst#include <unistd.h>
563229Spst
573229Spst/* all fields are big endian */
583229Spststruct uath_fwmsg {
593229Spst	uint32_t	flags;
603229Spst#define UATH_WRITE_BLOCK	(1 << 4)
613229Spst
623229Spst	uint32_t	len;
633229Spst#define UATH_MAX_FWBLOCK_SIZE	2048
6497417Salfred
653229Spst	uint32_t	total;
663229Spst	uint32_t	remain;
673229Spst	uint32_t	rxtotal;
683229Spst	uint32_t	pad[123];
693229Spst} __packed;
703229Spst
713229Spst#define UATH_DATA_TIMEOUT	10000
723229Spst#define UATH_CMD_TIMEOUT	1000
733229Spst
743229Spst#define	VERBOSE(_fmt, ...) do {			\
753229Spst	if (verbose) {				\
763229Spst		printf(_fmt, __VA_ARGS__);	\
773229Spst		fflush(stdout);			\
783229Spst	}					\
793229Spst} while (0)
803229Spst
813229Spstextern	uint8_t _binary_ar5523_bin_start;
823229Spstextern	uint8_t _binary_ar5523_bin_end;
833229Spst
843229Spststatic int
853229Spstgetdevname(const char *devname, char *msgdev, char *datadev)
863229Spst{
873229Spst	char *bn, *dn;
883229Spst
893229Spst	dn = dirname(devname);
903229Spst	if (dn == NULL)
913229Spst		return (-1);
923229Spst	bn = basename(devname);
933229Spst	if (bn == NULL || strncmp(bn, "ugen", 4))
943229Spst		return (-1);
953229Spst	bn += 4;
963229Spst
973229Spst	/* NB: pipes are hardcoded */
983229Spst	snprintf(msgdev, 256, "%s/usb/%s.1", dn, bn);
993229Spst	snprintf(datadev, 256, "%s/usb/%s.2", dn, bn);
1003229Spst	return (0);
1013229Spst}
1023229Spst
1033229Spststatic void
1043229Spstusage(void)
1053229Spst{
1063229Spst	errx(-1, "usage: uathload [-v] -d devname [firmware]");
1073229Spst}
1083229Spst
1093229Spstint
1103229Spstmain(int argc, char *argv[])
1113229Spst{
1123229Spst	const char *fwname, *devname;
1133229Spst	char msgdev[256], datadev[256];
1143229Spst	struct uath_fwmsg txmsg, rxmsg;
1153229Spst	char *txdata;
1163229Spst	struct stat sb;
1173229Spst	int msg, data, fw, timeout, b, c;
1183229Spst	int bufsize = 512, verbose = 0;
1193229Spst	ssize_t len;
1203229Spst
1213229Spst	devname = NULL;
1223229Spst	while ((c = getopt(argc, argv, "d:v")) != -1) {
1233229Spst		switch (c) {
1243229Spst		case 'd':
1253229Spst			devname = optarg;
1263229Spst			break;
1273229Spst		case 'v':
1283229Spst			verbose = 1;
1293229Spst			break;
1303229Spst		default:
1313229Spst			usage();
1323229Spst			/*NOTREACHED*/
1333229Spst		}
1343229Spst	}
1353229Spst	argc -= optind;
1363229Spst	argv += optind;
1373229Spst
1383229Spst	if (devname == NULL)
1393229Spst		errx(-1, "No device name; use -d to specify the ugen device");
1403229Spst	if (argc > 1)
1413229Spst		usage();
1423229Spst
1433229Spst	if (argc == 1) {
1443229Spst		fwname = argv[0];
1453229Spst		fw = open(fwname, O_RDONLY, 0);
1463229Spst		if (fw < 0)
1473229Spst			err(-1, "open(%s)", fwname);
1483229Spst		if (fstat(fw, &sb) < 0)
1493229Spst			err(-1, "fstat(%s)", fwname);
1503229Spst		txdata = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fw, 0);
1513229Spst		if (txdata == MAP_FAILED)
1523229Spst			err(-1, "mmap(%s)", fwname);
1533229Spst		len = sb.st_size;
1543229Spst	} else {
1553229Spst		fwname = "ar5523.bin (builtin)";
1563229Spst		fw = -1;
1573229Spst		txdata = &_binary_ar5523_bin_start;
1583229Spst		len = &_binary_ar5523_bin_end - &_binary_ar5523_bin_start;
1593229Spst	}
1603229Spst	/* XXX verify device is an AR5005 part */
1613229Spst	if (getdevname(devname, msgdev, datadev))
1623229Spst		err(-1, "getdevname error");
1633229Spst
1643229Spst	msg = open(msgdev, O_RDWR, 0);
1653229Spst	if (msg < 0)
1663229Spst		err(-1, "open(%s)", msgdev);
1673229Spst	timeout = UATH_DATA_TIMEOUT;
1683229Spst	if (ioctl(msg, USB_SET_RX_TIMEOUT, &timeout) < 0)
1693229Spst		err(-1, "%s: USB_SET_RX_TIMEOUT(%u)", msgdev, UATH_DATA_TIMEOUT);
1703229Spst	if (ioctl(msg, USB_SET_RX_BUFFER_SIZE, &bufsize) < 0)
1713229Spst		err(-1, "%s: USB_SET_RX_BUFFER_SIZE(%u)", msgdev, bufsize);
1723229Spst
1733229Spst	data = open(datadev, O_WRONLY, 0);
1743229Spst	if (data < 0)
1753229Spst		err(-1, "open(%s)", datadev);
1763229Spst	timeout = UATH_DATA_TIMEOUT;
1773229Spst	if (ioctl(data, USB_SET_TX_TIMEOUT, &timeout) < 0)
1783229Spst		err(-1, "%s: USB_SET_TX_TIMEOUT(%u)", datadev,
1793229Spst		    UATH_DATA_TIMEOUT);
1803229Spst
1813229Spst	VERBOSE("Load firmware %s to %s\n", fwname, devname);
1823229Spst
1833229Spst	bzero(&txmsg, sizeof (struct uath_fwmsg));
1843229Spst	txmsg.flags = htobe32(UATH_WRITE_BLOCK);
1853229Spst	txmsg.total = htobe32(len);
1863229Spst
1873229Spst	b = 0;
1883229Spst	while (len > 0) {
1893229Spst		int mlen;
1903229Spst
1913229Spst		mlen = len;
1923229Spst		if (mlen > UATH_MAX_FWBLOCK_SIZE)
1933229Spst			mlen = UATH_MAX_FWBLOCK_SIZE;
1943229Spst		txmsg.remain = htobe32(len - mlen);
1953229Spst		txmsg.len = htobe32(mlen);
1963229Spst
1973229Spst		/* send firmware block meta-data */
1983229Spst		VERBOSE("send block %2u: %zd bytes remaining", b, len - mlen);
1993229Spst		if (write(msg, &txmsg, sizeof(txmsg)) != sizeof(txmsg)) {
2003229Spst			VERBOSE("%s", "\n");
2013229Spst			err(-1, "error sending msg (%s)", msgdev);
2023229Spst			break;
2033229Spst		}
2043229Spst
2053229Spst		/* send firmware block data */
2063229Spst		VERBOSE("%s", "\n             : data...");
2073229Spst		if (write(data, txdata, mlen) != mlen) {
2083229Spst			VERBOSE("%s", "\n");
2093229Spst			err(-1, "error sending data (%s)", datadev);
2103229Spst			break;
2113229Spst		}
2123229Spst
2133229Spst		/* wait for ack from firmware */
2143229Spst		VERBOSE("%s", "\n             : wait for ack...");
2153229Spst		bzero(&rxmsg, sizeof(rxmsg));
2163229Spst		if (read(msg, &rxmsg, sizeof(rxmsg)) != sizeof(rxmsg)) {
2173229Spst			VERBOSE("%s", "\n");
2183229Spst			err(-1, "error reading msg (%s)", msgdev);
2193229Spst			break;
2203229Spst		}
2213229Spst
2223229Spst		VERBOSE("flags=0x%x total=%d\n",
2233229Spst		    be32toh(rxmsg.flags), be32toh(rxmsg.rxtotal));
2243229Spst		len -= mlen;
2253229Spst		txdata += mlen;
2263229Spst		b++;
2273229Spst	}
2283229Spst	sleep(1);
2293229Spst	close(fw);
2303229Spst	close(msg);
2313229Spst	close(data);
2323229Spst	return 0;
2333229Spst}
2343229Spst