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

📄 usb_ohci.c

📁 看到最近大家都关心 usbhost 的实现, 论坛上能找到的代码仅是一些简单的 demo , 完整的源码级的协议层是找不到的 我就贡献一把, 将我前一段时间移植成功的 USBHost 代码奉上 注
💻 C
📖 第 1 页 / 共 4 页
字号:
    ed->hwNextED = 0;
    if (ohci->ed_controltail == NULL) {
      writel (ed, &ohci->regs->ed_controlhead);
    } else {
      ohci->ed_controltail->hwNextED = m32_swap ((unsigned long)ed);
    }
    ed->ed_prev = ohci->ed_controltail;
    if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
      !ohci->ed_rm_list[1] && !ohci->sleeping) {
      ohci->hc_control |= OHCI_CTRL_CLE;
      writel (ohci->hc_control, &ohci->regs->control);
    }
    ohci->ed_controltail = edi;
    break;

  case PIPE_BULK:
    ed->hwNextED = 0;
    if (ohci->ed_bulktail == NULL) {
      writel (ed, &ohci->regs->ed_bulkhead);
    } else {
      ohci->ed_bulktail->hwNextED = m32_swap ((unsigned long)ed);
    }
    ed->ed_prev = ohci->ed_bulktail;
    if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
      !ohci->ed_rm_list[1] && !ohci->sleeping) {
      ohci->hc_control |= OHCI_CTRL_BLE;
      writel (ohci->hc_control, &ohci->regs->control);
    }
    ohci->ed_bulktail = edi;
    break;

  case PIPE_INTERRUPT:
    load = ed->int_load;
    interval = ep_2_n_interval (ed->int_period);
    ed->int_interval = interval;
    int_branch = ep_int_ballance (ohci, interval, load);
    ed->int_branch = int_branch;

    for (i = 0; i < ep_rev (6, interval); i += inter) {
      inter = 1;
      for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]);
        (*ed_p != 0) && (((ed_t *)ed_p)->int_interval >= interval);
        ed_p = &(((ed_t *)ed_p)->hwNextED))
      {   inter = ep_rev (6, ((ed_t *)ed_p)->int_interval); }

      ed->hwNextED = *ed_p;
      *ed_p = m32_swap((unsigned long)ed);
    }
    break;
  }
  return 0;
}

/*-------------------------------------------------------------------------*/

/* scan the periodic table to find and unlink this ED */
static void periodic_unlink ( struct ohci *ohci, volatile struct ed *ed,
    unsigned index, unsigned period)
{
  for (; index < NUM_INTS; index += period) {
    unsigned int *ed_p = &ohci->hcca->int_table [index];

    /* ED might have been unlinked through another path */
    while (*ed_p != 0) {
      if (((struct ed *)m32_swap ((unsigned long)ed_p)) == ed) {
        *ed_p = ed->hwNextED;
        break;
      }
      ed_p = & (((struct ed *)m32_swap ((unsigned long)ed_p))->hwNextED);
    }
  }
}

/* unlink an ed from one of the HC chains.
 * just the link to the ed is unlinked.
 * the link from the ed still points to another operational ed or 0
 * so the HC can eventually finish the processing of the unlinked ed */

static int ep_unlink (ohci_t *ohci, ed_t *edi)
{
  volatile ed_t *ed = edi;
  int i;

  ed->hwINFO |= m32_swap (OHCI_ED_SKIP);

  switch (ed->type) {
  case PIPE_CONTROL:
    if (ed->ed_prev == NULL) {
      if (!ed->hwNextED) {
        ohci->hc_control &= ~OHCI_CTRL_CLE;
        writel (ohci->hc_control, &ohci->regs->control);
      }
      writel (m32_swap (*((unsigned int *)&ed->hwNextED)), &ohci->regs->ed_controlhead);
    } else {
      ed->ed_prev->hwNextED = ed->hwNextED;
    }
    if (ohci->ed_controltail == ed) {
      ohci->ed_controltail = ed->ed_prev;
    } else {
      ((ed_t *)m32_swap (*((unsigned int *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
    }
    break;

  case PIPE_BULK:
    if (ed->ed_prev == NULL) {
      if (!ed->hwNextED) {
        ohci->hc_control &= ~OHCI_CTRL_BLE;
        writel (ohci->hc_control, &ohci->regs->control);
      }
      writel (m32_swap (*((unsigned int *)&ed->hwNextED)), &ohci->regs->ed_bulkhead);
    } else {
      ed->ed_prev->hwNextED = ed->hwNextED;
    }
    if (ohci->ed_bulktail == ed) {
      ohci->ed_bulktail = ed->ed_prev;
    } else {
      ((ed_t *)m32_swap (*((unsigned int *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
    }
    break;

  case PIPE_INTERRUPT:
    periodic_unlink (ohci, ed, 0, 1);
    for (i = ed->int_branch; i < 32; i += ed->int_interval)
        ohci->ohci_int_load[i] -= ed->int_load;
    break;
  }
  ed->state = ED_UNLINK;
  return 0;
}

/*-------------------------------------------------------------------------*/

/* add/reinit an endpoint; this should be done once at the
 * usb_set_configuration command, but the USB stack is a little bit
 * stateless so we do it at every transaction if the state of the ed
 * is ED_NEW then a dummy td is added and the state is changed to
 * ED_UNLINK in all other cases the state is left unchanged the ed
 * info fields are setted anyway even though most of them should not
 * change
 */
static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe,
    int interval, int load)
{
  td_t *td;
  ed_t *ed_ret;
  volatile ed_t *ed;

  ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) |
      (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))];

  if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
    err("ep_add_ed: pending delete");
    /* pending delete request */
    return NULL;
  }

  if (ed->state == ED_NEW) {
    ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */
    /* dummy td; end of td list for ed */
    td = td_alloc (usb_dev);
    ed->hwTailP = m32_swap ((unsigned long)td);
    ed->hwHeadP = ed->hwTailP;
    ed->state = ED_UNLINK;
    ed->type = usb_pipetype (pipe);
    ohci_dev.ed_cnt++;
  }

  ed->hwINFO = m32_swap (usb_pipedevice (pipe)
      | usb_pipeendpoint (pipe) << 7
      | (usb_pipeisoc (pipe)? 0x8000: 0)
      | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000))
      | usb_pipeslow (pipe) << 13
      | usb_maxpacket (usb_dev, pipe) << 16);

  if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
    ed->int_period = interval;
    ed->int_load = load;
  }

  return ed_ret;
}

/*-------------------------------------------------------------------------*
 * TD handling functions
 *-------------------------------------------------------------------------*/

/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */

static void td_fill (ohci_t *ohci, unsigned int info,
  void *data, int len,
  struct usb_device *dev, int index, urb_priv_t *urb_priv)
{
  volatile td_t  *td, *td_pt;
#ifdef OHCI_FILL_TRACE
  int i;
#endif

  if (index > urb_priv->length) {
    err("index > length");
    return;
  }
  /* use this td as the next dummy */
  td_pt = urb_priv->td [index];
  td_pt->hwNextTD = 0;

  /* fill the old dummy TD */
  td = urb_priv->td [index] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf);

  td->ed = urb_priv->ed;
  td->next_dl_td = NULL;
  td->index = index;
  td->data = (unsigned int)data;
#ifdef OHCI_FILL_TRACE
  if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) {
    for (i = 0; i < len; i++)
    printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]);
    printf("\n");
  }
#endif
  if (!len)
    data = 0;

  td->hwINFO = m32_swap (info);
  td->hwCBP = m32_swap ((unsigned long)data);
  if (data)
  { td->hwBE = m32_swap ((unsigned long)data + len - 1);  }
  else
  { td->hwBE = 0; }

  td->hwNextTD = m32_swap ((unsigned long)td_pt);

  /* append to queue */
  td->ed->hwTailP = td->hwNextTD;
}

/*-------------------------------------------------------------------------*/

/* prepare all TDs of a transfer */

static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer,
  int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval)
{
  ohci_t *ohci = &gohci;
  int data_len = transfer_len;
  unsigned char *data;
  int cnt = 0;
  unsigned int info = 0;
  unsigned int toggle = 0;

  /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */
  if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {
    toggle = TD_T_TOGGLE;
  } else {
    toggle = TD_T_DATA0;
    usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1);
  }
  urb->td_cnt = 0;
  if (data_len)
    data = buffer;
  else
    data = 0;

  switch (usb_pipetype (pipe)) {
  case PIPE_BULK:
    info = usb_pipeout (pipe)?
      TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;
    while(data_len > 4096) {
      td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb);
      data += 4096; data_len -= 4096; cnt++;
    }
    info = usb_pipeout (pipe)?
      TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
    td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb);
    cnt++;

    if (!ohci->sleeping)
      writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
    break;

  case PIPE_CONTROL:
    info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
    td_fill (ohci, info, setup, 8, dev, cnt++, urb);
    if (data_len > 0) {
      info = usb_pipeout (pipe)?
        TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;
      /* NOTE:  mishandles transfers >8K, some >4K */
      td_fill (ohci, info, data, data_len, dev, cnt++, urb);
    }
    info = usb_pipeout (pipe)?
      TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
    td_fill (ohci, info, data, 0, dev, cnt++, urb);
    if (!ohci->sleeping)
      writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
    break;

  case PIPE_INTERRUPT:
    info = usb_pipeout (urb->pipe)?
      TD_CC | TD_DP_OUT | toggle:
      TD_CC | TD_R | TD_DP_IN | toggle;
    td_fill (ohci, info, data, data_len, dev, cnt++, urb);
    break;
  }
  if (urb->length != cnt)
    dbg("TD LENGTH %d != CNT %d", urb->length, cnt);
}

/*-------------------------------------------------------------------------*
 * Done List handling functions
 *-------------------------------------------------------------------------*/

/* calculate the transfer length and update the urb */

static void dl_transfer_length(td_t * td)
{
  unsigned int tdBE, tdCBP;
  urb_priv_t *lurb_priv = td->ed->purb;

  //tdINFO = m32_swap (td->hwINFO);
  tdBE   = m32_swap (td->hwBE);
  tdCBP  = m32_swap (td->hwCBP);

  if (!(usb_pipetype (lurb_priv->pipe) == PIPE_CONTROL &&
      ((td->index == 0) || (td->index == lurb_priv->length - 1)))) {
    if (tdBE != 0) {
      if (td->hwCBP == 0)
        lurb_priv->actual_length += tdBE - td->data + 1;
      else
        lurb_priv->actual_length += tdCBP - td->data;
    }
  }
}

/*-------------------------------------------------------------------------*/

/* replies to the request have to be on a FIFO basis so
 * we reverse the reversed done-list */

static td_t * dl_reverse_done_list (ohci_t *ohci)
{
  unsigned int td_list_hc;
  td_t *td_rev = NULL;
  td_t *td_list = NULL;
  urb_priv_t *lurb_priv = NULL;

  td_list_hc = m32_swap (ohci->hcca->done_head) & 0xfffffff0;
  ohci->hcca->done_head = 0;

  while (td_list_hc) {
    td_list = (td_t *)td_list_hc;

    if (TD_CC_GET (m32_swap (td_list->hwINFO))) {
      lurb_priv = td_list->ed->purb;
      dbg(" USB-error/status: %x : %p",
          TD_CC_GET (m32_swap (td_list->hwINFO)), td_list);
      if (td_list->ed->hwHeadP & m32_swap (0x1)) {
        if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) {
          td_list->ed->hwHeadP =
            (lurb_priv->td[lurb_priv->length - 1]->hwNextTD & m32_swap (0xfffffff0)) |
                  (td_list->ed->hwHeadP & m32_swap (0x2));
          lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1;
        } else
          td_list->ed->hwHeadP &= m32_swap (0xfffffff2);
      }
#ifdef CONFIG_MPC5200
      td_list->hwNextTD = 0;
#endif
    }

    td_list->next_dl_td = td_rev;
    td_rev = td_list;
    td_list_hc = m32_swap (td_list->hwNextTD) & 0xfffffff0;
  }
  return td_list;
}

/*-------------------------------------------------------------------------*/

/* td done list */
static int dl_done_list (ohci_t *ohci, td_t *td_list)
{
  td_t *td_list_next = NULL;
  ed_t *ed;
  int cc = 0;
  int stat = 0;
  /* urb_t *urb; */
  urb_priv_t *lurb_priv;
  unsigned int tdINFO, edHeadP, edTailP;

  while (td_list) {
    td_list_next = td_list->next_dl_td;

    tdINFO = m32_swap (td_list->hwINFO);

    ed = td_list->ed;
    lurb_priv = ed->purb;

    dl_transfer_length(td_list);

    /* error code of transfer */

⌨️ 快捷键说明

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