📄 lh7a40x_udc.c
字号:
_ep ? ep->ep.name : NULL);
return -EINVAL;
}
spin_lock_irqsave(&ep->dev->lock, flags);
usb_set_index(ep_index(ep));
/* Nuke all pending requests (does flush) */
nuke(ep, -ESHUTDOWN);
/* Disable ep IRQ */
pio_irq_disable(ep_index(ep));
ep->desc = 0;
ep->stopped = 1;
spin_unlock_irqrestore(&ep->dev->lock, flags);
DEBUG("%s: disabled %s\n", __FUNCTION__, _ep->name);
return 0;
}
static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep,
unsigned gfp_flags)
{
struct lh7a40x_request *req;
DEBUG("%s, %p\n", __FUNCTION__, ep);
req = kmalloc(sizeof *req, gfp_flags);
if (!req)
return 0;
memset(req, 0, sizeof *req);
INIT_LIST_HEAD(&req->queue);
return &req->req;
}
static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *_req)
{
struct lh7a40x_request *req;
DEBUG("%s, %p\n", __FUNCTION__, ep);
req = container_of(_req, struct lh7a40x_request, req);
WARN_ON(!list_empty(&req->queue));
kfree(req);
}
static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned bytes,
dma_addr_t * dma, unsigned gfp_flags)
{
char *retval;
DEBUG("%s (%p, %d, %d)\n", __FUNCTION__, ep, bytes, gfp_flags);
retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM));
if (retval)
*dma = virt_to_bus(retval);
return retval;
}
static void lh7a40x_free_buffer(struct usb_ep *ep, void *buf, dma_addr_t dma,
unsigned bytes)
{
DEBUG("%s, %p\n", __FUNCTION__, ep);
kfree(buf);
}
/** Queue one request
* Kickstart transfer if needed
* NOTE: Sets INDEX register
*/
static int lh7a40x_queue(struct usb_ep *_ep, struct usb_request *_req,
unsigned gfp_flags)
{
struct lh7a40x_request *req;
struct lh7a40x_ep *ep;
struct lh7a40x_udc *dev;
unsigned long flags;
DEBUG("\n\n\n%s, %p\n", __FUNCTION__, _ep);
req = container_of(_req, struct lh7a40x_request, req);
if (unlikely
(!_req || !_req->complete || !_req->buf
|| !list_empty(&req->queue))) {
DEBUG("%s, bad params\n", __FUNCTION__);
return -EINVAL;
}
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
DEBUG("%s, bad ep\n", __FUNCTION__);
return -EINVAL;
}
dev = ep->dev;
if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
DEBUG("%s, bogus device state %p\n", __FUNCTION__, dev->driver);
return -ESHUTDOWN;
}
DEBUG("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length,
_req->buf);
spin_lock_irqsave(&dev->lock, flags);
_req->status = -EINPROGRESS;
_req->actual = 0;
/* kickstart this i/o queue? */
DEBUG("Add to %d Q %d %d\n", ep_index(ep), list_empty(&ep->queue),
ep->stopped);
if (list_empty(&ep->queue) && likely(!ep->stopped)) {
u32 csr;
if (unlikely(ep_index(ep) == 0)) {
/* EP0 */
list_add_tail(&req->queue, &ep->queue);
lh7a40x_ep0_kick(dev, ep);
req = 0;
} else if (ep_is_in(ep)) {
/* EP1 & EP3 */
usb_set_index(ep_index(ep));
csr = usb_read(ep->csr1);
pio_irq_enable(ep_index(ep));
if ((csr & USB_IN_CSR1_FIFO_NOT_EMPTY) == 0) {
if (write_fifo(ep, req) == 1)
req = 0;
}
} else {
/* EP2 */
usb_set_index(ep_index(ep));
csr = usb_read(ep->csr1);
pio_irq_enable(ep_index(ep));
if (!(csr & USB_OUT_CSR1_FIFO_FULL)) {
if (read_fifo(ep, req) == 1)
req = 0;
}
}
}
/* pio or dma irq handler advances the queue. */
if (likely(req != 0))
list_add_tail(&req->queue, &ep->queue);
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
/* dequeue JUST ONE request */
static int lh7a40x_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct lh7a40x_ep *ep;
struct lh7a40x_request *req;
unsigned long flags;
DEBUG("%s, %p\n", __FUNCTION__, _ep);
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (!_ep || ep->ep.name == ep0name)
return -EINVAL;
spin_lock_irqsave(&ep->dev->lock, 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) {
spin_unlock_irqrestore(&ep->dev->lock, flags);
return -EINVAL;
}
done(ep, req, -ECONNRESET);
spin_unlock_irqrestore(&ep->dev->lock, flags);
return 0;
}
/** Halt specific EP
* Return 0 if success
* NOTE: Sets INDEX register to EP !
*/
static int lh7a40x_set_halt(struct usb_ep *_ep, int value)
{
struct lh7a40x_ep *ep;
unsigned long flags;
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
DEBUG("%s, bad ep\n", __FUNCTION__);
return -EINVAL;
}
usb_set_index(ep_index(ep));
DEBUG("%s, ep %d, val %d\n", __FUNCTION__, ep_index(ep), value);
spin_lock_irqsave(&ep->dev->lock, flags);
if (ep_index(ep) == 0) {
/* EP0 */
usb_set(EP0_SEND_STALL, ep->csr1);
} else if (ep_is_in(ep)) {
u32 csr = usb_read(ep->csr1);
if (value && ((csr & USB_IN_CSR1_FIFO_NOT_EMPTY)
|| !list_empty(&ep->queue))) {
/*
* Attempts to halt IN endpoints will fail (returning -EAGAIN)
* if any transfer requests are still queued, or if the controller
* FIFO still holds bytes that the host hasn抰 collected.
*/
spin_unlock_irqrestore(&ep->dev->lock, flags);
DEBUG
("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n",
(csr & USB_IN_CSR1_FIFO_NOT_EMPTY),
!list_empty(&ep->queue));
return -EAGAIN;
}
flush(ep);
if (value)
usb_set(USB_IN_CSR1_SEND_STALL, ep->csr1);
else {
usb_clear(USB_IN_CSR1_SEND_STALL, ep->csr1);
usb_set(USB_IN_CSR1_CLR_DATA_TOGGLE, ep->csr1);
}
} else {
flush(ep);
if (value)
usb_set(USB_OUT_CSR1_SEND_STALL, ep->csr1);
else {
usb_clear(USB_OUT_CSR1_SEND_STALL, ep->csr1);
usb_set(USB_OUT_CSR1_CLR_DATA_REG, ep->csr1);
}
}
if (value) {
ep->stopped = 1;
} else {
ep->stopped = 0;
}
spin_unlock_irqrestore(&ep->dev->lock, flags);
DEBUG("%s %s halted\n", _ep->name, value == 0 ? "NOT" : "IS");
return 0;
}
/** Return bytes in EP FIFO
* NOTE: Sets INDEX register to EP
*/
static int lh7a40x_fifo_status(struct usb_ep *_ep)
{
u32 csr;
int count = 0;
struct lh7a40x_ep *ep;
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (!_ep) {
DEBUG("%s, bad ep\n", __FUNCTION__);
return -ENODEV;
}
DEBUG("%s, %d\n", __FUNCTION__, ep_index(ep));
/* LPD can't report unclaimed bytes from IN fifos */
if (ep_is_in(ep))
return -EOPNOTSUPP;
usb_set_index(ep_index(ep));
csr = usb_read(ep->csr1);
if (ep->dev->gadget.speed != USB_SPEED_UNKNOWN ||
csr & USB_OUT_CSR1_OUT_PKT_RDY) {
count = usb_read(USB_OUT_FIFO_WC1);
}
return count;
}
/** Flush EP FIFO
* NOTE: Sets INDEX register to EP
*/
static void lh7a40x_fifo_flush(struct usb_ep *_ep)
{
struct lh7a40x_ep *ep;
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
DEBUG("%s, bad ep\n", __FUNCTION__);
return;
}
usb_set_index(ep_index(ep));
flush(ep);
}
/****************************************************************/
/* End Point 0 related functions */
/****************************************************************/
/* return: 0 = still running, 1 = completed, negative = errno */
static int write_fifo_ep0(struct lh7a40x_ep *ep, struct lh7a40x_request *req)
{
u32 max;
unsigned count;
int is_last;
max = ep_maxpacket(ep);
DEBUG_EP0("%s\n", __FUNCTION__);
count = write_packet(ep, req, max);
/* last packet is usually short (or a zlp) */
if (unlikely(count != max))
is_last = 1;
else {
if (likely(req->req.length != req->req.actual) || req->req.zero)
is_last = 0;
else
is_last = 1;
}
DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__,
ep->ep.name, count,
is_last ? "/L" : "", req->req.length - req->req.actual, req);
/* requests complete when all IN data is in the FIFO */
if (is_last) {
done(ep, req, 0);
return 1;
}
return 0;
}
static __inline__ int lh7a40x_fifo_read(struct lh7a40x_ep *ep,
unsigned char *cp, int max)
{
int bytes;
int count = usb_read(USB_OUT_FIFO_WC1);
volatile u32 *fifo = (volatile u32 *)ep->fifo;
if (count > max)
count = max;
bytes = count;
while (count--)
*cp++ = *fifo & 0xFF;
return bytes;
}
static __inline__ void lh7a40x_fifo_write(struct lh7a40x_ep *ep,
unsigned char *cp, int count)
{
volatile u32 *fifo = (volatile u32 *)ep->fifo;
DEBUG_EP0("fifo_write: %d %d\n", ep_index(ep), count);
while (count--)
*fifo = *cp++;
}
static int read_fifo_ep0(struct lh7a40x_ep *ep, struct lh7a40x_request *req)
{
u32 csr;
u8 *buf;
unsigned bufferspace, count, is_short;
volatile u32 *fifo = (volatile u32 *)ep->fifo;
DEBUG_EP0("%s\n", __FUNCTION__);
csr = usb_read(USB_EP0_CSR);
if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY))
return 0;
buf = req->req.buf + req->req.actual;
prefetchw(buf);
bufferspace = req->req.length - req->req.actual;
/* read all bytes from this packet */
if (likely(csr & EP0_OUT_PKT_RDY)) {
count = usb_read(USB_OUT_FIFO_WC1);
req->req.actual += min(count, bufferspace);
} else /* zlp */
count = 0;
is_short = (count < ep->ep.maxpacket);
DEBUG_EP0("read %s %02x, %d bytes%s req %p %d/%d\n",
ep->ep.name, csr, count,
is_short ? "/S" : "", req, req->req.actual, req->req.length);
while (likely(count-- != 0)) {
u8 byte = (u8) (*fifo & 0xff);
if (unlikely(bufferspace == 0)) {
/* this happens when the driver's buffer
* is smaller than what the host sent.
* discard the extra data.
*/
if (req->req.status != -EOVERFLOW)
DEBUG_EP0("%s overflow %d\n", ep->ep.name,
count);
req->req.status = -EOVERFLOW;
} else {
*buf++ = byte;
bufferspace--;
}
}
/* completion */
if (is_short || req->req.actual == req->req.length) {
done(ep, req, 0);
return 1;
}
/* finished that packet. the next one may be waiting... */
return 0;
}
/**
* udc_set_address - set the USB address for this device
* @address:
*
* Called from control endpoint function after it decodes a set address setup packet.
*/
static void udc_set_address(struct lh7a40x_udc *dev, unsigned char address)
{
DEBUG_EP0("%s: %d\n", __FUNCTION__, address);
/* c.f. 15.1.2.2 Table 15-4 address will be used after DATA_END is set */
dev->usb_address = address;
usb_set((address & USB_FA_FUNCTION_ADDR), USB_FA);
usb_set(USB_FA_ADDR_UPDATE | (address & USB_FA_FUNCTION_ADDR), USB_FA);
/* usb_read(USB_FA); */
}
/*
* DATA_STATE_RECV (OUT_PKT_RDY)
* - if error
* set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits
* - else
* set EP0_CLR_OUT bit
if last set EP0_DATA_END bit
*/
static void lh7a40x_ep0_out(struct lh7a40x_udc *dev, u32 csr)
{
struct lh7a40x_request *req;
struct lh7a40x_ep *ep = &dev->ep[0];
int ret;
DEBUG_EP0("%s: %x\n", __FUNCTION__, csr);
if (list_empty(&ep->queue))
req = 0;
else
req = list_entry(ep->queue.next, struct lh7a40x_request, queue);
if (req) {
if (req->req.length == 0) {
DEBUG_EP0("ZERO LENGTH OUT!\n");
usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR);
dev->ep0state = WAIT_FOR_SETUP;
return;
}
ret = read_fifo_ep0(ep, req);
if (ret) {
/* Done! */
DEBUG_EP0("%s: finished, waiting for status\n",
__FUNCTION__);
usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR);
dev->ep0state = WAIT_FOR_SETUP;
} else {
/* Not done yet.. */
DEBUG_EP0("%s: not finished\n", __FUNCTION__);
usb_set(EP0_CLR_OUT, USB_EP0_CSR);
}
} else {
DEBUG_EP0("NO REQ??!\n");
}
}
/*
* DATA_STATE_XMIT
*/
static int lh7a40x_ep0_in(struct lh7a40x_udc *dev, u32 csr)
{
struct lh7a40x_request *req;
struct lh7a40x_ep *ep = &dev->ep[0];
int ret, need_zlp = 0;
DEBUG_EP0("%s: %x\n", __FUNCTION__, csr);
if (list_empty(&ep->queue))
req = 0;
else
req = list_entry(ep->queue.next, struct lh7a40x_request, queue);
if (!req) {
DEBUG_EP0("%s: NULL REQ\n", __FUNCTION__);
return 0;
}
if (req->req.length == 0) {
usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR);
dev->ep0state = WAIT_FOR_SETUP;
return 1;
}
if (req->req.length - req->req.actual == EP0_PACKETSIZE) {
/* Next write will end with the packet size, */
/* so we need Zero-length-packet */
need_zlp = 1;
}
ret = write_fifo_ep0(ep, req);
if (ret == 1 && !need_zlp) {
/* Last packet */
DEBUG_EP0("%s: finished, waiting for status\n", __FUNCTION__);
usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR);
dev->ep0state = WAIT_FOR_SETUP;
} else {
DEBUG_EP0("%s: not finished\n", __FUNCTION__);
usb_set(EP0_IN_PKT_RDY, USB_EP0_CSR);
}
if (need_zlp) {
DEBUG_EP0("%s: Need ZLP!\n", __FUNCTION__);
usb_set(EP0_IN_PKT_RDY, USB_EP0_CSR);
dev->ep0state = DATA_STATE_NEED_ZLP;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -