- Import driver[acx(4)] for TI acx100/acx111 based WiFi NIC.
[dragonfly.git] / usr.sbin / acxcontrol / acxcontrol.c
1 /*
2  * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Sepherosa Ziehau <sepherosa@gmail.com> and
6  * Sascha Wildner <swildner@gmail.com>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  * $DragonFly: src/usr.sbin/acxcontrol/Attic/acxcontrol.c,v 1.1 2006/04/01 02:55:36 sephe Exp $
36  */
37
38 #include <sys/types.h>
39 #include <sys/ioctl.h>
40 #include <sys/mman.h>
41 #include <sys/socket.h>
42 #include <sys/stat.h>
43 #include <sys/sysctl.h>
44
45 #include <net/if.h>
46
47 #include <err.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <libgen.h>
51 #include <limits.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <sysexits.h>
56 #include <unistd.h>
57
58 #define SIOCSLOADFW     _IOW('i', 137, struct ifreq)    /* load firmware */
59 #define SIOCGRADIO      _IOW('i', 138, struct ifreq)    /* get radio type */
60 #define SIOCGSTATS      _IOW('i', 139, struct ifreq)    /* get acx stats */
61 #define SIOCSKILLFW     _IOW('i', 140, struct ifreq)    /* free firmware */
62 #define SIOCGFWVER      _IOW('i', 141, struct ifreq)    /* get firmware ver */
63 #define SIOCGHWID       _IOW('i', 142, struct ifreq)    /* get hardware id */
64
65 #define RADIO_FW_FMT    "radio%02x"
66
67 static int      do_req(const char *, unsigned long, void *);
68 static void     get_statistics(const char *);
69 static void     kill_firmware(const char *);
70 static void     load_firmware(const char *, const char *, int);
71 static void     mmap_file(const char *, uint8_t **, int *);
72 static void     usage(void);
73
74 struct firmware {
75         uint8_t *base_fw;
76         int     base_fw_len;
77         uint8_t *radio_fw;
78         int     radio_fw_len;
79 };
80
81 struct firmware_head {
82         uint32_t        fwh_cksum;
83         uint32_t        fwh_len;
84 };
85
86 struct statistic {
87         int             index;
88         const char      *desc;
89 };
90
91 static const struct statistic tbl[] = {
92         {  1, "Invalid param in TX description" },
93         {  2, "No WEP key exists" },
94         {  3, "MSDU timeouts" },
95         {  4, "Excessive TX retries" },
96         {  5, "Buffer overflows" },
97         {  6, "DMA errors" },
98         {  7, "Unknown errors" },
99         { -1, NULL }
100 };
101
102 static int
103 do_req(const char *iface, unsigned long req, void *data)
104 {
105         int s;
106         struct ifreq ifr;
107         int error;
108
109         if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
110                 err(EX_OSERR, "Can't create socket");
111
112         memset(&ifr, 0, sizeof(ifr));
113         strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
114         ifr.ifr_data = data;
115         error = ioctl(s, req, &ifr);
116
117         close(s);
118
119         return error;
120 }
121
122 static void
123 get_statistics(const char *iface)
124 {
125         uint32_t i;
126         uint64_t stats[16];
127         const struct statistic *stt;
128
129         if (do_req(iface, SIOCGHWID, &i) == -1)
130                 err(EX_OSERR, "Can't get hardware ID");
131         printf("Hardware ID                    0x%x\n", i);
132
133         if (do_req(iface, SIOCGFWVER, &i) == -1)
134                 err(EX_OSERR, "Can't get firmware version");
135         printf("Firmware Version               0x%x\n", i);
136
137         if (do_req(iface, SIOCGSTATS, &stats) == -1)
138                 err(EX_OSERR, "Can't get statistics");
139
140         for (stt = tbl; stt->index != -1; stt++)
141                 printf("%-30s %qd\n", stt->desc, stats[stt->index]);
142 }
143
144 static void
145 kill_firmware(const char *iface)
146 {
147         if (do_req(iface, SIOCSKILLFW, NULL) == -1)
148                 err(EX_OSERR, "Can't kill firmware");
149 }
150
151 static void
152 load_firmware(const char *iface, const char *filename, int uncombined)
153 {
154         char radio_name[FILENAME_MAX];
155         struct firmware fw;
156
157         memset(&fw, 0, sizeof(fw));
158         mmap_file(filename, &fw.base_fw, &fw.base_fw_len);
159
160         if (uncombined) {
161                 uint8_t radio_type;
162
163                 if (do_req(iface, SIOCGRADIO, &radio_type) == -1)
164                         err(EX_OSERR, "Can't get radio type");
165                 snprintf(radio_name, FILENAME_MAX, "%s/" RADIO_FW_FMT ".bin",
166                          dirname(filename), radio_type);
167                 mmap_file(radio_name, &fw.radio_fw, &fw.radio_fw_len);
168         }
169
170         do_req(iface, SIOCSLOADFW, &fw);
171 }
172
173 static void
174 mmap_file(const char *filename, uint8_t **addr, int *len)
175 {
176         struct stat st;
177         struct firmware_head *fwh;
178         uint32_t cksum;
179         uint8_t *p;
180         int i, fd;
181
182         fd = open(filename, O_RDONLY);
183         if (fd < 0)
184                 err(EX_OSERR, "Can't open %s", filename);
185
186         if (fstat(fd, &st) < 0)
187                 err(EX_OSERR, "Can't stat %s", filename);
188
189         if (st.st_size <= sizeof(struct firmware_head))
190                 err(EX_SOFTWARE, "%s is too short", filename);
191
192         fwh = mmap(NULL, st.st_size, PROT_READ, 0, fd, 0);
193         if (fwh == NULL)
194                 err(EX_OSERR, "Can't map %s into memory", filename);
195
196         if (fwh->fwh_len != st.st_size - sizeof(struct firmware_head))
197                 err(EX_SOFTWARE, "%s length mismatch", filename);
198
199         cksum = 0;
200         for (i = 0, p = (uint8_t *)&fwh->fwh_len;
201              i < st.st_size - sizeof(fwh->fwh_cksum);
202              ++i, ++p)
203                 cksum += *p;
204         if (cksum != fwh->fwh_cksum)
205                 err(EX_SOFTWARE, "%s checksum mismatch", filename);
206
207         *addr = (uint8_t *)(fwh + 1);
208         *len = st.st_size - sizeof(struct firmware_head);
209
210         close(fd);
211 }
212
213 static void
214 usage(void)
215 {
216         fprintf(stderr, "usage: acxcontrol iface\n"
217             "       acxcontrol iface -f file [-r]\n"
218             "       acxcontrol iface -k\n");
219         exit(EX_USAGE);
220 }
221
222 int
223 main(int argc, char *argv[])
224 {
225         int c;
226         int noflag = 1, kflag = 0, rflag = 0;
227         const char *iface = NULL, *path = NULL;
228
229         if (argc > 1 && argv[1][0] != '-') {
230                 iface = argv[1];
231                 optind++;
232         }
233
234         while ((c = getopt(argc, argv, "f:i:kr")) != -1) {
235                 if (c != 'i')
236                         noflag = 0;
237
238                 switch (c) {
239                 case 'f':
240                         path = optarg;
241                         break;
242                 case 'i':
243                         iface = optarg;
244                         break;
245                 case 'k':
246                         kflag = 1;
247                         break;
248                 case 'r':
249                         rflag = 1;
250                         break;
251                 default:
252                         usage();
253                 }
254         }
255
256         if (iface == NULL)
257                 usage();
258
259         if (kflag && ((path != NULL) || rflag))
260                 usage();
261
262         if (kflag)
263                 kill_firmware(iface);
264
265         if (path != NULL)
266                 load_firmware(iface, path, rflag);
267
268         if (noflag)
269                 get_statistics(iface);
270
271         return EX_OK;
272 }