📄 pxa2xx_udc.c
字号:
* nuke - dequeue ALL requests
*/
static void nuke(struct pxa2xx_ep *ep, int status)
{
struct pxa2xx_request *req;
/* called with irqs blocked */
#ifdef USE_DMA
if (ep->dma >= 0 && !ep->stopped)
cancel_dma(ep);
#endif
while (!list_empty(&ep->queue)) {
req = list_entry(ep->queue.next,
struct pxa2xx_request,
queue);
done(ep, req, status);
}
if (ep->desc)
pio_irq_disable (ep->bEndpointAddress);
}
/* dequeue JUST ONE request */
static int pxa2xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct pxa2xx_ep *ep;
struct pxa2xx_request *req;
unsigned long flags;
ep = container_of(_ep, struct pxa2xx_ep, ep);
if (!_ep || ep->ep.name == ep0name)
return -EINVAL;
local_irq_save(flags);
/* make sure it's actually queued on this endpoint */
list_for_each_entry (req, &ep->queue, queue) {
if (&req->req == _req)
break;
}
if (&req->req != _req) {
local_irq_restore(flags);
return -EINVAL;
}
#ifdef USE_DMA
if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) {
cancel_dma(ep);
done(ep, req, -ECONNRESET);
/* restart i/o */
if (!list_empty(&ep->queue)) {
req = list_entry(ep->queue.next,
struct pxa2xx_request, queue);
kick_dma(ep, req);
}
} else
#endif
done(ep, req, -ECONNRESET);
local_irq_restore(flags);
return 0;
}
/*-------------------------------------------------------------------------*/
static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value)
{
struct pxa2xx_ep *ep;
unsigned long flags;
ep = container_of(_ep, struct pxa2xx_ep, ep);
if (unlikely (!_ep
|| (!ep->desc && ep->ep.name != ep0name))
|| ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
DMSG("%s, bad ep\n", __FUNCTION__);
return -EINVAL;
}
if (value == 0) {
/* this path (reset toggle+halt) is needed to implement
* SET_INTERFACE on normal hardware. but it can't be
* done from software on the PXA UDC, and the hardware
* forgets to do it as part of SET_INTERFACE automagic.
*/
DMSG("only host can clear %s halt\n", _ep->name);
return -EROFS;
}
local_irq_save(flags);
if ((ep->bEndpointAddress & USB_DIR_IN) != 0
&& ((*ep->reg_udccs & UDCCS_BI_TFS) == 0
|| !list_empty(&ep->queue))) {
local_irq_restore(flags);
return -EAGAIN;
}
/* FST bit is the same for control, bulk in, bulk out, interrupt in */
*ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF;
/* ep0 needs special care */
if (!ep->desc) {
start_watchdog(ep->dev);
ep->dev->req_pending = 0;
ep->dev->ep0state = EP0_STALL;
/* and bulk/intr endpoints like dropping stalls too */
} else {
unsigned i;
for (i = 0; i < 1000; i += 20) {
if (*ep->reg_udccs & UDCCS_BI_SST)
break;
udelay(20);
}
}
local_irq_restore(flags);
DBG(DBG_VERBOSE, "%s halt\n", _ep->name);
return 0;
}
static int pxa2xx_ep_fifo_status(struct usb_ep *_ep)
{
struct pxa2xx_ep *ep;
ep = container_of(_ep, struct pxa2xx_ep, ep);
if (!_ep) {
DMSG("%s, bad ep\n", __FUNCTION__);
return -ENODEV;
}
/* pxa can't report unclaimed bytes from IN fifos */
if ((ep->bEndpointAddress & USB_DIR_IN) != 0)
return -EOPNOTSUPP;
if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN
|| (*ep->reg_udccs & UDCCS_BO_RFS) == 0)
return 0;
else
return (*ep->reg_ubcr & 0xfff) + 1;
}
static void pxa2xx_ep_fifo_flush(struct usb_ep *_ep)
{
struct pxa2xx_ep *ep;
ep = container_of(_ep, struct pxa2xx_ep, ep);
if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) {
DMSG("%s, bad ep\n", __FUNCTION__);
return;
}
/* toggle and halt bits stay unchanged */
/* for OUT, just read and discard the FIFO contents. */
if ((ep->bEndpointAddress & USB_DIR_IN) == 0) {
while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0)
(void) *ep->reg_uddr;
return;
}
/* most IN status is the same, but ISO can't stall */
*ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR
| (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
? 0 : UDCCS_BI_SST;
}
static struct usb_ep_ops pxa2xx_ep_ops = {
.enable = pxa2xx_ep_enable,
.disable = pxa2xx_ep_disable,
.alloc_request = pxa2xx_ep_alloc_request,
.free_request = pxa2xx_ep_free_request,
.alloc_buffer = pxa2xx_ep_alloc_buffer,
.free_buffer = pxa2xx_ep_free_buffer,
.queue = pxa2xx_ep_queue,
.dequeue = pxa2xx_ep_dequeue,
.set_halt = pxa2xx_ep_set_halt,
.fifo_status = pxa2xx_ep_fifo_status,
.fifo_flush = pxa2xx_ep_fifo_flush,
};
/* ---------------------------------------------------------------------------
* device-scoped parts of the api to the usb controller hardware
* ---------------------------------------------------------------------------
*/
static int pxa2xx_udc_get_frame(struct usb_gadget *_gadget)
{
return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff);
}
static int pxa2xx_udc_wakeup(struct usb_gadget *_gadget)
{
/* host may not have enabled remote wakeup */
if ((UDCCS0 & UDCCS0_DRWF) == 0)
return -EHOSTUNREACH;
udc_set_mask_UDCCR(UDCCR_RSM);
return 0;
}
static void stop_activity(struct pxa2xx_udc *, struct usb_gadget_driver *);
static void udc_enable (struct pxa2xx_udc *);
static void udc_disable(struct pxa2xx_udc *);
/* We disable the UDC -- and its 48 MHz clock -- whenever it's not
* in active use.
*/
static int pullup(struct pxa2xx_udc *udc, int is_active)
{
is_active = is_active && udc->vbus && udc->pullup;
DMSG("%s\n", is_active ? "active" : "inactive");
if (is_active)
udc_enable(udc);
else {
if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
DMSG("disconnect %s\n", udc->driver
? udc->driver->driver.name
: "(no driver)");
stop_activity(udc, udc->driver);
}
udc_disable(udc);
}
return 0;
}
/* VBUS reporting logically comes from a transceiver */
static int pxa2xx_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
{
struct pxa2xx_udc *udc;
udc = container_of(_gadget, struct pxa2xx_udc, gadget);
udc->vbus = is_active = (is_active != 0);
DMSG("vbus %s\n", is_active ? "supplied" : "inactive");
pullup(udc, is_active);
return 0;
}
/* drivers may have software control over D+ pullup */
static int pxa2xx_udc_pullup(struct usb_gadget *_gadget, int is_active)
{
struct pxa2xx_udc *udc;
udc = container_of(_gadget, struct pxa2xx_udc, gadget);
/* not all boards support pullup control */
if (!udc->mach->udc_command)
return -EOPNOTSUPP;
is_active = (is_active != 0);
udc->pullup = is_active;
pullup(udc, is_active);
return 0;
}
static const struct usb_gadget_ops pxa2xx_udc_ops = {
.get_frame = pxa2xx_udc_get_frame,
.wakeup = pxa2xx_udc_wakeup,
.vbus_session = pxa2xx_udc_vbus_session,
.pullup = pxa2xx_udc_pullup,
// .vbus_draw ... boards may consume current from VBUS, up to
// 100-500mA based on config. the 500uA suspend ceiling means
// that exclusively vbus-powered PXA designs violate USB specs.
};
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static const char proc_node_name [] = "driver/udc";
static int
udc_proc_read(char *page, char **start, off_t off, int count,
int *eof, void *_dev)
{
char *buf = page;
struct pxa2xx_udc *dev = _dev;
char *next = buf;
unsigned size = count;
unsigned long flags;
int i, t;
u32 tmp;
if (off != 0)
return 0;
local_irq_save(flags);
/* basic device status */
t = scnprintf(next, size, DRIVER_DESC "\n"
"%s version: %s\nGadget driver: %s\nHost %s\n\n",
driver_name, DRIVER_VERSION SIZE_STR DMASTR,
dev->driver ? dev->driver->driver.name : "(none)",
is_vbus_present() ? "full speed" : "disconnected");
size -= t;
next += t;
/* registers for device and ep0 */
t = scnprintf(next, size,
"uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
size -= t;
next += t;
tmp = UDCCR;
t = scnprintf(next, size,
"udccr %02X =%s%s%s%s%s%s%s%s\n", tmp,
(tmp & UDCCR_REM) ? " rem" : "",
(tmp & UDCCR_RSTIR) ? " rstir" : "",
(tmp & UDCCR_SRM) ? " srm" : "",
(tmp & UDCCR_SUSIR) ? " susir" : "",
(tmp & UDCCR_RESIR) ? " resir" : "",
(tmp & UDCCR_RSM) ? " rsm" : "",
(tmp & UDCCR_UDA) ? " uda" : "",
(tmp & UDCCR_UDE) ? " ude" : "");
size -= t;
next += t;
tmp = UDCCS0;
t = scnprintf(next, size,
"udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp,
(tmp & UDCCS0_SA) ? " sa" : "",
(tmp & UDCCS0_RNE) ? " rne" : "",
(tmp & UDCCS0_FST) ? " fst" : "",
(tmp & UDCCS0_SST) ? " sst" : "",
(tmp & UDCCS0_DRWF) ? " dwrf" : "",
(tmp & UDCCS0_FTF) ? " ftf" : "",
(tmp & UDCCS0_IPR) ? " ipr" : "",
(tmp & UDCCS0_OPR) ? " opr" : "");
size -= t;
next += t;
if (dev->has_cfr) {
tmp = UDCCFR;
t = scnprintf(next, size,
"udccfr %02X =%s%s\n", tmp,
(tmp & UDCCFR_AREN) ? " aren" : "",
(tmp & UDCCFR_ACM) ? " acm" : "");
size -= t;
next += t;
}
if (!is_vbus_present() || !dev->driver)
goto done;
t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
dev->stats.write.bytes, dev->stats.write.ops,
dev->stats.read.bytes, dev->stats.read.ops,
dev->stats.irqs);
size -= t;
next += t;
/* dump endpoint queues */
for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
struct pxa2xx_ep *ep = &dev->ep [i];
struct pxa2xx_request *req;
int t;
if (i != 0) {
const struct usb_endpoint_descriptor *d;
d = ep->desc;
if (!d)
continue;
tmp = *dev->ep [i].reg_udccs;
t = scnprintf(next, size,
"%s max %d %s udccs %02x irqs %lu/%lu\n",
ep->ep.name, le16_to_cpu (d->wMaxPacketSize),
(ep->dma >= 0) ? "dma" : "pio", tmp,
ep->pio_irqs, ep->dma_irqs);
/* TODO translate all five groups of udccs bits! */
} else /* ep0 should only have one transfer queued */
t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n",
ep->pio_irqs);
if (t <= 0 || t > size)
goto done;
size -= t;
next += t;
if (list_empty(&ep->queue)) {
t = scnprintf(next, size, "\t(nothing queued)\n");
if (t <= 0 || t > size)
goto done;
size -= t;
next += t;
continue;
}
list_for_each_entry(req, &ep->queue, queue) {
#ifdef USE_DMA
if (ep->dma >= 0 && req->queue.prev == &ep->queue)
t = scnprintf(next, size,
"\treq %p len %d/%d "
"buf %p (dma%d dcmd %08x)\n",
&req->req, req->req.actual,
req->req.length, req->req.buf,
ep->dma, DCMD(ep->dma)
// low 13 bits == bytes-to-go
);
else
#endif
t = scnprintf(next, size,
"\treq %p len %d/%d buf %p\n",
&req->req, req->req.actual,
req->req.length, req->req.buf);
if (t <= 0 || t > size)
goto done;
size -= t;
next += t;
}
}
done:
local_irq_restore(flags);
*eof = 1;
return count - size;
}
#define create_proc_files() \
create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
#define remove_proc_files() \
remove_proc_entry(proc_node_name, NULL)
#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
#define create_proc_files() do {} while (0)
#define remove_proc_files() do {} while (0)
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/* "function" sysfs attribute */
static ssize_t
show_function (struct device *_dev, struct device_attribute *attr, char *buf)
{
struct pxa2xx_udc *dev = dev_get_drvdata (_dev);
if (!dev->driver
|| !dev->driver->function
|| strlen (dev->driver->function) > PAGE_SIZE)
return 0;
return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
}
static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
/*-------------------------------------------------------------------------*/
/*
* udc_disable - disable USB device controller
*/
static void udc_disable(struct pxa2xx_udc *dev)
{
/* block all irqs */
udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM);
UICR0 = UICR1 = 0xff;
UFNRH = UFNRH_SIM;
/* if hardware supports it, disconnect from usb */
pullup_off();
udc_clear_mask_UDCCR(UDCCR_UDE);
#ifdef CONFIG_ARCH_PXA
/* Disable clock for USB device */
pxa_set_cken(CKEN11_USB, 0);
#endif
ep0_idle (dev);
dev->gadget.speed = USB_SPEED_UNKNOWN;
LED_CONNECTED_OFF;
}
/*
* udc_reinit - initialize software state
*/
static void udc_reinit(struct pxa2xx_udc *dev)
{
u32 i;
/* device/ep0 records init */
INIT_LIST_HEAD (&dev->gadget.ep_list);
INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
dev->ep0state = EP0_IDLE;
/* basic endpoint records init */
for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
struct pxa2xx_ep *ep = &dev->ep[i];
if (i != 0)
list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
ep->desc = NULL;
ep->stopped = 0;
INIT_LIST_HEAD (&ep->queue);
ep->pio_irqs = ep->dma_irqs = 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -