/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress;
__u8 bmAttributes;
__le16 wMaxPacketSize;
__u8 bInterval;
/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
#define USB_DT_ENDPOINT_SIZE 7
struct usb_ep {
void *driver_data;
const char *name;
const struct usb_ep_ops *ops;
struct list_head ep_list;
struct usb_ep_caps caps;
bool claimed;
bool enabled;
unsigned maxpacket:16;
unsigned maxpacket_limit:16;
unsigned max_streams:16;
unsigned mult:2;
unsigned maxburst:5;
u8 address;
const struct usb_endpoint_descriptor *desc;
const struct usb_ss_ep_comp_descriptor *comp_desc;
};
struct s3c2410_ep {
struct list_head queue;
unsigned long last_io; /* jiffies timestamp */
struct usb_gadget *gadget;
struct s3c2410_udc *dev;
struct usb_ep ep;
u8 num;
unsigned short fifo_size;
u8 bEndpointAddress;
u8 bmAttributes;
unsigned halted : 1;
unsigned already_seen : 1;
unsigned setup_stage : 1;
};
static const struct usb_ep_ops s3c2410_ep_ops = {
.enable = s3c2410_udc_ep_enable,
.disable = s3c2410_udc_ep_disable,
};
*
* s3c2410_udc_ep_enable
*/
static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct s3c2410_udc *dev;
struct s3c2410_ep *ep;
u32 max, tmp;
unsigned long flags;
u32 csr1, csr2;
u32 int_en_reg;
ep = to_s3c2410_ep(_ep);
if (!_ep || !desc
|| _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT)
return -EINVAL;
dev = ep->dev;
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
max = usb_endpoint_maxp(desc) & 0x1fff; ////根据endpoint 描述符设置 udc reg
local_irq_save(flags);
_ep->maxpacket = max & 0x7ff;
ep->ep.desc = desc;
ep->halted = 0;
ep->bEndpointAddress = desc->bEndpointAddress;
/* set max packet */
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(max >> 3, S3C2410_UDC_MAXP_REG);
/* set type, direction, address; reset fifo counters */
if (desc->bEndpointAddress & USB_DIR_IN) {
csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT;
csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN;
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);
} else {
/* don't flush in fifo or it will cause endpoint interrupt */
csr1 = S3C2410_UDC_ICSR1_CLRDT;
csr2 = S3C2410_UDC_ICSR2_DMAIEN;
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);
csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
csr2 = S3C2410_UDC_OCSR2_DMAIEN;
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(csr1, S3C2410_UDC_OUT_CSR1_REG);
udc_write(ep->num, S3C2410_UDC_INDEX_REG);
udc_write(csr2, S3C2410_UDC_OUT_CSR2_REG);
}
/* enable irqs */
int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
udc_write(int_en_reg | (1 << ep->num), S3C2410_UDC_EP_INT_EN_REG);
/* print some debug message */
tmp = desc->bEndpointAddress;
dprintk(DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
_ep->name, ep->num, tmp,
desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);
local_irq_restore(flags);
s3c2410_udc_set_halt(_ep, 0);
return 0;
}
/*
* s3c2410_udc_ep_disable
*/
static int s3c2410_udc_ep_disable(struct usb_ep *_ep)
{
struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
unsigned long flags;
u32 int_en_reg;
if (!_ep || !ep->ep.desc) {
dprintk(DEBUG_NORMAL, "%s not enabled\n",
_ep ? ep->ep.name : NULL);
return -EINVAL;
}
local_irq_save(flags);
dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name);
ep->ep.desc = NULL;
ep->halted = 1;
s3c2410_udc_nuke(ep->dev, ep, -ESHUTDOWN);
/* disable irqs */
int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
udc_write(int_en_reg & ~(1<<ep->num), S3C2410_UDC_EP_INT_EN_REG);
local_irq_restore(flags);
dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name);
return 0;
}