sdio接口wifi模块_连接路由器的用哪个接口

sdio接口wifi模块_连接路由器的用哪个接口SDIO-WiFi即基于SDIO接口符合WiFi标准的嵌入式模块,内置802.11协议栈以及TCP/IP协议栈,可实现主平台铜鼓SDIO到无线网络之间转换SDIO:传输数据块,兼容SD,MMC接口等先以SDIO设备注册,然后检测到再注册WiFi功能,即用SDIO协议发送命令和数据sdio基本概念接口1.SD的IO接口,透过SD的IO接口连接外设,透过SD卡的IO数据接位…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

SDIO-WiFi即基于SDIO接口符合WiFi标准的嵌入式模块,内置802.11协议栈以及TCP/IP协议栈,可实现主平台铜鼓SDIO到无线网络之间转换
SDIO:传输数据块,兼容SD,MMC接口等
先以SDIO设备注册,然后检测到再注册WiFi功能,即用SDIO协议发送命令和数据

sdio
 基本概念
接口
     1.SD的IO接口,透过SD的IO接口连接外设,透过SD卡的IO数据接位与外设传递数据,具备完整的SDIO stack驱动,热门好用
       常见SDIO外设:WiFi card/CMOS sensor card /GPS card/GSM,GPRS moderm card/Bluetooth card / SDIO将会取代GPIO式的SPI接口
 总线
   与USB总线类似,有两端,host端,device端,host端发命令,device端解析命令,然后可以互通
   CLK:host给device的时钟信号,一个时钟周期一个命令
   CMD:双向信号,用于传输命令和反应
   DAT0-DAT3:四条数据线
  VDD:电源信号
   VSS1-VSS2:电源地信号
 热插拔原理
   方法:设置定时器检查/插拔中断检测
   硬件:例如GPG10(EINT18)用于检测SD卡,GPG10为高电平,即未插入SD卡,GPG10为低电平,即插入了SD卡
 命令
    总线上host发请求,device回应,SDIO命令由6个字节组成
    a–command: 用于开始传输命令,
    b–response:device返回给host的命令,作为command的回应也是cmd传送
    c–data:双向,可以设置1线也可设置4线模式,通过data0–data4线
    读命令:host->device   device–>host(握手信号),host收到回应,通过数据线传送,包含CRC校验码,读完host发送命令通知device,device返回响应
    写命令: host->device  发送命令,device回应握手,host收到握手发送数据,同时包含CRC,写入完毕,发送命令通知device操作完毕,device回应

驱动(分层&&分离思想)  设备驱动层(wifi设备)–>核心层(向上向下提供接口)—>主机驱动层(SDIO驱动)

   Linux-5.4.rc8源码
   文件路径 /include/linux/mmc/host.h
  struct mmc_host SD控制器结构定义
  struct mmc_card SD卡定义
  struct mmc_driver mmc驱动定义
  struct sdio_func  功能设备定义
  struct mmc_host_ops 操作接口函数定义,从主机控制器向core层注册操作函数,将core层与具体的控制器隔离,即core通过他操作控制器,不直接调用具体主控制器函数

  1.host驱动
    sdhci-s3c定义的host驱动程序  /driver/mmc/host/sdhci-s3c.c
static struct platform_driver sdhci_s3c_driver = {

    .probe        = sdhci_s3c_probe,
    .remove        = sdhci_s3c_remove,
    .id_table    = sdhci_s3c_driver_ids,
    .driver        = {

        .name    = “s3c-sdhci”,
        .of_match_table = of_match_ptr(sdhci_s3c_dt_match),
        .pm    = &sdhci_s3c_pmops,
    },
};

static int sdhci_s3c_probe(struct platform_device *pdev)
{

    struct s3c_sdhci_platdata *pdata;
    struct sdhci_s3c_drv_data *drv_data;
    struct device *dev = &pdev->dev;
    struct sdhci_host *host;
    struct sdhci_s3c *sc;
    struct resource *res;
    int ret, irq, ptr, clks;

    if (!pdev->dev.platform_data && !pdev->dev.of_node) {

        dev_err(dev, “no device data specified\n”);
        return -ENOENT;
    }

    irq = platform_get_irq(pdev, 0);
    if (irq < 0)
        return irq;

    host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c));
    if (IS_ERR(host)) {

        dev_err(dev, “sdhci_alloc_host() failed\n”);
        return PTR_ERR(host);
    }
    sc = sdhci_priv(host);

    pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
    if (!pdata) {

        ret = -ENOMEM;
        goto err_pdata_io_clk;
    }

    if (pdev->dev.of_node) {

        ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata);
        if (ret)
            goto err_pdata_io_clk;
    } else {

        memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
        sc->ext_cd_gpio = -1; /* invalid gpio number */
    }

    drv_data = sdhci_s3c_get_driver_data(pdev);

    sc->host = host;
    sc->pdev = pdev;
    sc->pdata = pdata;
    sc->cur_clk = -1;

    platform_set_drvdata(pdev, host);

    sc->clk_io = devm_clk_get(dev, “hsmmc”);
    if (IS_ERR(sc->clk_io)) {

        dev_err(dev, “failed to get io clock\n”);
        ret = PTR_ERR(sc->clk_io);
        goto err_pdata_io_clk;
    }

    /* enable the local io clock and keep it running for the moment. */
    clk_prepare_enable(sc->clk_io);

    for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) {

        char name[14];

        snprintf(name, 14, “mmc_busclk.%d”, ptr);
        sc->clk_bus[ptr] = devm_clk_get(dev, name);
        if (IS_ERR(sc->clk_bus[ptr]))
            continue;

        clks++;
        sc->clk_rates[ptr] = clk_get_rate(sc->clk_bus[ptr]);

        dev_info(dev, “clock source %d: %s (%ld Hz)\n”,
                ptr, name, sc->clk_rates[ptr]);
    }

    if (clks == 0) {

        dev_err(dev, “failed to find any bus clocks\n”);
        ret = -ENOENT;
        goto err_no_busclks;
    }

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(host->ioaddr)) {

        ret = PTR_ERR(host->ioaddr);
        goto err_req_regs;
    }

    /* Ensure we have minimal gpio selected CMD/CLK/Detect */
    if (pdata->cfg_gpio)
        pdata->cfg_gpio(pdev, pdata->max_width);

    host->hw_name = “samsung-hsmmc”;
    host->ops = &sdhci_s3c_ops;
    host->quirks = 0;
    host->quirks2 = 0;
    host->irq = irq;

    /* Setup quirks for the controller */
    host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
    host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
    if (drv_data) {

        host->quirks |= drv_data->sdhci_quirks;
        sc->no_divider = drv_data->no_divider;
    }

#ifndef CONFIG_MMC_SDHCI_S3C_DMA

    /* we currently see overruns on errors, so disable the SDMA
     * support as well. */
    host->quirks |= SDHCI_QUIRK_BROKEN_DMA;

#endif /* CONFIG_MMC_SDHCI_S3C_DMA */

    /* It seems we do not get an DATA transfer complete on non-busy
     * transfers, not sure if this is a problem with this specific
     * SDHCI block, or a missing configuration that needs to be set. */
    host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ;

    /* This host supports the Auto CMD12 */
    host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;

    /* Samsung SoCs need BROKEN_ADMA_ZEROLEN_DESC */
    host->quirks |= SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC;

    if (pdata->cd_type == S3C_SDHCI_CD_NONE ||
        pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
        host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;

    if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
        host->mmc->caps = MMC_CAP_NONREMOVABLE;

    switch (pdata->max_width) {

    case 8:
        host->mmc->caps |= MMC_CAP_8_BIT_DATA;
        /* Fall through */
    case 4:
        host->mmc->caps |= MMC_CAP_4_BIT_DATA;
        break;
    }

    if (pdata->pm_caps)
        host->mmc->pm_caps |= pdata->pm_caps;

    host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR |
             SDHCI_QUIRK_32BIT_DMA_SIZE);

    /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */
    host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;

    /*
     * If controller does not have internal clock divider,
     * we can use overriding functions instead of default.
     */
    if (sc->no_divider) {

        sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock;
        sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock;
        sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock;
    }

    /* It supports additional host capabilities if needed */
    if (pdata->host_caps)
        host->mmc->caps |= pdata->host_caps;

    if (pdata->host_caps2)
        host->mmc->caps2 |= pdata->host_caps2;

    pm_runtime_enable(&pdev->dev);
    pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
    pm_runtime_use_autosuspend(&pdev->dev);
    pm_suspend_ignore_children(&pdev->dev, 1);

    ret = mmc_of_parse(host->mmc);
    if (ret)
        goto err_req_regs;

    ret = sdhci_add_host(host);
    if (ret)
        goto err_req_regs;

