USB设备驱动程序-USB Gadget Driver(二)

FORM:http://blog.chinaunix.net/uid-14518381-id-3921457.html

修改排版
1. UDC驱动是作为platform driver向platform子系统注册的,因此UDC驱动首先就需要实现struct platform_driver结构中的函数成员:
struct platform_driver {
	int (*probe)(struct platform_device *);//驱动和设备绑定
	int (*remove)(struct platform_device *);//支持热插拔的设备移除
	void (*shutdown)(struct platform_device *);//设备关闭
	int (*suspend)(struct platform_device *, pm_message_t state);//电源管理相关,挂起设备
	int (*resume)(struct platform_device *);
	struct device_driver driver;
	struct platform_device_id *id_table;//驱动和设备匹配信息
};

在下面的源码分析中以s3c2410_udc.c文件为例:

static struct platform_driver udc_driver_2410 = {
	.driver		= {
		.name	= "s3c2410-usbgadget",
		.owner	= THIS_MODULE,
	},
	.probe		= s3c2410_udc_probe,
	.remove		= s3c2410_udc_remove,
	.suspend	= s3c2410_udc_suspend,
	.resume		= s3c2410_udc_resume,
};
其中以s3c2410_udc_probe和s3c2410_udc_remove函数最为重要,s3c2410_udc_probe函数实现驱动和设备的匹配绑定,并分配资源;而s3c2410_udc_remove函数实现资源的释放。
static int s3c2410_udc_probe(struct platform_device *pdev)
{
	struct s3c2410_udc *udc = &memory;//s3c2410的UDC设备,在其中对usb_gadget设备对象和端点等进行了初始化
	struct device *dev = &pdev->dev;
	int retval;
	int irq;

	dev_dbg(dev, "%s()\n", __func__);
	//获取总线时钟并使能
	usb_bus_clock = clk_get(NULL, "usb-bus-gadget");
	if (IS_ERR(usb_bus_clock)) {
		dev_err(dev, "failed to get usb bus clock source\n");
		return PTR_ERR(usb_bus_clock);
	}
	//获取设备时钟并使能
	clk_enable(usb_bus_clock);

	udc_clock = clk_get(NULL, "usb-device");
	if (IS_ERR(udc_clock)) {
		dev_err(dev, "failed to get udc clock source\n");
		return PTR_ERR(udc_clock);
	}

	clk_enable(udc_clock);

	mdelay(10);

	dev_dbg(dev, "got and enabled clocks\n");

	if (strncmp(pdev->name, "s3c2440", 7) == 0) {
		dev_info(dev, "S3C2440: increasing FIFO to 128 bytes\n");
		memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE;
		memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE;
		memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE;
		memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
	}

	spin_lock_init (&udc->lock);//初始化设备的自旋锁
	udc_info = pdev->dev.platform_data;

	rsrc_start = S3C2410_PA_USBDEV;//s3c2410 UDC端口起始地址
	rsrc_len   = S3C24XX_SZ_USBDEV;//s3c2410端口地址长度

	if (!request_mem_region(rsrc_start, rsrc_len, gadget_name))//申请端口资源
		return -EBUSY;

	base_addr = ioremap(rsrc_start, rsrc_len);//端口映射
	if (!base_addr) {
		retval = -ENOMEM;
		goto err_mem;
	}

	device_initialize(&udc->gadget.dev);//初始化device设备对象
	udc->gadget.dev.parent = &pdev->dev;//当前UDC设备的父设备对象是platform_device
	udc->gadget.dev.dma_mask = pdev->dev.dma_mask;

	the_controller = udc;
	platform_set_drvdata(pdev, udc);//驱动和设备绑定,在platform_device结构中保存udc设备对象
	/*重新初始化设备*/
	s3c2410_udc_disable(udc);
	s3c2410_udc_reinit(udc);
	/*申请中断,并绑定中断函数,中断函数是UDC功能驱动的入口函数*/
	/* irq setup after old hardware state is cleaned up */
	retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
			     IRQF_DISABLED, gadget_name, udc);

	if (retval != 0) {
		dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);
		retval = -EBUSY;
		goto err_map;
	}

	dev_dbg(dev, "got irq %i\n", IRQ_USBD);

	if (udc_info && udc_info->vbus_pin > 0) {
		retval = gpio_request(udc_info->vbus_pin, "udc vbus");
		if (retval < 0) {
			dev_err(dev, "cannot claim vbus pin\n");
			goto err_int;
		}

		irq = gpio_to_irq(udc_info->vbus_pin);
		if (irq < 0) {
			dev_err(dev, "no irq for gpio vbus pin\n");
			goto err_gpio_claim;
		}

		retval = request_irq(irq, s3c2410_udc_vbus_irq,
				     IRQF_DISABLED | IRQF_TRIGGER_RISING
				     | IRQF_TRIGGER_FALLING | IRQF_SHARED,
				     gadget_name, udc);

		if (retval != 0) {
			dev_err(dev, "can't get vbus irq %d, err %d\n",
				irq, retval);
			retval = -EBUSY;
			goto err_gpio_claim;
		}

		dev_dbg(dev, "got irq %i\n", irq);
	} else {
		udc->vbus = 1;
	}

	if (s3c2410_udc_debugfs_root) {//创建虚拟文件debugfs
		udc->regs_info = debugfs_create_file("registers", S_IRUGO,
				s3c2410_udc_debugfs_root,
				udc, &s3c2410_udc_debugfs_fops);
		if (!udc->regs_info)
			dev_warn(dev, "debugfs file creation failed\n");
	}

	dev_dbg(dev, "probe ok\n");

	return 0;

err_gpio_claim:
	if (udc_info && udc_info->vbus_pin > 0)
		gpio_free(udc_info->vbus_pin);
err_int:
	free_irq(IRQ_USBD, udc);
err_map:
	iounmap(base_addr);
err_mem:
	release_mem_region(rsrc_start, rsrc_len);

	return retval;
}
从s3c2410_udc_probe函数可以看出,probe函数主要完成的就是将platform_device设备对象和UDC设备对象建立关系,UDC设备和驱动的一些初始化工作,并申请驱动所需的资源,若端口区间、中断号等,并将中断函数和中断号绑定。这个中断处理函数非常重要,对这个设备的所有操作都将从中断函数入口。
static int s3c2410_udc_remove(struct platform_device *pdev)
{
             struct s3c2410_udc *udc = platform_get_drvdata(pdev); //获取UDC设备对象,在probe函数中绑定的
             unsigned int irq;
            if (udc->driver) //设备的驱动usb_gadget_driver对象,说明设备正在使用
                       return -EBUSY;
          debugfs_remove(udc->regs_info); //移除debugfs文件系统中建立的文件
          if (udc_info && udc_info->vbus_pin > 0) {
                          irq = gpio_to_irq(udc_info->vbus_pin);
                          free_irq(irq, udc);
                    }
           free_irq(IRQ_USBD, udc); //释放中断
           /*释放端口资源*/
           iounmap(base_addr);
           release_mem_region(rsrc_start, rsrc_len);
          /*解除绑定*/
           platform_set_drvdata(pdev, NULL);
          /*释放时钟*/
            if (!IS_ERR(udc_clock) && udc_clock != NULL) {
                              clk_disable(udc_clock);
                               clk_put(udc_clock);
                              udc_clock = NULL;
             }
             if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
                                clk_disable(usb_bus_clock);
                                 clk_put(usb_bus_clock);
                                  usb_bus_clock = NULL;
            }
          dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);
          return 0;
}

可以看出,remove函数基本上是probe函数的逆操作,将probe函数中申请的资源释放掉。

2. UDC驱动程序还需要为上层实现usb_gadget_register_driver和usb_gadget_unregister_driver两个gadget driver注册接口,这两个函数将实现gadget driver和udc driver绑定。

int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
          struct s3c2410_udc *udc = the_controller; //UDC设备对象
           int retval;
         /* Sanity checks */
          if (!udc)
                      return -ENODEV;
        /*UDC设备只能绑定一个gadget driver对象*/
         if (udc->driver)
                   return -EBUSY;
         /*检查gadget driver是否实现了绑定函数、setup函数。同时低速设备也不支持gadget driver*/
        if (!driver->bind || !driver->setup|| driver->speed < USB_SPEED_FULL) {
                   printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n",driver->bind, driver->setup, driver->speed);
                    return -EINVAL;
         }
         //支持卸载的话,还需要实现unbind函数
         #if defined(MODULE)
                   if (!driver->unbind) {
                                  printk(KERN_ERR "Invalid driver: no unbind method\n");
                                   return -EINVAL;
                    }
         #endif
         /* Hook the driver */
        /*将gadget driver和udc设备绑定*/
           udc->driver = driver;
           udc->gadget.dev.driver = &driver->driver;
           /* Bind the driver */
           if ((retval = device_add(&udc->gadget.dev)) != 0) { //完成gadget设备在内核中的注册
                         printk(KERN_ERR "Error in device_add() : %d\n",retval);
                        goto register_error;
           }
          if ((retval = driver->bind (&udc->gadget)) != 0) {//gadget驱动绑定函数
                    device_del(&udc->gadget.dev);
                     goto register_error;
         }
         /* Enable udc */
         //使能UDC设备
         s3c2410_udc_enable(udc);
          return 0;
         register_error:
                   udc->driver = NULL;
                   udc->gadget.dev.driver = NULL;
                   return retval;
}


/*gadget 驱动注销函数*/
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
             struct s3c2410_udc *udc = the_controller;
             if (!udc)
                        return -ENODEV;
             /*驱动必须和注册时的驱动是一致的,同时实现了unbind函数*/
             if (!driver || driver != udc->driver || !driver->unbind)
                        return -EINVAL;
             dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n", driver->driver.name);
             /*调用gadget driver实现的unbind函数*/
             driver->unbind(&udc->gadget);
             device_del(&udc->gadget.dev); //和register函数中的device_add对应
             udc->driver = NULL;//这个很重要,这里说明gadget驱动注销之后,才能移除udc设备
             /* Disable udc */
             /*关闭设备*/
               s3c2410_udc_disable(udc);
               return 0;
  }

3. 中断函数

中断处理函数是UDC驱动层的核心函数,由于UDC是从设备,主机端是控制端,所有的操作都是主机端发起的,所以中断处理函数是UDC驱动层的核心函数。

