usb_gadget

第一次使用Astah画图,还不熟悉这个工具的使用,粗略的画了一个小流程

如下图所示,UDC驱动层会回调usb Gadget设备层提供的bind方法,即composite_bind,composite_bind又回调了我们在gadget功能驱动层提供的bind函数.
可以简单对比的是在platfrom bus中 :
platfrom bus 提供的 probe函数(如果总线有提供,如 IIC bus)最终也会回调我们platfrom driver 提供的probe()函数,我们就可以在probe函数中做一些操作了,如获取platfrom device 提供的一些资源信息

这里写图片描述

/*
    UDC驱动是一个platform driver,
    平台总线、平台设备、平台驱动 这样的设计真是太好了,驱动与设备信息分开
    貌似和 设备树 有一比,这些都是linux kernel 架构绝美精华部分,对之敬仰之情,犹如滔滔江水.........!!
*/
static int __init udc_init(void)
{
    int retval;
    ....
    ....    
    /*
        注册平台驱动,如果有同名的平台设备的话,那么该驱动的probe将会被调用
        下面看这个 udc_driver_24x0
    */
    retval = platform_driver_register(&udc_driver_24x0);
    if (retval)
        goto err;
    return 0;
err:
    debugfs_remove(s3c2410_udc_debugfs_root);
    return retval;
}

static struct platform_driver udc_driver_24x0 = {
    .driver = {
        .name  = "s3c24x0-usbgadget",
        .owner = THIS_MODULE,   
    },
    .probe    = s3c2410_udc_probe,   // probe函数

};

static int s3c2410_udc_probe(struct platform_device * pdev)
{
    /*
        这个是下面传递给中断处理函数的参数,下面看
    */
    struct s3c2410_udc * udc = &memory;
    /**
        注册了一个中断处理函数,看第五个参数 udc 被传递给中断处理函数s3c2410_udc_irq了 
    */
    retval = request_irq(IRQ_USBD,s3c2410_udc_irq,0,gadget_name,udc);
    if (retval != 0){
        ...
    }
}
/*
    传递给中断处理函数的参数,就是那个udc
    这是一个很重要的结构体,看它的成员gadget的ops 是 s3c2410_ops
    这个s3c2410_ops提供的函数都会被一一的按照顺序在各个时间段调用.
    什么阶段调用这里面的哪个函数,在下面会有分析
    这里先列出来
    static const struct usb_gadget_ops s3c2410_ops = {
        .get_frame      = s3c2410_udc_get_frame,
        .wakeup         = s3c2410_udc_wakeup,
        .set_selfpowered    = s3c2410_udc_set_selfpowered,
        .pullup         = s3c2410_udc_pullup,
        .vbus_session       = s3c2410_udc_vbus_session,
        .vbus_draw      = s3c2410_vbus_draw,
        .udc_start      = s3c2410_udc_start,              //首先被调用的就是这个函数了
        .udc_stop       = s3c2410_udc_stop,
    };
*/
static struct s3c2410_udc memory = {
    .gadget = {
        .ops        = &s3c2410_ops,
        .ep0        = &memory.ep[0].ep,
        .name       = gadget_name,
        .dev = {
            .init_name  = "gadget",
        },
    },

    /* 控制端点,端点0  ep0 */
    .ep[0] = {
        .num        = 0,
        .ep = {
            .name       = ep0name,          // "ep0"
            .ops        = &s3c2410_ep_ops,  // ep0的操作函数
            .maxpacket  = EP0_FIFO_SIZE,    // 8
        },
        .dev        = &memory,
    },
    .ep[1] = {
        ....
    },
    .ep[2] = {
        ....
    },
    .ep[3] = {
        ....
    },
    .ep[4] = {
        ....
    }
};

