📄 s3c2410_udc.c
字号:
return container_of(gadget, struct s3c2410_udc, gadget);
}
static inline struct s3c2410_request *to_s3c2410_req(struct usb_request *req)
{
return container_of(req, struct s3c2410_request, req);
}
/*
* 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->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 = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff;
local_irq_save (flags);
_ep->maxpacket = max & 0x7ff;
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->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->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;
}
/*
* s3c2410_udc_alloc_request
*/
static struct usb_request *
s3c2410_udc_alloc_request(struct usb_ep *_ep, gfp_t mem_flags)
{
struct s3c2410_request *req;
dprintk(DEBUG_VERBOSE,"%s(%p,%d)\n", __func__, _ep, mem_flags);
if (!_ep)
return NULL;
req = kzalloc (sizeof(struct s3c2410_request), mem_flags);
if (!req)
return NULL;
INIT_LIST_HEAD (&req->queue);
return &req->req;
}
/*
* s3c2410_udc_free_request
*/
static void
s3c2410_udc_free_request(struct usb_ep *_ep, struct usb_request *_req)
{
struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
struct s3c2410_request *req = to_s3c2410_req(_req);
dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);
if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
return;
WARN_ON (!list_empty (&req->queue));
kfree(req);
}
/*
* s3c2410_udc_queue
*/
static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
gfp_t gfp_flags)
{
struct s3c2410_request *req = to_s3c2410_req(_req);
struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
struct s3c2410_udc *dev;
u32 ep_csr = 0;
int fifo_count = 0;
unsigned long flags;
if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__);
return -EINVAL;
}
dev = ep->dev;
if (unlikely (!dev->driver
|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
return -ESHUTDOWN;
}
local_irq_save (flags);
if (unlikely(!_req || !_req->complete
|| !_req->buf || !list_empty(&req->queue))) {
if (!_req)
dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);
else {
dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n",
__func__, !_req->complete,!_req->buf,
!list_empty(&req->queue));
}
local_irq_restore(flags);
return -EINVAL;
}
_req->status = -EINPROGRESS;
_req->actual = 0;
dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n",
__func__, ep->bEndpointAddress, _req->length);
if (ep->bEndpointAddress) {
udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG);
ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN)
? S3C2410_UDC_IN_CSR1_REG
: S3C2410_UDC_OUT_CSR1_REG);
fifo_count = s3c2410_udc_fifo_count_out();
} else {
udc_write(0, S3C2410_UDC_INDEX_REG);
ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
fifo_count = s3c2410_udc_fifo_count_out();
}
/* kickstart this i/o queue? */
if (list_empty(&ep->queue) && !ep->halted) {
if (ep->bEndpointAddress == 0 /* ep0 */) {
switch (dev->ep0state) {
case EP0_IN_DATA_PHASE:
if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY)
&& s3c2410_udc_write_fifo(ep,
req)) {
dev->ep0state = EP0_IDLE;
req = NULL;
}
break;
case EP0_OUT_DATA_PHASE:
if ((!_req->length)
|| ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
&& s3c2410_udc_read_fifo(ep,
req))) {
dev->ep0state = EP0_IDLE;
req = NULL;
}
break;
default:
local_irq_restore(flags);
return -EL2HLT;
}
} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
&& (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY))
&& s3c2410_udc_write_fifo(ep, req)) {
req = NULL;
} else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
&& fifo_count
&& s3c2410_udc_read_fifo(ep, req)) {
req = NULL;
}
}
/* pio or dma irq handler advances the queue. */
if (likely (req != 0))
list_add_tail(&req->queue, &ep->queue);
local_irq_restore(flags);
dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);
return 0;
}
/*
* s3c2410_udc_dequeue
*/
static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
struct s3c2410_udc *udc;
int retval = -EINVAL;
unsigned long flags;
struct s3c2410_request *req = NULL;
dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);
if (!the_controller->driver)
return -ESHUTDOWN;
if (!_ep || !_req)
return retval;
udc = to_s3c2410_udc(ep->gadget);
local_irq_save (flags);
list_for_each_entry (req, &ep->queue, queue) {
if (&req->req == _req) {
list_del_init (&req->queue);
_req->status = -ECONNRESET;
retval = 0;
break;
}
}
if (retval == 0) {
dprintk(DEBUG_VERBOSE,
"dequeued req %p from %s, len %d buf %p\n",
req, _ep->name, _req->length, _req->buf);
s3c2410_udc_done(ep, req, -ECONNRESET);
}
local_irq_restore (flags);
return retval;
}
/*
* s3c2410_udc_set_halt
*/
static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value)
{
struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
u32 ep_csr = 0;
unsigned long flags;
u32 idx;
if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
dprintk(DEBUG_NORMAL, "%s: inval 2\n", __func__);
return -EINVAL;
}
local_irq_save (flags);
idx = ep->bEndpointAddress & 0x7F;
if (idx == 0) {
s3c2410_udc_set_ep0_ss(base_addr);
s3c2410_udc_set_ep0_de_out(base_addr);
} else {
udc_write(idx, S3C2410_UDC_INDEX_REG);
ep_csr = udc_read((ep->bEndpointAddress &USB_DIR_IN)
? S3C2410_UDC_IN_CSR1_REG
: S3C2410_UDC_OUT_CSR1_REG);
if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
if (value)
udc_write(ep_csr | S3C2410_UDC_ICSR1_SENDSTL,
S3C2410_UDC_IN_CSR1_REG);
else {
ep_csr &= ~S3C2410_UDC_ICSR1_SENDSTL;
udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG);
ep_csr |= S3C2410_UDC_ICSR1_CLRDT;
udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG);
}
} else {
if (value)
udc_write(ep_csr | S3C2410_UDC_OCSR1_SENDSTL,
S3C2410_UDC_OUT_CSR1_REG);
else {
ep_csr &= ~S3C2410_UDC_OCSR1_SENDSTL;
udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG);
ep_csr |= S3C2410_UDC_OCSR1_CLRDT;
udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG);
}
}
}
ep->halted = value ? 1 : 0;
local_irq_restore (flags);
return 0;
}
static const struct usb_ep_ops s3c2410_ep_ops = {
.enable = s3c2410_udc_ep_enable,
.disable = s3c2410_udc_ep_disable,
.alloc_request = s3c2410_udc_alloc_request,
.free_request = s3c2410_udc_free_request,
.queue = s3c2410_udc_queue,
.dequeue = s3c2410_udc_dequeue,
.set_halt = s3c2410_udc_set_halt,
};
/*------------------------- usb_gadget_ops ----------------------------------*/
/*
* s3c2410_udc_get_frame
*/
static int s3c2410_udc_get_frame(struct usb_gadget *_gadget)
{
int tmp;
dprintk(DEBUG_VERBOSE, "%s()\n", __func__);
tmp = udc_read(S3C2410_UDC_FRAME_NUM2_REG) << 8;
tmp |= udc_read(S3C2410_UDC_FRAME_NUM1_REG);
return tmp;
}
/*
* s3c2410_udc_wakeup
*/
static int s3c2410_udc_wakeup(struct usb_gadget *_gadget)
{
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
return 0;
}
/*
* s3c2410_udc_set_selfpowered
*/
static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value)
{
struct s3c2410_udc *udc = to_s3c2410_udc(gadget);
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
if (value)
udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
else
udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
return 0;
}
static void s3c2410_udc_disable(struct s3c2410_udc *dev);
static void s3c2410_udc_enable(struct s3c2410_udc *dev);
static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on)
{
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
if (udc_info && udc_info->udc_command) {
if (is_on)
s3c2410_udc_enable(udc);
else {
if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
if (udc->driver && udc->driver->disconnect)
udc->driver->disconnect(&udc->gadget);
}
s3c2410_udc_disable(udc);
}
}
else
return -EOPNOTSUPP;
return 0;
}
static int s3c2410_udc_vbus_session(struct usb_gadget *gadget, int is_active)
{
struct s3c2410_udc *udc = to_s3c2410_udc(gadget);
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
udc->vbus = (is_active != 0);
s3c2410_udc_set_pullup(udc, is_active);
return 0;
}
static int s3c2410_udc_pullup(struct usb_gadget *gadget, int is_on)
{
struct s3c2410_udc *udc = to_s3c2410_udc(gadget);
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
s3c2410_udc_set_pullup(udc, is_on ? 0 : 1);
return 0;
}
static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev)
{
struct s3c2410_udc *dev = _dev;
unsigned int value;
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
value = s3c2410_gpio_getpin(udc_info->vbus_pin);
if (udc_info->vbus_pin_inverted)
value = !value;
if (value != dev->vbus)
s3c2410_udc_vbus_session(&dev->gadget, value);
return IRQ_HANDLED;
}
static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
{
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
if (udc_info && udc_info->vbus_draw) {
udc_info->vbus_draw(ma);
return 0;
}
return -ENOTSUPP;
}
static const struct usb_gadget_ops s3c2410_ops = {
.get_frame = s3c2410_udc_get_frame,
.wakeup = s3c2410_udc_wakeup,
.set_selfpowered = s3c2410_udc_set_selfpowered,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -