xref: /freebsd/usr.bin/sdiotool/sdiotool.c (revision 8657387683946d0c03e09fe77029edfe309eeb20)
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 }
650