]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/sdiotool/sdiotool.c
Import zstandard 1.3.1
[FreeBSD/FreeBSD.git] / usr.bin / sdiotool / sdiotool.c
1 /*-
2  * Copyright (c) 2016-2017 Ilya Bakulin
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/ioctl.h>
33 #include <sys/stdint.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/endian.h>
37 #include <sys/sbuf.h>
38 #include <sys/mman.h>
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <inttypes.h>
45 #include <limits.h>
46 #include <fcntl.h>
47 #include <ctype.h>
48 #include <err.h>
49 #include <libutil.h>
50 #include <unistd.h>
51
52 #include <cam/cam.h>
53 #include <cam/cam_debug.h>
54 #include <cam/cam_ccb.h>
55 #include <cam/mmc/mmc_all.h>
56 #include <camlib.h>
57
58 struct cis_info {
59         uint16_t man_id;
60         uint16_t prod_id;
61         uint16_t max_block_size;
62 };
63
64 static int sdio_rw_direct(struct cam_device *dev,
65                           uint8_t func_number,
66                           uint32_t addr,
67                           uint8_t is_write,
68                           uint8_t *data,
69                           uint8_t *resp);
70 static uint8_t sdio_read_1(struct cam_device *dev, uint8_t func_number, uint32_t addr);
71 static void sdio_write_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint8_t val);
72 static int sdio_is_func_ready(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab);
73 static int sdio_is_func_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab);
74 static int sdio_func_enable(struct cam_device *dev, uint8_t func_number, int enable);
75 static int sdio_is_func_intr_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab);
76 static int sdio_func_intr_enable(struct cam_device *dev, uint8_t func_number, int enable);
77 static void sdio_card_reset(struct cam_device *dev);
78 static uint32_t sdio_get_common_cis_addr(struct cam_device *dev);
79 static void probe_bcrm(struct cam_device *dev);
80
81 /* Use CMD52 to read or write a single byte */
82 int
83 sdio_rw_direct(struct cam_device *dev,
84                uint8_t func_number,
85                uint32_t addr,
86                uint8_t is_write,
87                uint8_t *data, uint8_t *resp) {
88         union ccb *ccb;
89         uint32_t flags;
90         uint32_t arg;
91         int retval = 0;
92
93         ccb = cam_getccb(dev);
94         if (ccb == NULL) {
95                 warnx("%s: error allocating CCB", __func__);
96                 return (1);
97         }
98         bzero(&(&ccb->ccb_h)[1],
99               sizeof(union ccb) - sizeof(struct ccb_hdr));
100
101         flags = MMC_RSP_R5 | MMC_CMD_AC;
102         arg = SD_IO_RW_FUNC(func_number) | SD_IO_RW_ADR(addr);
103         if (is_write)
104                 arg |= SD_IO_RW_WR | SD_IO_RW_RAW | SD_IO_RW_DAT(*data);
105
106         cam_fill_mmcio(&ccb->mmcio,
107                        /*retries*/ 0,
108                        /*cbfcnp*/ NULL,
109                        /*flags*/ CAM_DIR_NONE,
110                        /*mmc_opcode*/ SD_IO_RW_DIRECT,
111                        /*mmc_arg*/ arg,
112                        /*mmc_flags*/ flags,
113                        /*mmc_data*/ 0,
114                        /*timeout*/ 5000);
115
116         if (((retval = cam_send_ccb(dev, ccb)) < 0)
117             || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
118                 const char warnstr[] = "error sending command";
119
120                 if (retval < 0)
121                         warn(warnstr);
122                 else
123                         warnx(warnstr);
124                 return (-1);
125         }
126
127         *resp = ccb->mmcio.cmd.resp[0] & 0xFF;
128         cam_freeccb(ccb);
129         return (retval);
130 }
131
132 #if 0
133 /*
134  * CMD53 -- IO_RW_EXTENDED
135  * Use to read or write memory blocks
136  *
137  * is_increment=1: FIFO mode
138  * blk_count > 0: block mode
139  */
140 int
141 sdio_rw_extended(struct cam_device *dev,
142                  uint8_t func_number,
143                  uint32_t addr,
144                  uint8_t is_write,
145                  uint8_t *data, size_t datalen,
146                  uint8_t is_increment,
147                  uint16_t blk_count) {
148         union ccb *ccb;
149         uint32_t flags;
150         uint32_t arg;
151         int retval = 0;
152
153         if (blk_count != 0) {
154                 warnx("%s: block mode is not supported yet", __func__);
155                 return (1);
156         }
157
158         ccb = cam_getccb(dev);
159         if (ccb == NULL) {
160                 warnx("%s: error allocating CCB", __func__);
161                 return (1);
162         }
163         bzero(&(&ccb->ccb_h)[1],
164               sizeof(union ccb) - sizeof(struct ccb_hdr));
165
166         flags = MMC_RSP_R5 | MMC_CMD_AC;
167         arg = SD_IO_RW_FUNC(func_number) | SD_IO_RW_ADR(addr);
168         if (is_write)
169                 arg |= SD_IO_RW_WR;
170
171         cam_fill_mmcio(&ccb->mmcio,
172                        /*retries*/ 0,
173                        /*cbfcnp*/ NULL,
174                        /*flags*/ CAM_DIR_NONE,
175                        /*mmc_opcode*/ SD_IO_RW_DIRECT,
176                        /*mmc_arg*/ arg,
177                        /*mmc_flags*/ flags,
178                        /*mmc_data*/ 0,
179                        /*timeout*/ 5000);
180
181         if (((retval = cam_send_ccb(dev, ccb)) < 0)
182             || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
183                 const char warnstr[] = "error sending command";
184
185                 if (retval < 0)
186                         warn(warnstr);
187                 else
188                         warnx(warnstr);
189                 return (-1);
190         }
191
192         *resp = ccb->mmcio.cmd.resp[0] & 0xFF;
193         cam_freeccb(ccb);
194         return (retval);
195 }
196 #endif
197
198 static int
199 sdio_read_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t func_number, uint8_t *is_enab) {
200         uint8_t resp;
201         int ret;
202
203         ret = sdio_rw_direct(dev, 0, addr, 0, NULL, &resp);
204         if (ret < 0)
205                 return ret;
206
207         *is_enab = (resp & (1 << func_number)) > 0 ? 1 : 0;
208
209         return (0);
210 }
211
212 static int
213 sdio_set_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t func_number, int enable) {
214         uint8_t resp;
215         int ret;
216         uint8_t is_enabled;
217
218         ret = sdio_rw_direct(dev, 0, addr, 0, NULL, &resp);
219         if (ret != 0)
220                 return ret;
221
222         is_enabled = resp & (1 << func_number);
223         if ((is_enabled !=0 && enable == 1) || (is_enabled == 0 && enable == 0))
224                 return 0;
225
226         if (enable)
227                 resp |= 1 << func_number;
228         else
229                 resp &= ~ (1 << func_number);
230
231         ret = sdio_rw_direct(dev, 0, addr, 1, &resp, &resp);
232
233         return ret;
234 }
235
236 static uint8_t
237 sdio_read_1(struct cam_device *dev, uint8_t func_number, uint32_t addr) {
238         uint8_t val;
239         sdio_rw_direct(dev, func_number, addr, 0, NULL, &val);
240         return val;
241 }
242
243 __unused static void
244 sdio_write_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint8_t val) {
245         uint8_t _val;
246         sdio_rw_direct(dev, func_number, addr, 0, &val, &_val);
247 }
248
249 static int
250 sdio_is_func_ready(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab) {
251         return sdio_read_bool_for_func(dev, SD_IO_CCCR_FN_READY, func_number, is_enab);
252 }
253
254 static int
255 sdio_is_func_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab) {
256         return sdio_read_bool_for_func(dev, SD_IO_CCCR_FN_ENABLE, func_number, is_enab);
257 }
258
259 static int
260 sdio_func_enable(struct cam_device *dev, uint8_t func_number, int enable) {
261         return sdio_set_bool_for_func(dev, SD_IO_CCCR_FN_ENABLE, func_number, enable);
262 }
263
264 static int
265 sdio_is_func_intr_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab) {
266         return sdio_read_bool_for_func(dev, SD_IO_CCCR_INT_ENABLE, func_number, is_enab);
267 }
268
269 static int
270 sdio_func_intr_enable(struct cam_device *dev, uint8_t func_number, int enable) {
271         return sdio_set_bool_for_func(dev, SD_IO_CCCR_INT_ENABLE, func_number, enable);
272 }
273
274 static int
275 sdio_card_set_bus_width(struct cam_device *dev, enum mmc_bus_width bw) {
276         int ret;
277         uint8_t ctl_val;
278         ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_BUS_WIDTH, 0, NULL, &ctl_val);
279         if (ret < 0) {
280                 warn("Error getting CCCR_BUS_WIDTH value");
281                 return ret;
282         }
283         ctl_val &= ~0x3;
284         switch (bw) {
285         case bus_width_1:
286                 /* Already set to 1-bit */
287                 break;
288         case bus_width_4:
289                 ctl_val |= CCCR_BUS_WIDTH_4;
290                 break;
291         case bus_width_8:
292                 warn("Cannot do 8-bit on SDIO yet");
293                 return -1;
294                 break;
295         }
296         ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_BUS_WIDTH, 1, &ctl_val, &ctl_val);
297         if (ret < 0) {
298                 warn("Error setting CCCR_BUS_WIDTH value");
299                 return ret;
300         }
301         return ret;
302 }
303
304 static int
305 sdio_func_read_cis(struct cam_device *dev, uint8_t func_number,
306                    uint32_t cis_addr, struct cis_info *info) {
307         uint8_t tuple_id, tuple_len, tuple_count;
308         uint32_t addr;
309
310         char *cis1_info[4];
311         int start, i, ch, count;
312         char cis1_info_buf[256];
313
314         tuple_count = 0; /* Use to prevent infinite loop in case of parse errors */
315         memset(cis1_info_buf, 0, 256);
316         do {
317                 addr = cis_addr;
318                 tuple_id = sdio_read_1(dev, 0, addr++);
319                 if (tuple_id == SD_IO_CISTPL_END)
320                         break;
321                 if (tuple_id == 0) {
322                         cis_addr++;
323                         continue;
324                 }
325                 tuple_len = sdio_read_1(dev, 0, addr++);
326                 if (tuple_len == 0 && tuple_id != 0x00) {
327                         warn("Parse error: 0-length tuple %02X\n", tuple_id);
328                         return -1;
329                 }
330
331                 switch (tuple_id) {
332                 case SD_IO_CISTPL_VERS_1:
333                         addr += 2;
334                         for (count = 0, start = 0, i = 0;
335                              (count < 4) && ((i + 4) < 256); i++) {
336                                 ch = sdio_read_1(dev, 0, addr + i);
337                                 printf("count=%d, start=%d, i=%d, Got %c (0x%02x)\n", count, start, i, ch, ch);
338                                 if (ch == 0xff)
339                                         break;
340                                 cis1_info_buf[i] = ch;
341                                 if (ch == 0) {
342                                         cis1_info[count] =
343                                                 cis1_info_buf + start;
344                                         start = i + 1;
345                                         count++;
346                                 }
347                         }
348                         printf("Card info:");
349                         for (i=0; i<4; i++)
350                                 if (cis1_info[i])
351                                         printf(" %s", cis1_info[i]);
352                         printf("\n");
353                         break;
354                 case SD_IO_CISTPL_MANFID:
355                         info->man_id =  sdio_read_1(dev, 0, addr++);
356                         info->man_id |= sdio_read_1(dev, 0, addr++) << 8;
357
358                         info->prod_id =  sdio_read_1(dev, 0, addr++);
359                         info->prod_id |= sdio_read_1(dev, 0, addr++) << 8;
360                         break;
361                 case SD_IO_CISTPL_FUNCID:
362                         /* not sure if we need to parse it? */
363                         break;
364                 case SD_IO_CISTPL_FUNCE:
365                         if (tuple_len < 4) {
366                                 printf("FUNCE is too short: %d\n", tuple_len);
367                                 break;
368                         }
369                         if (func_number == 0) {
370                                 /* skip extended_data */
371                                 addr++;
372                                 info->max_block_size  = sdio_read_1(dev, 0, addr++);
373                                 info->max_block_size |= sdio_read_1(dev, 0, addr++) << 8;
374                         } else {
375                                 info->max_block_size  = sdio_read_1(dev, 0, addr + 0xC);
376                                 info->max_block_size |= sdio_read_1(dev, 0, addr + 0xD) << 8;
377                         }
378                         break;
379                 default:
380                         printf("Skipping tuple ID %02X len %02X\n", tuple_id, tuple_len);
381                 }
382                 cis_addr += tuple_len + 2;
383                 tuple_count++;
384         } while (tuple_count < 20);
385
386         return 0;
387 }
388
389 static uint32_t
390 sdio_get_common_cis_addr(struct cam_device *dev) {
391         uint32_t addr;
392
393         addr =  sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR);
394         addr |= sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR + 1) << 8;
395         addr |= sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR + 2) << 16;
396
397         if (addr < SD_IO_CIS_START || addr > SD_IO_CIS_START + SD_IO_CIS_SIZE) {
398                 warn("Bad CIS address: %04X\n", addr);
399                 addr = 0;
400         }
401
402         return addr;
403 }
404
405 static void sdio_card_reset(struct cam_device *dev) {
406         int ret;
407         uint8_t ctl_val;
408         ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_CTL, 0, NULL, &ctl_val);
409         if (ret < 0)
410                 errx(1, "Error getting CCCR_CTL value");
411         ctl_val |= CCCR_CTL_RES;
412         ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_CTL, 1, &ctl_val, &ctl_val);
413         if (ret < 0)
414                 errx(1, "Error setting CCCR_CTL value");
415 }
416
417 /*
418  * How Linux driver works
419  *
420  * The probing begins by calling brcmf_ops_sdio_probe() which is defined as probe function in struct sdio_driver. http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c#L1126
421  *
422  * The driver does black magic by copying func struct for F2 and setting func number to zero there, to create an F0 func structure :)
423  * Driver state changes to BRCMF_SDIOD_DOWN.
424  * ops_sdio_probe() then calls brcmf_sdio_probe() -- at this point it has filled in sdiodev struct with the pointers to all three functions (F0, F1, F2).
425  *
426  * brcmf_sdiod_probe() sets block sizes for F1 and F2. It sets F1 block size to 64 and F2 to 512, not consulting the values stored in SDIO CCCR  / FBR registers!
427  * Then it increases timeout for F2 (what is this?!)
428  * Then it enables F1
429  * Then it attaches "freezer" (without PM this is NOP)
430  * Finally it calls brcmf_sdio_probe() http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c#L4082
431  *
432  * Here high-level workqueues and sg tables are allocated.
433  * It then calls brcmf_sdio_probe_attach()
434  *
435  * Here at the beginning there is a pr_debug() call with brcmf_sdiod_regrl() inside to addr #define SI_ENUM_BASE            0x18000000.
436  * Return value is 0x16044330.
437  * Then turns off PLL:  byte-write BRCMF_INIT_CLKCTL1 (0x28) ->  SBSDIO_FUNC1_CHIPCLKCSR (0x1000E)
438  * Then it reads value back, should be 0xe8.
439  * Then calls brcmf_chip_attach()
440  *
441  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L1054
442  * This func enumerates and resets all the cores on the dongle.
443  *  - brcmf_sdio_buscoreprep(): force clock to ALPAvail req only:
444  *    SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ -> SBSDIO_FUNC1_CHIPCLKCSR
445  * Wait up to 15ms to !SBSDIO_ALPAV(clkval) of the value from CLKCSR.
446  * Force ALP:
447  *    SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP (0x21)-> SBSDIO_FUNC1_CHIPCLKCSR
448  * Disaable SDIO pullups:
449  * byte 0 -> SBSDIO_FUNC1_SDIOPULLUP (0x0001000f)
450  *
451  *  Calls brcmf_chip_recognition()
452  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L908
453  * Read 0x18000000. Get 0x16044330: chip 4330 rev 4
454  * AXI chip, call  brcmf_chip_dmp_erom_scan() to get info about all cores.
455  * Then  brcmf_chip_cores_check() to check that CPU and RAM are found,
456  *
457  * Setting cores to passive: not clear which of CR4/CA7/CM3 our chip has.
458  *  Quite a few r/w calls to different parts of the chip to reset cores....
459  * Finally get_raminfo() called to fill in RAM info:
460  * brcmf_chip_get_raminfo: RAM: base=0x0 size=294912 (0x48000) sr=0 (0x0)
461  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L700
462  *
463  * Then brcmf_chip_setup() is called, this prints and fills in chipcommon rev and PMU caps:
464  *   brcmf_chip_setup: ccrev=39, pmurev=12, pmucaps=0x19583c0c
465  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L1015
466  *  Bus-specific setup code is NOP for SDIO.
467  *
468  * brcmf_sdio_kso_init() is called.
469  * Here it first reads 0x1 from SBSDIO_FUNC1_SLEEPCSR 0x18000650 and then writes it back... WTF?
470  *
471  * brcmf_sdio_drivestrengthinit() is called
472  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c#L3630
473  *
474  * Set card control so an SDIO card reset does a WLAN backplane reset
475  * set PMUControl so a backplane reset does PMU state reload
476  * === end of brcmf_sdio_probe_attach ===
477
478  **** Finished reading at http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c#L4152, line 2025 in the dump
479
480  * === How register reading works ===
481  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c#L357
482  * The address to read from is written to three byte-sized registers of F1:
483  *  - SBSDIO_FUNC1_SBADDRLOW  0x1000A
484  *  - SBSDIO_FUNC1_SBADDRMID  0x1000B
485  *  - SBSDIO_FUNC1_SBADDRHIGH 0x1000C
486  * If this is 32-bit read , a flag is set. The address is ANDed with SBSDIO_SB_OFT_ADDR_MASK which is 0x07FFF.
487  * Then brcmf_sdiod_regrw_helper() is called to read the reply.
488  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c#L306
489  * Based on the address it figures out where to read it from (CCCR / FBR in F0, or somewhere in F1).
490  * Reads are retried three times.
491  * 1-byte IO is done with CMD52, more is read with CMD53 with address increment (not FIFO mode).
492  * http://lxr.free-electrons.com/source/drivers/mmc/core/sdio_io.c#L458
493  * ==================================
494  *
495  *
496  */
497 __unused
498 static void
499 probe_bcrm(struct cam_device *dev) {
500         uint32_t cis_addr;
501         struct cis_info info;
502
503         sdio_card_set_bus_width(dev, bus_width_4);
504         cis_addr = sdio_get_common_cis_addr(dev);
505         printf("CIS address: %04X\n", cis_addr);
506
507         memset(&info, 0, sizeof(info));
508         sdio_func_read_cis(dev, 0, cis_addr, &info);
509         printf("Vendor 0x%04X product 0x%04X\n", info.man_id, info.prod_id);
510 }
511 __unused
512 static uint8_t *
513 mmap_fw() {
514         const char fw_path[] = "/home/kibab/repos/fbsd-bbb/brcm-firmware/brcmfmac4330-sdio.bin";
515         struct stat sb;
516         uint8_t *fw_ptr;
517
518         int fd = open(fw_path, O_RDONLY);
519         if (fd < 0)
520                 errx(1, "Cannot open firmware file");
521         if (fstat(fd, &sb) < 0)
522                 errx(1, "Cannot get file stat");
523         fw_ptr = mmap(NULL, sb.st_size, PROT_READ, 0, fd, 0);
524         if (fw_ptr == MAP_FAILED)
525                 errx(1, "Cannot map the file");
526
527         return fw_ptr;
528 }
529
530 static void
531 usage() {
532         printf("sdiotool -u <pass_dev_unit>\n");
533         exit(0);
534 }
535
536 static void
537 get_sdio_card_info(struct cam_device *dev) {
538         uint32_t cis_addr;
539         uint32_t fbr_addr;
540         struct cis_info info;
541
542         cis_addr = sdio_get_common_cis_addr(dev);
543
544         memset(&info, 0, sizeof(info));
545         sdio_func_read_cis(dev, 0, cis_addr, &info);
546         printf("F0: Vendor 0x%04X product 0x%04X max block size %d bytes\n",
547                info.man_id, info.prod_id, info.max_block_size);
548         for (int i = 1; i <= 2; i++) {
549                 fbr_addr = SD_IO_FBR_START * i + 0x9;
550                 cis_addr =  sdio_read_1(dev, 0, fbr_addr++);
551                 cis_addr |= sdio_read_1(dev, 0, fbr_addr++) << 8;
552                 cis_addr |= sdio_read_1(dev, 0, fbr_addr++) << 16;
553                 memset(&info, 0, sizeof(info));
554                 sdio_func_read_cis(dev, i, cis_addr, &info);
555                 printf("F%d: Vendor 0x%04X product 0x%04X max block size %d bytes\n",
556                        i, info.man_id, info.prod_id, info.max_block_size);
557         }
558 }
559
560 /* Test interrupt delivery when select() */
561 __unused static int
562 sdio_signal_intr(struct cam_device *dev) {
563         uint8_t resp;
564         int ret;
565
566         ret = sdio_rw_direct(dev, 0, 0x666, 0, NULL, &resp);
567         if (ret < 0)
568                 return ret;
569         return (0);
570 }
571
572 static void
573 do_intr_test(__unused struct cam_device *dev) {
574 }
575
576 int
577 main(int argc, char **argv) {
578         char device[] = "pass";
579         int unit = 0;
580         int func = 0;
581         uint8_t resp;
582         uint8_t is_enab;
583         __unused uint8_t *fw_ptr;
584         int ch;
585         struct cam_device *cam_dev;
586         int is_intr_test = 0;
587
588         //fw_ptr = mmap_fw();
589
590         while ((ch = getopt(argc, argv, "Iu:")) != -1) {
591                 switch (ch) {
592                 case 'u':
593                         unit = (int) strtol(optarg, NULL, 10);
594                         break;
595                 case 'f':
596                         func = (int) strtol(optarg, NULL, 10);
597                 case 'I':
598                         is_intr_test = 1;
599                 case '?':
600                 default:
601                         usage();
602                 }
603         }
604         argc -= optind;
605         argv += optind;
606
607         if ((cam_dev = cam_open_spec_device(device, unit, O_RDWR, NULL)) == NULL)
608                 errx(1, "Cannot open device");
609
610         get_sdio_card_info(cam_dev);
611         if (is_intr_test > 0)
612                 do_intr_test(cam_dev);
613         exit(0);
614         sdio_card_reset(cam_dev);
615
616         /* Read Addr 7 of func 0 */
617         int ret = sdio_rw_direct(cam_dev, 0, 7, 0, NULL, &resp);
618         if (ret < 0)
619                 errx(1, "Error sending CAM command");
620         printf("Result: %02x\n", resp);
621
622         /* Check if func 1 is enabled */
623         ret = sdio_is_func_enabled(cam_dev, 1, &is_enab);
624         if (ret < 0)
625                 errx(1, "Cannot check if func is enabled");
626         printf("F1 enabled: %d\n", is_enab);
627         ret = sdio_func_enable(cam_dev, 1, 1 - is_enab);
628         if (ret < 0)
629                 errx(1, "Cannot enable/disable func");
630         printf("F1 en/dis result: %d\n", ret);
631
632         /* Check if func 1 is ready */
633         ret = sdio_is_func_ready(cam_dev, 1, &is_enab);
634         if (ret < 0)
635                 errx(1, "Cannot check if func is ready");
636         printf("F1 ready: %d\n", is_enab);
637
638         /* Check if interrupts are enabled */
639         ret = sdio_is_func_intr_enabled(cam_dev, 1, &is_enab);
640         if (ret < 0)
641                 errx(1, "Cannot check if func intr is enabled");
642         printf("F1 intr enabled: %d\n", is_enab);
643         ret = sdio_func_intr_enable(cam_dev, 1, 1 - is_enab);
644         if (ret < 0)
645                 errx(1, "Cannot enable/disable func intr");
646         printf("F1 intr en/dis result: %d\n", ret);
647
648         cam_close_spec_device(cam_dev);
649 }