OTG驱动分析(一)

前一段时间弄了2个礼拜的OTG驱动调试,感觉精神疲惫啊。主要原因还是自己对OTG功能不了解造成的。

如今最终完毕可是对实质原理还有些模糊。所以自己又一次总结一下。

由于自己是菜鸟,所以用菜鸟的白话方式分析。

高手滤过吧。 所谓OTG功能就是具备该功能的设备就可以当主设备(host)去轮询别人,也能够当从设备(device)去被别人轮~~(双性人?)。

正所谓全部的产品和功能都是由于需求存在的,举个最简单的需求。原来MP3想传送一个歌曲都得通过电脑。如今仅仅要两个MP3链接,当中一个MP3有OTG功能作为主设备(相当于电脑主机),然后另外一个是从设备就能够实现数据的传送了。

那么话说回来,具有OTG功能的设备怎样确定自己是主还是从设备那。原来原来USB接口上有4个管脚。OTG功能有5个。原来4个各自是电 D+ D- 地。

如今添加了一个ID。这个ID线就决定了自己做主设备还是从设备。假设ID线是高则自己是从设备。反之是主设备。


以下開始分析代码。


 向平时一样定义platform_device资源等信息。 
定义platform_device结构
 static struct platform_device __maybe_unused dr_otg_device = 
{ .name = "fsl-usb2-otg", //设备的名称 日后匹配用

.id = -1, //仅仅有一个这种设备

.dev = { .release = dr_otg_release, 
.dma_mask = &dr_otg_dmamask, 
.coherent_dma_mask = 0xffffffff,
 },
 .resource = otg_resources, //设备的资源 看以下

.num_resources = ARRAY_SIZE(otg_resources), 
};

定义platform_device下的struct resource设备资源结构
static struct resource otg_resources[] = { 
[0] = { 
.start = (u32)(USB_OTGREGS_BASE), //描写叙述设备实体在cpu总线上的线性起始物理地址

.end = (u32)(USB_OTGREGS_BASE + 0x1ff), //描写叙述设备实体在cpu总线上的线性结尾物理地址

.flags = IORESOURCE_MEM, }, 
[1] = { 
.start = MXC_INT_USB_OTG, //中断号

.flags = IORESOURCE_IRQ, }, 
};

定义平台设备私有数据,以后驱动要使用
static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config = {
.name = "DR", 
.platform_init = usbotg_init, 
.platform_uninit = usbotg_uninit, 
.phy_mode = FSL_USB2_PHY_UTMI_WIDE, 
.power_budget = 500, /* via RT9706 */
.gpio_usb_active = gpio_usbotg_utmi_active, 
.gpio_usb_inactive = gpio_usbotg_utmi_inactive, 
.transceiver = "utmi", 
.wake_up_enable = _wake_up_enable, 
}; 
#define PDATA (&dr_utmi_config) 定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)

static inline void dr_register_otg(void) {
 PDATA->operating_mode = FSL_USB2_DR_OTG; //将模式更改(上面定义的时候定义的是FSL_USB2_PHY_UTMI_WIDE。不知道为什么開始不定义这个。可能是为了兼容)

dr_otg_device.dev.platform_data = PDATA; //该设备的私有数据赋值。就是上面定义的dr_utmi_config

if (platform_device_register(&dr_otg_device))
 printk(KERN_ERR "usb: can‘t register otg device\n");
else 
printk(KERN_INFO "usb: DR OTG registered\n"); 
}

上面几个过程主要是完毕了设备的注冊。这个过程是: 
1.定义platform_device结构。 
2.定义platform_device下的struct resource设备资源结构 
3.定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据) 
4.调用platform_device_register将platform_device结构
注冊上面4个过程调用结束后,设备的信息就被注冊到系统中。等待驱动的使用

以下分析驱动和设备的链接过程


定义platform_driver结构 
struct platform_driver fsl_otg_driver = { 
.probe = fsl_otg_probe, //定义处理函数,该函数在设备名字匹配到后调用。也就是发现该驱动相应的设备在系统中注冊过。

.remove = fsl_otg_remove, 
.driver = { 
.name = "fsl-usb2-otg", //通过该名字匹配開始注冊进系统的设备

.owner = THIS_MODULE, 
}, 
}; 
将platform_driver结构注冊进系统,系统通过注冊名字匹配该设备是否已经在系统中。假设在调用注冊的probe = fsl_otg_probe函数 
static int __init fsl_usb_otg_init(void) 

printk(KERN_INFO DRIVER_DESC " loaded, %s\n", DRIVER_VERSION); 
return platform_driver_register(&fsl_otg_driver); 
}


 

调用fsl_otg_probe函数。函数參数platform_device *pdev。就是我们上面注冊进系统的platform_device结构。如今由系统赋值调用fsl_otg_probe


static int __init fsl_otg_probe(struct platform_device *pdev)
{
    int status;
    struct fsl_usb2_platform_data *pdata;

DBG("pdev=0x%p\n", pdev);

if (!pdev)
        return -ENODEV;
/*推断是否有设备自己的数据,就是检查我们上面定义的3的过程*/
    if (!pdev->dev.platform_data)
        return -ENOMEM;

pdata = pdev->dev.platform_data;

/* configure the OTG */
    status = fsl_otg_conf(pdev);
    if (status) {
        printk(KERN_INFO "Couldn‘t init OTG module\n");
        return -status;
    }

/* start OTG */
    status = usb_otg_start(pdev);

if (register_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME, &otg_fops)) {
        printk(KERN_WARNING FSL_OTG_NAME
         ": unable to register FSL OTG device\n");
        return -EIO;
    }

create_proc_file();
    return status;
}

上面函数中调用了fsl_otg_conf。我们来看看他干了什么。


static int fsl_otg_conf(struct platform_device *pdev)
{
    int status;
    struct fsl_otg *fsl_otg_tc;
    struct fsl_usb2_platform_data *pdata;

pdata = pdev->dev.platform_data;

DBG();
/**************************************************************/

struct fsl_otg {
 struct otg_transceiver otg;
 struct otg_fsm fsm;
 struct usb_dr_mmap *dr_mem_map;
 struct delayed_work otg_event;

 /*used for usb host */
 struct work_struct work_wq;
 u8 host_working;

 int irq;
};

/**************************************************************/
    if (fsl_otg_dev)
        return 0;

/* allocate space to fsl otg device */
    fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL);
    if (!fsl_otg_tc)
        return -ENODEV;

INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event);

INIT_LIST_HEAD(&active_timers);
    status = fsl_otg_init_timers(&fsl_otg_tc->fsm);
    if (status) {
        printk(KERN_INFO "Couldn‘t init OTG timers\n");
        fsl_otg_uninit_timers();
        kfree(fsl_otg_tc);
        return status;
    }
    spin_lock_init(&fsl_otg_tc->fsm.lock);

/* Set OTG state machine operations */

/**************************************************************/

static struct otg_fsm_ops fsl_otg_ops = {
 .chrg_vbus = fsl_otg_chrg_vbus,
 .drv_vbus = fsl_otg_drv_vbus,
 .loc_conn = fsl_otg_loc_conn,
 .loc_sof = fsl_otg_loc_sof,
 .start_pulse = fsl_otg_start_pulse,

 .add_timer = fsl_otg_add_timer,
 .del_timer = fsl_otg_del_timer,

 .start_host = fsl_otg_start_host,
 .start_gadget = fsl_otg_start_gadget,
};

/**************************************************************/
    fsl_otg_tc->fsm.ops = &fsl_otg_ops;

/* initialize the otg structure */
    fsl_otg_tc->otg.label = DRIVER_DESC;
    fsl_otg_tc->otg.set_host = fsl_otg_set_host;
    fsl_otg_tc->otg.set_peripheral = fsl_otg_set_peripheral;
    fsl_otg_tc->otg.set_power = fsl_otg_set_power;
    fsl_otg_tc->otg.start_hnp = fsl_otg_start_hnp;
    fsl_otg_tc->otg.start_srp = fsl_otg_start_srp;

fsl_otg_dev = fsl_otg_tc;

/* Store the otg transceiver */

/***************************************************************/

int otg_set_transceiver(struct otg_transceiver *x)
{
 if (xceiv && x)
  return -EBUSY;
 xceiv = x;
 return 0;
}

该函数就是将struct otg_transceiver结构副给一个全局变量保存,供以后使用。以后会通过调用以下函数得到该结构

struct otg_transceiver *otg_get_transceiver(void)
{
 if (xceiv)
  get_device(xceiv->dev);
 return xceiv;
}

