1/* $NetBSD: aubtfwl.c,v 1.2 2011/12/22 20:06:59 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.2 2011/12/22 20:06:59 jakllsch Exp $"); 31 32#include <sys/param.h> 33#include <dev/usb/usb.h> 34#include <dev/usb/usbdevs.h> 35#include <dev/usb/usbdi.h> 36#include <dev/usb/usbdi_util.h> 37#include <dev/firmload.h> 38 39#define AR3K_FIRMWARE_HEADER_SIZE 20 40#define AR3K_FIRMWARE_CHUNK_SIZE 4096 41 42static int aubtfwl_match(device_t, cfdata_t, void *); 43static void aubtfwl_attach(device_t, device_t, void *); 44static int aubtfwl_detach(device_t, int); 45static void aubtfwl_attach_hook(device_t); 46 47struct aubtfwl_softc { 48 usbd_device_handle sc_udev; 49}; 50 51CFATTACH_DECL_NEW(aubtfwl, sizeof(struct aubtfwl_softc), aubtfwl_match, aubtfwl_attach, aubtfwl_detach, NULL); 52 53static int 54aubtfwl_match(device_t parent, cfdata_t match, void *aux) 55{ 56 const struct usb_attach_arg * const uaa = aux; 57 58 if (uaa->vendor == USB_VENDOR_ATHEROS2 && 59 uaa->product == USB_PRODUCT_ATHEROS2_AR3011) 60 return UMATCH_VENDOR_PRODUCT; 61 62 return UMATCH_NONE; 63} 64 65static void 66aubtfwl_attach(device_t parent, device_t self, void *aux) 67{ 68 const struct usb_attach_arg * const uaa = aux; 69 struct aubtfwl_softc * const sc = device_private(self); 70 aprint_naive("\n"); 71 aprint_normal("\n"); 72 sc->sc_udev = uaa->device; 73 74 config_mountroot(self, aubtfwl_attach_hook); 75} 76 77static int 78aubtfwl_detach(device_t self, int flags) 79{ 80 struct aubtfwl_softc * const sc = device_private(self); 81 82 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, self); 83 84 return 0; 85} 86 87static void 88aubtfwl_attach_hook(device_t self) 89{ 90 struct aubtfwl_softc * const sc = device_private(self); 91 usbd_interface_handle iface; 92 usbd_pipe_handle pipe; 93 usbd_xfer_handle xfer; 94 void *buf; 95 usb_device_request_t req; 96 int error; 97 firmware_handle_t fwh; 98 size_t fws; 99 size_t fwo = 0; 100 uint32_t n; 101 102 memset(&req, 0, sizeof(req)); 103 104 error = firmware_open("ubt", "ath3k-1.fw", &fwh); /* XXX revisit name */ 105 if (error != 0) { 106 aprint_error_dev(self, "ath3k-1.fw open fail %d\n", error); 107 return; 108 } 109 fws = firmware_get_size(fwh); 110 111 error = usbd_set_config_no(sc->sc_udev, 1, 0); 112 if (error != 0) { 113 aprint_error_dev(self, "could not set configuration no\n"); 114 goto out_firmware; 115 } 116 117 error = usbd_device2interface_handle(sc->sc_udev, 0, &iface); 118 if (error) { 119 aprint_error_dev(self, "failed to get interface, %s\n", 120 usbd_errstr(error)); 121 goto out_firmware; 122 } 123 124 error = usbd_open_pipe(iface, UE_DIR_OUT|2, USBD_EXCLUSIVE_USE, &pipe); 125 if (error) { 126 aprint_error_dev(self, "failed to open pipe, %s\n", 127 usbd_errstr(error)); 128 goto out_firmware; 129 } 130 131 xfer = usbd_alloc_xfer(sc->sc_udev); 132 if (xfer == NULL) { 133 aprint_error_dev(self, "failed to alloc xfer\n"); 134 goto out_pipe; 135 } 136 137 buf = usbd_alloc_buffer(xfer, 4096); 138 if (buf == NULL) { 139 aprint_error_dev(self, "failed to alloc buffer\n"); 140 goto out_xfer; 141 } 142 143 error = firmware_read(fwh, fwo, buf, AR3K_FIRMWARE_HEADER_SIZE); 144 if (error != 0) { 145 aprint_error_dev(self, "firmware_read failed %d\n", error); 146 goto out_xfer; 147 } 148 149 req.bRequest = 1; 150 req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 151 USETW(req.wValue, 0); 152 USETW(req.wIndex, 0); 153 USETW(req.wLength, AR3K_FIRMWARE_HEADER_SIZE); 154 155 aprint_verbose_dev(self, "beginning firmware load\n"); 156 157 error = usbd_do_request(sc->sc_udev, &req, buf); 158 if (error != 0) { 159 aprint_error_dev(self, "%s\n", usbd_errstr(error)); 160 return; 161 } 162 fwo = AR3K_FIRMWARE_HEADER_SIZE; 163 164 while (fwo < fws) { 165 n = min(AR3K_FIRMWARE_CHUNK_SIZE, fws - fwo); 166 error = firmware_read(fwh, fwo, buf, n); 167 if (error != 0) { 168 break;; 169 } 170 error = usbd_bulk_transfer(xfer, pipe, 171 USBD_NO_COPY, USBD_DEFAULT_TIMEOUT, 172 buf, &n, device_xname(self)); 173 if (error != USBD_NORMAL_COMPLETION) { 174 aprint_error_dev(self, "xfer failed, %s\n", 175 usbd_errstr(error)); 176 break;; 177 } 178 fwo += n; 179 } 180 aprint_verbose_dev(self, "firmware load complete\n"); 181 182out_xfer: 183 usbd_free_xfer(xfer); 184out_pipe: 185 usbd_close_pipe(pipe); 186out_firmware: 187 firmware_close(fwh); 188 189 return; 190} 191