#ifdef CONFIG_PM
    if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL)
        clk_disable_unprepare(sc->clk_io);
#endif
    return 0;

 err_req_regs:
    pm_runtime_disable(&pdev->dev);

 err_no_busclks:
    clk_disable_unprepare(sc->clk_io);

 err_pdata_io_clk:
    sdhci_free_host(host);

    return ret;
}

host->detected得到 /drivers/mmc/core/host.c
        INIT_DELAYED_WORK(&host->detect, mmc_rescan);
    INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);

    mmc_start_host(host);
    mmc_register_pm_notifier(host);

     /drivers/mmc/core/core.c

static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{

    host->f_init = freq;

    pr_debug(“%s: %s: trying to init card at %u Hz\n”,
        mmc_hostname(host), __func__, host->f_init);

    mmc_power_up(host, host->ocr_avail);

    /*
     * Some eMMCs (with VCCQ always on) may not be reset after power up, so
     * do a hardware reset if possible.
     */
    mmc_hw_reset_for_init(host);

    /*
     * sdio_reset sends CMD52 to reset card.  Since we do not know
     * if the card is being re-initialized, just send it.  CMD52
     * should be ignored by SD/eMMC cards.
     * Skip it if we already know that we do not support SDIO commands
     */
    if (!(host->caps2 & MMC_CAP2_NO_SDIO))
        sdio_reset(host);

    mmc_go_idle(host);

    if (!(host->caps2 & MMC_CAP2_NO_SD))
        mmc_send_if_cond(host, host->ocr_avail);

    /* Order’s important: probe SDIO, then SD, then MMC */
    if (!(host->caps2 & MMC_CAP2_NO_SDIO))
        if (!mmc_attach_sdio(host))
            return 0;

    if (!(host->caps2 & MMC_CAP2_NO_SD))
        if (!mmc_attach_sd(host))
            return 0;

    if (!(host->caps2 & MMC_CAP2_NO_MMC))
        if (!mmc_attach_mmc(host))
            return 0;

    mmc_power_off(host);
    return -EIO;
}

    static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{

    host->f_init = freq;

    pr_debug(“%s: %s: trying to init card at %u Hz\n”,
        mmc_hostname(host), __func__, host->f_init);

    mmc_power_up(host, host->ocr_avail);

    /*
     * Some eMMCs (with VCCQ always on) may not be reset after power up, so
     * do a hardware reset if possible.
     */
    mmc_hw_reset_for_init(host);

    /*
     * sdio_reset sends CMD52 to reset card.  Since we do not know
     * if the card is being re-initialized, just send it.  CMD52
     * should be ignored by SD/eMMC cards.
     * Skip it if we already know that we do not support SDIO commands
     */
    if (!(host->caps2 & MMC_CAP2_NO_SDIO))
        sdio_reset(host);

    mmc_go_idle(host);

    if (!(host->caps2 & MMC_CAP2_NO_SD))
        mmc_send_if_cond(host, host->ocr_avail);

    /* Order’s important: probe SDIO, then SD, then MMC */
    if (!(host->caps2 & MMC_CAP2_NO_SDIO))
        if (!mmc_attach_sdio(host))
            return 0;

    if (!(host->caps2 & MMC_CAP2_NO_SD))
        if (!mmc_attach_sd(host))
            return 0;

    if (!(host->caps2 & MMC_CAP2_NO_MMC))
        if (!mmc_attach_mmc(host))
            return 0;

    mmc_power_off(host);
    return -EIO;
}

/driver/mmc/core/stdio.c

int mmc_attach_sdio(struct mmc_host *host)
{

    int err, i, funcs;
    u32 ocr, rocr;
    struct mmc_card *card;

    WARN_ON(!host->claimed);

    err = mmc_send_io_op_cond(host, 0, &ocr);
    if (err)
        return err;

    mmc_attach_bus(host, &mmc_sdio_ops);
    if (host->ocr_avail_sdio)
        host->ocr_avail = host->ocr_avail_sdio;

    rocr = mmc_select_voltage(host, ocr);

/drivers/mmc/core/mmc.c
static int mmc_init_card(struct mmc_host *host, u32 ocr,
    struct mmc_card *oldcard)
{

    struct mmc_card *card;
    int err;
    u32 cid[4];
    u32 rocr;

    WARN_ON(!host->claimed);

    /* Set correct bus mode for MMC before attempting init */
    if (!mmc_host_is_spi(host))
        mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);

    /*
     * Since we’re changing the OCR value, we seem to
     * need to tell some cards to go back to the idle
     * state.  We wait 1ms to give cards time to
     * respond.
     * mmc_go_idle is needed for eMMC that are asleep
     */
    mmc_go_idle(host);

    /* The extra bit indicates that we support high capacity */
    err =

 /drivers/mmc/core/stdio.c
for (i = 0; i < funcs; i++, card->sdio_funcs++) {

        err = sdio_init_func(host->card, i + 1);
        if (err)
            goto remove;

        /*
         * Enable Runtime PM for this func (if supported)
         */
        if (host->caps & MMC_CAP_POWER_OFF_CARD)
            pm_runtime_enable(&card->sdio_func[i]->dev);
    }

  2.设备热插拔
    SDIO插拔会触发中断通知cpu,执行卡检测中断处理函数在这个中断服务函数中
     mmc_detect_change->mmc_schedule_delayed_work(&host->detect,delay)
    INIT_DELAY_WORK(&host->detect,mmc_rescan)调度mmc_rescan,触发初始化流程,检测到有效SDIO设备,将他注册都系统中

static irqreturn_t msdc_irq(int irq, void *dev_id)
{

    struct msdc_host *host = (struct msdc_host *) dev_id;

    while (true) {

        unsigned long flags;
        struct mmc_request *mrq;
        struct mmc_command *cmd;
        struct mmc_data *data;
        u32 events, event_mask;

        spin_lock_irqsave(&host->lock, flags);
        events = readl(host->base + MSDC_INT);
        event_mask = readl(host->base + MSDC_INTEN);
        if ((events & event_mask) & MSDC_INT_SDIOIRQ)
            __msdc_enable_sdio_irq(host, 0);
        /* clear interrupts */
        writel(events & event_mask, host->base + MSDC_INT);

        mrq = host->mrq;
        cmd = host->cmd;
        data = host->data;
        spin_unlock_irqrestore(&host->lock, flags);

        if ((events & event_mask) & MSDC_INT_SDIOIRQ)
            sdio_signal_irq(host->mmc);

        if ((events & event_mask) & MSDC_INT_CDSC) {

            if (host->internal_cd)
                mmc_detect_change(host->mmc, msecs_to_jiffies(20));
            events &= ~MSDC_INT_CDSC;
        }

        if (!(events & (event_mask & ~MSDC_INT_SDIOIRQ)))
            break;

        if (!mrq) {

            dev_err(host->dev,
                “%s: MRQ=NULL; events=%08X; event_mask=%08X\n”,
                __func__, events, event_mask);
            WARN_ON(1);
            break;
        }

        dev_dbg(host->dev, “%s: events=%08X\n”, __func__, events);

        if (cmd)
            msdc_cmd_done(host, events, mrq, cmd);
        else if (data)
            msdc_data_xfer_done(host, events, mrq, data);
    }

    return IRQ_HANDLED;

驱动分析(主机端和SDIO设备)
天线接收802.11帧的数据,主机接收802.3帧,固件负责转换
天线收到数据,firmware处理放在buffer中,产生中断,主机收到中断去读取buffer数据

   1)注册&&匹配
     /include/linux/mmc/stdio_func.h
  /*
 * SDIO function device driver
 */
struct sdio_driver {

    char *name;
    const struct sdio_device_id *id_table;

    int (*probe)(struct sdio_func *, const struct sdio_device_id *);
    void (*remove)(struct sdio_func *);

    struct device_driver drv;
};

   具体函数填充(不同WiFi对应的wireless文件夹下的接口文件夹不同)
     static struct sdio_driver if_sdio_driver = {

    .name        = “libertas_sdio”,
    .id_table    = if_sdio_ids,
    .probe        = if_sdio_probe,
    .remove        = if_sdio_remove,
    .drv = {

        .pm = &if_sdio_pm_ops,
    },
};
  设备注册函数 /driver/mmc/core/sdio_bus.c
   
/**
 *    sdio_register_driver – register a function driver
 *    @drv: SDIO function driver
 */
int sdio_register_driver(struct sdio_driver *drv)
{

    drv->drv.name = drv->name;
    drv->drv.bus = &sdio_bus_type;
    return driver_register(&drv->drv);
}
EXPORT_SYMBOL_GPL(sdio_register_driver);

   总线函数 /include/linux/device.h
   /**
 * struct bus_type – The bus type of the device
 *
 * @name:    The name of the bus.
 * @dev_name:    Used for subsystems to enumerate devices like (“foo%u”, dev->id).
 * @dev_root:    Default device to use as the parent.
 * @bus_groups:    Default attributes of the bus.
 * @dev_groups:    Default attributes of the devices on the bus.
 * @drv_groups: Default attributes of the device drivers on the bus.
 * @match:    Called, perhaps multiple times, whenever a new device or driver
 *        is added for this bus. It should return a positive value if the
 *        given device can be handled by the given driver and zero
 *        otherwise. It may also return error code if determining that
 *        the driver supports the device is not possible. In case of
 *        -EPROBE_DEFER it will queue the device for deferred probing.
 * @uevent:    Called when a device is added, removed, or a few other things
 *        that generate uevents to add the environment variables.
 * @probe:    Called when a new device or driver add to this bus, and callback
 *        the specific driver’s probe to initial the matched device.
 * @remove:    Called when a device removed from this bus.
 * @shutdown:    Called at shut-down time to quiesce the device.
 *
 * @online:    Called to put the device back online (after offlining it).
 * @offline:    Called to put the device offline for hot-removal. May fail.
 *
 * @suspend:    Called when a device on this bus wants to go to sleep mode.
 * @resume:    Called to bring a device on this bus out of sleep mode.
 * @num_vf:    Called to find out how many virtual functions a device on this
 *        bus supports.
 * @dma_configure:    Called to setup DMA configuration on a device on
 *            this bus.
 * @pm:        Power management operations of this bus, callback the specific
 *        device driver’s pm-ops.
 * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU
 *              driver implementations to a bus and allow the driver to do
 *              bus-specific setup
 * @p:        The private data of the driver core, only the driver core can
 *        touch this.
 * @lock_key:    Lock class key for use by the lock validator
 * @need_parent_lock:    When probing or removing a device on this bus, the
 *            device core should lock the device’s parent.
 *
 * A bus is a channel between the processor and one or more devices. For the
 * purposes of the device model, all devices are connected via a bus, even if
 * it is an internal, virtual, “platform” bus. Buses can plug into each other.
 * A USB controller is usually a PCI device, for example. The device model
 * represents the actual connections between buses and the devices they control.
 * A bus is represented by the bus_type structure. It contains the name, the
 * default attributes, the bus’ methods, PM operations, and the driver core’s
 * private data.
 */
struct bus_type {

    const char        *name;
    const char        *dev_name;
    struct device        *dev_root;
    const struct attribute_group **bus_groups;
    const struct attribute_group **dev_groups;
    const struct attribute_group **drv_groups;

    int (*match)(struct device *dev, struct device_driver *drv);
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);

    int (*online)(struct device *dev);
    int (*offline)(struct device *dev);

    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);

    int (*num_vf)(struct device *dev);

    int (*dma_configure)(struct device *dev);

    const struct dev_pm_ops *pm;

    const struct iommu_ops *iommu_ops;

    struct subsys_private *p;
    struct lock_class_key lock_key;

    bool need_parent_lock;
};

//填充函数
 /drivers/mmc/core/sdio_bus.c
 
static struct bus_type sdio_bus_type = {

    .name        = “sdio”,
    .dev_groups    = sdio_dev_groups,
    .match        = sdio_bus_match,
    .uevent        = sdio_bus_uevent,
    .probe        = sdio_bus_probe,
    .remove        = sdio_bus_remove,
    .pm        = &sdio_bus_pm_ops,
};

static int sdio_bus_match(struct device *dev, struct device_driver *drv)
{

    struct sdio_func *func = dev_to_sdio_func(dev);
    struct sdio_driver *sdrv = to_sdio_driver(drv);

    if (sdio_match_device(func, sdrv))
        return 1;

    return 0;
}

static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
    struct sdio_driver *sdrv)
{

    const struct sdio_device_id *ids;

    ids = sdrv->id_table;

    if (ids) {

        while (ids->class || ids->vendor || ids->device) {

            if (sdio_match_one(func, ids))
                return ids;
            ids++;
        }
    }

    return NULL;
}
   
   2)if_sdio_probe函数 /driver/mmc/core/sdio_bus.c
   
    static int sdio_bus_probe(struct device *dev)
{

    struct sdio_driver *drv = to_sdio_driver(dev->driver);
    struct sdio_func *func = dev_to_sdio_func(dev);
    const struct sdio_device_id *id;
    int ret;

    id = sdio_match_device(func, drv);
    if (!id)
        return -ENODEV;

    ret = dev_pm_domain_attach(dev, false);
    if (ret)
        return ret;

    /* Unbound SDIO functions are always suspended.
     * During probe, the function is set active and the usage count
     * is incremented.  If the driver supports runtime PM,
     * it should call pm_runtime_put_noidle() in its probe routine and
    }

    /* Set the default block size so the driver is sure it’s something
     * sensible. */
    sdio_claim_host(func);
    ret = sdio_set_block_size(func, 0);
    sdio_release_host(func);
    if (ret)
        goto disable_runtimepm;

    ret = drv->probe(func, id);
    if (ret)
        goto disable_runtimepm;

    return 0;

disable_runtimepm:
    if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
        pm_runtime_put_noidle(dev);
    dev_pm_domain_detach(dev, false);
    return ret;
}

    chunk = sdio_align_size(card->func, size);

    ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk);
    if (ret)
        goto out;

    switch (type) {

    case MVMS_CMD:
        ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk – 4);
        if (ret)
            goto out;
        break;
    case MVMS_DAT:
        ret = if_sdio_handle_data(card, card->buffer + 4, chunk – 4);
        if (ret)
            goto out;
        break;
    case MVMS_EVENT:
        ret = if_sdio_handle_event(card, card->buffer + 4, chunk – 4);
        if (ret)
            goto out;
        break;
    default:
        lbs_deb_sdio(“invalid type (%d) from firmware\n”,
                (int)type);
        ret = -EINVAL;
        goto out;
    }
    
   3)通过中断方式接收数据