/***************************************************************/
    status = otg_set_transceiver(&fsl_otg_tc->otg);
    if (status) {
        printk(KERN_WARNING ": unable to register OTG transceiver.\n");
        return status;
    }

return 0;
}


int usb_otg_start(struct platform_device *pdev)
{
    struct fsl_otg *p_otg;

/*获得otg_transceiver结构*/
    struct otg_transceiver *otg_trans = otg_get_transceiver();
    struct otg_fsm *fsm;
    volatile unsigned long *p;
    int status;
    struct resource *res;
    u32 temp;

/*获得设备的私有数据*/
    struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
/*使用container_of宏定义能够通过结构中一个变量的指针获得该结构首地址*/
    p_otg = container_of(otg_trans, struct fsl_otg, otg);
    fsm = &p_otg->fsm;

/* Initialize the state machine structure with default values */
    SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED);
    fsm->transceiver = &p_otg->otg;

/* We don‘t require predefined MEM/IRQ resource index */

/*获得设备的资源,是在设备注冊时结构体里面的内容*/
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res)
        return -ENXIO;

/* We don‘t request_mem_region here to enable resource sharing
     * with host/device */
/*通过资源中获得的物理地址映射一个能够被驱动訪问的虚拟地址指针*/
    usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap));

/*将该指针保存到p_otg->dr_mem_map中*/
    p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs;
    pdata->regs = (void *)usb_dr_regs;

/* request irq */

/*获得设备注冊时候的中断并注冊。在OTG ID发生变化时触发中断。然后调用注冊的中断例程函数,函数后面分析*/
    p_otg->irq = platform_get_irq(pdev, 0);
    status = request_irq(p_otg->irq, fsl_otg_isr,
                IRQF_SHARED, driver_name, p_otg);
    if (status) {
        dev_dbg(p_otg->otg.dev, "can‘t get IRQ %d, error %d\n",
            p_otg->irq, status);
        iounmap(p_otg->dr_mem_map);
        kfree(p_otg);
        return status;
    }

if (pdata->platform_init && pdata->platform_init(pdev) != 0)
        return -EINVAL;

/* Export DR controller resources */

/**************************************************/

int otg_set_resources(struct resource *resources)
{
 otg_resources = resources;
 return 0;
}

和otg_set_transceiver功能相似将设备资源保存到一个全局变量中

/**************************************************/
    otg_set_resources(pdev->resource);
/*開始配置USB寄存器*/
    /* stop the controller */
    temp = readl(&p_otg->dr_mem_map->usbcmd);
    temp &= ~USB_CMD_RUN_STOP;
    writel(temp, &p_otg->dr_mem_map->usbcmd);

/* reset the controller */
    temp = readl(&p_otg->dr_mem_map->usbcmd);
    temp |= USB_CMD_CTRL_RESET;
    writel(temp, &p_otg->dr_mem_map->usbcmd);

/* wait reset completed */
    while (readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET) ;

/* configure the VBUSHS as IDLE(both host and device) */
    temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0);
    writel(temp, &p_otg->dr_mem_map->usbmode);

/* configure PHY interface */
    temp = readl(&p_otg->dr_mem_map->portsc);
    temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW);
    switch (pdata->phy_mode) {
    case FSL_USB2_PHY_ULPI:
        temp |= PORTSC_PTS_ULPI;
        break;
    case FSL_USB2_PHY_UTMI_WIDE:
        temp |= PORTSC_PTW_16BIT;
        /* fall through */
    case FSL_USB2_PHY_UTMI:
        temp |= PORTSC_PTS_UTMI;
        /* fall through */
    default:
        break;
    }
    writel(temp, &p_otg->dr_mem_map->portsc);

if (pdata->have_sysif_regs) {
        /* configure control enable IO output, big endian register */
        p = (volatile unsigned long *)(&p_otg->dr_mem_map->control);
        temp = *p;
        temp |= USB_CTRL_IOENB;
        *p = temp;
    }

/* disable all interrupt and clear all OTGSC status */
    temp = readl(&p_otg->dr_mem_map->otgsc);
    temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK;
    temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE;
    writel(temp, &p_otg->dr_mem_map->otgsc);

/*
     * The identification (id) input is FALSE when a Mini-A plug is inserted
     * in the devices Mini-AB receptacle. Otherwise, this input is TRUE.
     * Also: record initial state of ID pin
     */
    if (le32_to_cpu(p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) {
        p_otg->otg.state = OTG_STATE_UNDEFINED;
        p_otg->fsm.id = 1;
    } else {
        p_otg->otg.state = OTG_STATE_A_IDLE;
        p_otg->fsm.id = 0;
    }

DBG("initial ID pin=%d\n", p_otg->fsm.id);

/* enable OTG ID pin interrupt */
    temp = readl(&p_otg->dr_mem_map->otgsc);
    temp |= OTGSC_INTR_USB_ID_EN;
    temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN);
    writel(temp, &p_otg->dr_mem_map->otgsc);

return 0;
}

以下分析下 中断例程函数


该函数就是推断ID的高低,也就是自己做主设备还是从设备

irqreturn_t fsl_otg_isr(int irq, void *dev_id)
{
    struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm;
    struct otg_transceiver *otg = &((struct fsl_otg *)dev_id)->otg;
    u32 otg_int_src, otg_sc;
/*获得ID的变化信息*/
    otg_sc = le32_to_cpu(usb_dr_regs->otgsc);
    otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8);

/* Only clear otg interrupts */
    usb_dr_regs->otgsc |= cpu_to_le32(otg_sc & OTGSC_INTSTS_MASK);

/*FIXME: ID change not generate when init to 0 */
    fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
    otg->default_a = (fsm->id == 0);

/* process OTG interrupts */
    if (otg_int_src) {
        if (otg_int_src & OTGSC_INTSTS_USB_ID) {
            fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
            otg->default_a = (fsm->id == 0);
            /* clear conn information */
            if (fsm->id)
                fsm->b_conn = 0;
            else
                fsm->a_conn = 0;

if (otg->host)
                otg->host->is_b_host = fsm->id;
            if (otg->gadget)
                otg->gadget->is_a_peripheral = !fsm->id;
            VDBG("ID int (ID is %d)\n", fsm->id);

if (fsm->id) {    /* switch to gadget *///从设备

/*schedule_delayed_work函数先停止主设备后打开从设备*/

/***************************************************/

schedule_delayed_work(&((struct fsl_otg *)
                            dev_id)->otg_event,
                            100);

函数就是延迟100秒调用otg_event。就是以下函数。

static void fsl_otg_event(struct work_struct *work)
{
 struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work);
 struct otg_fsm *fsm = &og->fsm;

 if (fsm->id) {  /* switch to gadget */
  fsl_otg_start_host(fsm, 0);
  otg_drv_vbus(fsm, 0);
  fsl_otg_start_gadget(fsm, 1);
 }
}

/***************************************************/
                schedule_delayed_work(&((struct fsl_otg *)
                            dev_id)->otg_event,
                            100);
            } else {    /* switch to host *///主设备
                cancel_delayed_work(&
                         ((struct fsl_otg *)dev_id)->
                         otg_event);
                fsl_otg_start_gadget(fsm, 0);//停止从设备
                otg_drv_vbus(fsm, 1);
                fsl_otg_start_host(fsm, 1);//打开主
            }

return IRQ_HANDLED;
        }
    }

return IRQ_NONE;
}

int fsl_otg_start_host(struct otg_fsm *fsm, int on)
{
    struct otg_transceiver *xceiv = fsm->transceiver;
    struct device *dev;
    struct fsl_otg *otg_dev = container_of(xceiv, struct fsl_otg, otg);
    struct platform_driver *host_pdrv;
    struct platform_device *host_pdev;
    u32 retval = 0;
/*推断是否有主设备的驱动注冊进系统*/
    if (!xceiv->host)
        return -ENODEV;
    dev = xceiv->host->controller;

/*找到主设备驱动的platform_driver结构,为以下的停止和恢复函数调用做准备*/
    host_pdrv = container_of((dev->driver), struct platform_driver, driver);
    host_pdev = to_platform_device(dev);

/* Update a_vbus_vld state as a_vbus_vld int is disabled
     * in device mode
     */
    fsm->a_vbus_vld =
     (le32_to_cpu(usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID) ? 1 : 0;
    if (on) {
        /* start fsl usb host controller */
        if (otg_dev->host_working)
            goto end;
        else {
            otg_reset_controller();
            VDBG("host on......\n");
            if (host_pdrv->resume) {
                retval = host_pdrv->resume(host_pdev);
                if (fsm->id) {
                    /* default-b */
                    fsl_otg_drv_vbus(1);
                    /* Workaround: b_host can‘t driver
                     * vbus, but PP in PORTSC needs to
                     * be 1 for host to work.
                     * So we set drv_vbus bit in
                     * transceiver to 0 thru ULPI. */
#if defined(CONFIG_ISP1504_MXC)
                    write_ulpi(0x0c, 0x20);
#endif
                }
            }

otg_dev->host_working = 1;
        }
    } else {
        /* stop fsl usb host controller */
        if (!otg_dev->host_working)
            goto end;
        else {
            VDBG("host off......\n");
            if (host_pdrv->suspend) {
                retval = host_pdrv->suspend(host_pdev,
                            otg_suspend_state);
                if (fsm->id)
                    /* default-b */
                    fsl_otg_drv_vbus(0);
            }
            otg_dev->host_working = 0;
        }
    }
end:
    return retval;
}

能够看到最后设备是使用还是停止调用的函数各自是

host_pdrv->suspend

host_pdrv->resume

而上面两个指针的函数赋值是在主设备驱动中完毕的。

int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
{
    struct otg_transceiver *xceiv = fsm->transceiver;
    struct device *dev;
    struct platform_driver *gadget_pdrv;
    struct platform_device *gadget_pdev;
/*推断是否有从设备驱动注冊*/
    if (!xceiv->gadget || !xceiv->gadget->dev.parent)
        return -ENODEV;

VDBG("gadget %s \n", on ? "on" : "off");
    dev = xceiv->gadget->dev.parent;
/*找到从设备驱动的platform_driver结构首地址,为以下调用其提供的功能函数做准备*/
    gadget_pdrv = container_of((dev->driver),
            struct platform_driver, driver);
    gadget_pdev = to_platform_device(dev);

if (on)
        gadget_pdrv->resume(gadget_pdev);
    else
        gadget_pdrv->suspend(gadget_pdev, otg_suspend_state);

return 0;
}

和上面主设备一样

究竟是从设备停止还是恢复是调用
        gadget_pdrv->resume(gadget_pdev);
        gadget_pdrv->suspend(gadget_pdev, otg_suspend_state);

上面两个函数的指针就是在从设备驱动注冊时链接的。

上面部分就是 OTG功能的 OTG驱动部分。

OTG功能还要有做主设备使用的主设备驱动和做从设备的从设备驱动。

从上面代码分析我们归纳出流程:

分两个大部分:

一 设备的注冊  当中包含

1.定义platform_device结构。

2.定义platform_device下的struct resource设备资源结构 
3.定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据) 
4.调用platform_device_register将platform_device结构

二 OTG驱动的注冊 当中包含

1.struct platform_driver fsl_otg_driver 结构的注冊

2.匹配到有设备存在时调用的PORE函数,对设备进行初始化设置和功能函数的绑定

3.完毕中断函数的绑定和中断例程的注冊。

经过上面的处理后,仅仅要OTG ID的变化就会触发中断,调用中断例程函数。决定是调用主设备还是从设备驱动。 而主设备和从设备驱动和OTG调用的链接是分别在主从设备驱动中完毕的。

后面我们介绍主从设备驱动中会介绍到。

在文章的最后想起来这次调OTG遇见的问题。分享给大家希望大家有帮助。

我调试OTG时,開始将OTG编译到内核中。(Y)。结果插入U盘没有反应。后来发现原来我添加内核后,主设备驱动的先OTG设备驱动被运行,造成主设备函数和OTG功能的链接出现故障。

(应该是OTG先初始化 然后从和主设备驱动链接。

)后来我使用模块方式编译OTG功能。依照先载入OTG后载入从和主设备。

(insmod方式),结果OTG就能够使用了。

后来通过减少主设备的优先级方式,把OTG编译进内核,然后由于主设备优先级低所以最后被调用。 也就是在主设备注冊那使用
late_initcall(ehci_hcd_init);取代//module_init(ehci_hcd_init);。这样主设备的优先级就低于设备驱动的优先级就在驱动载入完载入了。 可是总感觉这样不是非常合理的方式,假设有朋友有更好的办法请不吝赐教。

时间: 2024-11-14 13:45:43

OTG驱动分析(一)的相关文章

OTG驱动分析(二)

上回介绍了OTG功能的 OTG部分驱动,本片分析OTG功能的从设备部分驱动.从设备的注册过程和OTG的一样,首先注册设备. 流程是: 1.定义platform_device结构. 2.定义platform_device下的struct resource设备资源结构 3.定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据) 4.调用platform_device_register将platform_device结构注册上面4个过程调用结束后,设备的信息就被注册到

20150311 NandFlash驱动分析

20150311 NandFlash驱动分析 2015-03-11 李海沿 一.结构体详解 MTD体系结构: 在linux中提供了MTD(Memory Technology Device,内存技术设备)系统来建立Flash针对linux的统一.抽象的接口 引入MTD后,linux系统中的Flash设备驱动及接口可分为4层: 设备节点 MTD设备层 MTD原始设备层 硬件驱动层 硬件驱动层:Flash硬件驱动层负责底层硬件设备实际的读.写.擦除,Linux MTD设备的NAND型Flash驱动位于

linux串口驱动分析

linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作.UART 使用系统时钟能够支持最高 115.2Kbps 的波特率.每一个 UART 通道对于接收器和发送器包含了 2 个 64 位的 FIFO. 寄存器 名称 地址 在linux中的描写叙述 (2410 和 2440 处理器对内存地址映射关系同样) UART 线性控制寄存器(ULCONn) ULC

atheros wifi 驱动分析

Ar6003 驱动文档摘要 1.  wmi : wireless module interface //无线模块结构 2.  bmi : bootloader message interface 3.  htc : host target communications 4.  wps:wifi protected setup 5.  CS:connection services module 6.  STA:station 7.  AP:access point Wireless applica

蓝牙驱动分析 linux

蓝牙驱动分析 这个驱动分析的是OK6410开发板自带的内核版本是linux3.0.1,所支持的wifi和蓝牙一体芯片是marvell的8688和8787.根据开发板的设计,芯片与主机之间是通过sdio协议接口通信的,所以驱动也是通过sdio的方式写的. 个人分析驱动的过程是从插入设备驱动的动作开始的. 首先每次插入设备和拔出设备驱动都会通过终端打印相应的信息,判断在sd卡槽中肯定是触发中断的,通过看硬件原理图和数据手册中的SDMMC控制器可知用于mmc的中断号分别为56和57,回到代码中.在内核

[国嵌攻略][140][触摸屏驱动分析]

触摸屏驱动分析 初始化 1.使能ADC时钟 2.将物理地址转化为虚拟地址 3.让触摸屏进入等待中断模式 4.分配输入设备结构 5.设置可能上报的事件类型和按键类型 6.为TC和ADC中断注册处理函数 7.注册输入型设备 按下处理 1.判断按下或弹起 2.如果是按下情况,那么启动XY坐标的AD转换 3.进行4次ADC转换,获取4次XY坐标值 4.计算4次采集的平均值,并上报给内核

【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析

[linux驱动分析]之dm9000驱动分析(一):dm9000原理及硬件分析 [linux驱动分析]之dm9000驱动分析(二):定义在板文件里的资源和设备以及几个宏 [linux驱动分析]之dm9000驱动分析(四):net_device结构体 [linux驱动分析]之dm9000驱动分析(五):另外几个重要的结构体 [linux驱动分析]之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现 [linux驱动分析]之dm9000驱动分析(七):dm9000的卸

linux input设备驱动分析

linux input设备驱动分析 工作机制 输入设备工作机制: 输入动作–>产生中断–>CPU通过总线或者IO读取数据到缓冲区 构架层次 app //-------------------- input_event_driver //-------------------- input_core //-------------------- input_device_driver //-------------------- hardware 数据结构 输入设备对象 struct input

S3C6410 LCD驱动分析(转)

一. 理论分析1. 几个概念:FIMC :    Fully Interactive Mobile Camera (完全交互式移动摄像机)FIMD:     Fully Interactive Mobile Display (完全交互式移动显示设备)2. 设置VCLK在VIDCON0中bit[3:2]-->Select the Video Clock source =00 --> HCLK=133MHZbit[13:6] --> CLKVAL_F = 13  (这个值是在驱动中计算出来的