📄 usb-ohci.c
字号:
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;
}
/*-------------------------------------------------------------------------*/
/* 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 int_branch;
int i;
int inter;
int interval;
__u32 * ed_p;
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:
int_branch = ed->int_branch;
interval = ed->int_interval;
for (i = 0; i < ep_rev (6, interval); i += inter) {
for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]), inter = 1;
(*ed_p != 0) && (*ed_p != ed->hwNextED);
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)) {
if((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
*ed_p = ed->hwNextED;
break;
}
}
}
for (i = int_branch; i < 32; i += 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 {
for (i = 0; i < 32; i++) {
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);
if((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
*ed_p = ed->hwNextED;
break;
}
}
}
}
#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 */
td->ed->hwTailP = td->hwNextTD;
}
/*-------------------------------------------------------------------------*/
/* 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 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);
}
urb_priv->td_cnt = 0;
if (data_len) {
/*
data = pci_map_single (ohci->ohci_dev,
urb->transfer_buffer, data_len,
usb_pipeout (urb->pipe)
? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE
);
*/
data = (dma_addr_t)urb->transfer_buffer;
} else
data = 0;
switch (usb_pipetype (urb->pipe)) {
case PIPE_BULK:
info = usb_pipeout (urb->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, urb, cnt);
data += 4096; data_len -= 4096; cnt++;
}
info = usb_pipeout (urb->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, urb, cnt);
cnt++;
if (!ohci->sleeping)
Ohci_WriteReg (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
break;
case PIPE_INTERRUPT:
info = usb_pipeout (urb->pipe)?
TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -