⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 s3c2410_udc.c

📁 S3C2410 USBD驱动程序 s3c2410_udc
💻 C
📖 第 1 页 / 共 4 页
字号:
   return container_of(gadget, struct s3c2410_udc, gadget);
}

static inline struct s3c2410_request *to_s3c2410_req(struct usb_request *req)
{
   return container_of(req, struct s3c2410_request, req);
}

/*
 *   s3c2410_udc_ep_enable
 */
static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
             const struct usb_endpoint_descriptor *desc)
{
   struct s3c2410_udc   *dev;
   struct s3c2410_ep   *ep;
   u32         max, tmp;
   unsigned long      flags;
   u32         csr1,csr2;
   u32         int_en_reg;

   ep = to_s3c2410_ep(_ep);

   if (!_ep || !desc || ep->desc
         || _ep->name == ep0name
        || desc->bDescriptorType != USB_DT_ENDPOINT)
      return -EINVAL;

   dev = ep->dev;
   if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
      return -ESHUTDOWN;
   max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff;

   local_irq_save (flags);
   _ep->maxpacket = max & 0x7ff;
   ep->desc = desc;
   ep->halted = 0;
   ep->bEndpointAddress = desc->bEndpointAddress;

   /* set max packet */
   udc_write(ep->num, S3C2410_UDC_INDEX_REG);
  udc_write(max >> 3, S3C2410_UDC_MAXP_REG);

   /* set type, direction, address; reset fifo counters */
   if (desc->bEndpointAddress & USB_DIR_IN) {
      csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT;
     csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN;

      udc_write(ep->num, S3C2410_UDC_INDEX_REG);
      udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
      udc_write(ep->num, S3C2410_UDC_INDEX_REG);
      udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);
   } else {
      /* don't flush in fifo or it will cause endpoint interrupt */
      csr1 = S3C2410_UDC_ICSR1_CLRDT;
      csr2 = S3C2410_UDC_ICSR2_DMAIEN;

     udc_write(ep->num, S3C2410_UDC_INDEX_REG);
      udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
      udc_write(ep->num, S3C2410_UDC_INDEX_REG);
      udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);

      csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
     csr2 = S3C2410_UDC_OCSR2_DMAIEN;

      udc_write(ep->num, S3C2410_UDC_INDEX_REG);
      udc_write(csr1, S3C2410_UDC_OUT_CSR1_REG);
      udc_write(ep->num, S3C2410_UDC_INDEX_REG);
      udc_write(csr2, S3C2410_UDC_OUT_CSR2_REG);
  }

   /* enable irqs */
   int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
   udc_write(int_en_reg | (1 << ep->num), S3C2410_UDC_EP_INT_EN_REG);

   /* print some debug message */
   tmp = desc->bEndpointAddress;
   dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
       _ep->name,ep->num, tmp,
       desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);

   local_irq_restore (flags);
   s3c2410_udc_set_halt(_ep, 0);

   return 0;
}

/*
 * s3c2410_udc_ep_disable
 */
static int s3c2410_udc_ep_disable(struct usb_ep *_ep)
{
   struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
   unsigned long flags;
   u32 int_en_reg;

   if (!_ep || !ep->desc) {
     dprintk(DEBUG_NORMAL, "%s not enabled\n",
         _ep ? ep->ep.name : NULL);
      return -EINVAL;
   }

   local_irq_save(flags);

   dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name);

  ep->desc = NULL;
   ep->halted = 1;

   s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN);

   /* disable irqs */
   int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
   udc_write(int_en_reg & ~(1<<ep->num), S3C2410_UDC_EP_INT_EN_REG);

  local_irq_restore(flags);

  dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name);

   return 0;
}

/*
 * s3c2410_udc_alloc_request
 */
static struct usb_request *
s3c2410_udc_alloc_request(struct usb_ep *_ep, gfp_t mem_flags)
{
   struct s3c2410_request *req;

   dprintk(DEBUG_VERBOSE,"%s(%p,%d)\n", __func__, _ep, mem_flags);

   if (!_ep)
      return NULL;

   req = kzalloc (sizeof(struct s3c2410_request), mem_flags);
   if (!req)
      return NULL;
   INIT_LIST_HEAD (&req->queue);
   return &req->req;
}

/*
 * s3c2410_udc_free_request
 */
static void
s3c2410_udc_free_request(struct usb_ep *_ep, struct usb_request *_req)
{
   struct s3c2410_ep   *ep = to_s3c2410_ep(_ep);
   struct s3c2410_request   *req = to_s3c2410_req(_req);

   dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);

   if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
      return;

   WARN_ON (!list_empty (&req->queue));
   kfree(req);
}

/*
 *   s3c2410_udc_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 */) {
         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;
}

/*
 *   s3c2410_udc_dequeue
 */
static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
   struct s3c2410_ep   *ep = to_s3c2410_ep(_ep);
   struct s3c2410_udc   *udc;
  int         retval = -EINVAL;
   unsigned long      flags;
   struct s3c2410_request   *req = NULL;

   dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);
   if (!the_controller->driver)
      return -ESHUTDOWN;

   if (!_ep || !_req)
      return retval;

   udc = to_s3c2410_udc(ep->gadget);

  local_irq_save (flags);

   list_for_each_entry (req, &ep->queue, queue) {
      if (&req->req == _req) {
         list_del_init (&req->queue);
         _req->status = -ECONNRESET;
         retval = 0;
         break;
      }
   }

   if (retval == 0) {
      dprintk(DEBUG_VERBOSE,
         "dequeued req %p from %s, len %d buf %p\n",
         req, _ep->name, _req->length, _req->buf);
      s3c2410_udc_done(ep, req, -ECONNRESET);
   }

   local_irq_restore (flags);
   return retval;
}

/*
 * s3c2410_udc_set_halt
 */
static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value)
{
   struct s3c2410_ep   *ep = to_s3c2410_ep(_ep);
   u32         ep_csr = 0;
   unsigned long      flags;
   u32         idx;

  if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
      dprintk(DEBUG_NORMAL, "%s: inval 2\n", __func__);
      return -EINVAL;
   }

   local_irq_save (flags);
   idx = ep->bEndpointAddress & 0x7F;

   if (idx == 0) {
      s3c2410_udc_set_ep0_ss(base_addr);
      s3c2410_udc_set_ep0_de_out(base_addr);
   } else {
      udc_write(idx, S3C2410_UDC_INDEX_REG);
      ep_csr = udc_read((ep->bEndpointAddress &USB_DIR_IN)
            ? S3C2410_UDC_IN_CSR1_REG
            : S3C2410_UDC_OUT_CSR1_REG);

      if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
         if (value)
            udc_write(ep_csr | S3C2410_UDC_ICSR1_SENDSTL,
               S3C2410_UDC_IN_CSR1_REG);
         else {
            ep_csr &= ~S3C2410_UDC_ICSR1_SENDSTL;
            udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG);
            ep_csr |= S3C2410_UDC_ICSR1_CLRDT;
            udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG);
         }
      } else {
         if (value)
            udc_write(ep_csr | S3C2410_UDC_OCSR1_SENDSTL,
               S3C2410_UDC_OUT_CSR1_REG);
         else {
           ep_csr &= ~S3C2410_UDC_OCSR1_SENDSTL;
            udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG);
            ep_csr |= S3C2410_UDC_OCSR1_CLRDT;
            udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG);
         }
      }
   }

   ep->halted = value ? 1 : 0;
  local_irq_restore (flags);

   return 0;
}
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,
};

/*------------------------- usb_gadget_ops ----------------------------------*/

/*
*   s3c2410_udc_get_frame
 */
static int s3c2410_udc_get_frame(struct usb_gadget *_gadget)
{
   int tmp;

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

   tmp = udc_read(S3C2410_UDC_FRAME_NUM2_REG) << 8;
   tmp |= udc_read(S3C2410_UDC_FRAME_NUM1_REG);
  return tmp;
}

/*
 *   s3c2410_udc_wakeup
 */
static int s3c2410_udc_wakeup(struct usb_gadget *_gadget)
{
   dprintk(DEBUG_NORMAL, "%s()\n", __func__);
   return 0;
}

/*
 *   s3c2410_udc_set_selfpowered
 */
static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value)
{
   struct s3c2410_udc *udc = to_s3c2410_udc(gadget);

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

   if (value)
      udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
   else
      udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);

   return 0;
}

static void s3c2410_udc_disable(struct s3c2410_udc *dev);
static void s3c2410_udc_enable(struct s3c2410_udc *dev);

static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on)
{
   dprintk(DEBUG_NORMAL, "%s()\n", __func__);

   if (udc_info && udc_info->udc_command) {
      if (is_on)
         s3c2410_udc_enable(udc);
     else {
         if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
            if (udc->driver && udc->driver->disconnect)
               udc->driver->disconnect(&udc->gadget);

        }
         s3c2410_udc_disable(udc);
      }
   }
  else
      return -EOPNOTSUPP;

   return 0;
}

static int s3c2410_udc_vbus_session(struct usb_gadget *gadget, int is_active)
{
   struct s3c2410_udc *udc = to_s3c2410_udc(gadget);

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

   udc->vbus = (is_active != 0);
   s3c2410_udc_set_pullup(udc, is_active);
   return 0;
}

static int s3c2410_udc_pullup(struct usb_gadget *gadget, int is_on)
{
   struct s3c2410_udc *udc = to_s3c2410_udc(gadget);

   dprintk(DEBUG_NORMAL, "%s()\n", __func__);
   s3c2410_udc_set_pullup(udc, is_on ? 0 : 1);
   return 0;
}

static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev)
{
   struct s3c2410_udc   *dev = _dev;
   unsigned int      value;

  dprintk(DEBUG_NORMAL, "%s()\n", __func__);
   value = s3c2410_gpio_getpin(udc_info->vbus_pin);

   if (udc_info->vbus_pin_inverted)
      value = !value;
   if (value != dev->vbus)
      s3c2410_udc_vbus_session(&dev->gadget, value);

   return IRQ_HANDLED;
}

static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
{
   dprintk(DEBUG_NORMAL, "%s()\n", __func__);

   if (udc_info && udc_info->vbus_draw) {
      udc_info->vbus_draw(ma);
      return 0;
  }

   return -ENOTSUPP;
}

static const struct usb_gadget_ops s3c2410_ops = {
   .get_frame      = s3c2410_udc_get_frame,
   .wakeup         = s3c2410_udc_wakeup,
   .set_selfpowered   = s3c2410_udc_set_selfpowered,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -