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

📄 usb_ohci.c

📁 看到最近大家都关心 usbhost 的实现, 论坛上能找到的代码仅是一些简单的 demo , 完整的源码级的协议层是找不到的 我就贡献一把, 将我前一段时间移植成功的 USBHost 代码奉上 注
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
 * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200 and PCI bus.
 *
 * Interrupt support is added. Now, it has been tested
 * on ULI1575 chip and works well with USB keyboard.
 *
 * (C) Copyright 2007
 * Zhang Wei, Freescale Semiconductor, Inc. <wei.zhang@freescale.com>
 *
 * (C) Copyright 2003
 * Gary Jennejohn, DENX Software Engineering <gj@denx.de>
 *
 * Note: Much of this code has been derived from Linux 2.4
 * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
 * (C) Copyright 2000-2002 David Brownell
 *
 * Modified for the MP2USB by (C) Copyright 2005 Eric Benard
 * ebenard@eukrea.com - based on s3c24x0's driver
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 *
 */
/*
 * IMPORTANT NOTES
 * 1 - Read doc/README.generic_usb_ohci
 * 2 - this driver is intended for use with USB Mass Storage Devices
 *     (BBB) and USB keyboard. There is NO support for Isochronous pipes!
 * 2 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG
 *     to activate workaround for bug #41 or this driver will NOT work!
 */


#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <absacc.h>
#include "..\hardware\LPC2468.h"  /* 24xx Peripheral Registers */
#include "..\rtxOS\rtl.h"


void os_dly_waitms(unsigned int ms);

#include "usb.h"
#include "usbhost.h"
#include "usb_ohci.h"
unsigned int ohci_init( void );


#define OHCI_USE_NPS    /* force NoPowerSwitching mode */

/* For initializing controller (mask in an HCFS mode too) */
#define OHCI_CONTROL_INIT \
  (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE


#define readl(a)     (*((volatile unsigned long *)(a)))
#define writel(a, b) (*((volatile unsigned long *)(b)) = (unsigned long)a)

#define min_t(x,y) ((x) < (y) ? (x): (y))



#define dbg(format, arg...) 

#define err(format, arg...) 

#define info(format, arg...) 

#define m16_swap(x) swap_16(x)
#define m32_swap(x) swap_32(x)

/* global ohci_t */
static ohci_t gohci;
/* this must be aligned to a 256 byte boundary */
struct ohci_hcca ghcca[32]  __at(USB_OHCI_ADDR);
/* a pointer to the aligned storage */
struct ohci_hcca *phcca;
/* this allocates EDs for all possible endpoints */
struct ohci_device ohci_dev;
/* RHSC flag */
int got_rhsc;
/* device which was disconnected */
struct usb_device *devgone;

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

/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
 * The erratum (#4) description is incorrect.  AMD's workaround waits
 * till some bits (mostly reserved) are clear; ok for all revs.
 */
#define OHCI_QUIRK_AMD756 0xabcd
#define read_roothub(hc, registera, mask) ({ \
  unsigned int temp = readl (&hc->regs->roothub.registera); \
  if (hc->flags & OHCI_QUIRK_AMD756) \
    while (temp & mask) \
      temp = readl (&hc->regs->roothub.registera); \
  temp; })

static unsigned int roothub_a (struct ohci *hc)
{ unsigned int temp = readl (&hc->regs->roothub.a);
    if (hc->flags & OHCI_QUIRK_AMD756)
    { while (temp & 0xfc0fe000)
      { temp = readl (&hc->regs->roothub.a);  }
    }
    return temp; 
}
static __inline unsigned int roothub_b (struct ohci *hc)
{ return readl (&hc->regs->roothub.b); }
static __inline unsigned int roothub_status (struct ohci *hc)
{ return readl (&hc->regs->roothub.status); }

static unsigned int roothub_portstatus (struct ohci *hc, int i)
{ unsigned int temp = readl (&hc->regs->roothub.portstatus [i]);
    if (hc->flags & OHCI_QUIRK_AMD756)
    { while (temp & 0xffe0fce0)
      { temp = readl (&hc->regs->roothub.portstatus [i]);  }
    }
    return temp; 
}

/* forward declaration */
static int hc_interrupt (void);
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);

#define NUM_TD 64
/* +1 so we can align the storage */
td_t gtd[NUM_TD+1];
/* pointers to aligned storage */
td_t *ptd;

/* TDs ... */
static __inline struct td * td_alloc (struct usb_device *usb_dev)
{
  int i;
  struct td  *td;

  td = NULL;
  for (i = 0; i < NUM_TD; i++)
  {
    if (ptd[i].usb_dev == NULL)
    {
      td = &ptd[i];
      td->usb_dev = usb_dev;
      break;
    }
  }

  return td;
}

__inline void ed_free (struct ed *ed)
{
  ed->usb_dev = NULL;
}
/*-------------------------------------------------------------------------*
 * URB support functions
 *-------------------------------------------------------------------------*/

/* free HCD-private data associated with this URB */

static void urb_free_priv (urb_priv_t * urb)
{
  int    i;
  int    last;
  struct td  * td;

  last = urb->length - 1;
  if (last >= 0) {
    for (i = 0; i <= last; i++) {
      td = urb->td[i];
      if (td) {
        td->usb_dev = NULL;
        urb->td[i] = NULL;
      }
    }
  }
  free(urb);
}