int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr,
    int count)
{

    return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count);
}
EXPORT_SYMBOL_GPL(sdio_readsb);
   4)数据发送
   5)移除SDIO卡

static int sdio_bus_remove(struct device *dev)
{

    struct sdio_driver *drv = to_sdio_driver(dev->driver);
    struct sdio_func *func = dev_to_sdio_func(dev);

    /* Make sure card is powered before invoking ->remove() */
    if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
        pm_runtime_get_sync(dev);

    drv->remove(func);

    if (func->irq_handler) {

        pr_warn(“WARNING: driver %s did not remove its interrupt handler!\n”,
            drv->name);
        sdio_claim_host(func);
        sdio_release_irq(func);
        sdio_release_host(func);
    }

    /* First, undo the increment made directly above */
    if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
        pm_runtime_put_noidle(dev);

    /* Then undo the runtime PM settings in sdio_bus_probe() */
    if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
        pm_runtime_put_sync(dev);

    dev_pm_domain_detach(dev, false);

    return 0;
}     * pm_runtime_get_noresume() in its remove routine.
    }

    /* Set the default block size so the driver is sure it’s something
     * sensible. */
    sdio_claim_host(func);
    ret = sdio_set_block_size(func, 0);
    sdio_release_host(func);
    if (ret)
        goto disable_runtimepm;

    ret = drv->probe(func, id);
    if (ret)
        goto disable_runtimepm;

    return 0;

disable_runtimepm:
    if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
        pm_runtime_put_noidle(dev);
    dev_pm_domain_detach(dev, false);
    return ret;
}

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/186284.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号