static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
{
              struct s3c2410_udc *dev = _dev; //UDC设备对象
              int usb_status;
              int usbd_status;
              int pwr_reg;
              int ep0csr;
              int i;
              u32 idx;
             unsigned long flags;
             spin_lock_irqsave(&dev->lock, flags);
              /* Driver connected ? */
             if (!dev->driver) { //还没有和驱动绑定,清除中断
                                            /* Clear interrupts */
                                         udc_write(udc_read(S3C2410_UDC_USB_INT_REG), S3C2410_UDC_USB_INT_REG);
                                          udc_write(udc_read(S3C2410_UDC_EP_INT_REG),S3C2410_UDC_EP_INT_REG);
                   }
             /* Save index */
              idx = udc_read(S3C2410_UDC_INDEX_REG); //这是哪个端点产生中断的索引号
               /* Read status registers */
             usb_status = udc_read(S3C2410_UDC_USB_INT_REG); //UDC设备状态
              usbd_status = udc_read(S3C2410_UDC_EP_INT_REG); //UDC产生中断的端点状态
               pwr_reg = udc_read(S3C2410_UDC_PWR_REG);
                udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
               ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
              dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", usb_status, usbd_status, pwr_reg, ep0csr);
            
               /*
                   开始中断的实际处理,这里的中断只有两种类型:
                   1. UDC设备中断: 重置、挂起和恢复
                   2. 端点中断
                */
                /* UDC设备RESET操作处理 */
              if (usb_status & S3C2410_UDC_USBINT_RESET) { //Reset中断
                                  /* two kind of reset :
                                   * - reset start -> pwr reg = 8
                                   * - reset end -> pwr reg = 0
                                   **/
                                  dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n", ep0csr, pwr_reg);
                                   dev->gadget.speed = USB_SPEED_UNKNOWN;
                                   udc_write(0x00, S3C2410_UDC_INDEX_REG);
                                   udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3, S3C2410_UDC_MAXP_REG);
                                   dev->address = 0;
                                   dev->ep0state = EP0_IDLE;
                                    dev->gadget.speed = USB_SPEED_FULL;
                                     /* clear interrupt */
                                    udc_write(S3C2410_UDC_USBINT_RESET, S3C2410_UDC_USB_INT_REG);
                                    udc_write(idx, S3C2410_UDC_INDEX_REG);
                                     spin_unlock_irqrestore(&dev->lock, flags);
                                   return IRQ_HANDLED;
                }
              /* UDC设备RESUME操作处理 */
               if (usb_status & S3C2410_UDC_USBINT_RESUME) { //Resume中断
                                               dprintk(DEBUG_NORMAL, "USB resume\n");
                                               /* clear interrupt */
                                               udc_write(S3C2410_UDC_USBINT_RESUME, S3C2410_UDC_USB_INT_REG);
                                             if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver&& dev->driver->resume)//调用resume函数
                                                                        dev->driver->resume(&dev->gadget);
               }
               /* UDC设备SUSPEND操作处理 */
             if (usb_status & S3C2410_UDC_USBINT_SUSPEND) { //Suspend中断
                                         dprintk(DEBUG_NORMAL, "USB suspend\n");
                                        /* clear interrupt */
                                       udc_write(S3C2410_UDC_USBINT_SUSPEND, S3C2410_UDC_USB_INT_REG);
                                       if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver&& dev->driver->suspend)//调用suspend函数
                                                                      dev->driver->suspend(&dev->gadget);
                                       dev->ep0state = EP0_IDLE;
               }
             /* 下面就是端点中断得处理 */
             /* 首先是控制端点(端点0)的中断处理*/
              /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
                      * generate an interrupt
               */
                if (usbd_status & S3C2410_UDC_INT_EP0) { //端点0的中断
                                           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);
                                            s3c2410_udc_handle_ep0(dev); //处理端点0
                 }
                /* 其他端点,就是数据传输的处理*/
                for (i = 1; i < S3C2410_ENDPOINTS; i++) { //遍历所有端点,找出中断的端点
                                           u32 tmp = 1 << i;
                                           if (usbd_status & tmp) {
                                                                             dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);
                                                                              /* Clear the interrupt bit by setting it to 1 */
                                                                               udc_write(tmp, S3C2410_UDC_EP_INT_REG);
                                                                             s3c2410_udc_handle_ep(&dev->ep[i]); //处理对应端点
                                                }
                           }
                dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", IRQ_USBD);
                /* Restore old index */
                 udc_write(idx, S3C2410_UDC_INDEX_REG);
                 spin_unlock_irqrestore(&dev->lock, flags);
                  return IRQ_HANDLED;
}

4.  端点操作函数

端点操作函数是UDC驱动的基础,因为大部分的动作其实都是和端点相关的,如数据传输等。

首先来看中断函数中涉及的两个函数,一个是端点0的处理函数,一个是其他端点的处理函数。

static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)
{
               u32 ep0csr;
              struct s3c2410_ep *ep = &dev->ep[0];
               struct s3c2410_request *req;
               struct usb_ctrlrequest crq;
               if (list_empty(&ep->queue))
                            req = NULL;
              else
                          req = list_entry(ep->queue.next, struct s3c2410_request, queue);
             /* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to
               * S3C2410_UDC_EP0_CSR_REG when index is zero */
              udc_write(0, S3C2410_UDC_INDEX_REG);
            ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
            dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n", ep0csr, ep0states[dev->ep0state]);
            /* clear stall status */
             if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) { //清除STALL状态
                              s3c2410_udc_nuke(dev, ep, -EPIPE);
                              dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
                              s3c2410_udc_clear_ep0_sst(base_addr);
                               dev->ep0state = EP0_IDLE;
                                             return;
                 }
                /* clear setup end */
                if (ep0csr & S3C2410_UDC_EP0_CSR_SE) {
                              dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
                              s3c2410_udc_nuke(dev, ep, 0);
                               s3c2410_udc_clear_ep0_se(base_addr);
                                dev->ep0state = EP0_IDLE;
                 }
                /*端点0的状态处理*/
               switch (dev->ep0state) {
                               case EP0_IDLE:
                                                s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr); //在这个函数中会调用上层提供的gadget_driver中实现的setup函数来处理控制数据包
                                                break;
                                case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ //向主机发送数据
                                              dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
                                              if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) {
                                                                s3c2410_udc_write_fifo(ep, req); //写UDC FIFO
                                              }
                                               break;
                               case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ //从主机接收数据
                                              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:
                                             dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
                                             dev->ep0state = EP0_IDLE;
                                               break;
                            case EP0_STALL:
                                        dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
                                       dev->ep0state = EP0_IDLE;
                                        break;
            }
}

/*
* handle_ep - Manage I/O endpoints
其他端点的处理函数,主要是数据发送和接收
*/
static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
{
                       struct s3c2410_request *req;
                       int is_in = ep->bEndpointAddress & USB_DIR_IN;
                       u32 ep_csr1;
                       u32 idx;
                     if (likely (!list_empty(&ep->queue))) //取出申请
                                         req = list_entry(ep->queue.next, struct s3c2410_request, queue);
                     else
                                      req = NULL;
                   idx = ep->bEndpointAddress & 0x7F; //端点地址
                  if (is_in) { //向主机发送数据
                                     udc_write(idx, S3C2410_UDC_INDEX_REG);
                                    ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
                                    dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n", idx, ep_csr1, req ? 1 : 0);
                                    if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) {
                                                      dprintk(DEBUG_VERBOSE, "st\n");
                                                       udc_write(idx, S3C2410_UDC_INDEX_REG);
                                                       udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL, S3C2410_UDC_IN_CSR1_REG);
                                                                 return;
                                     }
                                   if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) {
                                                   s3c2410_udc_write_fifo(ep,req);
                                    }
                  }
                 else  //从主机接收数据
                   {
                                  udc_write(idx, S3C2410_UDC_INDEX_REG);
                                  ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG);
                                   dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1);
                                  if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) {
                                                         udc_write(idx, S3C2410_UDC_INDEX_REG);
                                                         udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL, S3C2410_UDC_OUT_CSR1_REG);
                                                         return;
                                    }
                                 if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) {
                                                      s3c2410_udc_read_fifo(ep,req);
                                 }
                 }
}


端点操作函数集:端点的基本操作函数

static const struct usb_ep_ops s3c2410_ep_ops = {
                        .enable = s3c2410_udc_ep_enable, //端点使能
                        .disable = s3c2410_udc_ep_disable, //关闭端点
                        .alloc_request = s3c2410_udc_alloc_request, //分配一个请求
                          .free_request = s3c2410_udc_free_request, //释放请求
                              .queue = s3c2410_udc_queue, //向端点提交一个请求
                            .dequeue = s3c2410_udc_dequeue, //从端点请求队列中删除一个请求
                          .set_halt = s3c2410_udc_set_halt,
};

主要分析queue这个函数,因为上层主要和这个函数打交道,接收或发送数据都需要对应的端点队列提交请求

 static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
{
                  struct s3c2410_request *req = to_s3c2410_req(_req);
                  struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
                   struct s3c2410_udc *dev;
                   u32 ep_csr = 0;
                  int fifo_count = 0;
                     unsigned long flags;
                 if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
                                dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__);
                                return -EINVAL;
                   }
                dev = ep->dev;
                if (unlikely (!dev->driver|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
                                  return -ESHUTDOWN;
                    }
                local_irq_save (flags);
               if (unlikely(!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) {
                              if (!_req)
                                            dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);
                              else {
                                            dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n", __func__, !_req->complete,!_req->buf, !list_empty(&req->queue));
                                    }
                              local_irq_restore(flags);
                             return -EINVAL;
                }
              _req->status = -EINPROGRESS;
              _req->actual = 0;
            dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n", __func__, ep->bEndpointAddress, _req->length);
            if (ep->bEndpointAddress) { //设置端点号
                            udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG);
                           ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN) ? S3C2410_UDC_IN_CSR1_REG: S3C2410_UDC_OUT_CSR1_REG);
                            fifo_count = s3c2410_udc_fifo_count_out();
           }
          else
            {
                           udc_write(0, S3C2410_UDC_INDEX_REG);
                            ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
                             fifo_count = s3c2410_udc_fifo_count_out();
             }
            /* kickstart this i/o queue? */
              if (list_empty(&ep->queue) && !ep->halted) { //该端点队列为空,且没有关闭,则直接完成申请
                                     if (ep->bEndpointAddress == 0 /* ep0 */) { //端点0
                                                               switch (dev->ep0state) {
                                                                               case EP0_IN_DATA_PHASE:
                                                                                             if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY) && s3c2410_udc_write_fifo(ep,req)) {
                                                                                                                   dev->ep0state = EP0_IDLE;
                                                                                                                     req = NULL;
                                                                                                }
                                                                                                break;
                                                                                case EP0_OUT_DATA_PHASE:
                                                                                                if ((!_req->length)|| ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)&& s3c2410_udc_read_fifo(ep,req))) {
                                                                                                                               dev->ep0state = EP0_IDLE;
                                                                                                                       req = NULL;
                                                                                                 }
                                                                                                break;
                                                                                default:
                                                                                               local_irq_restore(flags);
                                                                                               return -EL2HLT;
                                                                  }
                                        }
                                      else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 && (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) && s3c2410_udc_write_fifo(ep, req)) {
                                                               req = NULL;
                                     }
                                      else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)&& fifo_count&& s3c2410_udc_read_fifo(ep, req)) {
                                                               req = NULL;
                                      }
                }
           /* pio or dma irq handler advances the queue. */
            if (likely (req != 0))
                            list_add_tail(&req->queue, &ep->queue); //加入队列,等待中断处理
            local_irq_restore(flags);
            dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);
            return 0;
}
 UDC驱动中还有一个重要函数,在数据传输或接收完成,即一个请求完成之后,会调用这个请求的完成函数来通知上层驱动。

static void s3c2410_udc_done(struct s3c2410_ep *ep,  struct s3c2410_request *req,  int status) 
{ 
             unsigned halted = ep->halted;
             list_del_init(&req->queue);  //将这个请求从端点请求队列中删除
             if (likely (req->req.status == -EINPROGRESS)) 
                           req->req.status = status; //返回完成状态 
            else 
                           status = req->req.status;
            ep->halted = 1; 
            req->req.complete(&ep->ep, &req->req); 
            ep->halted = halted; 
}

总结:

       UDC设备驱动层的源码就分析得差不多了,其他很多函数都是操作寄存器,与UDC设备密切相关,但总的来说完成的功能都是一致的。可以发现,在UDC设备驱动层主要需要做以下几个工作:

1. 对usb_gadget、usb_ep、usb_request三个标准数据结构进行封装,根据自己UDC的一些设备特性,设计对应的自己的数据结构;

