Author: imp
Date: Sun Jul  9 17:05:48 2017
New Revision: 320847
URL: https://svnweb.freebsd.org/changeset/base/320847

Log:
  Added new tool for doing experiments with SDIO card.
  
  Due to its experimental nature, it's not yet connected to the build.
  
  Submitted by: Ilya Babulin

Added:
  head/usr.bin/sdiotool/
  head/usr.bin/sdiotool/Makefile   (contents, props changed)
  head/usr.bin/sdiotool/sdiotool.c   (contents, props changed)

Added: head/usr.bin/sdiotool/Makefile
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/usr.bin/sdiotool/Makefile      Sun Jul  9 17:05:48 2017        
(r320847)
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG=  sdiotool
+SRCS=  sdiotool.c
+
+LIBADD= cam util
+MAN=
+
+.include <bsd.prog.mk>

Added: head/usr.bin/sdiotool/sdiotool.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/usr.bin/sdiotool/sdiotool.c    Sun Jul  9 17:05:48 2017        
(r320847)
@@ -0,0 +1,649 @@
+/*-
+ * Copyright (c) 2016-2017 Ilya Bakulin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/ioctl.h>
+#include <sys/stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/endian.h>
+#include <sys/sbuf.h>
+#include <sys/mman.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <err.h>
+#include <libutil.h>
+#include <unistd.h>
+
+#include <cam/cam.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_ccb.h>
+#include <cam/mmc/mmc_all.h>
+#include <camlib.h>
+
+struct cis_info {
+       uint16_t man_id;
+       uint16_t prod_id;
+       uint16_t max_block_size;
+};
+
+static int sdio_rw_direct(struct cam_device *dev,
+                         uint8_t func_number,
+                         uint32_t addr,
+                         uint8_t is_write,
+                         uint8_t *data,
+                         uint8_t *resp);
+static uint8_t sdio_read_1(struct cam_device *dev, uint8_t func_number, 
uint32_t addr);
+static void sdio_write_1(struct cam_device *dev, uint8_t func_number, uint32_t 
addr, uint8_t val);
+static int sdio_is_func_ready(struct cam_device *dev, uint8_t func_number, 
uint8_t *is_enab);
+static int sdio_is_func_enabled(struct cam_device *dev, uint8_t func_number, 
uint8_t *is_enab);
+static int sdio_func_enable(struct cam_device *dev, uint8_t func_number, int 
enable);
+static int sdio_is_func_intr_enabled(struct cam_device *dev, uint8_t 
func_number, uint8_t *is_enab);
+static int sdio_func_intr_enable(struct cam_device *dev, uint8_t func_number, 
int enable);
+static void sdio_card_reset(struct cam_device *dev);
+static uint32_t sdio_get_common_cis_addr(struct cam_device *dev);
+static void probe_bcrm(struct cam_device *dev);
+
+/* Use CMD52 to read or write a single byte */
+int
+sdio_rw_direct(struct cam_device *dev,
+              uint8_t func_number,
+              uint32_t addr,
+              uint8_t is_write,
+              uint8_t *data, uint8_t *resp) {
+       union ccb *ccb;
+       uint32_t flags;
+       uint32_t arg;
+       int retval = 0;
+
+       ccb = cam_getccb(dev);
+       if (ccb == NULL) {
+               warnx("%s: error allocating CCB", __func__);
+               return (1);
+       }
+       bzero(&(&ccb->ccb_h)[1],
+             sizeof(union ccb) - sizeof(struct ccb_hdr));
+
+       flags = MMC_RSP_R5 | MMC_CMD_AC;
+       arg = SD_IO_RW_FUNC(func_number) | SD_IO_RW_ADR(addr);
+       if (is_write)
+               arg |= SD_IO_RW_WR | SD_IO_RW_RAW | SD_IO_RW_DAT(*data);
+
+       cam_fill_mmcio(&ccb->mmcio,
+                      /*retries*/ 0,
+                      /*cbfcnp*/ NULL,
+                      /*flags*/ CAM_DIR_NONE,
+                      /*mmc_opcode*/ SD_IO_RW_DIRECT,
+                      /*mmc_arg*/ arg,
+                      /*mmc_flags*/ flags,
+                      /*mmc_data*/ 0,
+                      /*timeout*/ 5000);
+
+       if (((retval = cam_send_ccb(dev, ccb)) < 0)
+           || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
+               const char warnstr[] = "error sending command";
+
+               if (retval < 0)
+                       warn(warnstr);
+               else
+                       warnx(warnstr);
+               return (-1);
+       }
+
+       *resp = ccb->mmcio.cmd.resp[0] & 0xFF;
+       cam_freeccb(ccb);
+       return (retval);
+}
+
+#if 0
+/*
+ * CMD53 -- IO_RW_EXTENDED
+ * Use to read or write memory blocks
+ *
+ * is_increment=1: FIFO mode
+ * blk_count > 0: block mode
+ */
+int
+sdio_rw_extended(struct cam_device *dev,
+                uint8_t func_number,
+                uint32_t addr,
+                uint8_t is_write,
+                uint8_t *data, size_t datalen,
+                uint8_t is_increment,
+                uint16_t blk_count) {
+       union ccb *ccb;
+       uint32_t flags;
+       uint32_t arg;
+       int retval = 0;
+
+       if (blk_count != 0) {
+               warnx("%s: block mode is not supported yet", __func__);
+               return (1);
+       }
+
+       ccb = cam_getccb(dev);
+       if (ccb == NULL) {
+               warnx("%s: error allocating CCB", __func__);
+               return (1);
+       }
+       bzero(&(&ccb->ccb_h)[1],
+             sizeof(union ccb) - sizeof(struct ccb_hdr));
+
+       flags = MMC_RSP_R5 | MMC_CMD_AC;
+       arg = SD_IO_RW_FUNC(func_number) | SD_IO_RW_ADR(addr);
+       if (is_write)
+               arg |= SD_IO_RW_WR;
+
+       cam_fill_mmcio(&ccb->mmcio,
+                      /*retries*/ 0,
+                      /*cbfcnp*/ NULL,
+                      /*flags*/ CAM_DIR_NONE,
+                      /*mmc_opcode*/ SD_IO_RW_DIRECT,
+                      /*mmc_arg*/ arg,
+                      /*mmc_flags*/ flags,
+                      /*mmc_data*/ 0,
+                      /*timeout*/ 5000);
+
+       if (((retval = cam_send_ccb(dev, ccb)) < 0)
+           || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
+               const char warnstr[] = "error sending command";
+
+               if (retval < 0)
+                       warn(warnstr);
+               else
+                       warnx(warnstr);
+               return (-1);
+       }
+
+       *resp = ccb->mmcio.cmd.resp[0] & 0xFF;
+       cam_freeccb(ccb);
+       return (retval);
+}
+#endif
+
+static int
+sdio_read_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t 
func_number, uint8_t *is_enab) {
+       uint8_t resp;
+       int ret;
+
+       ret = sdio_rw_direct(dev, 0, addr, 0, NULL, &resp);
+       if (ret < 0)
+               return ret;
+
+       *is_enab = (resp & (1 << func_number)) > 0 ? 1 : 0;
+
+       return (0);
+}
+
+static int
+sdio_set_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t 
func_number, int enable) {
+       uint8_t resp;
+       int ret;
+       uint8_t is_enabled;
+
+       ret = sdio_rw_direct(dev, 0, addr, 0, NULL, &resp);
+       if (ret != 0)
+               return ret;
+
+       is_enabled = resp & (1 << func_number);
+       if ((is_enabled !=0 && enable == 1) || (is_enabled == 0 && enable == 0))
+               return 0;
+
+       if (enable)
+               resp |= 1 << func_number;
+       else
+               resp &= ~ (1 << func_number);
+
+       ret = sdio_rw_direct(dev, 0, addr, 1, &resp, &resp);
+
+       return ret;
+}
+
+static uint8_t
+sdio_read_1(struct cam_device *dev, uint8_t func_number, uint32_t addr) {
+       uint8_t val;
+       sdio_rw_direct(dev, func_number, addr, 0, NULL, &val);
+       return val;
+}
+
+__unused static void
+sdio_write_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, 
uint8_t val) {
+       uint8_t _val;
+       sdio_rw_direct(dev, func_number, addr, 0, &val, &_val);
+}
+
+static int
+sdio_is_func_ready(struct cam_device *dev, uint8_t func_number, uint8_t 
*is_enab) {
+       return sdio_read_bool_for_func(dev, SD_IO_CCCR_FN_READY, func_number, 
is_enab);
+}
+
+static int
+sdio_is_func_enabled(struct cam_device *dev, uint8_t func_number, uint8_t 
*is_enab) {
+       return sdio_read_bool_for_func(dev, SD_IO_CCCR_FN_ENABLE, func_number, 
is_enab);
+}
+
+static int
+sdio_func_enable(struct cam_device *dev, uint8_t func_number, int enable) {
+       return sdio_set_bool_for_func(dev, SD_IO_CCCR_FN_ENABLE, func_number, 
enable);
+}
+
+static int
+sdio_is_func_intr_enabled(struct cam_device *dev, uint8_t func_number, uint8_t 
*is_enab) {
+       return sdio_read_bool_for_func(dev, SD_IO_CCCR_INT_ENABLE, func_number, 
is_enab);
+}
+
+static int
+sdio_func_intr_enable(struct cam_device *dev, uint8_t func_number, int enable) 
{
+       return sdio_set_bool_for_func(dev, SD_IO_CCCR_INT_ENABLE, func_number, 
enable);
+}
+
+static int
+sdio_card_set_bus_width(struct cam_device *dev, enum mmc_bus_width bw) {
+       int ret;
+       uint8_t ctl_val;
+       ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_BUS_WIDTH, 0, NULL, &ctl_val);
+       if (ret < 0) {
+               warn("Error getting CCCR_BUS_WIDTH value");
+               return ret;
+       }
+       ctl_val &= ~0x3;
+       switch (bw) {
+       case bus_width_1:
+               /* Already set to 1-bit */
+               break;
+       case bus_width_4:
+               ctl_val |= CCCR_BUS_WIDTH_4;
+               break;
+       case bus_width_8:
+               warn("Cannot do 8-bit on SDIO yet");
+               return -1;
+               break;
+       }
+       ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_BUS_WIDTH, 1, &ctl_val, 
&ctl_val);
+       if (ret < 0) {
+               warn("Error setting CCCR_BUS_WIDTH value");
+               return ret;
+       }
+       return ret;
+}
+
+static int
+sdio_func_read_cis(struct cam_device *dev, uint8_t func_number,
+                  uint32_t cis_addr, struct cis_info *info) {
+       uint8_t tuple_id, tuple_len, tuple_count;
+       uint32_t addr;
+
+       char *cis1_info[4];
+       int start, i, ch, count;
+       char cis1_info_buf[256];
+
+       tuple_count = 0; /* Use to prevent infinite loop in case of parse 
errors */
+       memset(cis1_info_buf, 0, 256);
+       do {
+               addr = cis_addr;
+               tuple_id = sdio_read_1(dev, 0, addr++);
+               if (tuple_id == SD_IO_CISTPL_END)
+                       break;
+               if (tuple_id == 0) {
+                       cis_addr++;
+                       continue;
+               }
+               tuple_len = sdio_read_1(dev, 0, addr++);
+               if (tuple_len == 0 && tuple_id != 0x00) {
+                       warn("Parse error: 0-length tuple %02X\n", tuple_id);
+                       return -1;
+               }
+
+               switch (tuple_id) {
+               case SD_IO_CISTPL_VERS_1:
+                       addr += 2;
+                       for (count = 0, start = 0, i = 0;
+                            (count < 4) && ((i + 4) < 256); i++) {
+                               ch = sdio_read_1(dev, 0, addr + i);
+                               printf("count=%d, start=%d, i=%d, Got %c 
(0x%02x)\n", count, start, i, ch, ch);
+                               if (ch == 0xff)
+                                       break;
+                               cis1_info_buf[i] = ch;
+                               if (ch == 0) {
+                                       cis1_info[count] =
+                                               cis1_info_buf + start;
+                                       start = i + 1;
+                                       count++;
+                               }
+                       }
+                       printf("Card info:");
+                       for (i=0; i<4; i++)
+                               if (cis1_info[i])
+                                       printf(" %s", cis1_info[i]);
+                       printf("\n");
+                       break;
+               case SD_IO_CISTPL_MANFID:
+                       info->man_id =  sdio_read_1(dev, 0, addr++);
+                       info->man_id |= sdio_read_1(dev, 0, addr++) << 8;
+
+                       info->prod_id =  sdio_read_1(dev, 0, addr++);
+                       info->prod_id |= sdio_read_1(dev, 0, addr++) << 8;
+                       break;
+               case SD_IO_CISTPL_FUNCID:
+                       /* not sure if we need to parse it? */
+                       break;
+               case SD_IO_CISTPL_FUNCE:
+                       if (tuple_len < 4) {
+                               printf("FUNCE is too short: %d\n", tuple_len);
+                               break;
+                       }
+                       if (func_number == 0) {
+                               /* skip extended_data */
+                               addr++;
+                               info->max_block_size  = sdio_read_1(dev, 0, 
addr++);
+                               info->max_block_size |= sdio_read_1(dev, 0, 
addr++) << 8;
+                       } else {
+                               info->max_block_size  = sdio_read_1(dev, 0, 
addr + 0xC);
+                               info->max_block_size |= sdio_read_1(dev, 0, 
addr + 0xD) << 8;
+                       }
+                       break;
+               default:
+                       printf("Skipping tuple ID %02X len %02X\n", tuple_id, 
tuple_len);
+               }
+               cis_addr += tuple_len + 2;
+               tuple_count++;
+       } while (tuple_count < 20);
+
+       return 0;
+}
+
+static uint32_t
+sdio_get_common_cis_addr(struct cam_device *dev) {
+       uint32_t addr;
+
+       addr =  sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR);
+       addr |= sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR + 1) << 8;
+       addr |= sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR + 2) << 16;
+
+       if (addr < SD_IO_CIS_START || addr > SD_IO_CIS_START + SD_IO_CIS_SIZE) {
+               warn("Bad CIS address: %04X\n", addr);
+               addr = 0;
+       }
+
+       return addr;
+}
+
+static void sdio_card_reset(struct cam_device *dev) {
+       int ret;
+       uint8_t ctl_val;
+       ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_CTL, 0, NULL, &ctl_val);
+       if (ret < 0)
+               errx(1, "Error getting CCCR_CTL value");
+       ctl_val |= CCCR_CTL_RES;
+       ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_CTL, 1, &ctl_val, &ctl_val);
+       if (ret < 0)
+               errx(1, "Error setting CCCR_CTL value");
+}
+
+/*
+ * How Linux driver works
+ *
+ * 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
+ *
+ * The driver does black magic by copying func struct for F2 and setting func 
number to zero there, to create an F0 func structure :)
+ * Driver state changes to BRCMF_SDIOD_DOWN.
+ * 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).
+ *
+ * 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!
+ * Then it increases timeout for F2 (what is this?!)
+ * Then it enables F1
+ * Then it attaches "freezer" (without PM this is NOP)
+ * Finally it calls brcmf_sdio_probe() 
http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c#L4082
+ *
+ * Here high-level workqueues and sg tables are allocated.
+ * It then calls brcmf_sdio_probe_attach()
+ *
+ * Here at the beginning there is a pr_debug() call with brcmf_sdiod_regrl() 
inside to addr #define SI_ENUM_BASE            0x18000000.
+ * Return value is 0x16044330.
+ * Then turns off PLL:  byte-write BRCMF_INIT_CLKCTL1 (0x28) ->  
SBSDIO_FUNC1_CHIPCLKCSR (0x1000E)
+ * Then it reads value back, should be 0xe8.
+ * Then calls brcmf_chip_attach()
+ *
+ * 
http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L1054
+ * This func enumerates and resets all the cores on the dongle.
+ *  - brcmf_sdio_buscoreprep(): force clock to ALPAvail req only:
+ *    SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ -> 
SBSDIO_FUNC1_CHIPCLKCSR
+ * Wait up to 15ms to !SBSDIO_ALPAV(clkval) of the value from CLKCSR.
+ * Force ALP:
+ *    SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP (0x21)-> 
SBSDIO_FUNC1_CHIPCLKCSR
+ * Disaable SDIO pullups:
+ * byte 0 -> SBSDIO_FUNC1_SDIOPULLUP (0x0001000f)
+ *
+ *  Calls brcmf_chip_recognition()
+ * 
http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L908
+ * Read 0x18000000. Get 0x16044330: chip 4330 rev 4
+ * AXI chip, call  brcmf_chip_dmp_erom_scan() to get info about all cores.
+ * Then  brcmf_chip_cores_check() to check that CPU and RAM are found,
+ *
+ * Setting cores to passive: not clear which of CR4/CA7/CM3 our chip has.
+ *  Quite a few r/w calls to different parts of the chip to reset cores....
+ * Finally get_raminfo() called to fill in RAM info:
+ * brcmf_chip_get_raminfo: RAM: base=0x0 size=294912 (0x48000) sr=0 (0x0)
+ * 
http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L700
+ *
+ * Then brcmf_chip_setup() is called, this prints and fills in chipcommon rev 
and PMU caps:
+ *   brcmf_chip_setup: ccrev=39, pmurev=12, pmucaps=0x19583c0c
+ * 
http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L1015
+ *  Bus-specific setup code is NOP for SDIO.
+ *
+ * brcmf_sdio_kso_init() is called.
+ * Here it first reads 0x1 from SBSDIO_FUNC1_SLEEPCSR 0x18000650 and then 
writes it back... WTF?
+ *
+ * brcmf_sdio_drivestrengthinit() is called
+ * 
http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c#L3630
+ *
+ * Set card control so an SDIO card reset does a WLAN backplane reset
+ * set PMUControl so a backplane reset does PMU state reload
+ * === end of brcmf_sdio_probe_attach ===
+
+ **** Finished reading at 
http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c#L4152,
 line 2025 in the dump
+
+ * === How register reading works ===
+ * 
http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c#L357
+ * The address to read from is written to three byte-sized registers of F1:
+ *  - SBSDIO_FUNC1_SBADDRLOW  0x1000A
+ *  - SBSDIO_FUNC1_SBADDRMID  0x1000B
+ *  - SBSDIO_FUNC1_SBADDRHIGH 0x1000C
+ * If this is 32-bit read , a flag is set. The address is ANDed with 
SBSDIO_SB_OFT_ADDR_MASK which is 0x07FFF.
+ * Then brcmf_sdiod_regrw_helper() is called to read the reply.
+ * 
http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c#L306
+ * Based on the address it figures out where to read it from (CCCR / FBR in 
F0, or somewhere in F1).
+ * Reads are retried three times.
+ * 1-byte IO is done with CMD52, more is read with CMD53 with address 
increment (not FIFO mode).
+ * http://lxr.free-electrons.com/source/drivers/mmc/core/sdio_io.c#L458
+ * ==================================
+ *
+ *
+ */
+__unused
+static void
+probe_bcrm(struct cam_device *dev) {
+       uint32_t cis_addr;
+       struct cis_info info;
+
+       sdio_card_set_bus_width(dev, bus_width_4);
+       cis_addr = sdio_get_common_cis_addr(dev);
+       printf("CIS address: %04X\n", cis_addr);
+
+       memset(&info, 0, sizeof(info));
+       sdio_func_read_cis(dev, 0, cis_addr, &info);
+       printf("Vendor 0x%04X product 0x%04X\n", info.man_id, info.prod_id);
+}
+__unused
+static uint8_t *
+mmap_fw() {
+       const char fw_path[] = 
"/home/kibab/repos/fbsd-bbb/brcm-firmware/brcmfmac4330-sdio.bin";
+       struct stat sb;
+       uint8_t *fw_ptr;
+
+       int fd = open(fw_path, O_RDONLY);
+       if (fd < 0)
+               errx(1, "Cannot open firmware file");
+       if (fstat(fd, &sb) < 0)
+               errx(1, "Cannot get file stat");
+       fw_ptr = mmap(NULL, sb.st_size, PROT_READ, 0, fd, 0);
+       if (fw_ptr == MAP_FAILED)
+               errx(1, "Cannot map the file");
+
+       return fw_ptr;
+}
+
+static void
+usage() {
+       printf("sdiotool -u <pass_dev_unit>\n");
+       exit(0);
+}
+
+static void
+get_sdio_card_info(struct cam_device *dev) {
+       uint32_t cis_addr;
+       uint32_t fbr_addr;
+       struct cis_info info;
+
+       cis_addr = sdio_get_common_cis_addr(dev);
+
+       memset(&info, 0, sizeof(info));
+       sdio_func_read_cis(dev, 0, cis_addr, &info);
+       printf("F0: Vendor 0x%04X product 0x%04X max block size %d bytes\n",
+              info.man_id, info.prod_id, info.max_block_size);
+       for (int i = 1; i <= 2; i++) {
+               fbr_addr = SD_IO_FBR_START * i + 0x9;
+               cis_addr =  sdio_read_1(dev, 0, fbr_addr++);
+               cis_addr |= sdio_read_1(dev, 0, fbr_addr++) << 8;
+               cis_addr |= sdio_read_1(dev, 0, fbr_addr++) << 16;
+               memset(&info, 0, sizeof(info));
+               sdio_func_read_cis(dev, i, cis_addr, &info);
+               printf("F%d: Vendor 0x%04X product 0x%04X max block size %d 
bytes\n",
+                      i, info.man_id, info.prod_id, info.max_block_size);
+       }
+}
+
+/* Test interrupt delivery when select() */
+__unused static int
+sdio_signal_intr(struct cam_device *dev) {
+       uint8_t resp;
+       int ret;
+
+       ret = sdio_rw_direct(dev, 0, 0x666, 0, NULL, &resp);
+       if (ret < 0)
+               return ret;
+       return (0);
+}
+
+static void
+do_intr_test(__unused struct cam_device *dev) {
+}
+
+int
+main(int argc, char **argv) {
+       char device[] = "pass";
+       int unit = 0;
+       int func = 0;
+       uint8_t resp;
+       uint8_t is_enab;
+       __unused uint8_t *fw_ptr;
+       int ch;
+       struct cam_device *cam_dev;
+       int is_intr_test = 0;
+
+       //fw_ptr = mmap_fw();
+
+       while ((ch = getopt(argc, argv, "Iu:")) != -1) {
+               switch (ch) {
+               case 'u':
+                       unit = (int) strtol(optarg, NULL, 10);
+                       break;
+               case 'f':
+                       func = (int) strtol(optarg, NULL, 10);
+               case 'I':
+                       is_intr_test = 1;
+               case '?':
+               default:
+                       usage();
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       if ((cam_dev = cam_open_spec_device(device, unit, O_RDWR, NULL)) == 
NULL)
+               errx(1, "Cannot open device");
+
+       get_sdio_card_info(cam_dev);
+       if (is_intr_test > 0)
+               do_intr_test(cam_dev);
+       exit(0);
+       sdio_card_reset(cam_dev);
+
+       /* Read Addr 7 of func 0 */
+       int ret = sdio_rw_direct(cam_dev, 0, 7, 0, NULL, &resp);
+       if (ret < 0)
+               errx(1, "Error sending CAM command");
+       printf("Result: %02x\n", resp);
+
+       /* Check if func 1 is enabled */
+       ret = sdio_is_func_enabled(cam_dev, 1, &is_enab);
+       if (ret < 0)
+               errx(1, "Cannot check if func is enabled");
+       printf("F1 enabled: %d\n", is_enab);
+       ret = sdio_func_enable(cam_dev, 1, 1 - is_enab);
+       if (ret < 0)
+               errx(1, "Cannot enable/disable func");
+       printf("F1 en/dis result: %d\n", ret);
+
+       /* Check if func 1 is ready */
+       ret = sdio_is_func_ready(cam_dev, 1, &is_enab);
+       if (ret < 0)
+               errx(1, "Cannot check if func is ready");
+       printf("F1 ready: %d\n", is_enab);
+
+       /* Check if interrupts are enabled */
+       ret = sdio_is_func_intr_enabled(cam_dev, 1, &is_enab);
+       if (ret < 0)
+               errx(1, "Cannot check if func intr is enabled");
+       printf("F1 intr enabled: %d\n", is_enab);
+       ret = sdio_func_intr_enable(cam_dev, 1, 1 - is_enab);
+       if (ret < 0)
+               errx(1, "Cannot enable/disable func intr");
+       printf("F1 intr en/dis result: %d\n", ret);
+
+       cam_close_spec_device(cam_dev);
+}
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to