📄 usb-ohci.c
字号:
return 0;
}
/*-------------------------------------------------------------------------*/
/* tell us the current USB frame number */
static int sohci_get_current_frame_number (struct usb_device *usb_dev)
{
ohci_t * ohci = usb_dev->bus->hcpriv;
return le16_to_cpu (ohci->hcca->frame_no);
}
/*-------------------------------------------------------------------------*/
struct usb_operations sohci_device_operations = {
sohci_alloc_dev,
sohci_free_dev,
sohci_get_current_frame_number,
sohci_submit_urb,
sohci_unlink_urb
};
/*-------------------------------------------------------------------------*
* 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;
}
/*-------------------------------------------------------------------------*/
/* link an ed into one of the HC chains */
static int ep_link (ohci_t * ohci, ed_t * edi)
{
int int_branch;
int i;
int inter;
int interval;
int load;
__u32 * ed_p;
volatile ed_t * ed = edi;
ed->state = ED_OPER;
switch (ed->type) {
case PIPE_CONTROL:
ed->hwNextED = 0;
if (ohci->ed_controltail == NULL) {
Ohci_WriteReg (ed->dma, &ohci->regs->ed_controlhead);
} else {
ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
}
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;
Ohci_WriteReg (ohci->hc_control, &ohci->regs->control);
}
ohci->ed_controltail = edi;
break;
case PIPE_BULK:
ed->hwNextED = 0;
if (ohci->ed_bulktail == NULL) {
Ohci_WriteReg (ed->dma, &ohci->regs->ed_bulkhead);
} else {
ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
}
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;
Ohci_WriteReg (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) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval);
ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED))
inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
ed->hwNextED = *ed_p;
*ed_p = cpu_to_le32 (ed->dma);
}
#ifdef DEBUG
ep_print_int_eds (ohci, "LINK_INT");
#endif
break;
case PIPE_ISOCHRONOUS:
ed->hwNextED = 0;
ed->int_interval = 1;
if (ohci->ed_isotail != NULL) {
ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
ed->ed_prev = ohci->ed_isotail;
} else {
for ( i = 0; i < 32; i += inter) {
inter = 1;
for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]);
*ed_p != 0;
ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED))
inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
*ed_p = cpu_to_le32 (ed->dma);
}
ed->ed_prev = NULL;
}
ohci->ed_isotail = edi;
#ifdef DEBUG
ep_print_int_eds (ohci, "LINK_ISO");
#endif
break;
}
return 0;
}
/*-------------------------------------------------------------------------*/
/* scan the periodic table to find and unlink this ED */
static void periodic_unlink (
struct ohci *ohci,
struct ed *ed,
unsigned index,
unsigned period
) {
for (; index < NUM_INTS; index += period) {
__u32 *ed_p = &ohci->hcca->int_table [index];
/* ED might have been unlinked through another path */
while (*ed_p != 0) {
if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
*ed_p = ed->hwNextED;
break;
}
ed_p = & ((dma_to_ed (ohci,
le32_to_cpup (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 * ed)
{
int i;
ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
switch (ed->type) {
case PIPE_CONTROL:
if (ed->ed_prev == NULL) {
if (!ed->hwNextED) {
ohci->hc_control &= ~OHCI_CTRL_CLE;
Ohci_WriteReg (ohci->hc_control, &ohci->regs->control);
}
Ohci_WriteReg (le32_to_cpup (&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 {
(dma_to_ed (ohci, le32_to_cpup (&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;
Ohci_WriteReg (ohci->hc_control, &ohci->regs->control);
}
Ohci_WriteReg (le32_to_cpup (&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 {
(dma_to_ed (ohci, le32_to_cpup (&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;
#ifdef DEBUG
ep_print_int_eds (ohci, "UNLINK_INT");
#endif
break;
case PIPE_ISOCHRONOUS:
if (ohci->ed_isotail == ed)
ohci->ed_isotail = ed->ed_prev;
if (ed->hwNextED != 0)
(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
->ed_prev = ed->ed_prev;
if (ed->ed_prev != NULL)
ed->ed_prev->hwNextED = ed->hwNextED;
else
periodic_unlink (ohci, ed, 0, 1);
#ifdef DEBUG
ep_print_int_eds (ohci, "UNLINK_ISO");
#endif
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 int pipe,
int interval,
int load,
int mem_flags
)
{
ohci_t * ohci = usb_dev->bus->hcpriv;
td_t * td;
ed_t * ed_ret;
volatile ed_t * ed;
unsigned long flags;
spin_lock_irqsave (&usb_ed_lock, flags);
ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) |
(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
/* pending delete request */
spin_unlock_irqrestore (&usb_ed_lock, flags);
return NULL;
}
if (ed->state == ED_NEW) {
ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */
/* dummy td; end of td list for ed */
td = td_alloc (ohci, SLAB_ATOMIC);
/* hash the ed for later reverse mapping */
if (!td || !hash_add_ed (ohci, (ed_t *)ed)) {
/* out of memory */
if (td)
td_free(ohci, td);
spin_unlock_irqrestore (&usb_ed_lock, flags);
return NULL;
}
ed->hwTailP = cpu_to_le32 (td->td_dma);
ed->hwHeadP = ed->hwTailP;
ed->state = ED_UNLINK;
ed->type = usb_pipetype (pipe);
usb_to_ohci (usb_dev)->ed_cnt++;
}
ohci->dev[usb_pipedevice (pipe)] = usb_dev;
ed->hwINFO = cpu_to_le32 (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, usb_pipeout (pipe)) << 16);
if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
ed->int_period = interval;
ed->int_load = load;
}
spin_unlock_irqrestore (&usb_ed_lock, flags);
return ed_ret;
}
/*-------------------------------------------------------------------------*/
/* request the removal of an endpoint
* put the ep on the rm_list and request a stop of the bulk or ctrl list
* real removal is done at the next start frame (SF) hardware interrupt */
static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed)
{
unsigned int frame;
ohci_t * ohci = usb_dev->bus->hcpriv;
if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL))
return;
ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
if (!ohci->disabled) {
switch (ed->type) {
case PIPE_CONTROL: /* stop control list */
ohci->hc_control &= ~OHCI_CTRL_CLE;
Ohci_WriteReg (ohci->hc_control, &ohci->regs->control);
break;
case PIPE_BULK: /* stop bulk list */
ohci->hc_control &= ~OHCI_CTRL_BLE;
Ohci_WriteReg (ohci->hc_control, &ohci->regs->control);
break;
}
}
frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1;
ed->ed_rm_list = ohci->ed_rm_list[frame];
ohci->ed_rm_list[frame] = ed;
if (!ohci->disabled && !ohci->sleeping) {
/* enable SOF interrupt */
Ohci_WriteReg (OHCI_INTR_SF, &ohci->regs->intrstatus);
Ohci_WriteReg (OHCI_INTR_SF, &ohci->regs->intrenable);
}
}
/*-------------------------------------------------------------------------*
* 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,
dma_addr_t data, int len,
urb_t * urb, int index)
{
volatile td_t * td, * td_pt;
urb_priv_t * urb_priv = urb->hcpriv;
if (index >= urb_priv->length) {
err("internal OHCI error: TD 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] = dma_to_td (ohci,
le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf);
td->ed = urb_priv->ed;
td->next_dl_td = NULL;
td->index = index;
td->urb = urb;
td->data_dma = data;
if (!len)
data = 0;
td->hwINFO = cpu_to_le32 (info);
if ((td->ed->type) == PIPE_ISOCHRONOUS) {
td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
td->ed->last_iso = info & 0xffff;
} else {
td->hwCBP = cpu_to_le32 (data);
}
if (data)
td->hwBE = cpu_to_le32 (data + len - 1);
else
td->hwBE = 0;
td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
/* append to queue */
wmb();
td->ed->hwTailP = td->hwNextTD;
}
/*-------------------------------------------------------------------------*/
void submit_urb_out_debug()
{
}
/* prepare all TDs of a transfer */
static void td_submit_urb (urb_t * urb)
{
urb_priv_t * urb_priv = urb->hcpriv;
ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv;
dma_addr_t data;
int data_len = urb->transfer_buffer_length;
int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
int cnt = 0;
__u32 info = 0;
unsigned int toggle = 0;
/* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */
if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)))
{
toggle = TD_T_TOGGLE;
}
else
{
toggle = TD_T_DATA0;
usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -