1:增加platform_device
imx6q_add_sdhci_usdhc_imx(0, &mx6_evk_sd1_data);
#define imx6q_add_sdhci_usdhc_imx(id, pdata) \
imx_add_sdhci_esdhc_imx(&imx6q_sdhci_usdhc_imx_data[id], pdata)
const struct imx_sdhci_esdhc_imx_data
imx6q_sdhci_usdhc_imx_data[] __initconst = {
#define imx6q_sdhci_usdhc_imx_data_entry(_id, _hwid) \
imx_sdhci_usdhc_imx_data_entry(MX6Q, _id, _hwid)
imx6q_sdhci_usdhc_imx_data_entry(0, 1),
imx6q_sdhci_usdhc_imx_data_entry(1, 2),
imx6q_sdhci_usdhc_imx_data_entry(2, 3),
imx6q_sdhci_usdhc_imx_data_entry(3, 4),
};
//imx6q_sdhci_usdhc_imx_data_entry(0, 1),
static const struct esdhc_platform_data mx6_evk_sd1_data __initconst = {
.always_present = 1,
.cd_gpio = -1,
.wp_gpio = -1,
.keep_power_at_suspend = 1,
.delay_line = 0,
.support_18v = 0,
.platform_pad_change = plt_sd_pad_change,
.cd_type = ESDHC_CD_GPIO,
};
#define imx_sdhci_usdhc_imx_data_entry(soc, id, hwid) \
[id] = imx_sdhci_usdhc_imx_data_entry_single(soc, id, hwid)
//soc = MX6Q id= 0 hwid = 1
#define imx_sdhci_usdhc_imx_data_entry_single(soc, _id, hwid) \
{ \
.id = _id, //0 \
.iobase = soc ## _USDHC ## hwid ## _BASE_ADDR, \ //MX6Q__USDHC1__BASE_ADDR
//#define MX6Q_USDHC1_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x10000)
.irq = soc ## _INT_USDHC ## hwid,
//MX6Q_INT_USDHC1 \
//#define MX6Q_INT_USDHC1 54
}
所以:
imx_add_sdhci_esdhc_imx(&imx6q_sdhci_usdhc_imx_data[id], pdata)
struct platform_device *__init imx_add_sdhci_esdhc_imx(
const struct imx_sdhci_esdhc_imx_data *data,
const struct esdhc_platform_data *pdata)
{
struct resource res[] = {
{
.start = data->iobase,//#define MX6Q_USDHC1_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x10000)
.end = data->iobase + SZ_16K - 1,
.flags = IORESOURCE_MEM,
}, {
.start = data->irq,//#define MX6Q_INT_USDHC1 54
.end = data->irq,//#define MX6Q_INT_USDHC1 54
.flags = IORESOURCE_IRQ,
},
};
return imx_add_platform_device_dmamask("sdhci-esdhc-imx", data->id, res,
ARRAY_SIZE(res), pdata, sizeof(*pdata), DMA_BIT_MASK(32));
}
struct platform_device *__init imx_add_platform_device_dmamask(
const char *name, int id,
const struct resource *res, unsigned int num_resources,
const void *data, size_t size_data, u64 dmamask)
{
int ret = -ENOMEM;
struct platform_device *pdev;
pdev = platform_device_alloc(name, id);//分配一个platform_device
if (!pdev)
goto err;
if (dmamask) {
pdev->dev.dma_mask =
kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL);
if (!pdev->dev.dma_mask)
/* ret is still -ENOMEM; */
goto err;
*pdev->dev.dma_mask = dmamask;
pdev->dev.coherent_dma_mask = dmamask;
}
if (res) {
ret = platform_device_add_resources(pdev, res, num_resources);
if (ret)
goto err;
}
if (data) {
ret = platform_device_add_data(pdev, data, size_data);
if (ret)
goto err;
}
ret = platform_device_add(pdev);//platform 总线上增加设备
if (ret) {
err:
if (dmamask)
kfree(pdev->dev.dma_mask);
platform_device_put(pdev);
return ERR_PTR(ret);
}
return pdev;
}
2:增加platform 驱动
static const struct platform_device_id sdhci_pltfm_ids[] = {
{ "sdhci", },
#ifdef CONFIG_MMC_SDHCI_ESDHC_IMX
{ "sdhci-esdhc-imx", (kernel_ulong_t)&sdhci_esdhc_imx_pdata },
#endif
};
MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids);
MODULE_DEVICE_TABLE(设备类型,设备表),其中,设备类型,包括USB,PCI等,也可以自己起名字,上述代码中是针对不同的平台分的类;设备表也是自己定义的,它的最后一项必须是空,用来标识结束。
static struct platform_driver sdhci_pltfm_driver = {
.driver = {
.name = "sdhci",
.owner = THIS_MODULE,
},
.probe = sdhci_pltfm_probe,
.remove = __devexit_p(sdhci_pltfm_remove),
.id_table = sdhci_pltfm_ids,
.suspend = sdhci_pltfm_suspend,
.resume = sdhci_pltfm_resume,
};
static int __init sdhci_drv_init(void)
{
return platform_driver_register(&sdhci_pltfm_driver);
}
驱动添加以后,会通过sys,kobjetc,kset查找,匹配,调用sdhci_pltfm_probe
static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
{
const struct platform_device_id *platid = platform_get_device_id(pdev);
struct sdhci_pltfm_data *pdata;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct resource *iomem;
int ret;
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
//dev_err(&pdev->dev, "Invalid iomem size. You may "
// "experience problems.\n");
//&pdev->dev = sdhci-esdhc-imx.0
host = sdhci_alloc_host(&pdev->dev, sizeof(*pltfm_host));
pltfm_host = sdhci_priv(host);
if (!request_mem_region(iomem->start, resource_size(iomem),
mmc_hostname(host->mmc))) {
dev_err(&pdev->dev, "cannot request region\n");
ret = -EBUSY;
goto err_request;
}
host->ioaddr = ioremap(iomem->start, resource_size(iomem));
ret = sdhci_add_host(host);
platform_set_drvdata(pdev, host);
return 0;
}
分配host
struct sdhci_host *sdhci_alloc_host(struct device *dev,
size_t priv_size)
{
struct mmc_host *mmc;
struct sdhci_host *host;
mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);
return host;
}
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
int err;
struct mmc_host *host;
if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
return NULL;
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);分配host
if (!host)
return NULL;
spin_lock(&mmc_host_lock);
err = idr_get_new(&mmc_host_idr, host, &host->index);
spin_unlock(&mmc_host_lock);
host->parent = dev;
host->class_dev.parent = dev;
host->class_dev.class = &mmc_host_class;
device_initialize(&host->class_dev);
mmc_host_clk_init(host); //初始化时钟
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
wake_lock_init(&host->detect_wake_lock, WAKE_LOCK_SUSPEND,
kasprintf(GFP_KERNEL, "%s_detect", mmc_hostname(host)));
INIT_DELAYED_WORK(&host->detect, mmc_rescan);//mmc_rescan,加入到host->detect,
INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
/*
* By default, hosts do not support SGIO or large requests.
* They have to set these according to their abilities.
*/
host->max_segs = 1;
host->max_seg_size = PAGE_CACHE_SIZE;
host->max_req_size = PAGE_CACHE_SIZE;
host->max_blk_size = 512;
host->max_blk_count = PAGE_CACHE_SIZE / 512;
return host;
free:
kfree(host);
return NULL;
}
static inline void *sdhci_priv(struct sdhci_host *host)
{
return (void *)host->private;
}
sdhci_add_host(host);
int sdhci_add_host(struct sdhci_host *host)
{
tasklet_init(&host->card_tasklet,sdhci_tasklet_card, (unsigned long)host);
tasklet_init(&host->finish_tasklet,sdhci_tasklet_finish, (unsigned long)host);
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
sdhci_init(host, 0);
ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
mmc_hostname(mmc), host);
mmc_add_host(mmc);
sdhci_enable_card_detection(host);
sdhci_disable_clk(host, CLK_TIMEOUT);
return 0;
}
request_irq(host->irq, sdhci_irq, IRQF_SHARED,mmc_hostname(mmc), host);
sdhci_irq来处理热拨插引起的中断
static irqreturn_t sdhci_irq(int irq, void *dev_id)
{
tasklet_schedule(&host->card_tasklet);
//tasklet_init(&host->card_tasklet,sdhci_tasklet_card, (unsigned long)host);
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
if (intmask & SDHCI_INT_CMD_MASK) {
sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
SDHCI_INT_STATUS);
sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
}
if (intmask & SDHCI_INT_DATA_MASK) {//中断数据
sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK,
SDHCI_INT_STATUS);
sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
}
intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
intmask &= ~SDHCI_INT_ERROR;
if (intmask & SDHCI_INT_BUS_POWER) {//处理中断寄存器
printk(KERN_ERR "%s: Card is consuming too much power!\n",
mmc_hostname(host->mmc));
sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
}
}
static void sdhci_tasklet_card(unsigned long param)
{
mmc_detect_change(host->mmc, msecs_to_jiffies(500));
}
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
wake_lock(&host->detect_wake_lock);
mmc_schedule_delayed_work(&host->detect, delay);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);//mmc_rescan,加入到host->detect,
}
int mmc_add_host(struct mmc_host *host)
{
mmc_start_host(host);
return 0;
}
void mmc_start_host(struct mmc_host *host)
{
mmc_power_off(host);
mmc_detect_change(host, 0);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);//mmc_rescan,加入到host->detect
}
void mmc_rescan(struct work_struct *work)
{
mmc_rescan_try_freq(host, max(freqs[i], host->f_min);
}
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
mmc_send_if_cond(host, host->ocr_avail);
/* Order's important: probe SDIO, then SD, then MMC */
if (!mmc_attach_sdio(host))
return 0;
mmc_power_off(host); //如果没有设备,关闭host
return -EIO;
}
1:mmc_send_if_cond 发送CMD8需要支持 SDHC或SDXC
2:分析
int mmc_attach_sdio(struct mmc_host *host)
{
int err, i, funcs;
u32 ocr;
struct mmc_card *card;
BUG_ON(!host);
WARN_ON(!host->claimed);
err = mmc_send_io_op_cond(host, 0, &ocr); //发送cmd5,扫描,发挥信息ocr
if (err)
return err;
mmc_attach_bus(host, &mmc_sdio_ops);
if (host->ocr_avail_sdio)
host->ocr_avail = host->ocr_avail_sdio;
host->ocr = mmc_select_voltage(host, ocr);//设置电压
err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
if (err)
goto err;
card = host->card;
funcs = (ocr & 0x70000000) >> 28;
card->sdio_funcs = 0;
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
err = sdio_init_func(host->card, i + 1);//分配func
if (err)
goto remove;
if (host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_enable(&card->sdio_func[i]->dev);
}
mmc_release_host(host);
err = mmc_add_card(host->card);
for (i = 0;i < funcs;i++) {
err = sdio_add_func(host->card->sdio_func[i]);//将sdio功能设备挂载到sdio_bus_types总线
mmc_claim_host(host);
return 0;
}
host主要的操作函数
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.post_req = sdhci_post_req,
.pre_req = sdhci_pre_req,
.set_ios = sdhci_set_ios,
.get_ro = sdhci_get_ro,
.enable_sdio_irq = sdhci_enable_sdio_irq,
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
.execute_tuning = sdhci_execute_tuning,
.enable_preset_value = sdhci_enable_preset_value,
};
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
host->mrq = mrq;
sdhci_prepare_data(host, cmd);
sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
sdhci_set_transfer_mode(host, cmd);
if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
sdhci_send_command(host, mrq->sbc);
else
sdhci_send_command(host, mrq->cmd);
}
}
static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
{
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
}
static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
{
writew(val, host->ioaddr + reg);
}
//寄存器写命令
3.wifi驱动分析
dhd_module_init(void)
{
err = dhd_wifi_platform_register_drv();
}
int dhd_wifi_platform_register_drv(void)
{
err = wifi_ctrlfunc_register_drv();
}
static int wifi_ctrlfunc_register_drv(void)
{
wifi_plat_dev_probe_ret = dhd_wifi_platform_load();
}
static int dhd_wifi_platform_load()
{
dhd_wifi_platform_load_sdio();
}
static int dhd_wifi_platform_load_sdio(void)
{
int i;
int err = 0;
wifi_adapter_info_t *adapter;
if (!(dhd_watchdog_prio < 0 && dhd_dpc_prio < 0) &&
!(dhd_watchdog_prio >= 0 && dhd_dpc_prio >= 0 && dhd_deferred_tx))
return -EINVAL;
#if defined(BCMLXSDMMC) && !defined(DHD_PRELOAD)
if (dhd_wifi_platdata == NULL) {
DHD_ERROR(("DHD wifi platform data is required for Android build\n"));
return -EINVAL;
}
do {
sema_init(&dhd_chipup_sem, 0);
err = dhd_bus_reg_sdio_notify(&dhd_chipup_sem);
if (err) {
DHD_ERROR(("%s dhd_bus_reg_sdio_notify fail(%d)\n\n",
__FUNCTION__, err));
return err;
}
err = wifi_platform_set_power(adapter, TRUE, WIFI_TURNON_DELAY);
if (err) {
/* WL_REG_ON state unknown, Power off forcely */
wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
continue;
} else {
wifi_platform_bus_enumerate(adapter, TRUE);
err = 0;
}
if (down_timeout(&dhd_chipup_sem, msecs_to_jiffies(POWERUP_WAIT_MS)) == 0) {
dhd_bus_unreg_sdio_notify();
chip_up = TRUE;
break;
}
dhd_bus_unreg_sdio_notify();
wifi_platform_set_power(adapter, FALSE, WIFI_TURNOFF_DELAY);
wifi_platform_bus_enumerate(adapter, FALSE);
} while (retry--);
if (!chip_up) {
DHD_ERROR(("failed to power up %s, max retry reached**\n", adapter->name));
return -ENODEV;
}
}
err = dhd_bus_register();
err = down_timeout(&dhd_registration_sem, msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT));
if (err) {
DHD_ERROR(("%s: sdio_register_driver timeout or error \n", __FUNCTION__));
dhd_bus_unregister();
goto fail;
}
return err;
}
wifi_platform_set_power 给wifi芯片上电,正常返回0,调用
wifi_platform_bus_enumerate(adapter, TRUE);
int wifi_platform_bus_enumerate(wifi_adapter_info_t *adapter, bool device_present)
{
int err = 0;
struct wifi_platform_data *plat_data;
if (!adapter || !adapter->wifi_plat_data)
return -EINVAL;
plat_data = adapter->wifi_plat_data;
DHD_ERROR(("%s device present %d\n", __FUNCTION__, device_present));
if (plat_data->set_carddetect) {
err = plat_data->set_carddetect(device_present);
}
return err;
}
struct wifi_platform_data dhd_wlan_control = {
.set_power = dhd_wlan_set_power,
.set_reset = dhd_wlan_set_reset,
.set_carddetect = dhd_wlan_set_carddetect,
.get_mac_addr = dhd_wlan_get_mac_addr,
#ifdef CONFIG_DHD_USE_STATIC_BUF
.mem_prealloc = dhd_wlan_mem_prealloc,
#endif /* CONFIG_DHD_USE_STATIC_BUF */
.get_country_code = dhd_wlan_get_country_code,
};
int dhd_wlan_set_carddetect(bool present)
{
mmc_detect_change(host->mmc, 0);
}
会调用sdio的mmc_rescan.就走入sdio HOST扫描设备,看上面对host的分析
设备已经识别,func功能,代表了wifi设备,
现在需要加载wifi驱动,注册到SDIO总线
dhd_bus_register(void)
{
return bcmsdh_register(&dhd_sdio);
}
bcmsdh_register(bcmsdh_driver_t *driver)
{
error = bcmsdh_register_client_driver();
}
int bcmsdh_register_client_driver(void)
{
return sdio_register_driver(&bcmsdh_sdmmc_driver);
}
static struct sdio_driver bcmsdh_sdmmc_driver = {
.probe = bcmsdh_sdmmc_probe,
.remove = bcmsdh_sdmmc_remove,
.name = "bcmsdh_sdmmc",
.id_table = bcmsdh_sdmmc_ids,
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM)
.drv = {
.pm = &bcmsdh_sdmmc_pm_ops,
},
#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */
};
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;
}
const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
const struct sdio_device_id *id)
{
if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)
return NULL;
if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)
return NULL;
if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)
return NULL;
return id;
}
static const struct sdio_device_id bcmsdh_sdmmc_ids[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4324) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43239) },
{ SDIO_DEVICE_CLASS(SDIO_CLASS_NONE) },
{ /* end: all zeroes */ },
}
支持的wifi ID号
匹配成功以后,调用
tatic int bcmsdh_sdmmc_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
ret = sdioh_probe(func);
}
int sdioh_probe(struct sdio_func *func)
{
}。。。
最后调用
drvinfo.probe((vendevid >> 16), (vendevid & 0xFFFF), bus_num,
slot_num, 0, bus_type, (void *)regs, osh, bcmsdh);
开始wifi驱动

SDIO Host层驱动相关推荐

  1. Linux 下wifi 驱动开发(三)—— SDIO接口WiFi驱动浅析

    SDIO-Wifi模块是基于SDIO接口的符合wifi无线网络标准的嵌入式模块,内置无线网络协议IEEE802.11协议栈以及TCP/IP协议栈,能够实现用户主平台数据通过SDIO口到无线网络之间的转 ...

  2. 【鸿蒙OS开发入门】18 - HDF驱动子系统:加速度计传感器 Driver层驱动代码分析

    [鸿蒙OS开发入门]18 - HDF驱动子系统:加速度计传感器 Driver层代码分析 一.如何添加速度计传感器驱动代码(代码.编译.配置) 1.驱动代码实现 2.驱动编译配置 2.1 linux 编 ...

  3. TI Cortex-M4 USB Host CDC 驱动详解及源代码

    1. USB CDC介绍 USB的CDC类是USB通信设备类(Communication Device Class)的简称.CDC类是USB组织定义的一类专门给各种通信设备(电信通信设备和中速网络通信 ...

  4. Android 系统(4)---Android HAL层与Linux Kernel层驱动开发简介

    Android HAL层与Linux Kernel层驱动开发简介 近日稍微对Android中的驱动开发做了一些简要的了解,稍稍理清了一下Android驱动开发的套路,总结一下笔记. HAL:Hardw ...

  5. Android HAL层与Linux Kernel层驱动开发简介

    Android HAL层与Linux Kernel层驱动开发简介 阅读数:5070 近日稍微对Android中的驱动开发做了一些简要的了解,稍稍理清了一下Android驱动开发的套路,总结一下笔记. ...

  6. 安卓RK3399编译驱动MPU6050,实现内核层与HAL层驱动

    新手编译安卓驱动学习 今天我们一起学习一下如何实现对一款有驱动代码的传感器适配安卓系统 开发板:某AR眼镜公司的开发板RK3399 文章目录 新手编译安卓驱动学习 安卓驱动开发常用知识(非新手可以跳过 ...

  7. linux用户层驱动--VFIO(五)

    概述 VFIO是一套用户态驱动框架,它提供两种基本服务: 向用户态提供访问硬件设备的接口 向用户态提供配置IOMMU的接口 VFIO由平台无关的接口层与平台相关的实现层组成.接口层将服务抽象为IOCT ...

  8. linux用户层驱动--VFIO(四)

    VFIO--将设备暴露到用户态 在开始之前我们先要说一个东西就是 DMA,直接让设备访问内存,可以不通过 CPU 搬运数据. 这是一个比较简单的体系结构图,设备 和 CPU 通过存储控制器访问存储器. ...

  9. 【PCI】ARM架构——PCI总线驱动、RC驱动、Host Bridge驱动、xilinx xdma ip驱动(八)

    本文以xilinx RC IP为例,讲解ARM的RC驱动(PL). IP例程参考网址:https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/188 ...

最新文章

  1. NC | 植物分泌蛋白酶抑制细菌入侵的机理
  2. 自然语言处理NLP之文本摘要、机器翻译、OCR、信息检索、信息抽取、校对纠错
  3. 网站推广专员浅析网站推广期间如何降低网站优化短板威胁?
  4. for循环 消耗时间计算
  5. 网站搭建 (第09天) 博客统计排行
  6. u3d局域网游戏网络(c# socket select 模型)
  7. git 清除 另一个git进程似乎在这个仓库中运行。。。。。
  8. ASP.NET MVC 之 View 测试
  9. Q87:间接光照(Indirect Illumination)的高光反射(Glossy Reflcetion)
  10. 【CC2640R2F】香瓜CC2640R2F之LED
  11. Flexbox 布局教程
  12. 阿里云服务器端口请求失败(在控制台把端口添加到服务器的安全组)
  13. 海康威视SDK使用总结
  14. 随机森林随机回归预测_随机森林回归预测电子商务销售额
  15. elasticsearch实践之代码结构设计
  16. 原形网络(Prototypical Networks)基于PyTorch的实现
  17. [LeetCode] 面试题 02.07. 链表相交
  18. 数据库面试题(选择题)
  19. 使用react 写一个 仿淘宝 图片放大镜效果
  20. 消费者与电商变革传统企业

热门文章

  1. 【Python语言】Python编程基础
  2. STM32基础之中断--外部中断
  3. 基于Jetson AGX Xavier GMSL9296硬件设计与软硬件调试
  4. 高性能,高扩展,高可用架构
  5. git拉取项目、提交代码简单教程
  6. LeetCode——223. 矩形面积(Rectangle Area)[中等]——分析及代码(C++)
  7. keil5 添加芯片支持包(pack)
  8. 新东方托福词汇(List 31 ~ List 35)
  9. MarkDown高阶语法手册
  10. 现代信号处理——平稳随机信号的功率谱密度