/*-------------------------------------------------------------------------*
 * Interface functions (URB)
 *-------------------------------------------------------------------------*/

/* get a transfer request */

int sohci_submit_job(urb_priv_t *urb, struct devrequest *setup)
{
  ohci_t *ohci;
  ed_t * ed;
  urb_priv_t *purb_priv = urb;
  int i, size = 0;
  struct usb_device *dev = urb->dev;
  unsigned long pipe = urb->pipe;
  void *buffer = urb->transfer_buffer;
  int transfer_len = urb->transfer_buffer_length;
  int interval = urb->interval;

  ohci = &gohci;

  /* when controller's hung, permit only roothub cleanup attempts
   * such as powering down ports */
  if (ohci->disabled) {
    err("sohci_submit_job: EPIPE");
    return -1;
  }

  /* we're about to begin a new transaction here so mark the URB unfinished */
  urb->finished = 0;

  /* every endpoint has a ed, locate and fill it */
  ed = ep_add_ed (dev, pipe, interval, 1);
  if ( !ed ) {
    err("sohci_submit_job: ENOMEM");
    return -1;
  }

  /* for the private part of the URB we need the number of TDs (size) */
  switch (usb_pipetype (pipe)) {
    case PIPE_BULK: /* one TD for every 4096 Byte */
      size = (transfer_len - 1) / 4096 + 1;
      break;
    case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
      size = (transfer_len == 0)? 2:
            (transfer_len - 1) / 4096 + 3;
      break;
    case PIPE_INTERRUPT: /* 1 TD */
      size = 1;
      break;
  }

  ed->purb = urb;

  if (size >= (N_URB_TD - 1)) {
    err("need %d TDs, only have %d", size, N_URB_TD);
    return -1;
  }
  purb_priv->pipe = pipe;

  /* fill the private part of the URB */
  purb_priv->length = size;
  purb_priv->ed = ed;
  purb_priv->actual_length = 0;

  /* allocate the TDs */
  /* note that td[0] was allocated in ep_add_ed */
  for (i = 0; i < size; i++) {
    purb_priv->td[i] = td_alloc (dev);
    if (!purb_priv->td[i]) {
      purb_priv->length = i;
      urb_free_priv (purb_priv);
      err("sohci_submit_job: ENOMEM");
      return -1;
    }
  }

  if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
    urb_free_priv (purb_priv);
    err("sohci_submit_job: EINVAL");
    return -1;
  }

  /* link the ed into a chain if is not already */
  if (ed->state != ED_OPER)
    ep_link (ohci, ed);

  /* fill the TDs and link it to the ed */
  td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval);

  return 0;
}

static __inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb)
{
  struct ohci_regs *regs = hc->regs;

  switch (usb_pipetype (urb->pipe)) {
  case PIPE_INTERRUPT:
    /* implicitly requeued */
    urb->dev->irq_act_len = urb->actual_length;
    if (urb->dev->irq_handle && urb->dev->irq_act_len) {
      writel (OHCI_INTR_WDH, &regs->intrenable);
      readl (&regs->intrenable); /* PCI posting flush */
      urb->dev->irq_handle(urb->dev);
      writel (OHCI_INTR_WDH, &regs->intrdisable);
      readl (&regs->intrdisable); /* PCI posting flush */
    }
    urb->actual_length = 0;
    td_submit_job (
        urb->dev,
        urb->pipe,
        urb->transfer_buffer,
        urb->transfer_buffer_length,
        NULL,
        urb,
        urb->interval);
    break;
  case PIPE_CONTROL:
  case PIPE_BULK:
    break;
  default:
    return 0;
  }
  return 1;
}


/*-------------------------------------------------------------------------*
 * ED handling functions
 *-------------------------------------------------------------------------*/

/* search for the right branch to insert an interrupt ed into the int tree
 * do some load ballancing;
 * returns the branch and
 * sets the interval to interval = 2^integer (ld (interval)) */

static int ep_int_ballance (ohci_t * ohci, int interval, int load)
{
  int i, branch = 0;

  /* search for the least loaded interrupt endpoint
   * branch of all 32 branches
   */
  for (i = 0; i < 32; i++)
    if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i])
      branch = i;

  branch = branch % interval;
  for (i = branch; i < 32; i += interval)
    ohci->ohci_int_load [i] += load;

  return branch;
}

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

/*  2^int( ld (inter)) */

static int ep_2_n_interval (int inter)
{
  int i;
  for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++);
  return 1 << i;
}

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

/* the int tree is a binary tree
 * in order to process it sequentially the indexes of the branches have to be mapped
 * the mapping reverses the bits of a word of num_bits length */

static int ep_rev (int num_bits, int word)
{
  int i, wout = 0;

  for (i = 0; i < num_bits; i++)
    wout |= (((word >> i) & 1) << (num_bits - i - 1));
  return wout;
}

/*-------------------------------------------------------------------------*
 * ED handling functions
 *-------------------------------------------------------------------------*/

/* link an ed into one of the HC chains */

static int ep_link (ohci_t *ohci, ed_t *edi)
{
  volatile ed_t *ed = edi;
  int int_branch;
  int i;
  int inter;
  int interval;
  int load;
  unsigned int * ed_p;

  ed->state = ED_OPER;
  ed->int_interval = 0;

  switch (ed->type) {
  case PIPE_CONTROL:

⌨️ 快捷键说明

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