📄 usb-uhci.c
字号:
unlink_td (s, td, 0); // no physical unlink
delete_desc (s, td);
}
delete_desc (s, qh);
return 0;
}
/*-------------------------------------------------------------------*/
_static void clean_td_chain (uhci_t *s, uhci_desc_t *td)
{
struct list_head *p;
uhci_desc_t *td1;
if (!td)
return;
while ((p = td->horizontal.next) != &td->horizontal) {
td1 = list_entry (p, uhci_desc_t, horizontal);
delete_desc (s, td1);
}
delete_desc (s, td);
}
/*-------------------------------------------------------------------*/
_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
{
td->hw.td.status = cpu_to_le32(status);
td->hw.td.info = cpu_to_le32(info);
td->hw.td.buffer = cpu_to_le32(buffer);
}
/*-------------------------------------------------------------------*/
// Removes ALL qhs in chain (paranoia!)
_static void cleanup_skel (uhci_t *s)
{
unsigned int n;
uhci_desc_t *td;
dbg("cleanup_skel");
clean_descs(s,1);
if (s->td32ms) {
unlink_td(s,s->td32ms,1);
delete_desc(s, s->td32ms);
}
for (n = 0; n < 8; n++) {
td = s->int_chain[n];
clean_td_chain (s, td);
}
if (s->iso_td) {
for (n = 0; n < 1024; n++) {
td = s->iso_td[n];
clean_td_chain (s, td);
}
kfree (s->iso_td);
}
if (s->framelist)
pci_free_consistent(s->uhci_pci, PAGE_SIZE,
s->framelist, s->framelist_dma);
if (s->control_chain) {
// completed init_skel?
struct list_head *p;
uhci_desc_t *qh, *qh1;
qh = s->control_chain;
while ((p = qh->horizontal.next) != &qh->horizontal) {
qh1 = list_entry (p, uhci_desc_t, horizontal);
delete_qh (s, qh1);
}
delete_qh (s, qh);
}
else {
if (s->ls_control_chain)
delete_desc (s, s->ls_control_chain);
if (s->control_chain)
delete_desc (s, s->control_chain);
if (s->bulk_chain)
delete_desc (s, s->bulk_chain);
if (s->chain_end)
delete_desc (s, s->chain_end);
}
if (s->desc_pool) {
pci_pool_destroy(s->desc_pool);
s->desc_pool = NULL;
}
dbg("cleanup_skel finished");
}
/*-------------------------------------------------------------------*/
// allocates framelist and qh-skeletons
// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT)
_static int init_skel (uhci_t *s)
{
int n, ret;
uhci_desc_t *qh, *td;
dbg("init_skel");
s->framelist = pci_alloc_consistent(s->uhci_pci, PAGE_SIZE,
&s->framelist_dma);
if (!s->framelist)
return -ENOMEM;
memset (s->framelist, 0, 4096);
dbg("creating descriptor pci_pool");
s->desc_pool = pci_pool_create("uhci_desc", s->uhci_pci,
sizeof(uhci_desc_t), 16, 0,
GFP_DMA | GFP_ATOMIC);
if (!s->desc_pool)
goto init_skel_cleanup;
dbg("allocating iso desc pointer list");
s->iso_td = (uhci_desc_t **) kmalloc (1024 * sizeof (uhci_desc_t*), GFP_KERNEL);
if (!s->iso_td)
goto init_skel_cleanup;
s->ls_control_chain = NULL;
s->control_chain = NULL;
s->bulk_chain = NULL;
s->chain_end = NULL;
dbg("allocating iso descs");
for (n = 0; n < 1024; n++) {
// allocate skeleton iso/irq-tds
if (alloc_td (s, &td, 0))
goto init_skel_cleanup;
s->iso_td[n] = td;
s->framelist[n] = cpu_to_le32((__u32) td->dma_addr);
}
dbg("allocating qh: chain_end");
if (alloc_qh (s, &qh))
goto init_skel_cleanup;
s->chain_end = qh;
if (alloc_td (s, &td, 0))
goto init_skel_cleanup;
fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 1ms interrupt (enabled on demand)
insert_td (s, qh, td, 0);
qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); // remove TERM bit
s->td1ms=td;
dbg("allocating qh: bulk_chain");
if (alloc_qh (s, &qh))
goto init_skel_cleanup;
insert_qh (s, s->chain_end, qh, 0);
s->bulk_chain = qh;
dbg("allocating qh: control_chain");
ret = alloc_qh (s, &qh);
if (ret)
goto init_skel_cleanup;
insert_qh (s, s->bulk_chain, qh, 0);
s->control_chain = qh;
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
// disabled reclamation loop
set_qh_head(s->chain_end, s->control_chain->dma_addr | UHCI_PTR_QH | UHCI_PTR_TERM);
#endif
dbg("allocating qh: ls_control_chain");
if (alloc_qh (s, &qh))
goto init_skel_cleanup;
insert_qh (s, s->control_chain, qh, 0);
s->ls_control_chain = qh;
for (n = 0; n < 8; n++)
s->int_chain[n] = 0;
dbg("allocating skeleton INT-TDs");
for (n = 0; n < 8; n++) {
uhci_desc_t *td;
if (alloc_td (s, &td, 0))
goto init_skel_cleanup;
s->int_chain[n] = td;
if (n == 0) {
set_td_link(s->int_chain[0], s->ls_control_chain->dma_addr | UHCI_PTR_QH);
}
else {
set_td_link(s->int_chain[n], s->int_chain[0]->dma_addr);
}
}
dbg("Linking skeleton INT-TDs");
for (n = 0; n < 1024; n++) {
// link all iso-tds to the interrupt chains
int m, o;
dbg("framelist[%i]=%x",n,le32_to_cpu(s->framelist[n]));
if ((n&127)==127)
((uhci_desc_t*) s->iso_td[n])->hw.td.link = cpu_to_le32(s->int_chain[0]->dma_addr);
else
for (o = 1, m = 2; m <= 128; o++, m += m)
if ((n & (m - 1)) == ((m - 1) / 2))
set_td_link(((uhci_desc_t*) s->iso_td[n]), s->int_chain[o]->dma_addr);
}
if (alloc_td (s, &td, 0))
goto init_skel_cleanup;
fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt (activated later)
s->td32ms=td;
insert_td_horizontal (s, s->int_chain[5], td);
mb();
dbg("init_skel exit");
return 0;
init_skel_cleanup:
cleanup_skel (s);
return -ENOMEM;
}
/*-------------------------------------------------------------------*/
// LOW LEVEL STUFF
// assembles QHs und TDs for control, bulk and iso
/*-------------------------------------------------------------------*/
_static int uhci_submit_control_urb (struct urb *urb)
{
uhci_desc_t *qh, *td;
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
urb_priv_t *urb_priv = urb->hcpriv;
unsigned long destination, status;
int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
unsigned long len;
char *data;
int depth_first=USE_CTRL_DEPTH_FIRST; // UHCI descriptor chasing method
dbg("uhci_submit_control start");
if (alloc_qh (s, &qh)) // alloc qh for this request
return -ENOMEM;
if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first)) // get td for setup stage
{
delete_qh (s, qh);
return -ENOMEM;
}
/* The "pipe" thing contains the destination in bits 8--18 */
destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
/* 3 errors */
status = TD_CTRL_ACTIVE
| (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD)
| (3 << 27);
if (urb->dev->speed == USB_SPEED_LOW)
status |= TD_CTRL_LS;
/* Build the TD for the control request, try forever, 8 bytes of data */
fill_td (td, status, destination | (7 << 21), urb_priv->setup_packet_dma);
insert_td (s, qh, td, 0); // queue 'setup stage'-td in qh
#if 0
{
char *sp=urb->setup_packet;
dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]);
}
//uhci_show_td(td);
#endif
len = urb->transfer_buffer_length;
data = urb->transfer_buffer;
/* If direction is "send", change the frame from SETUP (0x2D)
to OUT (0xE1). Else change it from SETUP to IN (0x69). */
destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN);
while (len > 0) {
int pktsze = len;
if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))
goto fail_unmap_enomem;
if (pktsze > maxsze)
pktsze = maxsze;
destination ^= 1 << TD_TOKEN_TOGGLE; // toggle DATA0/1
// Status, pktsze bytes of data
fill_td (td, status, destination | ((pktsze - 1) << 21),
urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer));
insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); // queue 'data stage'-td in qh
data += pktsze;
len -= pktsze;
}
/* Build the final TD for control status */
/* It's only IN if the pipe is out AND we aren't expecting data */
destination &= ~UHCI_PID;
if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0))
destination |= USB_PID_IN;
else
destination |= USB_PID_OUT;
destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */
if (alloc_td (s, &td, UHCI_PTR_DEPTH))
goto fail_unmap_enomem;
status &=~TD_CTRL_SPD;
/* no limit on errors on final packet , 0 bytes of data */
fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21),
0);
insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); // queue status td
list_add (&qh->desc_list, &urb_priv->desc_list);
queue_urb (s, urb); // queue before inserting in desc chain
qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM);
/* Start it up... put low speed first */
if (urb->dev->speed == USB_SPEED_LOW)
insert_qh (s, s->control_chain, qh, 0);
else
insert_qh (s, s->bulk_chain, qh, 0);
dbg("uhci_submit_control end");
return 0;
fail_unmap_enomem:
delete_qh(s, qh);
return -ENOMEM;
}
/*-------------------------------------------------------------------*/
// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!
_static int uhci_submit_bulk_urb (struct urb *urb, struct urb *bulk_urb)
{
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL;
uhci_desc_t *qh, *td, *nqh=NULL, *bqh=NULL, *first_td=NULL;
unsigned long destination, status;
char *data;
unsigned int pipe = urb->pipe;
int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
int info, len, last;
int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method
if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
return -EPIPE;
queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);
upriv = (urb_priv_t*)urb->hcpriv;
if (!bulk_urb) {
if (alloc_qh (s, &qh)) // get qh for this request
return -ENOMEM;
if (urb->transfer_flags & USB_QUEUE_BULK) {
if (alloc_qh(s, &nqh)) // placeholder for clean unlink
{
delete_desc (s, qh);
return -ENOMEM;
}
upriv->next_qh = nqh;
queue_dbg("new next qh %p",nqh);
}
}
else {
bpriv = (urb_priv_t*)bulk_urb->hcpriv;
qh = bpriv->bottom_qh; // re-use bottom qh and next qh
nqh = bpriv->next_qh;
upriv->next_qh=nqh;
upriv->prev_queued_urb=bulk_urb;
}
if (urb->transfer_flags & USB_QUEUE_BULK) {
if (alloc_qh (s, &bqh)) // "bottom" QH
{
if (!bulk_urb) {
delete_desc(s, qh);
delete_desc(s, nqh);
}
return -ENOMEM;
}
set_qh_element(bqh, UHCI_PTR_TERM);
set_qh_head(bqh, nqh->dma_addr | UHCI_PTR_QH); // element
upriv->bottom_qh = bqh;
}
queue_dbg("uhci_submit_bulk: qh %p bqh %p nqh %p",qh, bqh, nqh);
/* The "pipe" thing contains the destination in bits 8--18. */
destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
/* 3 errors */
status = TD_CTRL_ACTIVE
| ((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD)
| (3 << 27);
if (urb->dev->speed == USB_SPEED_LOW)
status |= TD_CTRL_LS;
/* Build the TDs for the bulk request */
len = urb->transfer_buffer_length;
data = urb->transfer_buffer;
do { // TBD: Really allow zero-length packets?
int pktsze = len;
if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))
{
delete_qh (s, qh);
return -ENOMEM;
}
if (pktsze > maxsze)
pktsze = maxsze;
// pktsze bytes of data
info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) |
(usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE);
fill_td (td, status, info,
urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer));
data += pktsze;
len -= pktsze;
// Use USB_ZERO_PACKET to finish bulk OUTs always with a zero length packet
last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_ZERO_PACKET)));
if (last)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -