Index: head/sys/conf/files =================================================================== --- head/sys/conf/files +++ head/sys/conf/files @@ -68,6 +68,16 @@ compile-with "${AWK} -f $S/tools/usbdevs2h.awk $S/dev/usb/usbdevs -d" \ no-obj no-implicit-rule before-depend \ clean "usbdevs_data.h" +sdiodevs.h optional mmccam \ + dependency "$S/tools/sdiodevs2h.awk $S/dev/sdio/sdiodevs" \ + compile-with "${AWK} -f $S/tools/sdiodevs2h.awk $S/dev/sdio/sdiodevs -h" \ + no-obj no-implicit-rule before-depend \ + clean "sdiodevs.h" +sdiodevs_data.h optional mmccam \ + dependency "$S/tools/sdiodevs2h.awk $S/dev/sdio/sdiodevs" \ + compile-with "${AWK} -f $S/tools/sdiodevs2h.awk $S/dev/sdio/sdiodevs -d" \ + no-obj no-implicit-rule before-depend \ + clean "sdiodevs_data.h" cam/cam.c optional scbus cam/cam_compat.c optional scbus cam/cam_iosched.c optional scbus @@ -3009,6 +3019,9 @@ dev/sdhci/sdhci_if.m optional sdhci dev/sdhci/sdhci_acpi.c optional sdhci acpi dev/sdhci/sdhci_pci.c optional sdhci pci ++dev/sdio/sdio_if.m optional mmccam ++dev/sdio/sdio_subr.c optional mmccam ++dev/sdio/sdiob.c optional mmccam dev/sge/if_sge.c optional sge pci dev/siis/siis.c optional siis pci dev/sis/if_sis.c optional sis pci Index: head/sys/conf/kmod.mk =================================================================== --- head/sys/conf/kmod.mk +++ head/sys/conf/kmod.mk @@ -475,6 +475,18 @@ ${AWK} -f ${SYSDIR}/tools/usbdevs2h.awk ${SYSDIR}/dev/usb/usbdevs -d .endif +.if !empty(SRCS:Msdiodevs.h) +CLEANFILES+= sdiodevs.h +sdiodevs.h: ${SYSDIR}/tools/sdiodevs2h.awk ${SYSDIR}/dev/sdio/sdiodevs + ${AWK} -f ${SYSDIR}/tools/sdiodevs2h.awk ${SYSDIR}/dev/sdio/sdiodevs -h +.endif + +.if !empty(SRCS:Msdiodevs_data.h) +CLEANFILES+= sdiodevs_data.h +sdiodevs_data.h: ${SYSDIR}/tools/sdiodevs2h.awk ${SYSDIR}/dev/sdio/sdiodevs + ${AWK} -f ${SYSDIR}/tools/sdiodevs2h.awk ${SYSDIR}/dev/sdio/sdiodevs -d +.endif + .if !empty(SRCS:Macpi_quirks.h) CLEANFILES+= acpi_quirks.h acpi_quirks.h: ${SYSDIR}/tools/acpi_quirks2h.awk ${SYSDIR}/dev/acpica/acpi_quirks Index: head/sys/dev/sdio/sdio_if.m =================================================================== --- head/sys/dev/sdio/sdio_if.m +++ head/sys/dev/sdio/sdio_if.m @@ -0,0 +1,79 @@ +#- +# Copyright (c) 2019 The FreeBSD Foundation +# +# Portions of this software were developed by Björn Zeeb +# under sponsorship from the FreeBSD Foundation. +# +# 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 ``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 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 +#include + +INTERFACE sdio; + +# +# READ DIRECT (1byte) +# +METHOD int read_direct { + device_t dev; + uint8_t fn; + uint32_t addr; + uint8_t *val; +}; + +# +# WRITE DIRECT (1byte) +# +METHOD int write_direct { + device_t dev; + uint8_t fn; + uint32_t addr; + uint8_t val; +}; + +# +# READ EXTENDED +# +METHOD int read_extended { + device_t dev; + uint8_t fn; + uint32_t addr; + uint32_t size; + uint8_t *buffer; + bool incaddr; +}; + +# +# WRITE EXTENDED +# +METHOD int write_extended { + device_t dev; + uint8_t fn; + uint32_t addr; + uint32_t size; + uint8_t *buffer; + bool incaddr; +}; + +# end Index: head/sys/dev/sdio/sdio_subr.h =================================================================== --- head/sys/dev/sdio/sdio_subr.h +++ head/sys/dev/sdio/sdio_subr.h @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 2017 Ilya Bakulin. All rights reserved. + * Copyright (c) 2018-2019 The FreeBSD Foundation + * + * Portions of this software were developed by Björn Zeeb + * under sponsorship from the FreeBSD Foundation. + * + * 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 ``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 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. + * + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * $FreeBSD$ + */ + +#ifndef _SDIO_SUBR_H_ +#define _SDIO_SUBR_H_ + +/* + * This file contains structures and functions to work with SDIO cards. + */ + +struct sdio_func { + device_t dev; /* The device to talk to CAM. */ + uintptr_t drvdata; /* Driver specific data. */ + + uint8_t fn; /* Function number. */ + + uint8_t class; /* Class of function. */ + uint16_t vendor; /* Manufacturer ID. */ + uint16_t device; /* Card ID. */ + + uint16_t max_blksize; /* Maximum block size of function. */ + uint16_t cur_blksize; /* Current block size of function. */ + + uint16_t retries; /* Retires for CAM operations. */ + uint32_t timeout; /* Timeout. */ +}; + +struct card_info { + struct sdio_func f[8]; + + /* Compared to R4 Number of I/O Functions we DO count F0 here. */ + uint8_t num_funcs; + + bool support_multiblk; /* Support Multiple Block Transfer */ +}; + +#ifdef _KERNEL +int sdio_enable_func(struct sdio_func *); +int sdio_disable_func(struct sdio_func *); +int sdio_set_block_size(struct sdio_func *, uint16_t); + +uint8_t sdio_readb(struct sdio_func *, uint32_t, int *); +void sdio_writeb(struct sdio_func *, uint8_t, uint32_t, int *); +uint32_t sdio_readl(struct sdio_func *, uint32_t, int *); +void sdio_writel(struct sdio_func *, uint32_t, uint32_t, int *); + +uint8_t sdio_f0_readb(struct sdio_func *, uint32_t, int *); +void sdio_f0_writeb(struct sdio_func *, uint8_t, uint32_t, int *); +#endif /* _KERNEL */ + +#endif /* _SDIO_SUBR_H_ */ Index: head/sys/dev/sdio/sdio_subr.c =================================================================== --- head/sys/dev/sdio/sdio_subr.c +++ head/sys/dev/sdio/sdio_subr.c @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 2017 Ilya Bakulin. All rights reserved. + * Copyright (c) 2018-2019 The FreeBSD Foundation + * + * Portions of this software were developed by Björn Zeeb + * under sponsorship from the FreeBSD Foundation. + * + * 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 ``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 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. + * + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "sdio_if.h" + +/* Works on F0. */ +static int +sdio_set_bool_for_func(device_t dev, uint32_t addr, uint8_t fn, bool enable) +{ + device_t pdev; + int error; + uint8_t val; + bool enabled; + + pdev = device_get_parent(dev); + error = SDIO_READ_DIRECT(pdev, 0, addr, &val); + if (error != 0) + return (error); + + enabled = (val & (1 << fn)) ? true : false; + if (enabled == enable) + return (0); + + if (enable) + val |= (1 << fn); + else + val &= ~(1 << fn); + error = SDIO_WRITE_DIRECT(pdev, 0, addr, val); + return (error); +} + +int +sdio_enable_func(struct sdio_func *f) +{ + + return (sdio_set_bool_for_func(f->dev, SD_IO_CCCR_FN_ENABLE, + f->fn, true)); +} + +int +sdio_disable_func(struct sdio_func *f) +{ + + return (sdio_set_bool_for_func(f->dev, SD_IO_CCCR_FN_ENABLE, + f->fn, false)); +} + +int +sdio_set_block_size(struct sdio_func *f, uint16_t bs) +{ + device_t pdev; + int error; + uint32_t addr; + uint16_t v; + + if (!sdio_get_support_multiblk(f->dev)) + return (EOPNOTSUPP); + + pdev = device_get_parent(f->dev); + addr = SD_IO_FBR_START * f->fn + SD_IO_FBR_IOBLKSZ; + v = htole16(bs); + /* Always write through F0. */ + error = SDIO_WRITE_DIRECT(pdev, 0, addr, v & 0xff); + if (error == 0) + error = SDIO_WRITE_DIRECT(pdev, 0, addr + 1, + (v >> 8) & 0xff); + if (error == 0) + f->cur_blksize = bs; + + return (error); +} + +uint8_t +sdio_readb(struct sdio_func *f, uint32_t addr, int *err) +{ + int error; + uint8_t v; + + error = SDIO_READ_DIRECT(device_get_parent(f->dev), f->fn, addr, &v); + if (error) { + if (err != NULL) + *err = error; + return (0xff); + } else { + if (err != NULL) + *err = 0; + return (v); + } +} + +void +sdio_writeb(struct sdio_func *f, uint8_t val, uint32_t addr, int *err) +{ + int error; + + error = SDIO_WRITE_DIRECT(device_get_parent(f->dev), f->fn, addr, val); + if (err != NULL) + *err = error; +} + +uint32_t +sdio_readl(struct sdio_func *f, uint32_t addr, int *err) +{ + int error; + uint32_t v; + + error = SDIO_READ_EXTENDED(device_get_parent(f->dev), f->fn, addr, + sizeof(v), (uint8_t *)&v, false); + if (error) { + if (err != NULL) + *err = error; + return (0xffffffff); + } else { + if (err != NULL) + *err = 0; + return (le32toh(v)); + } +} + +void +sdio_writel(struct sdio_func *f, uint32_t val, uint32_t addr, int *err) +{ + int error; + + error = SDIO_WRITE_EXTENDED(device_get_parent(f->dev), f->fn, addr, + sizeof(val), (uint8_t *)&val, false); + if (err != NULL) + *err = error; +} + +uint8_t +sdio_f0_readb(struct sdio_func *f, uint32_t addr, int *err) +{ + int error; + uint8_t v; + + error = SDIO_READ_DIRECT(device_get_parent(f->dev), 0, addr, &v); + if (error) { + if (err != NULL) + *err = error; + return (0xff); + } else { + if (err != NULL) + *err = 0; + return (v); + } +} + +void +sdio_f0_writeb(struct sdio_func *f, uint8_t val, uint32_t addr, int *err) +{ + int error; + + error = SDIO_WRITE_DIRECT(device_get_parent(f->dev), 0, addr, val); + if (err != NULL) + *err = error; +} + +/* end */ Index: head/sys/dev/sdio/sdiob.h =================================================================== --- head/sys/dev/sdio/sdiob.h +++ head/sys/dev/sdio/sdiob.h @@ -0,0 +1,92 @@ +/*- + * Copyright (c) 2019 The FreeBSD Foundation + * + * Portions of this software were developed by Björn Zeeb + * under sponsorship from the FreeBSD Foundation. + * + * 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 ``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 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. + * + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * $FreeBSD$ + */ + +#ifndef _SDIOB_H +#define _SDIOB_H + +#define SDIOB_NAME sdiob +#define _SDIOB_NAME_S(x) __STRING(x) +#define SDIOB_NAME_S _SDIOB_NAME_S(SDIOB_NAME) + +#ifdef _SYS_BUS_H_ +/* Ivars for sdiob. */ +enum sdiob_dev_enum { + SDIOB_IVAR_SUPPORT_MULTIBLK, + SDIOB_IVAR_FUNCTION, + SDIOB_IVAR_FUNCNUM, + SDIOB_IVAR_CLASS, + SDIOB_IVAR_VENDOR, + SDIOB_IVAR_DEVICE, + SDIOB_IVAR_DRVDATA, +}; + +#define SDIOB_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(sdio, var, SDIOB, ivar, type) + +SDIOB_ACCESSOR(support_multiblk,SUPPORT_MULTIBLK, bool) +SDIOB_ACCESSOR(function, FUNCTION, struct sdio_func *) +SDIOB_ACCESSOR(funcnum, FUNCNUM, uint8_t) +SDIOB_ACCESSOR(class, CLASS, uint8_t) +SDIOB_ACCESSOR(vendor, VENDOR, uint16_t) +SDIOB_ACCESSOR(device, DEVICE, uint16_t) +SDIOB_ACCESSOR(drvdata, DRVDATA, void *) + +#undef SDIOB_ACCESSOR +#endif /* _SYS_BUS_H_ */ + +#endif /* _SDIOB_H */ Index: head/sys/dev/sdio/sdiob.c =================================================================== --- head/sys/dev/sdio/sdiob.c +++ head/sys/dev/sdio/sdiob.c @@ -0,0 +1,1188 @@ +/*- + * Copyright (c) 2017 Ilya Bakulin. All rights reserved. + * Copyright (c) 2018-2019 The FreeBSD Foundation + * + * Portions of this software were developed by Björn Zeeb + * under sponsorship from the FreeBSD Foundation. + * + * 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 ``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 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. + * + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + */ +/* + * Implements the (kernel specific) SDIO parts. + * This will hide all cam(4) functionality from the SDIO driver implementations + * which will just be newbus/device(9) and hence look like any other driver for, + * e.g., PCI. + * The sdiob(4) parts effetively "translate" between the two worlds "bridging" + * messages from MMCCAM to newbus and back. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_cam.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include /* for cam_path */ +#include + +#include + +#include +#include + +#include "sdio_if.h" + +#ifdef DEBUG +#define DPRINTF(...) printf(__VA_ARGS__) +#define DPRINTFDEV(_dev, ...) device_printf((_dev), __VA_ARGS__) +#else +#define DPRINTF(...) +#define DPRINTFDEV(_dev, ...) +#endif + +struct sdiob_softc { + uint32_t sdio_state; +#define SDIO_STATE_DEAD 0x0001 +#define SDIO_STATE_INITIALIZING 0x0002 +#define SDIO_STATE_READY 0x0004 + uint32_t nb_state; +#define NB_STATE_DEAD 0x0001 +#define NB_STATE_SIM_ADDED 0x0002 +#define NB_STATE_READY 0x0004 + + /* CAM side (including sim_dev). */ + struct card_info cardinfo; + struct cam_periph *periph; + union ccb *ccb; + struct task discover_task; + + /* Newbus side. */ + device_t dev; /* Ourselves. */ + device_t child[8]; +}; + +/* -------------------------------------------------------------------------- */ +/* + * SDIO CMD52 and CM53 implementations along with wrapper functions for + * read/write and a CAM periph helper function. + * These are the backend implementations of the sdio_if.m framework talking + * through CAM to sdhci. + * Note: these functions are also called during early discovery stage when + * we are not a device(9) yet. Hence they cannot always use device_printf() + * to log errors and have to call CAM_DEBUG() during these early stages. + */ + +static int +sdioerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) +{ + + return (cam_periph_error(ccb, cam_flags, sense_flags)); +} + +/* CMD52: direct byte access. */ +static int +sdiob_rw_direct_sc(struct sdiob_softc *sc, uint8_t fn, uint32_t addr, bool wr, + uint8_t *val) +{ + uint32_t arg, flags; + int error; + + KASSERT((val != NULL), ("%s val passed as NULL\n", __func__)); + + if (sc->ccb == NULL) + sc->ccb = xpt_alloc_ccb(); + else + memset(sc->ccb, 0, sizeof(*sc->ccb)); + xpt_setup_ccb(&sc->ccb->ccb_h, sc->periph->path, CAM_PRIORITY_NONE); + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("%s(fn=%d, addr=%#02x, wr=%d, *val=%#02x)\n", __func__, + fn, addr, wr, *val)); + + flags = MMC_RSP_R5 | MMC_CMD_AC; + arg = SD_IO_RW_FUNC(fn) | SD_IO_RW_ADR(addr); + if (wr) + arg |= SD_IO_RW_WR | SD_IO_RW_RAW | SD_IO_RW_DAT(*val); + + cam_fill_mmcio(&sc->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*/ sc->cardinfo.f[fn].timeout); + error = cam_periph_runccb(sc->ccb, sdioerror, CAM_FLAG_NONE, 0, NULL); + if (error != 0) { + if (sc->dev != NULL) + device_printf(sc->dev, + "%s: Failed to %s address %#10x error=%d\n", + __func__, (wr) ? "write" : "read", addr, error); + else + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_INFO, + ("%s: Failed to %s address: %#10x error=%d\n", + __func__, (wr) ? "write" : "read", addr, error)); + return (error); + } + + /* TODO: Add handling of MMC errors */ + /* ccb->mmcio.cmd.error ? */ + if (wr == false) + *val = sc->ccb->mmcio.cmd.resp[0] & 0xff; + + return (0); +} + +static int +sdio_rw_direct(device_t dev, uint8_t fn, uint32_t addr, bool wr, + uint8_t *val) +{ + struct sdiob_softc *sc; + int error; + + sc = device_get_softc(dev); + cam_periph_lock(sc->periph); + error = sdiob_rw_direct_sc(sc, fn, addr, wr, val); + cam_periph_unlock(sc->periph); + return (error); +} + +static int +sdiob_read_direct(device_t dev, uint8_t fn, uint32_t addr, uint8_t *val) +{ + int error; + uint8_t v; + + error = sdio_rw_direct(dev, fn, addr, false, &v); + /* Be polite and do not touch the value on read error. */ + if (error == 0 && val != NULL) + *val = v; + return (error); +} + +static int +sdiob_write_direct(device_t dev, uint8_t fn, uint32_t addr, uint8_t val) +{ + + return (sdio_rw_direct(dev, fn, addr, true, &val)); +} + +/* + * CMD53: IO_RW_EXTENDED, read and write multiple I/O registers. + * Increment false gets FIFO mode (single register address). + */ +/* + * A b_count of 0 means byte mode, b_count > 0 gets block mode. + * A b_count of >= 512 would mean infinitive block transfer, which would become + * b_count = 0, is not yet supported. + * For b_count == 0, blksz is the len of bytes, otherwise it is the amount of + * full sized blocks (you must not round the blocks up and leave the last one + * partial!) + * For byte mode, the maximum of blksz is the functions cur_blksize. + * This function should ever only be called by sdio_rw_extended_sc()! + */ +static int +sdiob_rw_extended_cam(struct sdiob_softc *sc, uint8_t fn, uint32_t addr, + bool wr, uint8_t *buffer, bool incaddr, uint32_t b_count, uint16_t blksz) +{ + struct mmc_data mmcd; + uint32_t arg, cam_flags, flags, len; + int error; + + if (sc->ccb == NULL) + sc->ccb = xpt_alloc_ccb(); + else + memset(sc->ccb, 0, sizeof(*sc->ccb)); + xpt_setup_ccb(&sc->ccb->ccb_h, sc->periph->path, CAM_PRIORITY_NONE); + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("%s(fn=%d addr=%#0x wr=%d b_count=%u blksz=%u buf=%p incr=%d)\n", + __func__, fn, addr, wr, b_count, blksz, buffer, incaddr)); + + KASSERT((b_count <= 511), ("%s: infinitive block transfer not yet " + "supported: b_count %u blksz %u, sc %p, fn %u, addr %#10x, %s, " + "buffer %p, %s\n", __func__, b_count, blksz, sc, fn, addr, + wr ? "wr" : "rd", buffer, incaddr ? "incaddr" : "fifo")); + /* Blksz needs to be within bounds for both byte and block mode! */ + KASSERT((blksz <= sc->cardinfo.f[fn].cur_blksize), ("%s: blksz " + "%u > bur_blksize %u, sc %p, fn %u, addr %#10x, %s, " + "buffer %p, %s, b_count %u\n", __func__, blksz, + sc->cardinfo.f[fn].cur_blksize, sc, fn, addr, + wr ? "wr" : "rd", buffer, incaddr ? "incaddr" : "fifo", + b_count)); + if (b_count == 0) { + /* Byte mode */ + len = blksz; + if (blksz == 512) + blksz = 0; + arg = SD_IOE_RW_LEN(blksz); + } else { + /* Block mode. */ +#ifdef __notyet__ + if (b_count > 511) { + /* Infinitive block transfer. */ + b_count = 0; + } +#endif + len = b_count * blksz; + arg = SD_IOE_RW_BLK | SD_IOE_RW_LEN(b_count); + } + + flags = MMC_RSP_R5 | MMC_CMD_ADTC; + arg |= SD_IOE_RW_FUNC(fn) | SD_IOE_RW_ADR(addr); + if (incaddr) + arg |= SD_IOE_RW_INCR; + + memset(&mmcd, 0, sizeof(mmcd)); + mmcd.data = buffer; + mmcd.len = len; + if (arg & SD_IOE_RW_BLK) { + /* XXX both should be known from elsewhere, aren't they? */ + mmcd.block_size = blksz; + mmcd.block_count = b_count; + } + + if (wr) { + arg |= SD_IOE_RW_WR; + cam_flags = CAM_DIR_OUT; + mmcd.flags = MMC_DATA_WRITE; + } else { + cam_flags = CAM_DIR_IN; + mmcd.flags = MMC_DATA_READ; + } +#ifdef __notyet__ + if (b_count == 0) { + /* XXX-BZ TODO FIXME. Cancel I/O: CCCR -> ASx */ + /* Stop cmd. */ + } +#endif + cam_fill_mmcio(&sc->ccb->mmcio, + /*retries*/ 0, + /*cbfcnp*/ NULL, + /*flags*/ cam_flags, + /*mmc_opcode*/ SD_IO_RW_EXTENDED, + /*mmc_arg*/ arg, + /*mmc_flags*/ flags, + /*mmc_data*/ &mmcd, + /*timeout*/ sc->cardinfo.f[fn].timeout); + if (arg & SD_IOE_RW_BLK) { + mmcd.flags |= MMC_DATA_BLOCK_SIZE; + if (b_count != 1) + sc->ccb->mmcio.cmd.data->flags |= MMC_DATA_MULTI; + } + + /* Execute. */ + error = cam_periph_runccb(sc->ccb, sdioerror, CAM_FLAG_NONE, 0, NULL); + if (error != 0) { + if (sc->dev != NULL) + device_printf(sc->dev, + "%s: Failed to %s address %#10x buffer %p size %u " + "%s b_count %u blksz %u error=%d\n", + __func__, (wr) ? "write to" : "read from", addr, + buffer, len, (incaddr) ? "incr" : "fifo", + b_count, blksz, error); + else + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_INFO, + ("%s: Failed to %s address %#10x buffer %p size %u " + "%s b_count %u blksz %u error=%d\n", + __func__, (wr) ? "write to" : "read from", addr, + buffer, len, (incaddr) ? "incr" : "fifo", + b_count, blksz, error)); + return (error); + } + + /* TODO: Add handling of MMC errors */ + /* ccb->mmcio.cmd.error ? */ + error = sc->ccb->mmcio.cmd.resp[0] & 0xff; + if (error != 0) { + if (sc->dev != NULL) + device_printf(sc->dev, + "%s: Failed to %s address %#10x buffer %p size %u " + "%s b_count %u blksz %u mmcio resp error=%d\n", + __func__, (wr) ? "write to" : "read from", addr, + buffer, len, (incaddr) ? "incr" : "fifo", + b_count, blksz, error); + else + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_INFO, + ("%s: Failed to %s address %#10x buffer %p size %u " + "%s b_count %u blksz %u mmcio resp error=%d\n", + __func__, (wr) ? "write to" : "read from", addr, + buffer, len, (incaddr) ? "incr" : "fifo", + b_count, blksz, error)); + } + return (error); +} + +static int +sdiob_rw_extended_sc(struct sdiob_softc *sc, uint8_t fn, uint32_t addr, + bool wr, uint32_t size, uint8_t *buffer, bool incaddr) +{ + int error; + uint32_t len; + uint32_t b_count; + + /* + * If block mode is supported and we have at least 4 bytes to write and + * the size is at least one block, then start doing blk transfers. + */ + while (sc->cardinfo.support_multiblk && + size > 4 && size >= sc->cardinfo.f[fn].cur_blksize) { + + b_count = size / sc->cardinfo.f[fn].cur_blksize; + KASSERT(b_count >= 1, ("%s: block count too small %u size %u " + "cur_blksize %u\n", __func__, b_count, size, + sc->cardinfo.f[fn].cur_blksize)); + +#ifdef __notyet__ + /* XXX support inifinite transfer with b_count = 0. */ +#else + if (b_count > 511) + b_count = 511; +#endif + len = b_count * sc->cardinfo.f[fn].cur_blksize; + error = sdiob_rw_extended_cam(sc, fn, addr, wr, buffer, incaddr, + b_count, sc->cardinfo.f[fn].cur_blksize); + if (error != 0) + return (error); + + size -= len; + buffer += len; + if (incaddr) + addr += len; + } + + while (size > 0) { + len = MIN(size, sc->cardinfo.f[fn].cur_blksize); + + error = sdiob_rw_extended_cam(sc, fn, addr, wr, buffer, incaddr, + 0, len); + if (error != 0) + return (error); + + /* Prepare for next iteration. */ + size -= len; + buffer += len; + if (incaddr) + addr += len; + } + + return (0); +} + +static int +sdiob_rw_extended(device_t dev, uint8_t fn, uint32_t addr, bool wr, + uint32_t size, uint8_t *buffer, bool incaddr) +{ + struct sdiob_softc *sc; + int error; + + sc = device_get_softc(dev); + cam_periph_lock(sc->periph); + error = sdiob_rw_extended_sc(sc, fn, addr, wr, size, buffer, incaddr); + cam_periph_unlock(sc->periph); + return (error); +} + +static int +sdiob_read_extended(device_t dev, uint8_t fn, uint32_t addr, uint32_t size, + uint8_t *buffer, bool incaddr) +{ + + return (sdiob_rw_extended(dev, fn, addr, false, size, buffer, incaddr)); +} + +static int +sdiob_write_extended(device_t dev, uint8_t fn, uint32_t addr, uint32_t size, + uint8_t *buffer, bool incaddr) +{ + + return (sdiob_rw_extended(dev, fn, addr, true, size, buffer, incaddr)); +} + +/* -------------------------------------------------------------------------- */ +/* Bus interface, ivars handling. */ + +static int +sdiob_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct sdiob_softc *sc; + struct sdio_func *f; + + f = device_get_ivars(child); + KASSERT(f != NULL, ("%s: dev %p child %p which %d, child ivars NULL\n", + __func__, dev, child, which)); + + switch (which) { + case SDIOB_IVAR_SUPPORT_MULTIBLK: + sc = device_get_softc(dev); + KASSERT(sc != NULL, ("%s: dev %p child %p which %d, sc NULL\n", + __func__, dev, child, which)); + *result = sc->cardinfo.support_multiblk; + break; + case SDIOB_IVAR_FUNCTION: + *result = (uintptr_t)f; + break; + case SDIOB_IVAR_FUNCNUM: + *result = f->fn; + break; + case SDIOB_IVAR_CLASS: + *result = f->class; + break; + case SDIOB_IVAR_VENDOR: + *result = f->vendor; + break; + case SDIOB_IVAR_DEVICE: + *result = f->device; + break; + case SDIOB_IVAR_DRVDATA: + *result = f->drvdata; + break; + default: + return (ENOENT); + } + return (0); +} + +static int +sdiob_write_ivar(device_t dev, device_t child, int which, uintptr_t value) +{ + struct sdio_func *f; + + f = device_get_ivars(child); + KASSERT(f != NULL, ("%s: dev %p child %p which %d, child ivars NULL\n", + __func__, dev, child, which)); + + switch (which) { + case SDIOB_IVAR_SUPPORT_MULTIBLK: + case SDIOB_IVAR_FUNCTION: + case SDIOB_IVAR_FUNCNUM: + case SDIOB_IVAR_CLASS: + case SDIOB_IVAR_VENDOR: + case SDIOB_IVAR_DEVICE: + return (EINVAL); /* Disallowed. */ + case SDIOB_IVAR_DRVDATA: + f->drvdata = value; + break; + default: + return (ENOENT); + } + + return (0); +} + + +/* -------------------------------------------------------------------------- */ +/* + * Newbus functions for ourselves to probe/attach/detach and become a proper + * device(9). Attach will also probe for child devices (another driver + * implementing SDIO). + */ + +static int +sdiob_probe(device_t dev) +{ + + device_set_desc(dev, "SDIO CAM-Newbus bridge"); + return (BUS_PROBE_DEFAULT); +} + +static int +sdiob_attach(device_t dev) +{ + struct sdiob_softc *sc; + int error, i; + + sc = device_get_softc(dev); + if (sc == NULL) + return (ENXIO); + + /* + * Now that we are a dev, create one child device per function, + * initialize the backpointer, so we can pass them around and + * call CAM operations on the parent, and also set the function + * itself as ivars, so that we can query/update them. + * Do this before any child gets a chance to attach. + */ + for (i = 0; i < sc->cardinfo.num_funcs; i++) { + + sc->child[i] = device_add_child(dev, NULL, -1); + if (sc->child[i] == NULL) { + device_printf(dev, "%s: failed to add child\n", __func__); + return (ENXIO); + } + sc->cardinfo.f[i].dev = sc->child[i]; + + /* Set the function as ivar to the child device. */ + device_set_ivars(sc->child[i], &sc->cardinfo.f[i]); + } + + /* + * No one will ever attach to F0; we do the above to have a "device" + * to talk to in a general way in the code. + * Also do the probe/attach in a 2nd loop, so that all devices are + * present as we do have drivers consuming more than one device/func + * and might play "tricks" in order to do that assuming devices and + * ivars are available for all. + */ + for (i = 1; i < sc->cardinfo.num_funcs; i++) { + error = device_probe_and_attach(sc->child[i]); + if (error != 0 && bootverbose) + device_printf(dev, "%s: device_probe_and_attach(%p %s) " + "failed %d for function %d, no child yet\n", + __func__, + sc->child, device_get_nameunit(sc->child[i]), + error, i); + } + + sc->nb_state = NB_STATE_READY; + + cam_periph_lock(sc->periph); + xpt_announce_periph(sc->periph, NULL); + cam_periph_unlock(sc->periph); + + return (0); +} + +static int +sdiob_detach(device_t dev) +{ + + /* XXX TODO? */ + return (EOPNOTSUPP); +} + + +/* -------------------------------------------------------------------------- */ +/* + * driver(9) and device(9) "control plane". + * This is what we use when we are making ourselves a device(9) in order to + * provide a newbus interface again, as well as the implementation of the + * SDIO interface. + */ + +static device_method_t sdiob_methods[] = { + + /* Device interface. */ + DEVMETHOD(device_probe, sdiob_probe), + DEVMETHOD(device_attach, sdiob_attach), + DEVMETHOD(device_detach, sdiob_detach), + + /* Bus interface. */ + DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + DEVMETHOD(bus_read_ivar, sdiob_read_ivar), + DEVMETHOD(bus_write_ivar, sdiob_write_ivar), + + /* SDIO interface. */ + DEVMETHOD(sdio_read_direct, sdiob_read_direct), + DEVMETHOD(sdio_write_direct, sdiob_write_direct), + DEVMETHOD(sdio_read_extended, sdiob_read_extended), + DEVMETHOD(sdio_write_extended, sdiob_write_extended), + + DEVMETHOD_END +}; + +static devclass_t sdiob_devclass; +static driver_t sdiob_driver = { + SDIOB_NAME_S, + sdiob_methods, + 0 +}; + + +/* -------------------------------------------------------------------------- */ +/* + * CIS related. + * Read card and function information and populate the cardinfo structure. + */ + +static int +sdio_read_direct_sc(struct sdiob_softc *sc, uint8_t fn, uint32_t addr, + uint8_t *val) +{ + int error; + uint8_t v; + + error = sdiob_rw_direct_sc(sc, fn, addr, false, &v); + if (error == 0 && val != NULL) + *val = v; + return (error); +} + +static int +sdio_func_read_cis(struct sdiob_softc *sc, uint8_t fn, uint32_t cis_addr) +{ + char cis1_info_buf[256]; + char *cis1_info[4]; + int start, i, count, ret; + uint32_t addr; + uint8_t ch, tuple_id, tuple_len, tuple_count, v; + + /* If we encounter any read errors, abort and return. */ +#define ERR_OUT(ret) \ + if (ret != 0) \ + goto err; + ret = 0; + /* Use to prevent infinite loop in case of parse errors. */ + tuple_count = 0; + memset(cis1_info_buf, 0, 256); + do { + addr = cis_addr; + ret = sdio_read_direct_sc(sc, 0, addr++, &tuple_id); + ERR_OUT(ret); + if (tuple_id == SD_IO_CISTPL_END) + break; + if (tuple_id == 0) { + cis_addr++; + continue; + } + ret = sdio_read_direct_sc(sc, 0, addr++, &tuple_len); + ERR_OUT(ret); + if (tuple_len == 0) { + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_PERIPH, + ("%s: parse error: 0-length tuple %#02x\n", + __func__, tuple_id)); + return (EIO); + } + + switch (tuple_id) { + case SD_IO_CISTPL_VERS_1: + addr += 2; + for (count = 0, start = 0, i = 0; + (count < 4) && ((i + 4) < 256); i++) { + ret = sdio_read_direct_sc(sc, 0, addr + i, &ch); + ERR_OUT(ret); + DPRINTF("%s: count=%d, start=%d, i=%d, got " + "(%#02x)\n", __func__, count, start, i, ch); + if (ch == 0xff) + break; + cis1_info_buf[i] = ch; + if (ch == 0) { + cis1_info[count] = + cis1_info_buf + start; + start = i + 1; + count++; + } + } + DPRINTF("Card info: "); + for (i=0; i < 4; i++) + if (cis1_info[i]) + DPRINTF(" %s", cis1_info[i]); + DPRINTF("\n"); + break; + case SD_IO_CISTPL_MANFID: + /* TPLMID_MANF */ + ret = sdio_read_direct_sc(sc, 0, addr++, &v); + ERR_OUT(ret); + sc->cardinfo.f[fn].vendor = v; + ret = sdio_read_direct_sc(sc, 0, addr++, &v); + ERR_OUT(ret); + sc->cardinfo.f[fn].vendor |= (v << 8); + /* TPLMID_CARD */ + ret = sdio_read_direct_sc(sc, 0, addr++, &v); + ERR_OUT(ret); + sc->cardinfo.f[fn].device = v; + ret = sdio_read_direct_sc(sc, 0, addr, &v); + ERR_OUT(ret); + sc->cardinfo.f[fn].device |= (v << 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("%s: FUNCE is too short: %d\n", + __func__, tuple_len); + break; + } + /* TPLFE_TYPE (Extended Data) */ + ret = sdio_read_direct_sc(sc, 0, addr++, &v); + ERR_OUT(ret); + if (fn == 0) { + if (v != 0x00) + break; + } else { + if (v != 0x01) + break; + addr += 0x0b; + } + ret = sdio_read_direct_sc(sc, 0, addr, &v); + ERR_OUT(ret); + sc->cardinfo.f[fn].max_blksize = v; + ret = sdio_read_direct_sc(sc, 0, addr+1, &v); + ERR_OUT(ret); + sc->cardinfo.f[fn].max_blksize |= (v << 8); + break; + default: + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_PERIPH, + ("%s: Skipping fn %d tuple %d ID %#02x " + "len %#02x\n", __func__, fn, tuple_count, + tuple_id, tuple_len)); + } + if (tuple_len == 0xff) { + /* Also marks the end of a tuple chain (E1 16.2) */ + /* The tuple is valid, hence this going at the end. */ + break; + } + cis_addr += 2 + tuple_len; + tuple_count++; + } while (tuple_count < 20); +err: +#undef ERR_OUT + return (ret); +} + +static int +sdio_get_common_cis_addr(struct sdiob_softc *sc, uint32_t *addr) +{ + int error; + uint32_t a; + uint8_t val; + + error = sdio_read_direct_sc(sc, 0, SD_IO_CCCR_CISPTR + 0, &val); + if (error != 0) + goto err; + a = val; + error = sdio_read_direct_sc(sc, 0, SD_IO_CCCR_CISPTR + 1, &val); + if (error != 0) + goto err; + a |= (val << 8); + error = sdio_read_direct_sc(sc, 0, SD_IO_CCCR_CISPTR + 2, &val); + if (error != 0) + goto err; + a |= (val << 16); + + if (a < SD_IO_CIS_START || a > SD_IO_CIS_START + SD_IO_CIS_SIZE) { +err: + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_PERIPH, + ("%s: bad CIS address: %#04x, error %d\n", __func__, a, + error)); + } else if (error == 0 && addr != NULL) + *addr = a; + + return (error); +} + +static int +sdiob_get_card_info(struct sdiob_softc *sc) +{ + struct mmc_params *mmcp; + uint32_t cis_addr, fbr_addr; + int fn, error; + uint8_t fn_max, val; + + error = sdio_get_common_cis_addr(sc, &cis_addr); + if (error != 0) + return (-1); + + memset(&sc->cardinfo, 0, sizeof(sc->cardinfo)); + + /* F0 must always be present. */ + fn = 0; + error = sdio_func_read_cis(sc, fn, cis_addr); + if (error != 0) + return (error); + sc->cardinfo.num_funcs++; + /* Read CCCR Card Capability. */ + error = sdio_read_direct_sc(sc, 0, SD_IO_CCCR_CARDCAP, &val); + if (error != 0) + return (error); + sc->cardinfo.support_multiblk = (val & CCCR_CC_SMB) ? true : false; + DPRINTF("%s: F%d: Vendor %#04x product %#04x max block size %d bytes " + "support_multiblk %s\n", + __func__, fn, sc->cardinfo.f[fn].vendor, sc->cardinfo.f[fn].device, + sc->cardinfo.f[fn].max_blksize, + sc->cardinfo.support_multiblk ? "yes" : "no"); + + /* mmcp->sdio_func_count contains the number of functions w/o F0. */ + mmcp = &sc->ccb->ccb_h.path->device->mmc_ident_data; + fn_max = MIN(mmcp->sdio_func_count + 1, nitems(sc->cardinfo.f)); + for (fn = 1; fn < fn_max; fn++) { + + fbr_addr = SD_IO_FBR_START * fn + SD_IO_FBR_CIS_OFFSET; + + error = sdio_read_direct_sc(sc, 0, fbr_addr++, &val); + if (error != 0) + break; + cis_addr = val; + error = sdio_read_direct_sc(sc, 0, fbr_addr++, &val); + if (error != 0) + break; + cis_addr |= (val << 8); + error = sdio_read_direct_sc(sc, 0, fbr_addr++, &val); + if (error != 0) + break; + cis_addr |= (val << 16); + + error = sdio_func_read_cis(sc, fn, cis_addr); + if (error != 0) + break; + + /* Read the Standard SDIO Function Interface Code. */ + fbr_addr = SD_IO_FBR_START * fn; + error = sdio_read_direct_sc(sc, 0, fbr_addr++, &val); + if (error != 0) + break; + sc->cardinfo.f[fn].class = (val & 0x0f); + if (sc->cardinfo.f[fn].class == 0x0f) { + error = sdio_read_direct_sc(sc, 0, fbr_addr, &val); + if (error != 0) + break; + sc->cardinfo.f[fn].class = val; + } + + sc->cardinfo.f[fn].fn = fn; + sc->cardinfo.f[fn].cur_blksize = sc->cardinfo.f[fn].max_blksize; + sc->cardinfo.f[fn].retries = 0; + sc->cardinfo.f[fn].timeout = 5000; + + DPRINTF("%s: F%d: Class %d Vendor %#04x product %#04x " + "max_blksize %d bytes\n", __func__, fn, + sc->cardinfo.f[fn].class, + sc->cardinfo.f[fn].vendor, sc->cardinfo.f[fn].device, + sc->cardinfo.f[fn].max_blksize); + if (sc->cardinfo.f[fn].vendor == 0) { + DPRINTF("%s: F%d doesn't exist\n", __func__, fn); + break; + } + sc->cardinfo.num_funcs++; + } + return (error); +} + + +/* -------------------------------------------------------------------------- */ +/* + * CAM periph registration, allocation, and detached from that a discovery + * task, which goes off reads cardinfo, and then adds ourselves to our SIM's + * device adding the devclass and registering the driver. This keeps the + * newbus chain connected though we will talk CAM in the middle (until one + * day CAM might be newbusyfied). + */ + +static int +sdio_newbus_sim_add(struct sdiob_softc *sc) +{ + device_t pdev; + devclass_t bus_devclass; + int error; + + /* Add ourselves to our parent (SIM) device. */ + + /* Add ourselves to our parent. That way we can become a parent. */ + KASSERT(sc->periph->sim->sim_dev != NULL, ("%s: sim_dev is NULL, sc %p " + "periph %p sim %p\n", __func__, sc, sc->periph, sc->periph->sim)); + + if (sc->dev == NULL) + sc->dev = BUS_ADD_CHILD(sc->periph->sim->sim_dev, 0, + SDIOB_NAME_S, -1); + if (sc->dev == NULL) + return (ENXIO); + device_set_softc(sc->dev, sc); + /* + * Don't set description here; devclass_add_driver() -> + * device_probe_child() -> device_set_driver() will nuke it again. + */ + + pdev = device_get_parent(sc->dev); + KASSERT(pdev != NULL, ("%s: sc %p dev %p (%s) parent is NULL\n", + __func__, sc, sc->dev, device_get_nameunit(sc->dev))); + bus_devclass = device_get_devclass(pdev); + if (bus_devclass == NULL) { + printf("%s: Failed to get devclass from %s.\n", __func__, + device_get_nameunit(pdev)); + return (ENXIO); + } + + mtx_lock(&Giant); + error = devclass_add_driver(bus_devclass, &sdiob_driver, + BUS_PASS_DEFAULT, &sdiob_devclass); + mtx_unlock(&Giant); + if (error != 0) { + printf("%s: Failed to add driver to devclass: %d.\n", + __func__, error); + return (error); + } + + /* Done. */ + sc->nb_state = NB_STATE_SIM_ADDED; + + return (0); +} + +static void +sdiobdiscover(void *context, int pending) +{ + struct cam_periph *periph; + struct sdiob_softc *sc; + int error; + + KASSERT(context != NULL, ("%s: context is NULL\n", __func__)); + periph = (struct cam_periph *)context; + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("%s\n", __func__)); + + /* Periph was held for us when this task was enqueued. */ + if ((periph->flags & CAM_PERIPH_INVALID) != 0) { + cam_periph_release(periph); + return; + } + + sc = periph->softc; + sc->sdio_state = SDIO_STATE_INITIALIZING; + + if (sc->ccb == NULL) + sc->ccb = xpt_alloc_ccb(); + else + memset(sc->ccb, 0, sizeof(*sc->ccb)); + xpt_setup_ccb(&sc->ccb->ccb_h, periph->path, CAM_PRIORITY_NONE); + + /* + * Read CCCR and FBR of each function, get manufacturer and device IDs, + * max block size, and whatever else we deem necessary. + */ + cam_periph_lock(periph); + error = sdiob_get_card_info(sc); + if (error == 0) + sc->sdio_state = SDIO_STATE_READY; + else + sc->sdio_state = SDIO_STATE_DEAD; + cam_periph_unlock(periph); + + if (error) + return; + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("%s: num_func %d\n", + __func__, sc->cardinfo.num_funcs)); + + /* + * Now CAM portion of the driver has been initialized and + * we know VID/PID of all the functions on the card. + * Time to hook into the newbus. + */ + error = sdio_newbus_sim_add(sc); + if (error != 0) + sc->nb_state = NB_STATE_DEAD; + + return; +} + +/* Called at the end of cam_periph_alloc() for us to finish allocation. */ +static cam_status +sdiobregister(struct cam_periph *periph, void *arg) +{ + struct sdiob_softc *sc; + int error; + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("%s: arg %p\n", __func__, arg)); + if (arg == NULL) { + printf("%s: no getdev CCB, can't register device pariph %p\n", + __func__, periph); + return(CAM_REQ_CMP_ERR); + } + if (periph->sim == NULL || periph->sim->sim_dev == NULL) { + printf("%s: no sim %p or sim_dev %p\n", __func__, periph->sim, + (periph->sim != NULL) ? periph->sim->sim_dev : NULL); + return(CAM_REQ_CMP_ERR); + } + + sc = (struct sdiob_softc *) malloc(sizeof(*sc), M_DEVBUF, + M_NOWAIT|M_ZERO); + if (sc == NULL) { + printf("%s: unable to allocate sc\n", __func__); + return (CAM_REQ_CMP_ERR); + } + sc->sdio_state = SDIO_STATE_DEAD; + sc->nb_state = NB_STATE_DEAD; + TASK_INIT(&sc->discover_task, 0, sdiobdiscover, periph); + + /* Refcount until we are setup. Can't block. */ + error = cam_periph_hold(periph, PRIBIO); + if (error != 0) { + printf("%s: lost periph during registration!\n", __func__); + free(sc, M_DEVBUF); + return(CAM_REQ_CMP_ERR); + } + periph->softc = sc; + sc->periph = periph; + cam_periph_unlock(periph); + + error = taskqueue_enqueue(taskqueue_thread, &sc->discover_task); + + cam_periph_lock(periph); + /* We will continue to hold a refcount for discover_task. */ + /* cam_periph_unhold(periph); */ + + xpt_schedule(periph, CAM_PRIORITY_XPT); + + return (CAM_REQ_CMP); +} + +static void +sdioboninvalidate(struct cam_periph *periph) +{ + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("%s:\n", __func__)); + + return; +} + +static void +sdiobcleanup(struct cam_periph *periph) +{ + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("%s:\n", __func__)); + + return; +} + +static void +sdiobstart(struct cam_periph *periph, union ccb *ccb) +{ + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("%s: ccb %p\n", __func__, ccb)); + + return; +} + +static void +sdiobasync(void *softc, uint32_t code, struct cam_path *path, void *arg) +{ + struct cam_periph *periph; + struct ccb_getdev *cgd; + cam_status status; + + periph = (struct cam_periph *)softc; + + CAM_DEBUG(path, CAM_DEBUG_TRACE, ("%s(code=%d)\n", __func__, code)); + switch (code) { + case AC_FOUND_DEVICE: + if (arg == NULL) + break; + cgd = (struct ccb_getdev *)arg; + if (cgd->protocol != PROTO_MMCSD) + break; + + /* We do not support SD memory (Combo) Cards. */ + if ((path->device->mmc_ident_data.card_features & + CARD_FEATURE_MEMORY)) { + CAM_DEBUG(path, CAM_DEBUG_TRACE, + ("Memory card, not interested\n")); + break; + } + + /* + * Allocate a peripheral instance for this device which starts + * the probe process. + */ + status = cam_periph_alloc(sdiobregister, sdioboninvalidate, + sdiobcleanup, sdiobstart, SDIOB_NAME_S, CAM_PERIPH_BIO, path, + sdiobasync, AC_FOUND_DEVICE, cgd); + if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) + CAM_DEBUG(path, CAM_DEBUG_PERIPH, + ("%s: Unable to attach to new device due to " + "status %#02x\n", __func__, status)); + break; + default: + CAM_DEBUG(path, CAM_DEBUG_PERIPH, + ("%s: cannot handle async code %#02x\n", __func__, code)); + cam_periph_async(periph, code, path, arg); + break; + } +} + +static void +sdiobinit(void) +{ + cam_status status; + + /* + * Register for new device notification. We will be notified for all + * already existing ones. + */ + status = xpt_register_async(AC_FOUND_DEVICE, sdiobasync, NULL, NULL); + if (status != CAM_REQ_CMP) + printf("%s: Failed to attach async callback, statux %#02x", + __func__, status); +} + +/* This function will allow unloading the KLD. */ +static int +sdiobdeinit(void) +{ + + return (EOPNOTSUPP); +} + +static struct periph_driver sdiobdriver = +{ + .init = sdiobinit, + .driver_name = SDIOB_NAME_S, + .units = TAILQ_HEAD_INITIALIZER(sdiobdriver.units), + .generation = 0, + .flags = 0, + .deinit = sdiobdeinit, +}; + +PERIPHDRIVER_DECLARE(SDIOB_NAME, sdiobdriver); +MODULE_VERSION(SDIOB_NAME, 1); Index: head/sys/dev/sdio/sdiodevs =================================================================== --- head/sys/dev/sdio/sdiodevs +++ head/sys/dev/sdio/sdiodevs @@ -0,0 +1,74 @@ +$FreeBSD$ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * Portions of this software were developed by Björn Zeeb + * under sponsorship from the FreeBSD Foundation. + * + * 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. + */ +/* + * Take SDIO CIS, CISTPL_MANFID, TPLMID_MANF and TPLMID_CARD information and + * present them as vendor and device IDs (a terminology we understand for + * other parts) and run them through the usbdevs2h.awk script to generate + * a header file with #defines for them in sdiodevs.h and sdiodevs_data.h + * which provides a structure with a description as well. + * The format of this file is modelled after sys/dev/usb/usbdevs. + * For more details see there. + */ + +/* + * -------------------------------------------------------------------------- + * List of TPLMID_MANF "vendor ID"s. + * Please sort by vendor ID ascending. + */ +vendor BROADCOM 0x02d0 Broadcom +vendor CYPRESS 0x02d0 Cypress/Broadcom + +/* + * -------------------------------------------------------------------------- + * List of TPLMID_CARD "product ID"s. + * Please group by vendor in same order as above. + */ + +/* Broadcom products */ +product BROADCOM 43241 0x4324 BCM43241 fullmac SDIO WiFi +product BROADCOM 4329 0x4329 BCM4329 fullmac SDIO WiFi +product BROADCOM 4330 0x4330 BCM4330 fullmac SDIO WiFi +product BROADCOM 4334 0x4334 BCM4334 fullmac SDIO WiFi +product BROADCOM 4335_4339 0x4335 BCM4335_4339 fullmac SDIO WiFi +product BROADCOM 4339 0x4339 BCM4339 fullmac SDIO WiFi +product BROADCOM 4345 0x4345 BCM4345 fullmac SDIO WiFi +product BROADCOM 4354 0x4354 BCM4354 fullmac SDIO WiFi +product BROADCOM 4356 0x4356 BCM4356 fullmac SDIO WiFi +product BROADCOM 43143 0xa887 BCM43143 fullmac SDIO WiFi +product BROADCOM 43340 0xa94c BCM43340 fullmac SDIO WiFi +product BROADCOM 43341 0xa94d BCM43341 fullmac SDIO WiFi +product BROADCOM 43362 0xa962 BCM43362 fullmac SDIO WiFi +product BROADCOM 43364 0xa9a4 BCM43364 fullmac SDIO WiFi +product BROADCOM 43430 0xa9a6 BCM43430 fullmac SDIO WiFi +product BROADCOM 43455 0xa9bf BCM43455 fullmac SDIO WiFi +product CYPRESS 4373 0x4373 CY4373 fullmac SDIO WiFi + +/* end */ Index: head/sys/modules/Makefile =================================================================== --- head/sys/modules/Makefile +++ head/sys/modules/Makefile @@ -324,6 +324,7 @@ sdhci \ ${_sdhci_acpi} \ sdhci_pci \ + sdio \ sem \ send \ ${_sfxge} \ Index: head/sys/modules/sdio/Makefile =================================================================== --- head/sys/modules/sdio/Makefile +++ head/sys/modules/sdio/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/sdio + +KMOD= sdio +SRCS= sdiob.c sdio_subr.c +SRCS+= sdio_if.c +SRCS+= device_if.h bus_if.h sdio_if.h + +.include Index: head/sys/tools/sdiodevs2h.awk =================================================================== --- head/sys/tools/sdiodevs2h.awk +++ head/sys/tools/sdiodevs2h.awk @@ -0,0 +1,265 @@ +#! /usr/bin/awk -f +#- +# $NetBSD: usb/devlist2h.awk,v 1.9 2001/01/18 20:28:22 jdolecek Exp $ +# $FreeBSD$ +# +# SPDX-License-Identifier: BSD-4-Clause +# +# Copyright (c) 1995, 1996 Christopher G. Demetriou +# 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Christopher G. Demetriou. +# 4. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. +# + +function usage() +{ + print "usage: sdiodevs2h.awk [-d|-h]"; + exit 1; +} + +function header(file) +{ + printf("/*\n") > file + printf(" * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n") \ + > file + printf(" *\n") > file + printf(" * generated from:\n") > file + printf(" *\t%s\n", VERSION) > file + printf(" */\n") > file +} + +function vendor(hfile) +{ + nvendors++ + + vendorindex[$2] = nvendors; # record index for this name, for later. + vendors[nvendors, 1] = $2; # name + vendors[nvendors, 2] = $3; # id + if (hfile) + printf("#define\tSDIO_VENDOR_%s\t%s\t", vendors[nvendors, 1], + vendors[nvendors, 2]) > hfile + i = 3; f = 4; + + # comments + ocomment = oparen = 0 + if (f <= NF) { + if (hfile) + printf("\t/* ") > hfile + ocomment = 1; + } + while (f <= NF) { + if ($f == "#") { + if (hfile) + printf("(") > hfile + oparen = 1 + f++ + continue + } + if (oparen) { + if (hfile) + printf("%s", $f) > hfile + if (f < NF && hfile) + printf(" ") > hfile + f++ + continue + } + vendors[nvendors, i] = $f + if (hfile) + printf("%s", vendors[nvendors, i]) > hfile + if (f < NF && hfile) + printf(" ") > hfile + i++; f++; + } + if (oparen && hfile) + printf(")") > hfile + if (ocomment && hfile) + printf(" */") > hfile + if (hfile) + printf("\n") > hfile +} + +function product(hfile) +{ + nproducts++ + + products[nproducts, 1] = $2; # vendor name + products[nproducts, 2] = $3; # product id + products[nproducts, 3] = $4; # id + if (hfile) + printf("#define\tSDIO_PRODUCT_%s_%s\t%s\t", \ + products[nproducts, 1], products[nproducts, 2], \ + products[nproducts, 3]) > hfile + + i=4; f = 5; + + # comments + ocomment = oparen = 0 + if (f <= NF) { + if (hfile) + printf("\t/* ") > hfile + ocomment = 1; + } + while (f <= NF) { + if ($f == "#") { + if (hfile) + printf("(") > hfile + oparen = 1 + f++ + continue + } + if (oparen) { + if (hfile) + printf("%s", $f) > hfile + if (f < NF && hfile) + printf(" ") > hfile + f++ + continue + } + products[nproducts, i] = $f + if (hfile) + printf("%s", products[nproducts, i]) > hfile + if (f < NF && hfile) + printf(" ") > hfile + i++; f++; + } + if (oparen && hfile) + printf(")") > hfile + if (ocomment && hfile) + printf(" */") > hfile + if (hfile) + printf("\n") > hfile +} + +function dump_dfile(dfile) +{ + printf("\n") > dfile + printf("const struct sdio_knowndev sdio_knowndevs[] = {\n") > dfile + for (i = 1; i <= nproducts; i++) { + printf("\t{\n") > dfile + printf("\t SDIO_VENDOR_%s, SDIO_PRODUCT_%s_%s,\n", + products[i, 1], products[i, 1], products[i, 2]) > dfile + printf("\t ") > dfile + printf("0") > dfile + printf(",\n") > dfile + + vendi = vendorindex[products[i, 1]]; + printf("\t \"") > dfile + j = 3; + needspace = 0; + while (vendors[vendi, j] != "") { + if (needspace) + printf(" ") > dfile + printf("%s", vendors[vendi, j]) > dfile + needspace = 1 + j++ + } + printf("\",\n") > dfile + + printf("\t \"") > dfile + j = 4; + needspace = 0; + while (products[i, j] != "") { + if (needspace) + printf(" ") > dfile + printf("%s", products[i, j]) > dfile + needspace = 1 + j++ + } + printf("\",\n") > dfile + printf("\t},\n") > dfile + } + for (i = 1; i <= nvendors; i++) { + printf("\t{\n") > dfile + printf("\t SDIO_VENDOR_%s, 0,\n", vendors[i, 1]) > dfile + printf("\t SDIO_KNOWNDEV_NOPROD,\n") > dfile + printf("\t \"") > dfile + j = 3; + needspace = 0; + while (vendors[i, j] != "") { + if (needspace) + printf(" ") > dfile + printf("%s", vendors[i, j]) > dfile + needspace = 1 + j++ + } + printf("\",\n") > dfile + printf("\t NULL,\n") > dfile + printf("\t},\n") > dfile + } + printf("\t{ 0, 0, 0, NULL, NULL, }\n") > dfile + printf("};\n") > dfile +} + +BEGIN { + +nproducts = nvendors = 0 +# Process the command line +for (i = 1; i < ARGC; i++) { + arg = ARGV[i]; + if (arg !~ /^-[dh]+$/ && arg !~ /devs$/) + usage(); + if (arg ~ /^-.*d/) + dfile="sdiodevs_data.h" + if (arg ~ /^-.*h/) + hfile="sdiodevs.h" + if (arg ~ /devs$/) + srcfile = arg; +} +ARGC = 1; +line=0; + +while ((getline < srcfile) > 0) { + line++; + if (line == 1) { + VERSION = $0 + gsub("\\$", "", VERSION) + if (dfile) + header(dfile) + if (hfile) + header(hfile) + continue; + } + if ($1 == "vendor") { + vendor(hfile) + continue + } + if ($1 == "product") { + product(hfile) + continue + } + if ($0 == "") + blanklines++ + if (hfile) + print $0 > hfile + if (blanklines < 2 && dfile) + print $0 > dfile +} + +# print out the match tables + +if (dfile) + dump_dfile(dfile) +}