/*
    好,上面演员都到场了,下面好戏要开始了
*/
static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
{
    /*
        那个 udc,就是那个memory
    */
    struct s3c2410_udc *dev = _dev;
    ...
    /*
        读取状态寄存器
    */
    usb_status  =  udc_read(S3C2410_UDC_USB_INT_REG);
    ...
    /*RESET  暂不分析实现 */
    if (usb_status & S3C2410_UDC_USBINT_RESET) {
    }
    /* RESUME  暂不分析实现*/
    if (usb_status & S3C2410_UDC_USBINT_RESUME) {
    }
    /*SUSPEND 暂不分析实现*/
    if (usb_status & S3C2410_UDC_USBINT_SUSPEND) {
    }
    /*
        看好了,要分析这个if分支了
    */
    if (usbd_status & S3C2410_UDC_INT_EP0) {
        dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
        /* Clear the interrupt bit by setting it to 1    清中断*/
        udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
        /**
            设备枚举的时候USB设备控制器产生的中断都会进入这个if分支,都会执行下面的这个函数,大名鼎鼎的端点0呀
            它最有可能会执行 switch case 分支中的 s3c2410_udc_handle_ep0_idle这个函数,
            所以,这里是一个设备枚举时操作的总入口
        */
        s3c2410_udc_handle_ep0(dev);
    }

}
static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)
{
    /*
        端点0 的寄存器,下面会用udc_read()来读取S3C2410_UDC_IN_CSR1_REG
        到了底层都是与寄存器打交道了.这些寄存器中的数据是上面神马层、神马结构体、神马链表的活动基础,
        端点0寄存器说没有我,你们都别玩了,该干嘛干嘛去把.玩 你们也玩不转.哈哈!
    */
    u32         ep0csr;
    /*
        s3c2410_ep 表示的是一个s3c2410 usb 设备控制器的一个端点
        这个dev还是那个memory,ep[0]请参考上面的memory的成员
    */
    struct s3c2410_ep   *ep = &dev->ep[0];
    /*
        表示的是usb_request 一次传输请求,类似与主机端的urb,
        源码中是这样说的: 
         NOTE:* this is analogous to 'struct urb' on the host side, except that
              * it's thinner and promotes more pre-allocation.
        它里面有一个回调函数很重要,原型是这样的
        void (*complete)(struct usb_ep *ep,struct usb_request *req); 下面做重点关注

        这个req是表示的一个聚集在端点上的一次请求,后面会从链表上取出来赋值给它

        linux kernel 的函数一上来就定义一坨坨变量,这些变量绝大多数都会在下面被直接赋值,或则是
        作为传出参数,很少有其他的功能,而这些被赋值大部分是从全局数组、链表、寄存器中取出来,其他函数
        穿越一层层 把数据放入这些数组、链表上来,写到这儿,真心感觉到了内存就是一个大大的database
    */
    struct s3c2410_request  *req;
    /*
        源码中说是 :  SETUP data for a USB device control request
                    This structure is used to send control requests to a USB device
                神马鬼??
                里面有一个成员 bRequest,后面的函数会使用switch case 来判断这个bRequest,
                判断是什么传输类型,然后根据你是什么传输类型,干什么样的活把
                bRequest 的值来源于 端点0的FIFO
                s3c2410_udc_read_fifo_crq()
                {
                        unsigned char *outbuf = (unsigned char *)crq;
                        ....
                        bytes_read = s3c2410_udc_fifo_count_out();
                        ....
                        readsb(S3C2410_UDC_EP0_FIFO_REG + base_addr, outbuf, bytes_read);
                }
    */
    struct usb_ctrlrequest  crq;
    /*
        设备的每一个端点上聚集了很多usb_request ,这些usb_request都挂载在一个链表上,
        请参见上图2
    */
    if (list_empty(&ep->queue))
        req = NULL;
    else  //如果不为空,那么就取出来一个赋值给 req
        req = list_entry(ep->queue.next, struct s3c2410_request, queue);

    udc_write(0, S3C2410_UDC_INDEX_REG);
    /*
        读吧,吧端点0的状态给读取出来,看你是什么状态,就让你干什么活,别谈,都给你设计规划好了,你没有选择的余地
        我想知道在这寄存器背后的复杂电路是怎么被设计出来的???
    */
    ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG); 
    ....
    /*
        判断此刻端点0是处于哪种状态
        那么端点0是处于哪种状态呢?看它的值 dev->ep0state 
        我们知道,设备插入主机后,第一个执行的中断是复位操作,而不是枚举,对首先要复位设备,
        没有明白为什么要先复位设备,usb协议哪章规定了要先复位设备的??

        这段代码就是上面 复位的时候,我们暂时没有分析实现的
        看,if分支中的 dev->ep0state 被赋值为了 EP0_IDLE
        static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
        {
            ......
            ......
            if (usb_status & S3C2410_UDC_USBINT_RESET) //会首先执行这个if分支
            {
               ....
               dev->ep0state = EP0_IDLE;
               dev->gadget.speed = USB_SPEED_FULL;
               ....
            }
        }
    */
    switch (dev->ep0state) {
        /*
            由上面我们可以知道,这个case 是首先会被执行,
            而下面那个 获取描述符的case 是不会第一个被执行的,
            那么 s3c2410_udc_handle_ep0_idle这个函数就会被执行了,
            好,下面看一下这个函数
        */
        case EP0_IDLE:
            s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr);
            break;
        /*
            获取设备的描述符
        */  
        case EP0_IN_DATA_PHASE:         
            dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
            if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req)
                s3c2410_udc_write_fifo(ep, req);
            break;
        /*
            设置描述符
        */   
        case EP0_OUT_DATA_PHASE:        
            dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
            if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req)
                s3c2410_udc_read_fifo(ep, req);
            break;
        case EP0_END_XFER:
            ....
        case EP0_STALL:
            ....
        }
    }
}

简单的结构框图
这里写图片描述

端点数据流程
这里写图片描述

端点0
这里写图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值