2. 实现platform_driver数据结构中的函数,将UDC设备驱动向platform系统进行注册;

3. 实现usb_gadget_ops函数集,这些函数主要是操作UDC设备的一些特性(针对设备);

4. 实现usb_ep_ops函数集,这些函数主要是操作端点的功能,如请求分配和提交等(针对端点);

5. 实现UDC设备的中断处理函数,这个函数基本上就是UDC设备驱动的核心;

6.实现上层功能驱动注册接口函数:

     int usb_gadget_register_driver(struct usb_gadget_driver *driver)
     int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)


1. UDC驱动是作为platform driver向platform子系统注册的,因此UDC驱动首先就需要实现struct platform_driver结构中的函数成员:

struct platform_driver {

           int (*probe)(struct platform_device *); //驱动和设备绑定

           int (*remove)(struct platform_device *); //支持热插拔的设备移除

           void (*shutdown)(struct platform_device *); //设备关闭

           int (*suspend)(struct platform_device *, pm_message_t state); //电源管理相关,挂起设备

           int (*resume)(struct platform_device *); //电源管理相关,恢复设备

           struct device_driver driver;

          struct platform_device_id *id_table; //驱动和设备匹配信息

};

在下面的源码分析中以s3c2410_udc.c文件为例:

static struct platform_driver udc_driver_2410 = {

          .driver = {

                          .name = "s3c2410-usbgadget",

                          .owner = THIS_MODULE,

          },

        .probe = s3c2410_udc_probe,

        .remove = s3c2410_udc_remove,

        .suspend = s3c2410_udc_suspend,

        .resume = s3c2410_udc_resume,

};

其中以s3c2410_udc_probe和s3c2410_udc_remove函数最为重要,s3c2410_udc_probe函数实现驱动和设备的匹配绑定,并分配资源;而s3c2410_udc_remove函数实现资源的释放。

static int s3c2410_udc_probe(struct platform_device *pdev)

{

        struct s3c2410_udc *udc = &memory; //s3c2410的UDC设备,在其中对usb_gadget设备对象和端点等进行了初始化

       struct device *dev = &pdev->dev;

       int retval;

      int irq;

     //获取总线时钟并使能

       usb_bus_clock = clk_get(NULL, "usb-bus-gadget");

        clk_enable(usb_bus_clock);

     //获取设备时钟并使能

      udc_clock = clk_get(NULL, "usb-device");

       clk_enable(udc_clock);

      mdelay(10);

       spin_lock_init (&udc->lock); //初始化设备的自旋锁

       udc_info = pdev->dev.platform_data;

        rsrc_start = S3C2410_PA_USBDEV; //s3c2410 UDC端口起始地址

       rsrc_len = S3C24XX_SZ_USBDEV; //s3c2410端口地址长度

 

      if (!request_mem_region(rsrc_start, rsrc_len, gadget_name)) //申请端口资源

                   return -EBUSY;

     base_addr = ioremap(rsrc_start, rsrc_len); //端口映射

      if (!base_addr) {

                 retval = -ENOMEM;

                 goto err_mem;

      }

 

      device_initialize(&udc->gadget.dev); //初始化device设备对象

      udc->gadget.dev.parent = &pdev->dev; //当前UDC设备的父设备对象是platform_device

    udc->gadget.dev.dma_mask = pdev->dev.dma_mask;

       the_controller = udc;

    platform_set_drvdata(pdev, udc); //驱动和设备绑定,在platform_device结构中保存udc设备对象

/*重新初始化设备*/

s3c2410_udc_disable(udc);

s3c2410_udc_reinit(udc);

/* irq setup after old hardware state is cleaned up */

/*申请中断,并绑定中断函数,中断函数是UDC功能驱动的入口函数*/

retval = request_irq(IRQ_USBD, s3c2410_udc_irq, IRQF_DISABLED, gadget_name, udc);

if (udc_info && udc_info->vbus_pin > 0) {

          retval = gpio_request(udc_info->vbus_pin, "udc vbus");

                    irq = gpio_to_irq(udc_info->vbus_pin);

                    retval = request_irq(irq, s3c2410_udc_vbus_irq, IRQF_DISABLED | IRQF_TRIGGER_RISING| IRQF_TRIGGER_FALLING | IRQF_SHARED,gadget_name, udc);

               }

          else {

                   udc->vbus = 1;

}

      

if (s3c2410_udc_debugfs_root) { //创建虚拟文件debugfs

          udc->regs_info = debugfs_create_file("registers", S_IRUGO, s3c2410_udc_debugfs_root,udc, &s3c2410_udc_debugfs_fops);

          if (!udc->regs_info)

                      dev_warn(dev, "debugfs file creation failed\n");

}

dev_dbg(dev, "probe ok\n");

return 0;

err_gpio_claim:

                   if (udc_info && udc_info->vbus_pin > 0)

              gpio_free(udc_info->vbus_pin);

err_int:

        free_irq(IRQ_USBD, udc);

err_map:

          iounmap(base_addr);

err_mem:

          release_mem_region(rsrc_start, rsrc_len);

return retval;

}

从s3c2410_udc_probe函数可以看出,probe函数主要完成的就是将platform_device设备对象和UDC设备对象建立关系,UDC设备和驱动的一些初始化工作,并申请驱动所需的资源,若端口区间、中断号等,并将中断函数和中断号绑定。这个中断处理函数非常重要,对这个设备的所有操作都将从中断函数入口。

static int s3c2410_udc_remove(struct platform_device *pdev)

{

             struct s3c2410_udc *udc = platform_get_drvdata(pdev); //获取UDC设备对象,在probe函数中绑定的

             unsigned int irq;

            if (udc->driver) //设备的驱动usb_gadget_driver对象,说明设备正在使用

                       return -EBUSY;

          debugfs_remove(udc->regs_info); //移除debugfs文件系统中建立的文件

          if (udc_info && udc_info->vbus_pin > 0) {

                          irq = gpio_to_irq(udc_info->vbus_pin);

                          free_irq(irq, udc);

                    }

           free_irq(IRQ_USBD, udc); //释放中断

           /*释放端口资源*/

           iounmap(base_addr);

           release_mem_region(rsrc_start, rsrc_len);

          /*解除绑定*/

           platform_set_drvdata(pdev, NULL);

          /*释放时钟*/

            if (!IS_ERR(udc_clock) && udc_clock != NULL) {

                              clk_disable(udc_clock);

                               clk_put(udc_clock);

                              udc_clock = NULL;

             }

             if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {

                                clk_disable(usb_bus_clock);

                                 clk_put(usb_bus_clock);

                                  usb_bus_clock = NULL;

            }

          dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);

          return 0;

}

可以看出,remove函数基本上是probe函数的逆操作,将probe函数中申请的资源释放掉。

2. UDC驱动程序还需要为上层实现usb_gadget_register_driver和usb_gadget_unregister_driver两个gadget driver注册接口,这两个函数将实现gadget driver和udc driver绑定。

int usb_gadget_register_driver(struct usb_gadget_driver *driver)

{

          struct s3c2410_udc *udc = the_controller; //UDC设备对象

           int retval;

         /* Sanity checks */

          if (!udc)

                      return -ENODEV;

        /*UDC设备只能绑定一个gadget driver对象*/

         if (udc->driver)

                   return -EBUSY;

         /*检查gadget driver是否实现了绑定函数、setup函数。同时低速设备也不支持gadget driver*/

        if (!driver->bind || !driver->setup|| driver->speed < USB_SPEED_FULL) {

                   printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n",driver->bind, driver->setup, driver->speed);

                    return -EINVAL;

         }

         //支持卸载的话,还需要实现unbind函数

         #if defined(MODULE)

                   if (!driver->unbind) {

                                  printk(KERN_ERR "Invalid driver: no unbind method\n");

                                   return -EINVAL;

                    }

         #endif

         /* Hook the driver */

        /*将gadget driver和udc设备绑定*/

           udc->driver = driver;

           udc->gadget.dev.driver = &driver->driver;

           /* Bind the driver */

           if ((retval = device_add(&udc->gadget.dev)) != 0) { //完成gadget设备在内核中的注册

                         printk(KERN_ERR "Error in device_add() : %d\n",retval);

                        goto register_error;

           }

          if ((retval = driver->bind (&udc->gadget)) != 0) {//gadget驱动绑定函数

                    device_del(&udc->gadget.dev);

                     goto register_error;

         }

         /* Enable udc */

         //使能UDC设备

         s3c2410_udc_enable(udc);

          return 0;

         register_error:

                   udc->driver = NULL;

                   udc->gadget.dev.driver = NULL;

                   return retval;

}

 

/*gadget 驱动注销函数*/

int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)

{

             struct s3c2410_udc *udc = the_controller;

             if (!udc)

                        return -ENODEV;

             /*驱动必须和注册时的驱动是一致的,同时实现了unbind函数*/

             if (!driver || driver != udc->driver || !driver->unbind)

                        return -EINVAL;

             dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n", driver->driver.name);

             /*调用gadget driver实现的unbind函数*/

             driver->unbind(&udc->gadget);

             device_del(&udc->gadget.dev); //和register函数中的device_add对应

             udc->driver = NULL;//这个很重要,这里说明gadget驱动注销之后,才能移除udc设备

             /* Disable udc */

             /*关闭设备*/

               s3c2410_udc_disable(udc);

               return 0;

  }

3. 中断函数

中断处理函数是UDC驱动层的核心函数,由于UDC是从设备,主机端是控制端,所有的操作都是主机端发起的,所以中断处理函数是UDC驱动层的核心函数。

static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)

{

              struct s3c2410_udc *dev = _dev; //UDC设备对象

              int usb_status;

              int usbd_status;

              int pwr_reg;

              int ep0csr;

              int i;

              u32 idx;

             unsigned long flags;

             spin_lock_irqsave(&dev->lock, flags);

              /* Driver connected ? */

             if (!dev->driver) { //还没有和驱动绑定,清除中断

                                            /* Clear interrupts */

                                         udc_write(udc_read(S3C2410_UDC_USB_INT_REG), S3C2410_UDC_USB_INT_REG);

                                          udc_write(udc_read(S3C2410_UDC_EP_INT_REG),S3C2410_UDC_EP_INT_REG);

                   }

             /* Save index */

              idx = udc_read(S3C2410_UDC_INDEX_REG); //这是哪个端点产生中断的索引号

               /* Read status registers */

             usb_status = udc_read(S3C2410_UDC_USB_INT_REG); //UDC设备状态

              usbd_status = udc_read(S3C2410_UDC_EP_INT_REG); //UDC产生中断的端点状态

               pwr_reg = udc_read(S3C2410_UDC_PWR_REG);

                udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);

               ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);

              dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", usb_status, usbd_status, pwr_reg, ep0csr);

            

               /*

                   开始中断的实际处理,这里的中断只有两种类型:

                   1. UDC设备中断: 重置、挂起和恢复

                   2. 端点中断

                */

                /* UDC设备RESET操作处理 */

              if (usb_status & S3C2410_UDC_USBINT_RESET) { //Reset中断

                                  /* two kind of reset :

                                   * - reset start -> pwr reg = 8

                                   * - reset end -> pwr reg = 0

                                   **/

                                  dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n", ep0csr, pwr_reg);

                                   dev->gadget.speed = USB_SPEED_UNKNOWN;

                                   udc_write(0x00, S3C2410_UDC_INDEX_REG);

                                   udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3, S3C2410_UDC_MAXP_REG);

                                   dev->address = 0;

                                   dev->ep0state = EP0_IDLE;

                                    dev->gadget.speed = USB_SPEED_FULL;

                                     /* clear interrupt */

                                    udc_write(S3C2410_UDC_USBINT_RESET, S3C2410_UDC_USB_INT_REG);

                                    udc_write(idx, S3C2410_UDC_INDEX_REG);

                                     spin_unlock_irqrestore(&dev->lock, flags);

                                   return IRQ_HANDLED;

                }

              /* UDC设备RESUME操作处理 */

               if (usb_status & S3C2410_UDC_USBINT_RESUME) { //Resume中断

                                               dprintk(DEBUG_NORMAL, "USB resume\n");

                                               /* clear interrupt */

                                               udc_write(S3C2410_UDC_USBINT_RESUME, S3C2410_UDC_USB_INT_REG);

                                             if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver&& dev->driver->resume)//调用resume函数

                                                                        dev->driver->resume(&dev->gadget);

               }

               /* UDC设备SUSPEND操作处理 */

             if (usb_status & S3C2410_UDC_USBINT_SUSPEND) { //Suspend中断

                                         dprintk(DEBUG_NORMAL, "USB suspend\n");

                                        /* clear interrupt */

                                       udc_write(S3C2410_UDC_USBINT_SUSPEND, S3C2410_UDC_USB_INT_REG);

                                       if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver&& dev->driver->suspend)//调用suspend函数

                                                                      dev->driver->suspend(&dev->gadget);

                                       dev->ep0state = EP0_IDLE;

               }

             /* 下面就是端点中断得处理 */

             /* 首先是控制端点(端点0)的中断处理*/

              /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready

                      * generate an interrupt

               */

                if (usbd_status & S3C2410_UDC_INT_EP0) { //端点0的中断

                                           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);

                                            s3c2410_udc_handle_ep0(dev); //处理端点0

                 }

                /* 其他端点,就是数据传输的处理*/

                for (i = 1; i < S3C2410_ENDPOINTS; i++) { //遍历所有端点,找出中断的端点

                                           u32 tmp = 1 << i;

                                           if (usbd_status & tmp) {

                                                                             dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);

                                                                              /* Clear the interrupt bit by setting it to 1 */

                                                                               udc_write(tmp, S3C2410_UDC_EP_INT_REG);

                                                                             s3c2410_udc_handle_ep(&dev->ep[i]); //处理对应端点

                                                }

                           }

                dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", IRQ_USBD);

                /* Restore old index */

                 udc_write(idx, S3C2410_UDC_INDEX_REG);

                 spin_unlock_irqrestore(&dev->lock, flags);

                  return IRQ_HANDLED;

}

4.  端点操作函数

端点操作函数是UDC驱动的基础,因为大部分的动作其实都是和端点相关的,如数据传输等。

首先来看中断函数中涉及的两个函数,一个是端点0的处理函数,一个是其他端点的处理函数。

static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)

{

               u32 ep0csr;

              struct s3c2410_ep *ep = &dev->ep[0];

               struct s3c2410_request *req;

               struct usb_ctrlrequest crq;

               if (list_empty(&ep->queue))

                            req = NULL;

              else

                          req = list_entry(ep->queue.next, struct s3c2410_request, queue);

             /* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to

               * S3C2410_UDC_EP0_CSR_REG when index is zero */

              udc_write(0, S3C2410_UDC_INDEX_REG);

            ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);

            dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n", ep0csr, ep0states[dev->ep0state]);

            /* clear stall status */

             if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) { //清除STALL状态

                              s3c2410_udc_nuke(dev, ep, -EPIPE);

                              dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");

                              s3c2410_udc_clear_ep0_sst(base_addr);

                               dev->ep0state = EP0_IDLE;

                                             return;

                 }

                /* clear setup end */

                if (ep0csr & S3C2410_UDC_EP0_CSR_SE) {

                              dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");

                              s3c2410_udc_nuke(dev, ep, 0);

                               s3c2410_udc_clear_ep0_se(base_addr);

                                dev->ep0state = EP0_IDLE;

                 }

                /*端点0的状态处理*/

               switch (dev->ep0state) {

                               case EP0_IDLE:

                                                s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr); //在这个函数中会调用上层提供的gadget_driver中实现的setup函数来处理控制数据包

                                                break;

                                case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ //向主机发送数据

                                              dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");

                                              if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) {

                                                                s3c2410_udc_write_fifo(ep, req); //写UDC FIFO

                                              }

                                               break;

                               case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ //从主机接收数据

                                              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:

                                             dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");

                                             dev->ep0state = EP0_IDLE;

                                               break;

                            case EP0_STALL:

                                        dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");

                                       dev->ep0state = EP0_IDLE;

                                        break;

            }

}

/*

* handle_ep - Manage I/O endpoints

其他端点的处理函数,主要是数据发送和接收

*/

static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)

{

                       struct s3c2410_request *req;

                       int is_in = ep->bEndpointAddress & USB_DIR_IN;

                       u32 ep_csr1;

                       u32 idx;

                     if (likely (!list_empty(&ep->queue))) //取出申请

                                         req = list_entry(ep->queue.next, struct s3c2410_request, queue);

                     else

                                      req = NULL;

                   idx = ep->bEndpointAddress & 0x7F; //端点地址

                  if (is_in) { //向主机发送数据

                                     udc_write(idx, S3C2410_UDC_INDEX_REG);

                                    ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);

                                    dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n", idx, ep_csr1, req ? 1 : 0);

                                    if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) {

                                                      dprintk(DEBUG_VERBOSE, "st\n");

                                                       udc_write(idx, S3C2410_UDC_INDEX_REG);

                                                       udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL, S3C2410_UDC_IN_CSR1_REG);

                                                                 return;

                                     }

                                   if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) {

                                                   s3c2410_udc_write_fifo(ep,req);

                                    }

                  }

                 else  //从主机接收数据

                   {

                                  udc_write(idx, S3C2410_UDC_INDEX_REG);

                                  ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG);

                                   dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1);

                                  if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) {

                                                         udc_write(idx, S3C2410_UDC_INDEX_REG);

                                                         udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL, S3C2410_UDC_OUT_CSR1_REG);

                                                         return;

                                    }

                                 if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) {

                                                      s3c2410_udc_read_fifo(ep,req);

                                 }

                 }

}

端点操作函数集:端点的基本操作函数

static const struct usb_ep_ops s3c2410_ep_ops = {

                        .enable = s3c2410_udc_ep_enable, //端点使能

                        .disable = s3c2410_udc_ep_disable, //关闭端点

                        .alloc_request = s3c2410_udc_alloc_request, //分配一个请求

                          .free_request = s3c2410_udc_free_request, //释放请求

                              .queue = s3c2410_udc_queue, //向端点提交一个请求

                            .dequeue = s3c2410_udc_dequeue, //从端点请求队列中删除一个请求

                          .set_halt = s3c2410_udc_set_halt,

};

主要分析queue这个函数,因为上层主要和这个函数打交道,接收或发送数据都需要对应的端点队列提交请求

            static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)

{

                  struct s3c2410_request *req = to_s3c2410_req(_req);

                  struct s3c2410_ep *ep = to_s3c2410_ep(_ep);

                   struct s3c2410_udc *dev;

                   u32 ep_csr = 0;

                  int fifo_count = 0;

                     unsigned long flags;

                 if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {

                                dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__);

                                return -EINVAL;

                   }

                dev = ep->dev;

                if (unlikely (!dev->driver|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {

                                  return -ESHUTDOWN;

                    }

                local_irq_save (flags);

               if (unlikely(!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) {

                              if (!_req)

                                            dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);

                              else {

                                            dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n", __func__, !_req->complete,!_req->buf, !list_empty(&req->queue));

                                    }

                              local_irq_restore(flags);

                             return -EINVAL;

                }

              _req->status = -EINPROGRESS;

              _req->actual = 0;

            dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n", __func__, ep->bEndpointAddress, _req->length);

            if (ep->bEndpointAddress) { //设置端点号

                            udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG);

                           ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN) ? S3C2410_UDC_IN_CSR1_REG: S3C2410_UDC_OUT_CSR1_REG);

                            fifo_count = s3c2410_udc_fifo_count_out();

           }

          else

            {

                           udc_write(0, S3C2410_UDC_INDEX_REG);

                            ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);

                             fifo_count = s3c2410_udc_fifo_count_out();

             }

            /* kickstart this i/o queue? */

              if (list_empty(&ep->queue) && !ep->halted) { //该端点队列为空,且没有关闭,则直接完成申请

                                     if (ep->bEndpointAddress == 0 /* ep0 */) { //端点0

                                                               switch (dev->ep0state) {

                                                                               case EP0_IN_DATA_PHASE:

                                                                                             if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY) && s3c2410_udc_write_fifo(ep,req)) {

                                                                                                                   dev->ep0state = EP0_IDLE;

                                                                                                                     req = NULL;

                                                                                                }

                                                                                                break;

                                                                                case EP0_OUT_DATA_PHASE:

                                                                                                if ((!_req->length)|| ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)&& s3c2410_udc_read_fifo(ep,req))) {

                                                                                                                               dev->ep0state = EP0_IDLE;

                                                                                                                       req = NULL;

                                                                                                 }

                                                                                                break;

                                                                                default:

                                                                                               local_irq_restore(flags);

                                                                                               return -EL2HLT;

                                                                  }

                                        }

                                      else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 && (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) && s3c2410_udc_write_fifo(ep, req)) {

                                                               req = NULL;

                                     }

                                      else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)&& fifo_count&&s3c2410_udc_read_fifo(ep, req)) {

                                                               req = NULL;

                                      }

                }

           /* pio or dma irq handler advances the queue. */

            if (likely (req != 0))

                            list_add_tail(&req->queue, &ep->queue); //加入队列,等待中断处理

            local_irq_restore(flags);

            dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);

            return 0;

}

          UDC驱动中还有一个重要函数,在数据传输或接收完成,即一个请求完成之后,会调用这个请求的完成函数来通知上层驱动。

static void s3c2410_udc_done(struct s3c2410_ep *ep,  struct s3c2410_request *req,  int status) 

             unsigned halted = ep->halted;

             list_del_init(&req->queue);  //将这个请求从端点请求队列中删除

             if (likely (req->req.status == -EINPROGRESS)) 
                           req->req.status = status; //返回完成状态 
            else 
                           status = req->req.status;

            ep->halted = 1; 
            req->req.complete(&ep->ep, &req->req); 
            ep->halted = halted; 
}

 

 

总结:

       UDC设备驱动层的源码就分析得差不多了,其他很多函数都是操作寄存器,与UDC设备密切相关,但总的来说完成的功能都是一致的。可以发现,在UDC设备驱动层主要需要做以下几个工作:

1. 对usb_gadget、usb_ep、usb_request三个标准数据结构进行封装,根据自己UDC的一些设备特性,设计对应的自己的数据结构;

2. 实现platform_driver数据结构中的函数,将UDC设备驱动向platform系统进行注册;

3. 实现usb_gadget_ops函数集,这些函数主要是操作UDC设备的一些特性(针对设备);

4. 实现usb_ep_ops函数集,这些函数主要是操作端点的功能,如请求分配和提交等(针对端点);

5. 实现UDC设备的中断处理函数,这个函数基本上就是UDC设备驱动的核心;

6.实现上层功能驱动注册接口函数:

     int usb_gadget_register_driver(struct usb_gadget_driver *driver)

     int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值