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