Index: sys/amd64/conf/GENERIC-MMCCAM =================================================================== --- sys/amd64/conf/GENERIC-MMCCAM +++ sys/amd64/conf/GENERIC-MMCCAM @@ -34,3 +34,6 @@ # Add CAMDEBUG stuff options CAMDEBUG options CAM_DEBUG_FLAGS=(CAM_DEBUG_INFO|CAM_DEBUG_PROBE|CAM_DEBUG_PERIPH) + +# Newbus debugging +options BUS_DEBUG Index: sys/cam/cam_sim.h =================================================================== --- sys/cam/cam_sim.h +++ sys/cam/cam_sim.h @@ -107,6 +107,7 @@ struct callout callout; struct cam_devq *devq; /* Device Queue to use for this SIM */ int refcount; /* References to the SIM. */ + device_t parent_dev; /* To export to attached peripherals */ }; #define CAM_SIM_LOCK(sim) mtx_lock((sim)->mtx) Index: sys/cam/mmc/sdio_ivars.h =================================================================== --- /dev/null +++ sys/cam/mmc/sdio_ivars.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2006 Bernd Walter. All rights reserved. + * Copyright (c) 2006 M. Warner Losh. 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 ``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 CAM_MMC_SDIO_IVARS_H +#define CAM_MMC_SDIO_IVARS_H + +enum sdio_device_ivars { + SDIO_IVAR_VENDOR_ID, + SDIO_IVAR_PRODUCT_ID, +}; + +/* + * Simplified accessors for mmc devices + */ +#define SDIO_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(sdio, var, SDIO, ivar, type) + +SDIO_ACCESSOR(vendor_id, VENDOR_ID, int) +SDIO_ACCESSOR(product_id, PRODUCT_ID, int) + +#endif /* CAM_MMC_SDIO_IVARS_H */ Index: sys/dev/brcmwl/brcmwl.c =================================================================== --- /dev/null +++ sys/dev/brcmwl/brcmwl.c @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 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 ``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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SDIO_VENDOR_BROADCOM 0x02d0 + +struct brcmwl_softc { + device_t dev; +}; + +static int brcmwl_probe(device_t dev); +static int brcmwl_attach(device_t dev); +static int brcmwl_detach(device_t dev); + +/* + * XXX Do we need to implement device_identify()? + */ +static int +brcmwl_probe(device_t dev) +{ + int vendor_id; + + device_printf(dev, "probe() called\n"); + vendor_id = sdio_get_vendor_id(dev); + device_printf(dev, "vendor_id=%d\n", vendor_id); + if (vendor_id != SDIO_VENDOR_BROADCOM) { + device_printf(dev, "Non-Broadcom card\n"); + return (-1); + } + return (BUS_PROBE_GENERIC); +} + +static int +brcmwl_attach(device_t dev) +{ + device_printf(dev, "attach() called\n"); + device_set_desc(dev, "Imaginary SDIO WiFi driver"); + return (0); +} + +static int +brcmwl_detach(device_t dev) +{ + return (0); +} + +static device_method_t brcmwl_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, brcmwl_probe), + DEVMETHOD(device_attach, brcmwl_attach), + DEVMETHOD(device_detach, brcmwl_detach), + + DEVMETHOD_END +}; + +static driver_t brcmwl_driver = { + "brcmwl", + brcmwl_methods, + sizeof(struct brcmwl_softc), +}; + +static devclass_t brcmwl_devclass; +DRIVER_MODULE(brcmwl, sdio, brcmwl_driver, brcmwl_devclass, NULL, NULL); Index: sys/dev/mmcnull/mmcnull.c =================================================================== --- sys/dev/mmcnull/mmcnull.c +++ sys/dev/mmcnull/mmcnull.c @@ -120,6 +120,14 @@ device_get_unit(dev), &sc->sc_mtx, 1, 1, sc->devq); + /* + * This is BAD and should be done another way! + * Changing cam_sim_alloc() to accept device_t instead of unit + * will be a good start, since it will be possible to call + * device_get_parent() and device_get_unit() inside cam_sim_alloc(). + */ + sc->sim->parent_dev = dev; + if (sc->sim == NULL) { cam_simq_free(sc->devq); device_printf(dev, "cannot allocate CAM SIM\n"); @@ -451,6 +459,11 @@ DEVMETHOD(device_probe, mmcnull_probe), DEVMETHOD(device_attach, mmcnull_attach), DEVMETHOD(device_detach, mmcnull_detach), + DEVMETHOD(device_detach, mmcnull_detach), + + /* Bus interface */ + DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD_END }; Index: sys/dev/sdio/sdio.c =================================================================== --- /dev/null +++ sys/dev/sdio/sdio.c @@ -0,0 +1,492 @@ +/*- + * Copyright (c) 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 ``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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include /* for cam_path */ +#include + +#include + +#include "sdio_subr.h" +#include "opt_cam.h" + +struct sdio_softc { + device_t dev; + device_t card_dev; +#define SDIO_STATE_INIT (0x1 << 1) +#define SDIO_STATE_READY (0x1 << 2) + uint8_t state; + + /* + * softc is used both in CAM and newbus, so we need to keep track + * which part is still alive. + * XXX Convert to a simple refcount field? + */ + uint8_t is_cam_attached; + uint8_t is_newbus_attached; + struct task start_init_task; + struct card_info cinfo; +}; + +/* Peripheral driver methods */ +static periph_init_t sdioinit; +static periph_deinit_t sdiodeinit; + +/* Peripheral device methods */ +static periph_ctor_t sdioregister; +static periph_dtor_t sdiocleanup; +static periph_start_t sdiostart; +static periph_oninv_t sdiooninvalidate; + +static void sdio_identify(driver_t *, device_t); +static void sdio_real_identify(driver_t *driver, + device_t parent, + struct sdio_softc *sc); +static int sdio_probe(device_t); +static int sdio_attach(device_t); +static int sdio_detach(device_t); +static int sdio_read_ivar(device_t dev, device_t child, int index, + uintptr_t *result); + +static void sdioasync(void *callback_arg, u_int32_t code, + struct cam_path *path, void *arg); + +static void sdio_start_init_task(void *context, int pending); + +static struct periph_driver sdiodriver = +{ + sdioinit, "sdio", + TAILQ_HEAD_INITIALIZER(sdiodriver.units), + /* generation */ 0, + /* flags */ 0, + sdiodeinit +}; + +PERIPHDRIVER_DECLARE(sdio, sdiodriver); +static MALLOC_DEFINE(M_SDIO, "sdio", "sdio buffers"); + +static device_method_t sdio_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, sdio_identify), + DEVMETHOD(device_probe, sdio_probe), + DEVMETHOD(device_attach, sdio_attach), + DEVMETHOD(device_detach, sdio_detach), + + /* bus interface */ + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), + DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), + DEVMETHOD(bus_get_resource_list, bus_generic_get_resource_list), + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_read_ivar, sdio_read_ivar), + DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), + + DEVMETHOD_END +}; + +driver_t sdio_driver = { + "sdio", + sdio_methods, + sizeof(struct sdio_softc), +}; + +static void +sdioinit(void) +{ + cam_status status; + /* + * Install a global async callback. This callback will + * receive async callbacks like "new device found". + */ + status = xpt_register_async(AC_FOUND_DEVICE, sdioasync, NULL, NULL); + + if (status != CAM_REQ_CMP) { + printf("sdio: Failed to attach master async callback " + "due to status 0x%x!\n", status); + } +} + +/* This function should just exist to allow unloading the KLD */ +static int +sdiodeinit(void) +{ + struct cam_periph *periph, *periph_temp; + + printf("CAM is calling sdiodeinit()\n"); + /* Walk through all instances and invalidate them manually */ + TAILQ_FOREACH_SAFE(periph, &sdiodriver.units, unit_links, periph_temp) { + cam_periph_lock(periph); + printf("Invalidating %s\n", periph->periph_name); + cam_periph_invalidate(periph); + } + return (0); +} + +static cam_status +sdioregister(struct cam_periph *periph, void *arg) +{ + struct sdio_softc *softc; + struct ccb_getdev *cgd; + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sdioregister\n")); + cgd = (struct ccb_getdev *)arg; + if (cgd == NULL) { + printf("sdioregister: no getdev CCB, can't register device\n"); + return(CAM_REQ_CMP_ERR); + } + + softc = (struct sdio_softc *)malloc(sizeof(*softc), M_DEVBUF, + M_NOWAIT|M_ZERO); + + if (softc == NULL) { + printf("sdioregister: Unable to probe new device. " + "Unable to allocate softc\n"); + return (CAM_REQ_CMP_ERR); + } + + softc->state = SDIO_STATE_INIT; + softc->is_cam_attached = 1; + + TASK_INIT(&softc->start_init_task, 0, sdio_start_init_task, periph); + + periph->softc = softc; + + xpt_schedule(periph, CAM_PRIORITY_XPT); + return (CAM_REQ_CMP); +} + +static void +sdioasync(void *callback_arg, u_int32_t code, + struct cam_path *path, void *arg) +{ + struct cam_periph *periph; + __unused struct sdio_softc *softc; + + periph = (struct cam_periph *)callback_arg; + CAM_DEBUG(path, CAM_DEBUG_TRACE, ("sdioasync(code=%d)\n", code)); + switch (code) { + case AC_FOUND_DEVICE: { + struct ccb_getdev *cgd; + cam_status status; + + cgd = (struct ccb_getdev *)arg; + if (cgd == NULL) + break; + + if (cgd->protocol != PROTO_MMCSD) + break; + + /* We support only SDIO cards without memory portion */ + 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 and start the probe + * process. + */ + status = cam_periph_alloc(sdioregister, sdiooninvalidate, + sdiocleanup, sdiostart, + "sdio", CAM_PERIPH_BIO, + path, sdioasync, + AC_FOUND_DEVICE, cgd); + + if (status != CAM_REQ_CMP + && status != CAM_REQ_INPROG) + CAM_DEBUG(path, CAM_DEBUG_PERIPH, ("sdioasync: Unable to attach to new device due to status 0x%x\n", status)); + break; + } + default: + CAM_DEBUG(path, CAM_DEBUG_PERIPH, ("Cannot handle async code 0x%02x\n", code)); + cam_periph_async(periph, code, path, arg); + break; + } +} + +static void +sdiooninvalidate(struct cam_periph *periph) +{ + struct sdio_softc *softc; + + softc = (struct sdio_softc *)periph->softc; + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sdiooninvalidate\n")); + + /* + * De-register any async callbacks. + */ + xpt_register_async(0, sdioasync, periph, periph->path); +} + +static void +sdiocleanup(struct cam_periph *periph) +{ + struct sdio_softc *softc; + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sdiocleanup\n")); + softc = (struct sdio_softc *)periph->softc; + /* + * If newbus deallocation code has already run, destroy softc + * Otherwise just mark CAM as detached so that newbus detach + * is allowed to release the memory. + */ + if (!softc->is_newbus_attached) + free(softc, M_DEVBUF); + else + softc->is_cam_attached = 0; + cam_periph_unlock(periph); +} + +static void +sdio_start_init_task(void *context, int pending) { + union ccb *new_ccb; + struct cam_periph *periph; + struct sdio_softc *softc; + + periph = (struct cam_periph *)context; + softc = (struct sdio_softc *)periph->softc; + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sdio_start_init_task\n")); + /* periph was held for us when this task was enqueued */ + if ((periph->flags & CAM_PERIPH_INVALID) != 0) { + cam_periph_release(periph); + return; + } + new_ccb = xpt_alloc_ccb(); + xpt_setup_ccb(&new_ccb->ccb_h, periph->path, + CAM_PRIORITY_NONE); + + cam_periph_lock(periph); + + /* + * Read CCCR and FBR of each function, get manufacturer and device IDs, + * max block size, possibly more information like this that might be useful + */ + get_sdio_card_info(new_ccb, &softc->cinfo); + + softc->state = SDIO_STATE_READY; + cam_periph_unlock(periph); + xpt_free_ccb(new_ccb); + + /* + * 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. + */ + sdio_real_identify(&sdio_driver, periph->sim->parent_dev, softc); +} + +static void +sdiostart(struct cam_periph *periph, union ccb *start_ccb) +{ + struct sdio_softc *softc = (struct sdio_softc *)periph->softc; + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sdiostart\n")); + + if (softc->state == SDIO_STATE_INIT) { + /* Make initialization */ + taskqueue_enqueue(taskqueue_thread, &softc->start_init_task); + xpt_release_ccb(start_ccb); + } else + xpt_release_ccb(start_ccb); + return; +} + +/* + * This is normally called by the parent bus. + * Thus, mmcnull calls us here. At this point we cannot really + * do anything, since even the card might not have been inserted yet. + * + * So the real work is done by sdio_real_identify(). + * It's called by CAM peripheral device "sdio" when it has scanned + * the SDIO card registers and knows what functions are available + * on the card and their VIDs/PIDs. + * Since sdio_real_identify is called not during loading/unloading + * modules or newbus-related events, we have to initiate device + * probe/attach ourselves. + */ +static void +sdio_identify(driver_t *driver, device_t parent) +{ + device_printf(parent, "sdio_identify() called\n"); +} + +static void +sdio_real_identify(driver_t *driver, device_t parent, struct sdio_softc *sc) { + device_t child; + int ret; + + if (resource_disabled("sdio", 0)) + return; + + device_printf(parent, "sdio_real_identify() called\n"); + /* Avoid duplicates. */ + if (device_find_child(parent, "sdio", -1)) { + device_printf(parent, "sdio_identify(): there is already a child\n"); + return; + } + + child = BUS_ADD_CHILD(parent, 20, "sdio", 0); + if (child == NULL) { + device_printf(parent, "add SDIO child failed\n"); + return; + } + + device_printf(parent, "BUS_ADD_CHILD() finished, child=%s\n", + device_get_nameunit(child)); + + device_set_desc(child, "SDIO bus"); + + /* + * Newbus stuff needs to be Giant-locked! + */ + mtx_lock(&Giant); + ret = device_probe_and_attach(child); + mtx_unlock(&Giant); + if (ret != 0) + device_printf(child, "attach() failed, ret=%d", ret); + /* + * Now attach() has returned, so we can fill in the softc. + * We use device_set_softc() which sets a flag DF_EXTERNALSOFTC + * so that softc is not freed when the device detaches. + * This is just what we need since we manage softc from within + * the CAM code. + */ + device_set_softc(child, sc); + sc->is_newbus_attached = 1; + sc->card_dev = bus_generic_add_child(child, 0, NULL, -1); + /* + * XXX If the brcmwl.ko was loaded before this code executes, + * it will not be probed/attached. But if it's loaded after, + * it is probed and attached just fine. + * Why? + */ +// bus_generic_attach(dev); +} + +static int +sdio_probe(device_t dev) +{ + device_printf(dev, "SDIO probe() called\n"); + device_set_desc(dev, "SDIO bus"); + return (BUS_PROBE_DEFAULT); +} + +static int +sdio_attach(device_t dev) +{ + device_printf(dev, "attached OK\n"); + return (0); +} + +static int +sdio_detach(device_t dev) +{ + struct sdio_softc *sc; + int ret; + + sc = device_get_softc(dev); + ret = device_delete_child(dev, sc->card_dev); + if (ret != 0) { + device_printf(dev, "Cannot detach child device %s: %d", + device_get_nameunit(sc->card_dev), ret); + } + sc->is_newbus_attached = 0; + if (!sc->is_cam_attached) + free(sc, M_DEVBUF); + device_printf(dev, "detached OK\n"); + return (0); + + /* + * This is not enough to delete the device from the tree! + * devinfo: + * + * isa0 + * vga0 + * mmcnull0 + * + * But devinfo -v: + * isa0 + * sc0 + * vga0 + * fdc0 + * ppc0 + * mmcnull0 + * sdio0 + */ +} + +static int +sdio_read_ivar(device_t dev, device_t child, int index, + uintptr_t *result) { + + device_printf(dev, "sdio_read_ivar(me, %s, %d)\n", + device_get_nameunit(child), + index); + switch (index) { + case SDIO_IVAR_VENDOR_ID: + *result = (int) 0xDEADBEEF; + break; + } + return (ENOENT); +} + +devclass_t sdio_devclass; + +MODULE_VERSION(sdio, 1); + +/* + * As possible parent devclass we should also list sdhci. + * mmcnull doesn't extend sdhci so it stays on its own. + */ +DRIVER_MODULE(sdio, mmcnull, sdio_driver, sdio_devclass, 0, 0); Index: sys/dev/sdio/sdio_subr.h =================================================================== --- /dev/null +++ sys/dev/sdio/sdio_subr.h @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 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 ``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. + * + */ + +#ifndef _SDIO_SUBR_H_ +#define _SDIO_SUBR_H_ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +/* + * This file contains functions to work with SDIO cards. + * All non-static functions should be usable BOTH from + * the kernel and from the userland. +*/ + +struct cis_info { + uint16_t man_id; + uint16_t prod_id; + uint16_t max_block_size; +}; + +struct card_info { + uint8_t num_funcs; + struct cis_info f[8]; +}; + +#ifdef _KERNEL +uint8_t sdio_read_1(union ccb *ccb, uint8_t func_number, uint32_t addr, int *ret); +uint32_t sdio_get_common_cis_addr(union ccb *ccb); +int sdio_func_read_cis(union ccb *ccb, uint8_t func_number, + uint32_t cis_addr, struct cis_info *info); +int get_sdio_card_info(union ccb *ccb, struct card_info *ci); +#else /* _KERNEL */ + +#endif /* _KERNEL */ +#endif /* _SDIO_SUBR_H_ */ Index: sys/dev/sdio/sdio_subr.c =================================================================== --- /dev/null +++ sys/dev/sdio/sdio_subr.c @@ -0,0 +1,268 @@ +/*- + * Copyright (c) 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 ``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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * This file contains functions to work with SDIO cards. + * All non-static functions should be useable BOTH from + * the kernel and from the userland. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include /* for cam_path */ +#include + +#include "sdio_subr.h" +#include "opt_cam.h" + +#ifdef _KERNEL + +#define warnx(fmt, ...) CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_PERIPH, (fmt, ##__VA_ARGS__)) + +/* CMD52: direct byte access */ +int sdio_rw_direct(union ccb *ccb, + uint8_t func_number, + uint32_t addr, + uint8_t is_write, + uint8_t *data, + uint8_t *resp); + +static int sdioerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags); + +int sdio_rw_direct(union ccb *ccb, + uint8_t func_number, + uint32_t addr, + uint8_t is_write, + uint8_t *data, + uint8_t *resp) { + struct ccb_mmcio *mmcio; + uint32_t flags; + uint32_t arg; + int retval = 0; + + CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("sdio_mmcio_rw_direct(f=%d, wr=%d, addr=%02x, data=%02x)\n", func_number, is_write, addr, (data == NULL ? 0 : *data))); + mmcio = &ccb->mmcio; + + 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(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); + + retval = cam_periph_runccb(ccb, sdioerror, CAM_FLAG_NONE, /*sense_flags*/0, NULL); + if (retval != 0) + return (retval); + + /* TODO: Add handling of MMC errors */ + *resp = ccb->mmcio.cmd.resp[0] & 0xFF; + return (0); +} + +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, NULL)); +} + +uint8_t +sdio_read_1(union ccb *ccb, uint8_t func_number, uint32_t addr, int *ret) { + uint8_t val; + *ret = sdio_rw_direct(ccb, func_number, addr, 0, NULL, &val); + return val; +} + +int +sdio_func_read_cis(union ccb *ccb, 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, ret; + 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(ccb, 0, addr++, &ret); + if (tuple_id == SD_IO_CISTPL_END) + break; + if (tuple_id == 0) { + cis_addr++; + continue; + } + tuple_len = sdio_read_1(ccb, 0, addr++, &ret); + if (tuple_len == 0 && tuple_id != 0x00) { + warnx("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(ccb, 0, addr + i, &ret); + 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(ccb, 0, addr++, &ret); + info->man_id |= sdio_read_1(ccb, 0, addr++, &ret) << 8; + + info->prod_id = sdio_read_1(ccb, 0, addr++, &ret); + info->prod_id |= sdio_read_1(ccb, 0, addr++, &ret) << 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(ccb, 0, addr++, &ret); + info->max_block_size |= sdio_read_1(ccb, 0, addr++, &ret) << 8; + } else { + info->max_block_size = sdio_read_1(ccb, 0, addr + 0xC, &ret); + info->max_block_size |= sdio_read_1(ccb, 0, addr + 0xD, &ret) << 8; + } + break; + default: + warnx("Skipping tuple ID %02X len %02X\n", tuple_id, tuple_len); + } + cis_addr += tuple_len + 2; + tuple_count++; + } while (tuple_count < 20); + + return (0); +} + +uint32_t +sdio_get_common_cis_addr(union ccb *ccb) { + uint32_t addr; + int ret; + + addr = sdio_read_1(ccb, 0, SD_IO_CCCR_CISPTR, &ret); + addr |= sdio_read_1(ccb, 0, SD_IO_CCCR_CISPTR + 1, &ret) << 8; + addr |= sdio_read_1(ccb, 0, SD_IO_CCCR_CISPTR + 2, &ret) << 16; + if (ret != 0) { + warnx("Failed to read CIS address\n"); + return 0xFF; + } + + if (addr < SD_IO_CIS_START || addr > SD_IO_CIS_START + SD_IO_CIS_SIZE) { + warnx("Bad CIS address: %04X\n", addr); + addr = 0xFF; + } + + return addr; +} + +int +get_sdio_card_info(union ccb *ccb, struct card_info *ci) { + uint32_t cis_addr; + uint32_t fbr_addr; + int ret; + + cis_addr = sdio_get_common_cis_addr(ccb); + if (cis_addr == 0xFF) + return (-1); + + memset(ci, 0, sizeof(struct card_info)); + ret = sdio_func_read_cis(ccb, 0, cis_addr, &ci->f[0]); + if (ret !=0) + return ret; + printf("F0: Vendor 0x%04X product 0x%04X max block size %d bytes\n", + ci->f[0].man_id, ci->f[0].prod_id, ci->f[0].max_block_size); + + + struct mmc_params *mmcp = &ccb->ccb_h.path->device->mmc_ident_data; + for (int i = 1; i <= mmcp->sdio_func_count; i++) { + fbr_addr = SD_IO_FBR_START * i + 0x9; + cis_addr = sdio_read_1(ccb, 0, fbr_addr++, &ret); + cis_addr |= sdio_read_1(ccb, 0, fbr_addr++, &ret) << 8; + cis_addr |= sdio_read_1(ccb, 0, fbr_addr++, &ret) << 16; + sdio_func_read_cis(ccb, i, cis_addr, &ci->f[i]); + printf("F%d: Vendor 0x%04X product 0x%04X max block size %d bytes\n", + i, ci->f[i].man_id, ci->f[i].prod_id, ci->f[i].max_block_size); + if (ci->f[i].man_id == 0) { + printf("F%d doesn't exist\n", i); + break; + } + ci->num_funcs++; + } + + return (0); +} + +#endif /* _KERNEL */ Index: sys/modules/brcmwl/Makefile =================================================================== --- /dev/null +++ sys/modules/brcmwl/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/brcmwl + +KMOD= brcmwl +SRCS= brcmwl.c + +.include Index: sys/modules/sdio/Makefile =================================================================== --- /dev/null +++ sys/modules/sdio/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/sdio + +KMOD= sdio +SRCS= sdio.c sdio_subr.c device_if.h bus_if.h + +.include