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$
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
143190688Sweongyo	if (argc == 1) {
144190688Sweongyo		fwname = argv[0];
145190688Sweongyo		fw = open(fwname, O_RDONLY, 0);
146190688Sweongyo		if (fw < 0)
147190688Sweongyo			err(-1, "open(%s)", fwname);
148190688Sweongyo		if (fstat(fw, &sb) < 0)
149190688Sweongyo			err(-1, "fstat(%s)", fwname);
150190688Sweongyo		txdata = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fw, 0);
151190688Sweongyo		if (txdata == MAP_FAILED)
152190688Sweongyo			err(-1, "mmap(%s)", fwname);
153190688Sweongyo		len = sb.st_size;
154190688Sweongyo	} else {
155190688Sweongyo		fwname = "ar5523.bin (builtin)";
156190688Sweongyo		fw = -1;
157190688Sweongyo		txdata = &_binary_ar5523_bin_start;
158190688Sweongyo		len = &_binary_ar5523_bin_end - &_binary_ar5523_bin_start;
159190688Sweongyo	}
160190688Sweongyo	/* XXX verify device is an AR5005 part */
161190688Sweongyo	if (getdevname(devname, msgdev, datadev))
162190688Sweongyo		err(-1, "getdevname error");
163190688Sweongyo
164190688Sweongyo	msg = open(msgdev, O_RDWR, 0);
165190688Sweongyo	if (msg < 0)
166190688Sweongyo		err(-1, "open(%s)", msgdev);
167190688Sweongyo	timeout = UATH_DATA_TIMEOUT;
168190688Sweongyo	if (ioctl(msg, USB_SET_RX_TIMEOUT, &timeout) < 0)
169190688Sweongyo		err(-1, "%s: USB_SET_RX_TIMEOUT(%u)", msgdev, UATH_DATA_TIMEOUT);
170190688Sweongyo	if (ioctl(msg, USB_SET_RX_BUFFER_SIZE, &bufsize) < 0)
171190688Sweongyo		err(-1, "%s: USB_SET_RX_BUFFER_SIZE(%u)", msgdev, bufsize);
172190688Sweongyo
173190688Sweongyo	data = open(datadev, O_WRONLY, 0);
174190688Sweongyo	if (data < 0)
175190688Sweongyo		err(-1, "open(%s)", datadev);
176190688Sweongyo	timeout = UATH_DATA_TIMEOUT;
177190688Sweongyo	if (ioctl(data, USB_SET_TX_TIMEOUT, &timeout) < 0)
178190688Sweongyo		err(-1, "%s: USB_SET_TX_TIMEOUT(%u)", datadev,
179190688Sweongyo		    UATH_DATA_TIMEOUT);
180190688Sweongyo
181190688Sweongyo	VERBOSE("Load firmware %s to %s\n", fwname, devname);
182190688Sweongyo
183190688Sweongyo	bzero(&txmsg, sizeof (struct uath_fwmsg));
184190688Sweongyo	txmsg.flags = htobe32(UATH_WRITE_BLOCK);
185190688Sweongyo	txmsg.total = htobe32(len);
186190688Sweongyo
187190688Sweongyo	b = 0;
188190688Sweongyo	while (len > 0) {
189190688Sweongyo		int mlen;
190190688Sweongyo
191190688Sweongyo		mlen = len;
192190688Sweongyo		if (mlen > UATH_MAX_FWBLOCK_SIZE)
193190688Sweongyo			mlen = UATH_MAX_FWBLOCK_SIZE;
194190688Sweongyo		txmsg.remain = htobe32(len - mlen);
195190688Sweongyo		txmsg.len = htobe32(mlen);
196190688Sweongyo
197190688Sweongyo		/* send firmware block meta-data */
198190688Sweongyo		VERBOSE("send block %2u: %zd bytes remaining", b, len - mlen);
199190688Sweongyo		if (write(msg, &txmsg, sizeof(txmsg)) != sizeof(txmsg)) {
200190688Sweongyo			VERBOSE("%s", "\n");
201190688Sweongyo			err(-1, "error sending msg (%s)", msgdev);
202190688Sweongyo			break;
203190688Sweongyo		}
204190688Sweongyo
205190688Sweongyo		/* send firmware block data */
206190688Sweongyo		VERBOSE("%s", "\n             : data...");
207190688Sweongyo		if (write(data, txdata, mlen) != mlen) {
208190688Sweongyo			VERBOSE("%s", "\n");
209190688Sweongyo			err(-1, "error sending data (%s)", datadev);
210190688Sweongyo			break;
211190688Sweongyo		}
212190688Sweongyo
213190688Sweongyo		/* wait for ack from firmware */
214190688Sweongyo		VERBOSE("%s", "\n             : wait for ack...");
215190688Sweongyo		bzero(&rxmsg, sizeof(rxmsg));
216190688Sweongyo		if (read(msg, &rxmsg, sizeof(rxmsg)) != sizeof(rxmsg)) {
217190688Sweongyo			VERBOSE("%s", "\n");
218190688Sweongyo			err(-1, "error reading msg (%s)", msgdev);
219190688Sweongyo			break;
220190688Sweongyo		}
221190688Sweongyo
222190688Sweongyo		VERBOSE("flags=0x%x total=%d\n",
223190688Sweongyo		    be32toh(rxmsg.flags), be32toh(rxmsg.rxtotal));
224190688Sweongyo		len -= mlen;
225190688Sweongyo		txdata += mlen;
226190688Sweongyo		b++;
227190688Sweongyo	}
228190688Sweongyo	sleep(1);
229190688Sweongyo	close(fw);
230190688Sweongyo	close(msg);
231190688Sweongyo	close(data);
232190688Sweongyo	return 0;
233190688Sweongyo}
234