xref: /netbsd-src/sys/dev/usb/aubtfwl.c (revision 6fe25d02bd7e64eb726ee1b39956c043d60dd4e3)
1 /* $NetBSD: aubtfwl.c,v 1.10 2020/05/30 17:19:45 jakllsch Exp $ */
2 
3 /*
4  * Copyright (c) 2011 Jonathan A. Kollasch
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: aubtfwl.c,v 1.10 2020/05/30 17:19:45 jakllsch Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/kmem.h>
34 #include <dev/usb/usb.h>
35 #include <dev/usb/usbdevs.h>
36 #include <dev/usb/usbdi.h>
37 #include <dev/usb/usbdivar.h>
38 #include <dev/usb/usbdi_util.h>
39 #include <dev/firmload.h>
40 
41 #include <dev/usb/aubtfwlreg.h>
42 
43 #define AR3K_FIRMWARE_CHUNK_SIZE 4096
44 
45 static int aubtfwl_match(device_t, cfdata_t, void *);
46 static void aubtfwl_attach(device_t, device_t, void *);
47 static int aubtfwl_detach(device_t, int);
48 static void aubtfwl_attach_hook(device_t);
49 
50 struct aubtfwl_softc {
51 	struct usbd_device *sc_udev;
52 	int sc_flags;
53 #define AUBT_IS_AR3012		1
54 };
55 
56 CFATTACH_DECL_NEW(aubtfwl, sizeof(struct aubtfwl_softc), aubtfwl_match, aubtfwl_attach, aubtfwl_detach, NULL);
57 
58 static const struct usb_devno ar3k_devs[] = {
59 	{ USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR3011 },
60 };
61 
62 static const struct usb_devno ar3k12_devs[] = {
63 	{ USB_VENDOR_FOXCONN, USB_PRODUCT_FOXCONN_AR3012 },
64 };
65 
66 static int
aubtfwl_match(device_t parent,cfdata_t match,void * aux)67 aubtfwl_match(device_t parent, cfdata_t match, void *aux)
68 {
69 	const struct usb_attach_arg * const uaa = aux;
70 
71 	if (usb_lookup(ar3k_devs, uaa->uaa_vendor, uaa->uaa_product))
72 		return UMATCH_VENDOR_PRODUCT;
73 
74 	if (usb_lookup(ar3k12_devs, uaa->uaa_vendor, uaa->uaa_product)) {
75 		return (UGETW(uaa->uaa_device->ud_ddesc.bcdDevice) > 1)?
76 			UMATCH_NONE : UMATCH_VENDOR_PRODUCT;
77 	}
78 
79 	return UMATCH_NONE;
80 }
81 
82 static void
aubtfwl_attach(device_t parent,device_t self,void * aux)83 aubtfwl_attach(device_t parent, device_t self, void *aux)
84 {
85 	const struct usb_attach_arg * const uaa = aux;
86 	struct aubtfwl_softc * const sc = device_private(self);
87 	aprint_naive("\n");
88 	aprint_normal("\n");
89 	sc->sc_udev = uaa->uaa_device;
90 	sc->sc_flags = 0;
91 
92 	if (usb_lookup(ar3k12_devs, uaa->uaa_vendor, uaa->uaa_product))
93 		sc->sc_flags |= AUBT_IS_AR3012;
94 
95 	config_mountroot(self, aubtfwl_attach_hook);
96 }
97 
98 static int
aubtfwl_detach(device_t self,int flags)99 aubtfwl_detach(device_t self, int flags)
100 {
101 	struct aubtfwl_softc * const sc = device_private(self);
102 
103 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, self);
104 
105 	return 0;
106 }
107 
108 /* Returns 0 if firmware was correctly loaded */
109 static int
aubtfwl_firmware_load(device_t self,const char * name)110 aubtfwl_firmware_load(device_t self, const char *name) {
111 	struct aubtfwl_softc * const sc = device_private(self);
112 	struct usbd_interface *iface;
113 	struct usbd_pipe *pipe;
114 	struct usbd_xfer *xfer;
115 	void *buf;
116 	usb_device_request_t req;
117 	int error = 0;
118 	firmware_handle_t fwh;
119 	size_t fws;
120 	size_t fwo = 0;
121 	uint32_t n;
122 
123 	memset(&req, 0, sizeof(req));
124 
125 	error = firmware_open("ubt", name, &fwh);
126 	if (error != 0) {
127 		aprint_error_dev(self, "'%s' open fail %d\n", name, error);
128 		return error;
129 	}
130 	fws = firmware_get_size(fwh);
131 
132 	error = usbd_set_config_no(sc->sc_udev, 1, 0);
133 	if (error != 0) {
134 		aprint_error_dev(self, "failed to set configuration"
135 		    ", err=%s\n", usbd_errstr(error));
136 		goto out_firmware;
137 	}
138 
139 	error = usbd_device2interface_handle(sc->sc_udev, 0, &iface);
140 	if (error) {
141 		aprint_error_dev(self, "failed to get interface, %s\n",
142 		   usbd_errstr(error));
143 		goto out_firmware;
144 	}
145 
146 	error = usbd_open_pipe(iface, UE_DIR_OUT|2, USBD_EXCLUSIVE_USE, &pipe);
147 	if (error) {
148 		aprint_error_dev(self, "failed to open pipe, %s\n",
149 		   usbd_errstr(error));
150 		goto out_firmware;
151 	}
152 
153 	error = usbd_create_xfer(pipe, AR3K_FIRMWARE_CHUNK_SIZE, 0, 0, &xfer);
154 	if (error) {
155 		aprint_verbose_dev(self, "cannot create xfer(%d)\n",
156 		    error);
157 		goto out_pipe;
158 	}
159 	buf = usbd_get_buffer(xfer);
160 
161 	error = firmware_read(fwh, fwo, buf, AR3K_FIRMWARE_HEADER_SIZE);
162 	if (error != 0) {
163 		aprint_error_dev(self, "firmware_read failed %d\n", error);
164 		goto out_xfer;
165 	}
166 
167 	req.bRequest = AR3K_SEND_FIRMWARE;
168 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
169 	USETW(req.wValue, 0);
170 	USETW(req.wIndex, 0);
171 	USETW(req.wLength, AR3K_FIRMWARE_HEADER_SIZE);
172 
173 	aprint_verbose_dev(self, "beginning firmware load\n");
174 
175 	error = usbd_do_request(sc->sc_udev, &req, buf);
176 	if (error != 0) {
177 		aprint_error_dev(self, "%s\n", usbd_errstr(error));
178 		return error;
179 	}
180 	fwo = AR3K_FIRMWARE_HEADER_SIZE;
181 
182 	while (fwo < fws) {
183 		n = uimin(AR3K_FIRMWARE_CHUNK_SIZE, fws - fwo);
184 		error = firmware_read(fwh, fwo, buf, n);
185 		if (error != 0) {
186 			break;
187 		}
188 		error = usbd_bulk_transfer(xfer, pipe, 0, USBD_DEFAULT_TIMEOUT,
189 		    buf, &n);
190 		if (error != USBD_NORMAL_COMPLETION) {
191 			aprint_error_dev(self, "xfer failed, %s\n",
192 			   usbd_errstr(error));
193 			break;
194 		}
195 		fwo += n;
196 	}
197 
198 	if (error == 0)
199 		aprint_verbose_dev(self, "firmware load complete\n");
200 
201 out_xfer:
202 	usbd_destroy_xfer(xfer);
203 out_pipe:
204 	usbd_close_pipe(pipe);
205 out_firmware:
206 	firmware_close(fwh);
207 
208 	return !!error;
209 }
210 
211 static int
aubtfwl_get_state(struct aubtfwl_softc * sc,uint8_t * state)212 aubtfwl_get_state(struct aubtfwl_softc *sc, uint8_t *state) {
213 	usb_device_request_t req;
214 	int error = 0;
215 
216 	memset(&req, 0, sizeof(req));
217 
218 	req.bRequest = AR3K_GET_STATE;
219 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
220 	USETW(req.wValue, 0);
221 	USETW(req.wIndex, 0);
222 	USETW(req.wLength, sizeof(*state));
223 
224 	error = usbd_do_request(sc->sc_udev, &req, state);
225 
226 	return error;
227 }
228 
229 static int
aubtfwl_get_version(struct aubtfwl_softc * sc,struct ar3k_version * ver)230 aubtfwl_get_version(struct aubtfwl_softc *sc, struct ar3k_version *ver) {
231 	usb_device_request_t req;
232 	int error = 0;
233 
234 	memset(&req, 0, sizeof(req));
235 
236 	req.bRequest = AR3K_GET_VERSION;
237 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
238 	USETW(req.wValue, 0);
239 	USETW(req.wIndex, 0);
240 	USETW(req.wLength, sizeof(*ver));
241 
242 	error = usbd_do_request(sc->sc_udev, &req, ver);
243 
244 #if BYTE_ORDER == BIG_ENDIAN
245 	if (error == USBD_NORMAL_COMPLETION) {
246 		ver->rom = bswap32(ver->rom);
247 		ver->build = bswap32(ver->build);
248 		ver->ram = bswap32(ver->ram);
249 	}
250 #endif
251 	return error;
252 }
253 
254 static int
aubtfwl_send_command(struct aubtfwl_softc * sc,uByte cmd)255 aubtfwl_send_command(struct aubtfwl_softc *sc, uByte cmd) {
256 	usb_device_request_t req;
257 	int error = 0;
258 
259 	memset(&req, 0, sizeof(req));
260 
261 	req.bRequest = cmd;
262 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
263 	USETW(req.wValue, 0);
264 	USETW(req.wIndex, 0);
265 	USETW(req.wLength, 0);
266 
267 	error = usbd_do_request(sc->sc_udev, &req, NULL);
268 
269 	return error;
270 }
271 
272 static void
aubtfwl_attach_hook(device_t self)273 aubtfwl_attach_hook(device_t self)
274 {
275 	struct aubtfwl_softc * const sc = device_private(self);
276 	char *fw_name;
277 	struct ar3k_version ver;
278 	uint8_t state;
279 	int clock = 0;
280 	int error = 0;
281 
282 	if (sc->sc_flags & AUBT_IS_AR3012) {
283 		error = aubtfwl_get_version(sc, &ver);
284 		if (!error)
285 			error = aubtfwl_get_state(sc, &state);
286 
287 		if (error) {
288 			aprint_error_dev(self,
289 				"couldn't get version or state\n");
290 			return;
291 		}
292 
293 		aprint_verbose_dev(self, "state is 0x%02x\n", state);
294 
295 		if (!(state & AR3K_STATE_IS_PATCHED)) {
296 			fw_name = kmem_asprintf("ar3k/AthrBT_0x%08x.dfu",
297 			    ver.rom);
298 			error = aubtfwl_firmware_load(self, fw_name);
299 			kmem_strfree(fw_name);
300 
301 			if (error)
302 				return;
303 		}
304 
305 		switch (ver.clock) {
306 		case AR3K_CLOCK_19M:
307 			clock = 19;
308 			break;
309 		case AR3K_CLOCK_26M:
310 			clock = 26;
311 			break;
312 		case AR3K_CLOCK_40M:
313 			clock = 40;
314 			break;
315 		}
316 
317 		fw_name = kmem_asprintf("ar3k/ramps_0x%08x_%d.dfu",
318 		    ver.rom, clock);
319 		aubtfwl_firmware_load(self, fw_name);
320 		kmem_strfree(fw_name);
321 
322 		if ((state & AR3K_STATE_MODE_MASK) != AR3K_STATE_MODE_NORMAL) {
323 			error = aubtfwl_send_command(sc, AR3K_SET_NORMAL_MODE);
324 			if (error) {
325 				aprint_error_dev(self,
326 					"couldn't set normal mode: %s",
327 					usbd_errstr(error));
328 				return;
329 			}
330 		}
331 
332 		/* Apparently some devices will fail this, so ignore result */
333 		(void) aubtfwl_send_command(sc, AR3K_SWITCH_VID_PID);
334 	} else {
335 		aubtfwl_firmware_load(self, "ath3k-1.fw");
336 	}
337 
338 	return;
339 }
340