📄 superh_udc.c
字号:
/* * Renesas SuperH USB 1.1 device controller (found on SH7705, SH7727...) * * Copyright (C) 2003 Renesas Technology Europe Limited * Copyright (C) 2003 Julian Back (jback@mpc-data.co.uk), MPC Data Limited * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* * This is a driver for the USB Device Controller found on Renesas SH * processors. This is a full-speed controller which has four * endpoints in a single fixed configuration. * * Limitations * * Only tested on SH7705. Mostly tested with Mass Storage gadget * using Bulk-Only Transport. It has been tested with Linux 2.4, * Linux 2.6, Windows 2000 and Windows XP hosts. * * DMA is not (yet) implemented. * * Handling of application stalls is tricky. We set a bit to stall an * endpoint. When the host tries to access the ep it gets a stall and * another stall bit is latched by the device. The host clears the * stall with a clear feature but the hardware doesn't inform us, the * latched bit is cleared but not the bit we have set, so the next * time the host accesses the ep it will get another stall and the * latch will be set again unless we have cleared our stall bit. The * solution adopted in this driver is to use a timer to clear the * application stall bit some time after setting the stall. This * seems to work most of the time but is not 100% reliable. Because * of this it is best to avoid USB protocols that require the USB * device to stall the host. Unfortunately USB mass storage does * require the device to stall when it gets unsupported commands, * Linux hosts don't send any of these unsupported commands but * Windows hosts do. * * Another place where the hardware is too clever is in the handling * of setup packets. Many setup packets including SET_INTERFACE and * SET_CONFIGURATION are handled by the hardware without informing the * driver software. But we need to inform the gadget driver of at * least one of these as it uses this to kick of it's data processing. * The solution adopted is that after we have recieved N setup packets * following a bus reset a fake SET_CONFIGURATION is sent to the * gadget. We also have to arrange things so that the reply to the * fake packet is not sent out. * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/ioport.h>#include <linux/types.h>#include <linux/version.h>#include <linux/errno.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/mm.h>#include <asm/atomic.h>#include <asm/byteorder.h>#include <asm/dma.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/unaligned.h>#include <linux/usb_ch9.h>#include <linux/usb_gadget.h>#undef DEBUG#undef VERY_NOISY#define DRIVER_DESC "SuperH USB Peripheral Controller"#define DRIVER_VERSION "alpha (11 November 2003)"#ifdef USE_DMA#error "DMA not supported"#endifstatic const char driver_name [] = "superh_udc";static const char driver_desc [] = DRIVER_DESC;static const char ep0name [] = "ep0";static const char *ep_name [] = { ep0name, "ep1out-bulk", "ep2in-bulk", "ep3in-bulk",};static struct superh_udc *the_controller;#include "superh_udc.h"/* High priority interrupts */#define F0_HIGH (EP1_FULL | EP2_TR | EP2_EMPTY )#define F1_HIGH (0)/* Low priority interrupts */#define F0_LOW (BRST | SETUP_TS | EP0o_TS | EP0i_TR | EP0i_TS)#define F1_LOW (EP3_TR | EP3_TS | VBUSF)/* How long to leave the stall bit set - this value is quite critical * to making stalls work. Unfortunately it doesn't seem possible to * get a value that will work reliably with both fast and slow * machines. */#define STALL_TIME (HZ/75)/* Number of endpoints to check in the unstall timer. It should not * be necessary to unstall bulk endpoints using the timer as long as * the gadget code is aware that this device cannot stall properly * (see the file backed storage gadget for an example). But if the * UDC driver stalls ep0 due to a bad SETUP then the timer is still * required otherwise the stall will never get cleared. If it is * necessary to unstall all endpoints using the timer then set this to * 4. */#define EP_TO_UNSTALL 1/* Number of packets to wait for before sending a fake * SET_CONFIGURATION to the gadget driver */#define DEFAULT_SETUP_COUNT 7#define RESET_SETUP_COUNT 2/* How long to wait for the number of packets specified above */#define SETUP_TIME (HZ/10 )static void superh_ep_fifo_flush(struct usb_ep *_ep);static void stop_activity(struct superh_udc *dev, struct usb_gadget_driver *driver);static int superh_ep_set_halt(struct usb_ep *_ep, int value);static void udc_timer(unsigned long _dev);static struct superh_request* process_ep_req(struct superh_ep *ep, struct superh_request *req);static void done(struct superh_ep *ep, struct superh_request *req, int status);/* * IO */static inline void and_b(u8 mask, unsigned long addr){ ctrl_outb(ctrl_inb(addr) & mask, addr);}static inline void or_b(u8 mask, unsigned long addr){ ctrl_outb(ctrl_inb(addr) | mask, addr);}static inline void ep0_idle (struct superh_udc *dev){ DBG(DBG_VERY_NOISY, "ep0_idle\n"); dev->ep0state = EP0_IDLE;}static void init_udc_timer(struct superh_udc *dev){ init_timer(&dev->timer); dev->timer.function = udc_timer; dev->timer.data = (unsigned long) dev; dev->timer.expires = jiffies + STALL_TIME; add_timer(&dev->timer);}/* Send a fake SET_CONFIGURATION to the gadget to start it up. * Needed because the hardware doesn't let us know when the real packet * has arrived. */static void send_fake_config(struct superh_udc *dev){ struct usb_ctrlrequest r; dev->fake_config = 1; dev->setup_countdown = 0; r.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; r.bRequest = USB_REQ_SET_CONFIGURATION; r.wValue = 1; /* configuration to select */ r.wIndex = 0; r.wLength = 0; if (dev->driver->setup(&dev->gadget, &r) < 0) { DMSG("SET_CONFIGURATION failed.\n"); }}/* * Timer function. Clears stall from any stalled endpoints as we * don't get informed when the host has sent a clear feature. */static void udc_timer(unsigned long _dev){ struct superh_udc *dev = (void *)_dev; int i; unsigned long flags; local_irq_save(flags); if (atomic_read(&dev->in_interrupt) == 0) { /* Check if a bus reset has been done and we haven't faked a SET_CONFIGURATION */ if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->setup_countdown > 0 && jiffies - dev->reset_time > SETUP_TIME &&list_empty(&dev->ep[0].queue)) { send_fake_config(dev); } /* Check if any end points are halted and restart them */ for (i = 0; i < EP_TO_UNSTALL; i++) { struct superh_ep *ep = &dev->ep[i]; if (ep->halted) { DBG(DBG_VERBOSE, "unstalling ep %d\n", i); superh_ep_set_halt(&ep->ep, 0); if (likely (!list_empty(&ep->queue))) { struct superh_request *req = list_entry(ep->queue.next, struct superh_request, queue); process_ep_req(ep, req); } } } } init_udc_timer(dev); local_irq_restore(flags);}/* * done - retire a request; caller blocked irqs */static void done(struct superh_ep *ep, struct superh_request *req, int status){ unsigned stopped = ep->stopped; DBG(DBG_NOISY, "done: %s %p %d\n", ep->ep.name, req, status); list_del_init(&req->queue); if (likely (req->req.status == -EINPROGRESS)) req->req.status = status; else status = req->req.status; if (status && status != -ESHUTDOWN) DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n", ep->ep.name, &req->req, status, req->req.actual, req->req.length); /* don't modify queue heads during completion callback */ ep->stopped = 1; req->req.complete(&ep->ep, &req->req); ep->stopped = stopped;}/* * Enable interrupts for the specified endpoint */static inline void pio_irq_enable(struct superh_ep *ep){ or_b(ep->interrupt_mask, ep->interrupt_reg);}/* * Disable interrupts for the specified endpoint */static inline void pio_irq_disable(struct superh_ep *ep){ and_b(~ep->interrupt_mask, ep->interrupt_reg);}/* * nuke - dequeue ALL requests */static void nuke(struct superh_ep *ep, int status){ struct superh_request *req; DBG(DBG_NOISY, "nuke %s %d\n", ep->ep.name, status); /* 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 superh_request, queue); done(ep, req, status); } if (ep->desc) pio_irq_disable (ep);}static inline void clear_ep_state (struct superh_udc *dev){ unsigned i; /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint * fifos, and pending transactions mustn't be continued in any case. */ for (i = 1; i < 4; i++) nuke(&dev->ep[i], -ECONNABORTED);}/* * write a packet to an endpoint data register */static intwrite_packet(u32 epdr, struct superh_request *req, unsigned max){ u8 *buf; unsigned length, count; buf = req->req.buf + req->req.actual; prefetch(buf); /* how big will this packet be? */ length = min(req->req.length - req->req.actual, max); req->req.actual += length; count = length; while (likely(count--)) ctrl_outb(*buf++, epdr); return length;}static intwrite_ep0_fifo (struct superh_ep *ep, struct superh_request *req){ unsigned count; int is_short; count = write_packet(USBEPDR0I, req, EP0_FIFO_SIZE); ep->dev->stats.write.bytes += count; /* last packet "must be" short (or a zlp) */ is_short = (count != EP0_FIFO_SIZE); DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count, req->req.length - req->req.actual, req); ctrl_outb(EP0i_PKTE, USBTRG); if (unlikely (is_short)) { ep->dev->ep0state = EP0_END_XFER; count = req->req.length; done (ep, req, 0); /* * If we have received a specified number of setups * after a bus reset or connect then fake a * SET_CONFIGURATION to the driver (as we don't get * them from the hardware). */ if (ep->dev->setup_countdown >= 0) ep->dev->setup_countdown--; if (ep->dev->setup_countdown == 0) { send_fake_config(ep->dev); } } return is_short;}/* * handle_ep0_setup * * Handles a SETUP request on EP0 */static void handle_ep0_setup(struct superh_udc* dev){ int i; union { u8 raw [8]; struct usb_ctrlrequest r; } u; for (i = 0; i < 8; i++) { u.raw[i] = ctrl_inb(USBEPDR0S); } /* Send ACK */ ctrl_outb(EP0s_RDFN, USBTRG); le16_to_cpus (&u.r.wValue); le16_to_cpus (&u.r.wIndex); le16_to_cpus (&u.r.wLength); DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n", u.r.bRequestType, u.r.bRequest, u.r.wValue, u.r.wIndex, u.r.wLength); if (u.r.bRequestType & USB_DIR_IN) { DBG(DBG_VERY_NOISY, "handle_ep0_setup: EP0_IN_DATA_PHASE\n"); dev->ep0state = EP0_IN_DATA_PHASE; } else { DBG(DBG_VERY_NOISY, "handle_ep0_setup: EP0_OUT_DATA_PHASE\n"); dev->ep0state = EP0_OUT_DATA_PHASE; } i = dev->driver->setup(&dev->gadget, &u.r); if (i < 0) { DMSG("SETUP %02x.%02x v%04x i%04x l%04x failed\n", u.r.bRequestType, u.r.bRequest, u.r.wValue, u.r.wIndex, u.r.wLength); superh_ep_set_halt(&dev->ep[0].ep, 1); }}/* * write to an IN endpoint fifo, as many packets as possible. * irqs will use this to write the rest later. * caller guarantees at least one packet buffer is ready. */static intwrite_fifo (struct superh_ep *ep, struct superh_request *req){ unsigned max; DBG(DBG_VERY_NOISY, "write_fifo\n"); if ((ep->bEndpointAddress & USB_DIR_IN) != USB_DIR_IN) { DMSG("write_fifo from invalid EP (%s)\n", ep->ep.name); return -EINVAL; } max = ep->desc->wMaxPacketSize; do {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -