1215195Semax/*
2215195Semax * ath3kfw.c
3215195Semax */
4215195Semax
5215195Semax/*-
6215195Semax * Copyright (c) 2010 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7215195Semax * All rights reserved.
8215195Semax *
9215195Semax * Redistribution and use in source and binary forms, with or without
10215195Semax * modification, are permitted provided that the following conditions
11215195Semax * are met:
12215195Semax * 1. Redistributions of source code must retain the above copyright
13215195Semax *    notice, this list of conditions and the following disclaimer.
14215195Semax * 2. Redistributions in binary form must reproduce the above copyright
15215195Semax *    notice, this list of conditions and the following disclaimer in the
16215195Semax *    documentation and/or other materials provided with the distribution.
17215195Semax *
18215195Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19215195Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20215195Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21215195Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22215195Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23215195Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24215195Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25215195Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26215195Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27215195Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28215195Semax * SUCH DAMAGE.
29215195Semax *
30215195Semax * $FreeBSD$
31215195Semax */
32215195Semax
33215195Semax#include <sys/types.h>
34215195Semax#include <errno.h>
35215195Semax#include <fcntl.h>
36215195Semax#include <libusb20_desc.h>
37215195Semax#include <libusb20.h>
38215195Semax#include <stdarg.h>
39215195Semax#include <stdio.h>
40215195Semax#include <stdlib.h>
41215195Semax#include <string.h>
42215195Semax#include <syslog.h>
43215195Semax#include <unistd.h>
44215195Semax
45215195Semax#define ATH3KFW			"ath3kfw"
46215195Semax#define ATH3KFW_VENDOR_ID	0x0cf3
47215195Semax#define ATH3KFW_PRODUCT_ID	0x3000
48215195Semax#define ATH3KFW_FW		"/usr/local/etc/ath3k-1.fw"
49215195Semax#define ATH3KFW_BULK_EP		0x02
50215195Semax#define	ATH3KFW_REQ_DFU_DNLOAD	1
51215195Semax#define	ATH3KFW_MAX_BSIZE	4096
52215195Semax
53215195Semaxstatic int	parse_ugen_name		(char const *ugen, uint8_t *bus,
54215195Semax					 uint8_t *addr);
55215195Semaxstatic int	find_device		(struct libusb20_backend *be,
56215195Semax					 uint8_t bus, uint8_t addr,
57215195Semax					 struct libusb20_device **dev);
58215195Semaxstatic int	download_firmware	(struct libusb20_device *dev,
59215195Semax					 char const *firmware);
60215195Semaxstatic void	usage			(void);
61215195Semax
62249179Sadrianstatic int			vendor_id = ATH3KFW_VENDOR_ID;
63249179Sadrianstatic int			product_id = ATH3KFW_PRODUCT_ID;
64249179Sadrian
65215195Semax/*
66215195Semax * Firmware downloader for Atheros AR3011 based USB Bluetooth devices
67215195Semax */
68215195Semax
69215195Semaxint
70215195Semaxmain(int argc, char **argv)
71215195Semax{
72215195Semax	uint8_t			bus, addr;
73215195Semax	char const		*firmware;
74215195Semax	struct libusb20_backend	*be;
75215195Semax	struct libusb20_device	*dev;
76215195Semax	int			n;
77215195Semax
78215195Semax	openlog(ATH3KFW, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_USER);
79215195Semax
80215195Semax	bus = 0;
81215195Semax	addr = 0;
82215195Semax	firmware = ATH3KFW_FW;
83215195Semax
84249179Sadrian	while ((n = getopt(argc, argv, "d:f:hp:v:")) != -1) {
85215195Semax		switch (n) {
86215195Semax		case 'd': /* ugen device name */
87215195Semax			if (parse_ugen_name(optarg, &bus, &addr) < 0)
88215195Semax				usage();
89215195Semax			break;
90215195Semax
91215195Semax		case 'f': /* firmware file */
92215195Semax			firmware = optarg;
93215195Semax			break;
94249179Sadrian		case 'p': /* product id */
95249179Sadrian			product_id = strtol(optarg, NULL, 0);
96249179Sadrian			break;
97249179Sadrian		case 'v': /* vendor id */
98249179Sadrian			vendor_id = strtol(optarg, NULL, 0);
99249179Sadrian			break;
100215195Semax		case 'h':
101215195Semax		default:
102215195Semax			usage();
103215195Semax			break;
104215195Semax			/* NOT REACHED */
105215195Semax		}
106215195Semax	}
107215195Semax
108215195Semax	be = libusb20_be_alloc_default();
109215195Semax	if (be == NULL) {
110215195Semax		syslog(LOG_ERR, "libusb20_be_alloc_default() failed");
111215195Semax		return (-1);
112215195Semax	}
113215195Semax
114215195Semax	if (find_device(be, bus, addr, &dev) < 0) {
115215195Semax		syslog(LOG_ERR, "ugen%d.%d is not recognized as " \
116215195Semax			"Atheros AR3011 based device " \
117215195Semax			"(possibly caused by lack of permissions)", bus, addr);
118215195Semax		return (-1);
119215195Semax	}
120215195Semax
121215195Semax	if (download_firmware(dev, firmware) < 0) {
122215195Semax		syslog(LOG_ERR, "could not download %s firmare to ugen%d.%d",
123215195Semax			firmware, bus, addr);
124215195Semax		return (-1);
125215195Semax	}
126215195Semax
127215195Semax	libusb20_be_free(be);
128215195Semax	closelog();
129215195Semax
130215195Semax	return (0);
131215195Semax}
132215195Semax
133215195Semax/*
134215195Semax * Parse ugen name and extract device's bus and address
135215195Semax */
136215195Semax
137215195Semaxstatic int
138215195Semaxparse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr)
139215195Semax{
140215195Semax	char	*ep;
141215195Semax
142215195Semax	if (strncmp(ugen, "ugen", 4) != 0)
143215195Semax		return (-1);
144215195Semax
145215195Semax	*bus = (uint8_t) strtoul(ugen + 4, &ep, 10);
146215195Semax	if (*ep != '.')
147215195Semax		return (-1);
148215195Semax
149215195Semax	*addr = (uint8_t) strtoul(ep + 1, &ep, 10);
150215195Semax	if (*ep != '\0')
151215195Semax		return (-1);
152215195Semax
153215195Semax	return (0);
154215195Semax}
155215195Semax
156215195Semax/*
157215195Semax * Find USB device
158215195Semax */
159215195Semax
160215195Semaxstatic int
161215195Semaxfind_device(struct libusb20_backend *be, uint8_t bus, uint8_t addr,
162215195Semax		struct libusb20_device **dev)
163215195Semax{
164215195Semax	struct LIBUSB20_DEVICE_DESC_DECODED	*desc;
165215195Semax
166215195Semax	*dev = NULL;
167215195Semax
168215195Semax	while ((*dev = libusb20_be_device_foreach(be, *dev)) != NULL) {
169215195Semax		if (libusb20_dev_get_bus_number(*dev) != bus ||
170215195Semax		    libusb20_dev_get_address(*dev) != addr)
171215195Semax			continue;
172215195Semax
173215195Semax		desc = libusb20_dev_get_device_desc(*dev);
174215195Semax		if (desc == NULL)
175215195Semax			continue;
176215195Semax
177249179Sadrian		if (desc->idVendor != vendor_id ||
178249179Sadrian		    desc->idProduct != product_id)
179215195Semax			continue;
180215195Semax
181215195Semax		break;
182215195Semax	}
183215195Semax
184215195Semax	return ((*dev == NULL)? -1 : 0);
185215195Semax}
186215195Semax
187215195Semax/*
188215195Semax * Download firmware
189215195Semax */
190215195Semax
191215195Semaxstatic int
192215195Semaxdownload_firmware(struct libusb20_device *dev, char const *firmware)
193215195Semax{
194215195Semax	struct libusb20_transfer		*bulk;
195215195Semax	struct LIBUSB20_CONTROL_SETUP_DECODED	req;
196215195Semax	int					fd, n, error;
197215195Semax	uint8_t					buf[ATH3KFW_MAX_BSIZE];
198215195Semax
199215195Semax	error = -1;
200215195Semax
201215195Semax	if (libusb20_dev_open(dev, 1) != 0) {
202215195Semax		syslog(LOG_ERR, "libusb20_dev_open() failed");
203215195Semax		return (error);
204215195Semax	}
205215195Semax
206215195Semax	if ((bulk = libusb20_tr_get_pointer(dev, 0)) == NULL) {
207215195Semax		syslog(LOG_ERR, "libusb20_tr_get_pointer() failed");
208215195Semax		goto out;
209215195Semax	}
210215195Semax
211215195Semax	if (libusb20_tr_open(bulk, ATH3KFW_MAX_BSIZE, 1, ATH3KFW_BULK_EP) != 0) {
212215195Semax		syslog(LOG_ERR, "libusb20_tr_open(%d, 1, %d) failed",
213215195Semax			ATH3KFW_MAX_BSIZE, ATH3KFW_BULK_EP);
214215195Semax		goto out;
215215195Semax	}
216215195Semax
217215195Semax	if ((fd = open(firmware, O_RDONLY)) < 0) {
218215195Semax		syslog(LOG_ERR, "open(%s) failed. %s",
219215195Semax			firmware, strerror(errno));
220215195Semax		goto out1;
221215195Semax	}
222215195Semax
223215195Semax	n = read(fd, buf, 20);
224215195Semax	if (n != 20) {
225215195Semax		syslog(LOG_ERR, "read(%s, 20) failed. %s",
226215195Semax			firmware, strerror(errno));
227215195Semax		goto out2;
228215195Semax	}
229215195Semax
230215195Semax	LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req);
231215195Semax	req.bmRequestType = LIBUSB20_REQUEST_TYPE_VENDOR;
232215195Semax	req.bRequest = ATH3KFW_REQ_DFU_DNLOAD;
233215195Semax	req.wLength = 20;
234215195Semax
235215195Semax	if (libusb20_dev_request_sync(dev, &req, buf, NULL, 5000, 0) != 0) {
236215195Semax		syslog(LOG_ERR, "libusb20_dev_request_sync() failed");
237215195Semax		goto out2;
238215195Semax	}
239215195Semax
240215195Semax	for (;;) {
241215195Semax		n = read(fd, buf, sizeof(buf));
242215195Semax		if (n < 0) {
243215195Semax			syslog(LOG_ERR, "read(%s, %d) failed. %s",
244215195Semax				firmware, (int) sizeof(buf), strerror(errno));
245215195Semax			goto out2;
246215195Semax		}
247215195Semax		if (n == 0)
248215195Semax			break;
249215195Semax
250215195Semax		libusb20_tr_setup_bulk(bulk, buf, n, 3000);
251215195Semax		libusb20_tr_start(bulk);
252215195Semax
253215195Semax		while (libusb20_dev_process(dev) == 0) {
254215195Semax			if (libusb20_tr_pending(bulk) == 0)
255215195Semax				break;
256215195Semax
257215195Semax			libusb20_dev_wait_process(dev, -1);
258215195Semax		}
259215195Semax
260215195Semax		if (libusb20_tr_get_status(bulk) != 0) {
261215195Semax			syslog(LOG_ERR, "bulk transfer failed with status %d",
262215195Semax				libusb20_tr_get_status(bulk));
263215195Semax			goto out2;
264215195Semax		}
265215195Semax	}
266215195Semax
267215195Semax	error = 0;
268215195Semaxout2:
269215195Semax	close(fd);
270215195Semaxout1:
271215195Semax	libusb20_tr_close(bulk);
272215195Semaxout:
273215195Semax	libusb20_dev_close(dev);
274215195Semax
275215195Semax	return (error);
276215195Semax}
277215195Semax
278215195Semax/*
279215195Semax * Display usage and exit
280215195Semax */
281215195Semax
282215195Semaxstatic void
283215195Semaxusage(void)
284215195Semax{
285215195Semax	printf(
286215195Semax"Usage: %s -d ugenX.Y -f firmware_file\n"
287215195Semax"Usage: %s -h\n" \
288215195Semax"Where:\n" \
289215195Semax"\t-d ugenX.Y           ugen device name\n" \
290215195Semax"\t-f firmware image    firmware image file name for download\n" \
291249179Sadrian"\t-v vendor_id         vendor id\n" \
292249179Sadrian"\t-p vendor_id         product id\n" \
293215195Semax"\t-h                   display this message\n", ATH3KFW, ATH3KFW);
294215195Semax
295215195Semax        exit(255);
296215195Semax}
297215195Semax
298