Linux gadget驱动分析2------设备识别过程
设备连上主机之后,设备驱动做了的事.
设备连上host的port之后,主机端会有一套策略发送请求获取device的一系列描述符.进行枚举过程.找到适合该device的驱动. 这样就可以与device进行通信.
usb device这边收到主机的请求,会进入中断处理函数.可以说usb device 控制器的活动都是从中断开始的.
#号之间的都是udc驱动中做的事,大部分与硬件相关,原理应该差不多.
########################nuc900_udc##########################
nuc9xx系列的中断处理函数:
static irqreturn_t nuc900_udc_irq(int irq, void *_dev)
{
struct nuc900_udc *dev;
struct nuc900_ep *ep;
u32 volatile IrqStL, IrqEnL;
u32 volatile IrqSt, IrqEn;
int i=, j; dev=(struct nuc900_udc *)(_dev); IrqStL = __raw_readl(controller.reg + REG_USBD_IRQ_STAT_L); /* 0x000 register get interrupt status */
IrqEnL = __raw_readl(controller.reg + REG_USBD_IRQ_ENB_L); IrqStL = IrqStL & IrqEnL ;
if (!IrqStL) {
printk("Not our interrupt !\n");
return IRQ_HANDLED;
} if (IrqStL & IRQ_USB_STAT) {
IrqSt = __raw_readl(controller.reg + REG_USBD_IRQ_STAT);
IrqEn = __raw_readl(controller.reg + REG_USBD_IRQ_ENB);
__raw_writel(IrqSt, controller.reg + REG_USBD_IRQ_STAT); IrqSt = IrqSt & IrqEn ; if (IrqSt && dev->driver) {
for (i=; i<; i++) {
if (IrqSt&(<<i)) {
paser_irq_stat(<<i,dev);
break;
}
}
} }//end IRQ_USB_STAT if (IrqStL & IRQ_CEP) {
IrqSt = __raw_readl(controller.reg + REG_USBD_CEP_IRQ_STAT);
IrqEn = __raw_readl(controller.reg + REG_USBD_CEP_IRQ_ENB);
//printk("cep:%x, %x\n", IrqSt, IrqEn);
IrqSt = IrqSt & IrqEn ;
__raw_writel(IrqSt, controller.reg + REG_USBD_CEP_IRQ_STAT); if (IrqSt && dev->driver) {
//for(i=12;i>=0;i--)
if (IrqSt&CEP_STS_END) { //deal with STS END
if (dev->ep0state == EP0_OUT_DATA_PHASE)
IrqSt &= 0x1BF7;
paser_irq_cep(CEP_STS_END,dev,IrqSt);
}
for (i=; i<; i++) {
if (i == )
continue;
if (IrqSt&(<<i)) {
paser_irq_cep(<<i,dev,IrqSt);
//break;
}
}
}
} if (IrqStL & IRQ_NCEP) {
IrqStL >>= ; for (j = ; j < ; j++) { //6 endpoints
if (IrqStL & ( << j)) {
//in-token and out token interrupt can deal with one only
IrqSt = __raw_readl(controller.reg + REG_USBD_EPA_IRQ_STAT + 0x28 * j);
IrqEn = __raw_readl(controller.reg + REG_USBD_EPA_IRQ_ENB + 0x28 * j); IrqSt = IrqSt & IrqEn ;
if (IrqSt && dev->driver) {
ep = &dev->ep[j+]; for (i=; i>=; i--) {
if (IrqSt&(<<i)) {
if ((<<i) == EP_BO_SHORT_PKT)
IrqSt &= 0x1FCF;//clear out token/RxED intr
if ((ep->EP_Type == EP_TYPE_BLK) || (ep->EP_Type == EP_TYPE_ISO))
paser_irq_nep(<<i, ep, IrqSt);
else if (ep->EP_Type == EP_TYPE_INT)
paser_irq_nepint(<<i, ep, IrqSt);
break;
}
}
}
}
}
}//if end return IRQ_HANDLED; }
基本原理就是从一些相关寄存器中读取出中断的类型,分别进入相应的处理流程.
20行 if (IrqStL & IRQ_USB_STAT) {
就是usb Suspend Resume 等中断发生时会进入该if
39行 if (IrqStL & IRQ_CEP) {
中断是控制端口中断,进入该if语句.
usb host会通过控制端口获取一系列描述符. 所以device这边会先进入这里.
然后再读取中断的子类型
进入 51或57行的
paser_irq_cep(1<<i,dev,IrqSt);
void paser_irq_cep(int irq, struct nuc900_udc *dev, u32 IrqSt)
{
struct nuc900_ep *ep = &dev->ep[];
struct nuc900_request *req;
int is_last=; if (list_empty(&ep->queue)) {
req = ;
} else {
req = list_entry(ep->queue.next, struct nuc900_request, queue);
} switch (irq) {
case CEP_SUPPKT://receive setup packet
dev->ep0state=EP0_IDLE;
dev->setup_ret = ; udc_isr_ctrl_pkt(dev);
break; case CEP_DATA_RXD: if (dev->ep0state == EP0_OUT_DATA_PHASE) {
if (req)
is_last = read_fifo(ep,req, ); __raw_writel(0x400, controller.reg + REG_USBD_CEP_IRQ_STAT); if (!is_last)
__raw_writel(0x440, controller.reg + REG_USBD_CEP_IRQ_ENB);//enable out token and status complete int
else { //transfer finished
__raw_writel(0x04C, controller.reg + REG_USBD_CEP_IRQ_STAT);
__raw_writel(CEP_NAK_CLEAR, controller.reg + REG_USBD_CEP_CTRL_STAT); // clear nak so that sts stage is complete
__raw_writel(0x400, controller.reg + REG_USBD_CEP_IRQ_ENB); // suppkt int//enb sts completion int
dev->ep0state = EP0_END_XFER;
}
} return; case CEP_IN_TOK: if ((IrqSt & CEP_STS_END))
dev->ep0state=EP0_IDLE; if (dev->setup_ret < ) { // == -EOPNOTSUPP)
printk("CEP send zero pkt\n");
__raw_writel(CEP_ZEROLEN, controller.reg + REG_USBD_CEP_CTRL_STAT);
__raw_writel(0x400, controller.reg + REG_USBD_CEP_IRQ_ENB); //enb sts completion int
} else if (dev->ep0state == EP0_IN_DATA_PHASE) { if (req) {
is_last = write_fifo(ep,req);
} if (!is_last)
__raw_writel(0x408, controller.reg + REG_USBD_CEP_IRQ_ENB);
else {
if (dev->setup_ret >= )
__raw_writel(CEP_NAK_CLEAR, controller.reg + REG_USBD_CEP_CTRL_STAT); // clear nak so that sts stage is complete
__raw_writel(0x402, controller.reg + REG_USBD_CEP_IRQ_ENB); // suppkt int//enb sts completion int if (dev->setup_ret < )//== -EOPNOTSUPP)
dev->ep0state=EP0_IDLE;
else if (dev->ep0state != EP0_IDLE)
dev->ep0state=EP0_END_XFER;
}
} return; case CEP_PING_TOK: __raw_writel(0x402, controller.reg + REG_USBD_CEP_IRQ_ENB); // suppkt int//enb sts completion int
return; case CEP_DATA_TXD:
return; case CEP_STS_END: __raw_writel(0x4A, controller.reg + REG_USBD_CEP_IRQ_ENB);
udc_isr_update_dev(dev);
dev->ep0state=EP0_IDLE;
dev->setup_ret = ; break; default:
break; } return ; }
很简单的switch语句,device控制器硬件会根据host发来的包类型设置相应寄存器. 中断处理函数再从该寄存器中获得类型,进入不同的switch分支.
host请求设备描述符是usb控制传输,并通过device的控制端口进行,接下去会进入哪个case分支跟控制传输的流程有关
大体上是 :
1,host发送 setup 到device .device收到后准备相关data
2,host发送 in 到device device收到后发送data到host
3,host 发送 out 到device host 把状态数据发到device
详细流程可参考网上文章.
case setup中
18行 udc_isr_ctrl_pkt(dev);
static void udc_isr_ctrl_pkt(struct nuc900_udc *dev)
{
u32 temp;
u32 ReqErr=;
struct nuc900_ep *ep = &dev->ep[];
struct usb_ctrlrequest crq;
struct nuc900_request *req;
int ret; if (list_empty(&ep->queue)) {
//printk("ctrl ep->queue is empty\n");
req = ;
} else {
req = list_entry(ep->queue.next, struct nuc900_request, queue);
//printk("req = %x\n", req);
} temp = __raw_readl(controller.reg + REG_USBD_SETUP1_0); Get_SetupPacket(&crq,temp); dev->crq = crq; switch (dev->ep0state) {
case EP0_IDLE:
switch (crq.bRequest) { case USBR_SET_ADDRESS:
ReqErr = ((crq.bRequestType == ) && ((crq.wValue & 0xff00) == )
&& (crq.wIndex == ) && (crq.wLength == )) ? : ; if ((crq.wValue & 0xffff) > 0x7f) { //within 7f
ReqErr=; //Devaddr > 127
} if (dev->usb_devstate == ) {
ReqErr=; //Dev is configured
} if (ReqErr==) {
break; //break this switch loop
} if (dev->usb_devstate == ) {
if (crq.wValue == )
dev->usb_devstate = ; //enter default state
dev->usb_address = crq.wValue; //if wval !=0,use new address
} if (dev->usb_devstate == ) {
if (crq.wValue != ) {
dev->usb_address = crq.wValue;
dev->usb_devstate = ;
}
} break; case USBR_SET_CONFIGURATION:
ReqErr = ((crq.bRequestType == ) && ((crq.wValue & 0xff00) == ) &&
((crq.wValue & 0x80) == ) && (crq.wIndex == ) &&
(crq.wLength == )) ? : ; if (dev->usb_devstate == ) {
ReqErr=;
} if (ReqErr==) {
break; //break this switch loop
} if (crq.wValue == )
dev->usb_devstate = ;
else
dev->usb_devstate = ;
break; case USBR_SET_INTERFACE:
ReqErr = ((crq.bRequestType == 0x1) && ((crq.wValue & 0xff80) == )
&& ((crq.wIndex & 0xfff0) == ) && (crq.wLength == )) ? : ; if (!((dev->usb_devstate == 0x3) && (crq.wIndex == 0x0) && (crq.wValue == 0x0)))
ReqErr=; if (ReqErr == ) {
break; //break this switch loop
} break; default:
break;
}//switch end if (crq.bRequestType & USB_DIR_IN) {
dev->ep0state = EP0_IN_DATA_PHASE;
__raw_writel(0x08, controller.reg + REG_USBD_CEP_IRQ_ENB);
} else {
dev->ep0state = EP0_OUT_DATA_PHASE;
__raw_writel(0x40, controller.reg + REG_USBD_CEP_IRQ_ENB);
} ret = dev->driver->setup(&dev->gadget, &crq);
dev->setup_ret = ret;
if (ret < ) { __raw_writel(0x400, controller.reg + REG_USBD_CEP_IRQ_STAT);
__raw_writel(0x448, controller.reg + REG_USBD_CEP_IRQ_ENB); // enable in/RxED/status complete interrupt
__raw_writel(CEP_NAK_CLEAR, controller.reg + REG_USBD_CEP_CTRL_STAT); //clear nak so that sts stage is complete if (ret == -EOPNOTSUPP)
printk("Operation %x not supported\n", crq.bRequest);
else {
printk("dev->driver->setup failed. (%d)\n",ret);
}
} else if (ret > ) { //DELAYED_STATUS
printk("DELAYED_STATUS:%p\n", req);
dev->ep0state = EP0_END_XFER;
__raw_writel(, controller.reg + REG_USBD_CEP_IRQ_ENB);
} break; case EP0_STALL:
break;
default:
break;
} if (ReqErr == ) {
__raw_writel(CEP_SEND_STALL, controller.reg + REG_USBD_CEP_CTRL_STAT);
dev->ep0state = EP0_STALL;
} }
18 20行就是从寄存器拿出来setup 包的数据,放到 struct usb_ctrlrequest crq;
struct usb_ctrlrequest {
__u8 bRequestType;
__u8 bRequest;
__le16 wValue;
__le16 wIndex;
__le16 wLength;
} __attribute__ ((packed));
这个数据结构与usb协议里的定义相一致.
下面的操作全是围绕这个结构进行的.
如果请bRequest是USBR_SET_ADDRESS, USBR_SET_CONFIGURATION,USBR_SET_INTERFACE.就地处理.
如果不是,进入103行的 调用gadget驱动的setup函数.
########################nuc900_udc##########################
这个setup就是上一篇
static struct usb_gadget_driver composite_driver = {
.speed = USB_SPEED_HIGH,
.bind = composite_bind,
.unbind = composite_unbind,
.setup = composite_setup,
.disconnect = composite_disconnect,
.suspend = composite_suspend,
.resume = composite_resume,
.driver = {
.owner = THIS_MODULE,
},
};
中的setup,就是composite_setup
/*
* The setup() callback implements all the ep0 functionality that's
* not handled lower down, in hardware or the hardware driver(like
* device and endpoint feature flags, and their status). It's all
* housekeeping for the gadget function we're implementing. Most of
* the work is in config and function specific setup.
*/
static int
composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u8 intf = w_index & 0xFF;
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
struct usb_function *f = NULL;
u8 endp; /* partial re-init of the response message; the function or the
* gadget might need to intercept e.g. a control-OUT completion
* when we delegate to it.
*/
req->zero = ;
req->complete = composite_setup_complete;
req->length = USB_BUFSIZ;
gadget->ep0->driver_data = cdev; switch (ctrl->bRequest) { /* we handle all standard USB descriptors */
case USB_REQ_GET_DESCRIPTOR:
if (ctrl->bRequestType != USB_DIR_IN)
goto unknown;
switch (w_value >> ) { case USB_DT_DEVICE: //
cdev->desc.bNumConfigurations =
count_configs(cdev, USB_DT_DEVICE);
value = min(w_length, (u16) sizeof cdev->desc);
memcpy(req->buf, &cdev->desc, value);
break;
case USB_DT_DEVICE_QUALIFIER:
if (!gadget_is_dualspeed(gadget))
break;
device_qual(cdev);
value = min_t(int, w_length,
sizeof(struct usb_qualifier_descriptor));
break;
case USB_DT_OTHER_SPEED_CONFIG:
if (!gadget_is_dualspeed(gadget))
break;
/* FALLTHROUGH */
case USB_DT_CONFIG: // 2 若干次跟config个数有关
value = config_desc(cdev, w_value);
if (value >= )
value = min(w_length, (u16) value);
break;
case USB_DT_STRING: // 3 若干次跟function个数有关
value = get_string(cdev, req->buf,
w_index, w_value & 0xff);
if (value >= )
value = min(w_length, (u16) value);
break;
}
break; /* any number of configs can work */
case USB_REQ_SET_CONFIGURATION: // 4
if (ctrl->bRequestType != )
goto unknown;
if (gadget_is_otg(gadget)) {
if (gadget->a_hnp_support)
DBG(cdev, "HNP available\n");
else if (gadget->a_alt_hnp_support)
DBG(cdev, "HNP on another port\n");
else
VDBG(cdev, "HNP inactive\n");
}
spin_lock(&cdev->lock);
value = set_config(cdev, ctrl, w_value);
spin_unlock(&cdev->lock);
break;
case USB_REQ_GET_CONFIGURATION:
if (ctrl->bRequestType != USB_DIR_IN)
goto unknown;
if (cdev->config)
*(u8 *)req->buf = cdev->config->bConfigurationValue;
else
*(u8 *)req->buf = ;
value = min(w_length, (u16) );
break; /* function drivers must handle get/set altsetting; if there's
* no get() method, we know only altsetting zero works.
*/
case USB_REQ_SET_INTERFACE:
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
goto unknown;
if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
if (w_value && !f->set_alt)
break;
value = f->set_alt(f, w_index, w_value);
break;
case USB_REQ_GET_INTERFACE:
if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
goto unknown;
if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
/* lots of interfaces only need altsetting zero... */
value = f->get_alt ? f->get_alt(f, w_index) : ;
if (value < )
break;
*((u8 *)req->buf) = value;
value = min(w_length, (u16) );
break;
default:
unknown:
VDBG(cdev,
"non-core control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length); /* functions always handle their interfaces and endpoints...
* punt other recipients (other, WUSB, ...) to the current
* configuration code.
*
* REVISIT it could make sense to let the composite device
* take such requests too, if that's ever needed: to work
* in config 0, etc.
*/
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
f = cdev->config->interface[intf];
break; case USB_RECIP_ENDPOINT:
endp = ((w_index & 0x80) >> ) | (w_index & 0x0f);
list_for_each_entry(f, &cdev->config->functions, list) {
if (test_bit(endp, f->endpoints))
break;
}
if (&f->list == &cdev->config->functions)
f = NULL;
break;
} if (f && f->setup)
value = f->setup(f, ctrl);
else {
struct usb_configuration *c; c = cdev->config;
if (c && c->setup)
value = c->setup(c, ctrl);
} goto done;
} /* respond with data transfer before status phase? */
if (value >= ) {
req->length = value;
req->zero = value < w_length;
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
if (value < ) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = ;
composite_setup_complete(gadget->ep0, req);
}
} done:
/* device either stalls (value < 0) or reports success */
return value;
}
这个函数做的就是根据host发来的请求,填充相应的req->buf,然后在最后173行,把req挂到req list上.
按照usb控制传输,setup阶段之后,是datain阶段. host会发送intoken包, device接到该包,会进到上面贴的paser_irq_cep函数的42行的case中调用 write_fifo.
write_fifo把数据写到缓冲区,并把req从req list上拿掉.
这个函数157行调用function的setup,处理特殊的setup请求.
之前的内核没有composite这一层,每一种gadget设备,都要有一个setup函数,后来的内核把通用的内容都放到了composite.c. 写gadget 驱动就简化了些.
设备识别时device这边是跟着host的请求做相应的动作,函数大体流程就是这样,细节部分以后再慢慢研究
Linux gadget驱动分析2------设备识别过程的更多相关文章
- Linux gadget驱动分析1------驱动加载过程
为了解决一个问题,简单看了一遍linux gadget驱动的加载流程.做一下记录. 使用的内核为linux 2.6.35 硬件为芯唐NUC950. gadget是在UDC驱动上面的一层,如果要编写ga ...
- Linux gadget驱动分析3------复合设备驱动
windows上面对usb复合设备的识别需要下面条件. “ 如果设备满足下列要求,则总线驱动程序还会报告 USB\COMPOSITE 的兼容标识符: 设备描述符的设备类字段 (bDeviceClass ...
- linux串口驱动分析——打开设备
串口驱动是由tty_driver架构实现的.一个应用程序中的函数要操作硬件,首先会经过tty,级级调用之后才会到达驱动之中.本文先介绍应用程序中打开设备的open函数的整个历程. 首先在串口初始化中会 ...
- linux串口驱动分析
linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作 ...
- Linux spi驱动分析(二)----SPI核心(bus、device_driver和device)
一.spi总线注册 这里所说的SPI核心,就是指/drivers/spi/目录下spi.c文件中提供给其他文件的函数,首先看下spi核心的初始化函数spi_init(void).程序如下: 点击(此处 ...
- Linux I2C驱动分析(三)----i2c_dev驱动和应用层分析 【转】
本文转载自:http://blog.chinaunix.net/uid-21558711-id-3959287.html 分类: LINUX 原文地址:Linux I2C驱动分析(三)----i2c_ ...
- 基于335X的Linux网口驱动分析
基于335X的linux网口驱动分析 一. 系统构成 1. 硬件平台 AM335X 2. LINUX内核版本 4.4.12 二. 网口驱动构架(mdio部分) mdio网口驱动部分 使用 总线.设 ...
- linux驱动基础系列--Linux I2c驱动分析
前言 主要是想对Linux I2c驱动框架有一个整体的把控,因此会忽略协议上的某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.设备模型.sysfs等也不进行详细说明原理,涉及到i2c协议部分也只 ...
- Linux触摸驱动分析
测试平台 宿主机平台:Ubuntu 12.04.4 LTS 目标机:Easy-ARM IMX283 目标机内核:Linux 2.6.35.3 触摸屏基础知识 一.结构 上图是电阻触摸屏的一个侧面剖视图 ...
随机推荐
- 导出数据到Excel表格
开发工具与关键技术:Visual Studio 和 ASP.NET.MVC,作者:陈鸿鹏撰写时间:2019年5月25日123下面是我们来学习的导出数据到Excel表格的总结首先在视图层写导出数据的点击 ...
- Windows提高_2.3第三部分:内核区同步
第三部分:内核区同步 等待函数(WaitForObject) 等待函数的形式 单个:WaitForSingleObject 多个:WaitForMultipleObjects 一个可以被等待的对象通常 ...
- JMeter在linux上分布式压测遇到的坑(三)
master和slave机要在同一网段内,才能做分布式(Jmeter要配环境变量,这样不用手动起server) 分布式不成功,解决方案: 1.master端和slave端要ping通 2.ping通后 ...
- 梦想MxWeb3D协同设计平台 2018.10.12更新
SDK开发包下载地址: http://www.mxdraw.com/ndetail_10107.html 1. 全新的在线的三维协同设计平台,高效异步方式,基于JavaScript和WebGL技术,前 ...
- wpf mvvm模式下 在ViewModel关闭view
本文只是博主用来记录笔记,误喷 使用到到了MVVM中消息通知功能 第一步:在需要关闭窗体中注册消息 public UserView() { this.DataContext = new UserVie ...
- Python Web开发
参考原文 Python廖雪峰 WSGI接口 WSGI(Web Server Gateway Interface)是一个接口,用来屏蔽底部的细节(如TCP的建立连接,HTTP原始请求和响应格式等).WS ...
- Javascript 原型链与constructor
Javascript中的constructor与prototype 在学习javascript面向对象编程的过程中, constructor和prototype一直让我觉得理解不透,慢慢的学习过程中记 ...
- LinuxMint19.1安装搜狗拼音输入法
Installation 1.到搜狗拼音输入法官网下载Linux版. 2.使用dpkg命令安装deb软件包 $ sudo dpkg -i sogoupinyin_2.2.0.0108_amd.deb ...
- React组件设计技巧
React组件设计 组件分类 展示组件和容器组件 展示组件 容器组件 关注事物的展示 关注事物如何工作 可能包含展示和容器组件,并且一般会有DOM标签和css样式 可能包含展示和容器组件,并且不会有D ...
- python virtualenv 虚拟环境的应用
为什么要使用python的虚拟环境呢?: 首先我们来说不实用虚拟环境的情况: 在Python应用程序开发的过程中,系统安装的Python3只有一个版本:3.7.所有第三方的包都会被pip3安装到 ...