out_be32(&ep->empty_td->buf_ptr, DUMMY_BD_BUFFER); /* get the next TD in the ring */
ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status);
ep->already_pushed_dummy_bd = true;
}
}
/* destroy an USB endpoint */ void fhci_ep0_free(struct fhci_usb *usb)
{ struct endpoint *ep; int size;
ep = usb->ep0; if (ep) { if (ep->td_base)
cpm_muram_free(cpm_muram_offset(ep->td_base));
if (kfifo_initialized(&ep->conf_frame_Q)) {
size = cq_howmany(&ep->conf_frame_Q); for (; size; size--) { struct packet *pkt = cq_get(&ep->conf_frame_Q);
kfree(pkt);
}
cq_delete(&ep->conf_frame_Q);
}
if (kfifo_initialized(&ep->empty_frame_Q)) {
size = cq_howmany(&ep->empty_frame_Q); for (; size; size--) { struct packet *pkt = cq_get(&ep->empty_frame_Q);
kfree(pkt);
}
cq_delete(&ep->empty_frame_Q);
}
if (kfifo_initialized(&ep->dummy_packets_Q)) {
size = cq_howmany(&ep->dummy_packets_Q); for (; size; size--) {
u8 *buff = cq_get(&ep->dummy_packets_Q);
kfree(buff);
}
cq_delete(&ep->dummy_packets_Q);
}
kfree(ep);
usb->ep0 = NULL;
}
}
/* * create the endpoint structure * * arguments: * usb A pointer to the data structure of the USB * data_mem The data memory partition(BUS) * ring_len TD ring length
*/
u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
u32 ring_len)
{ struct endpoint *ep; struct usb_td __iomem *td; unsignedlong ep_offset; char *err_for = "endpoint PRAM"; int ep_mem_size;
u32 i;
/* we need at least 3 TDs in the ring */ if (!(ring_len > 2)) {
fhci_err(usb->fhci, "illegal TD ring length parameters\n"); return -EINVAL;
}
ep = kzalloc(sizeof(*ep), GFP_KERNEL); if (!ep) return -ENOMEM;
/* initialize tds */
td = ep->td_base; for (i = 0; i < ring_len; i++) {
out_be32(&td->buf_ptr, 0);
out_be16(&td->status, 0);
out_be16(&td->length, 0);
out_be16(&td->extra, 0);
td++;
}
td--;
out_be16(&td->status, TD_W); /* for last TD set Wrap bit */
out_be16(&td->length, 0);
/* endpoint structure has been created */
usb->ep0 = ep;
return 0;
err:
fhci_ep0_free(usb);
kfree(ep);
fhci_err(usb->fhci, "no memory for the %s\n", err_for); return -ENOMEM;
}
/* * initialize the endpoint register according to the given parameters * * artuments: * usb A pointer to the data strucutre of the USB * ep A pointer to the endpoint structre * data_mem The data memory partition(BUS)
*/ void fhci_init_ep_registers(struct fhci_usb *usb, struct endpoint *ep, enum fhci_mem_alloc data_mem)
{
u8 rt;
/* set the endpoint registers according to the endpoint */
out_be16(&usb->fhci->regs->usb_usep[0],
USB_TRANS_CTR | USB_EP_MF | USB_EP_RTE);
out_be16(&usb->fhci->pram->ep_ptr[0],
cpm_muram_offset(ep->ep_pram_ptr));
/* * Collect the submitted frames and inform the application about them * It is also preparing the TDs for new frames. If the Tx interrupts * are disabled, the application should call that routine to get * confirmation about the submitted frames. Otherwise, the routine is * called from the interrupt service routine during the Tx interrupt. * In that case the application is informed by calling the application * specific 'fhci_transaction_confirm' routine
*/ staticvoid fhci_td_transaction_confirm(struct fhci_usb *usb)
{ struct endpoint *ep = usb->ep0; struct packet *pkt; struct usb_td __iomem *td;
u16 extra_data;
u16 td_status;
u16 td_length;
u32 buf;
/* * collect transmitted BDs from the chip. The routine clears all BDs * with R bit = 0 and the pointer to data buffer is not NULL, that is * BDs which point to the transmitted data buffer
*/ while (1) {
td = ep->conf_td;
td_status = in_be16(&td->status);
td_length = in_be16(&td->length);
buf = in_be32(&td->buf_ptr);
extra_data = in_be16(&td->extra);
/* check if the TD is empty */ if (!(!(td_status & TD_R) && ((td_status & ~TD_W) || buf))) break; /* check if it is a dummy buffer */ elseif ((buf == DUMMY_BD_BUFFER) && !(td_status & ~TD_W)) break;
/* mark TD as empty */
clrbits16(&td->status, ~TD_W);
out_be16(&td->length, 0);
out_be32(&td->buf_ptr, 0);
out_be16(&td->extra, 0); /* advance the TD pointer */
ep->conf_td = next_bd(ep->td_base, ep->conf_td, td_status);
/* check if it is a dummy buffer(type2) */ if ((buf == DUMMY2_BD_BUFFER) && !(td_status & ~TD_W)) continue;
pkt = cq_get(&ep->conf_frame_Q); if (!pkt)
fhci_err(usb->fhci, "no frame to confirm\n");
/* * Submitting a data frame to a specified endpoint of a USB device * The frame is put in the driver's transmit queue for this endpoint * * Arguments: * usb A pointer to the USB structure * pkt A pointer to the user frame structure * trans_type Transaction tyep - IN,OUT or SETUP * dest_addr Device address - 0~127 * dest_ep Endpoint number of the device - 0~16 * trans_mode Pipe type - ISO,Interrupt,bulk or control * dest_speed USB speed - Low speed or FULL speed * data_toggle Data sequence toggle - 0 or 1
*/
u32 fhci_host_transaction(struct fhci_usb *usb, struct packet *pkt, enum fhci_ta_type trans_type,
u8 dest_addr,
u8 dest_ep, enum fhci_tf_mode trans_mode, enum fhci_speed dest_speed, u8 data_toggle)
{ struct endpoint *ep = usb->ep0; struct usb_td __iomem *td;
u16 extra_data;
u16 td_status;
fhci_usb_disable_interrupt(usb); /* start from the next BD that should be filled */
td = ep->empty_td;
td_status = in_be16(&td->status);
if (td_status & TD_R && in_be16(&td->length)) { /* if the TD is not free */
fhci_usb_enable_interrupt(usb); return -1;
}
/* get the next TD in the ring */
ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status);
fhci_usb_enable_interrupt(usb);
pkt->priv_data = td;
out_be32(&td->buf_ptr, virt_to_phys(pkt->data)); /* sets up transaction parameters - addr,endp,dir,and type */
extra_data = (dest_ep << TD_ENDP_SHIFT) | dest_addr; switch (trans_type) { case FHCI_TA_IN:
extra_data |= TD_TOK_IN; break; case FHCI_TA_OUT:
extra_data |= TD_TOK_OUT; break; case FHCI_TA_SETUP:
extra_data |= TD_TOK_SETUP; break;
} if (trans_mode == FHCI_TF_ISO)
extra_data |= TD_ISO;
out_be16(&td->extra, extra_data);
/* sets up the buffer descriptor */
td_status = ((td_status & TD_W) | TD_R | TD_L | TD_I | TD_CNF); if (!(pkt->info & PKT_NO_CRC))
td_status |= TD_TC;
/* if the TD is not empty - we'll confirm it as Timeout */ if (td_status & TD_R)
out_be16(&td->status, (td_status & ~TD_R) | TD_TO); /* if this TD is dummy - let's skip this TD */ elseif (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER)
out_be32(&td->buf_ptr, DUMMY2_BD_BUFFER); /* if this is the last TD - break */ if (td_status & TD_W) break;
td++;
}
fhci_td_transaction_confirm(usb);
td = ep->td_base; do {
out_be16(&td->status, 0);
out_be16(&td->length, 0);
out_be32(&td->buf_ptr, 0);
out_be16(&td->extra, 0);
td++;
} while (!(in_be16(&td->status) & TD_W));
out_be16(&td->status, TD_W); /* for last TD set Wrap bit */
out_be16(&td->length, 0);
out_be32(&td->buf_ptr, 0);
out_be16(&td->extra, 0);
/* * Flush all transmitted packets from TDs in the actual frame. * This routine is called when something wrong with the controller and * we want to get rid of the actual frame and start again next frame
*/ void fhci_flush_actual_frame(struct fhci_usb *usb)
{
u8 mode;
u16 tb_ptr;
u16 td_status;
u32 buf_ptr; struct usb_td __iomem *td; struct endpoint *ep = usb->ep0;
/* disable the USB controller */
mode = in_8(&usb->fhci->regs->usb_usmod);
out_8(&usb->fhci->regs->usb_usmod, mode & ~USB_MODE_EN);
/* * Schedule another transaction to this frame only if we have * already confirmed all transaction in the frame.
*/ if (((fhci_get_sof_timer_count(usb) < usb->max_frame_usage) ||
(usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)) &&
(list_empty(&usb->actual_frame->tds_list)))
fhci_schedule_transactions(usb);
}
ep->already_pushed_dummy_bd = false;
td_status = in_be16(&td->status); /* gets the next TD in the ring */
td = next_bd(ep->td_base, td, td_status);
tb_ptr = cpm_muram_offset(td);
out_be16(&ep->ep_pram_ptr->tx_bd_ptr, tb_ptr);
/* start transmit only if we have something in the TDs */ if (in_be16(&td->status) & TD_R)
out_8(&usb->fhci->regs->usb_uscom, USB_CMD_STR_FIFO);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.