自己娯楽自楽8のLinux UDC駆動4(自作udc駆動、基本機能完了)
直接コードをつけて、私の前に書いたテンプレートと比較することができます
/***********************************
Copyright(C), 2013 LDP
FileName: s3c2440_udc.h
Author: wwxxxxll
Date:
Description:
History:
Author Date Desc
************************************/
#ifndef __S3C2440_UDC_H__
#define __S3C2440_UDC_H__
/************* **************/
#define S3C2440_DEBUG_FS // debugfs
#define DEBUG
#ifdef DEBUG
#define printInfo(ARGs...) printk(ARGs)
#else
#define printInfo(ARGs...)
#endif
//struct usb_ep_ops
//#define S3C2440_NEWSTYLE // udc_start
#define S3C2440_SETWEDHE // set_weght
#define S3C2440_FIFO_STATUS // fifo_status
#define S3C2440_FIFO_FLUSH // fifo_flush
//struct usb_gadget_ops
#define S3C2440_S3C2440_GET_FRAME // get_frame
#define S3C2440_WAKEUP // wakeup
#define S3C2440_SELFPOWERED //selfpowered
//#define S3C2440_VBUS_SESSION //vbus
//#define S3C2440_VBBUS_DRAW
#define S3C2440X_PULLUP //usb
//s3c2440
// CLKSLOW UPLL
//CLKCON USB device , clk,
// , ,
//
#define S3C2440_HAVE_CLK // CLK
#ifdef S3C2440_HAVE_CLK
#define CLK_DELAY_TIME 10 //ms
#endif
#define S3C2440_USE_IRQ
//
#define S3C2440_ENDPOINTS 5 //
//
#define EP0_FIFO_SIZE 8
#define EP1_FIFO_SIZE 64
#define EP2_FIFO_SIZE 64
#define EP3_FIFO_SIZE 64
#define EP4_FIFO_SIZE 64
#define EP1_ADDRESS 1
#define EP2_ADDRESS 2
#define EP3_ADDRESS 3
#define EP4_ADDRESS 4
#define EP1_ATTR USB_ENDPOINT_XFER_BULK
#define EP2_ATTR USB_ENDPOINT_XFER_BULK
#define EP3_ATTR USB_ENDPOINT_XFER_BULK
#define EP4_ATTR USB_ENDPOINT_XFER_BULK
//fifo
#define S3C2440_EP0_FIFO_SIZE 16
#define S3C2440_EP1_FIFO_SIZE 128
#define S3C2440_EP2_FIFO_SIZE 128
#define S3C2440_EP3_FIFO_SIZE 128
#define S3C2440_EP4_FIFO_SIZE 128
/***********************************/
/************* ************/
//s3c2440 MISCCR usb1
// , 。MISCCR USB
//
// 。
//
#define FUNC_ADDR_REG 0x140
//func_addr_reg usb , , 7
#define PWR_REG 0x144
/*
pwr_reg:
3: USB_RESET R , USB
2: MCS_RESUME R/W MCU MCU , , 10ms
1: SUSPEND_MODE R USB 。
0: SUBSPEND_EN R ,0: 1:
*/
// MCU ,MCU 。
#define EP_INT_REG 0x148
#define USB_INT_REG 0x158
//
#define EP_INT_EN_REG 0x15c
#define USB_INT_EN_REG 0x16c
//
#define FRAME_NUM1_REG 0x170 //
#define FRAME_NUM2_REG 0x174 //
// INDEX (INDEX_REG)( :0X178) 。 EP0
//CSR , IN_CSR1 ‘0x00’ INDEX_REG
#define INDEX_REG 0x178
#define MAXP_REG 0x180
/*
:
EP0 MAXP=8
EP1~4 MAXP=64, 64 data0 data1
*/
#define EP0_CSR 0x184
#define IN_CSR1_REG 0x184
#define IN_CSR2_REG 0x188
#define OUT_CSR1_REG 0x190
#define OUT_CSR2_REG 0x194
//FIFO
//
// , MCU
#define OUT_FIFO_CNT1 0x198
#define OUT_FIFO_CNT2 0x19c
//EPn_FIFO_REG MCU EPn FIFO
#define EP0_FIFO 0x1c0
#define EP1_FIFO 0x1c4
#define EP2_FIFO 0x1c8
#define EP3_FIFO 0x1cc
#define EP4_FIFO 0x1d0
//DMA
#define EP1_DMA_CON 0x200
#define EP2_DMA_CON 0x218
#define EP3_DMA_CON 0x240
#define EP4_DMA_CON 0x258
#define EP1_DMA_UNIT 0x204
#define EP2_DMA_UNIT 0x21c
#define EP3_DMA_UNIT 0x244
#define EP4_DMA_UNIT 0x25c
#define EP1_DMA_FIFO 0x208
#define EP2_DMA_FIFO 0x220
#define EP3_DMA_FIFO 0x248
#define EP4_DMA_FIFO 0x260
#define EP1_DMA_TTC_L 0x20c
#define EP1_DMA_TTC_M 0x210
#define EP1_DMA_TTC_H 0x214
#define EP2_DMA_TTC_L 0x224
#define EP2_DMA_TTC_M 0x228
#define EP2_DMA_TTC_H 0x22c
#define EP3_DMA_TTC_L 0x24c
#define EP3_DMA_TTC_M 0x250
#define EP3_DMA_TTC_H 0x254
#define EP4_DMA_TTC_L 0x264
#define EP4_DMA_TTC_M 0x268
#define EP4_DMA_TTC_H 0x26c
/***********************************/
/************ ***************/
#define WRITE_REG(_s3c2440_udc, reg, data) writel(data, _s3c2440_udc->virl_addr + reg)
#define READ_REG(_s3c2440_udc, reg) readl(_s3c2440_udc->virl_addr + reg)
#define SETB(_s3c2440_udc, reg, n) (writel((readl(_s3c2440_udc->virl_addr + reg) | (1 << n)), _s3c2440_udc->virl_addr + reg))
#define CLRB(_s3c2440_udc, reg, n) (writel((readl(_s3c2440_udc->virl_addr + reg) & (~(1 << n))), _s3c2440_udc->virl_addr + reg))
#define GETB(_s3c2440_udc, reg, n) ((readl(_s3c2440_udc->virl_addr + reg) >> n) & 1)
// D+ gpc5, , mmap gpio,
#define PULL_UP() do { \
writel((readl(S3C2410_GPCCON) | (1 << 10)) & (~(1 << 11)), S3C2410_GPCCON); \
writel(readl(S3C2410_GPCUP) & (~(1 << 5)), S3C2410_GPCUP); \
writel(readl(S3C2410_GPCDAT) | (1 << 5), S3C2410_GPCDAT); \
}while(0);
#define PULL_DOWN() do { \
writel((readl(S3C2410_GPCCON) | (1 << 10)) & (~(1 << 11)), S3C2410_GPCCON); \
writel(readl(S3C2410_GPCUP) & (~(1 << 5)), S3C2410_GPCUP); \
writel(readl(S3C2410_GPCDAT) & (~(1 << 5)), S3C2410_GPCDAT); \
}while(0);
/***********************************/
/************* **************/
// setup_end
#define EP0_CLRSE(_s3c2440_udc) do { \
WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \
SETB(_s3c2440_udc, EP0_CSR, 7); \
}while(0)
//out
#define EP0_CLROPR(_s3c2440_udc) do { \
WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \
SETB(_s3c2440_udc, EP0_CSR, 6); \
}while(0)
#define EP0_SETDE(_s3c2440_udc) do {\
WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \
SETB(_s3c2440_udc, EP0_CSR, 3); \
}while(0)
// stall
#define EP0_CLRSST(_s3c2440_udc) do { \
WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \
CLRB(_s3c2440_udc, EP0_CSR, 5); \
}while(0)
// stall
#define EP0_SETSST(_s3c2440_udc) do { \
WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \
SETB(_s3c2440_udc, EP0_CSR, 5); \
}while(0)
//
#define EP0_CLRDE(_s3c2440_udc) do { \
WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \
CLRB(_s3c2440_udc, EP0_CSR, 2); \
}while(0)
//in
#define EP0_SETIPR(_s3c2440_udc) do { \
WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \
SETB(_s3c2440_udc, EP0_CSR, 1); \
}while(0)
/***********************************/
struct s3c2440_ep
{
struct usb_ep ep; //
struct list_head queue;
struct s3c2440_udc *dev;
const struct usb_endpoint_descriptor *desc;
unsigned char fifosize;
unsigned char bEndpointAddress;
unsigned char bmAttributes;
u16 fifo_size;
u8 num;
unsigned stopped :1;//
#ifdef S3C2440_SETWEDHE
unsigned wedged :1;
#endif
};
#define to_s3c2440_ep(ep_p) container_of(ep_p, struct s3c2440_ep, ep)
struct s3c2440_request
{
struct list_head queue; /* ep's requests */
struct usb_request req; // urb
};
#define to_s3c2440_req(req_p) container_of(req_p, struct s3c2440_request, req)
// , ,
// ,
enum ep0state {
EP0_IDLE,
EP0_IN,
EP0_OUT,
EP0_STALL,
};
struct s3c2440_udc
{
spinlock_t lock;
void __iomem *virl_addr;
u32 phy_addr;
u32 reg_size;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
enum ep0state ep0state;
struct s3c2440_ep ep[S3C2440_ENDPOINTS];
struct s3c2440_request fifo_req;
#ifdef S3C2440_DEBUG_FS
struct dentry *debug_info;
#endif
#ifdef S3C2440_HAVE_CLK
struct clk *s3c2440_clk_upll;
struct clk *s3c2440_clk_udc;
#endif
#ifdef S3C2440_USE_IRQ
unsigned int irq_num;
#endif
u16 devstatus;
};
#define to_s3c2440_udc(gadget_p) container_of(gadget_p, struct s3c2440_udc, gadget)
#endif//__S3C2440_UDC_H__
/***********************************
Copyright(C), 2013 LDP
FileName: s3c2440_udc.c
Author: wwxxxxll
Date:
Description: linux-3.2-36
History:
Author Date Desc
************************************/
#include <linux/module.h>//MODULE_*
#include <linux/init.h>//printInfo
#include <linux/slab.h>//kzalloc() kfree()
#include <linux/usb/gadget.h>//struct usb_gadget
#include <linux/clk.h>//struct clk
#include <linux/platform_device.h>//platform
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/prefetch.h>
#include <asm/irq.h>
#include <asm/io.h>//ioremap
#include <mach/regs-gpio.h>
#include "s3c2440_udc.h"
#ifdef S3C2440_DEBUG_FS
#include <linux/debugfs.h>
#include <linux/seq_file.h>//seq_printf seq_read
#endif
#define DRIVER_DESC "S3C2440 USB Device Controller Gadget"
#define DRIVER_VERSION "2013"
#define DRIVER_AUTHOR "wwxxxxll"
static const char gadget_name[] = "s3c2440_udc";
static const char driver_desc[] = DRIVER_DESC;
//
// epautoconf.c
/* type-restriction: "-iso", "-bulk", or "-int".
* direction-restriction: "in", "out".
*/
//
static const char ep0name[] = "ep0";
static const char * const ep_name[] = {
ep0name,
"ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk",
};
// mount -t debugfs none /sys/kernel/debug/
///sys/kernel/debug
#ifdef S3C2440_DEBUG_FS
static struct dentry *s3c2440_udc_debugfs_root;
static int s3c2440_udc_debugfs_seq_show(struct seq_file *m, void *p)
{
seq_printf(m, "My name is %s
", gadget_name);
return 0;
}
static int s3c2440_udc_debugfs_fops_open(struct inode *inode,
struct file *file)
{
return single_open(file, s3c2440_udc_debugfs_seq_show, NULL);
}
static const struct file_operations s3c2440_udc_debugfs_fops =
{
.open = s3c2440_udc_debugfs_fops_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
#endif
/***********************hardware_handler************************/
//s3c2440 usb , start ,imx
static void s3c2440_usb_reset(struct s3c2440_udc *dev)
{
//disable intterupt
WRITE_REG(dev, EP_INT_EN_REG, 0x00);
WRITE_REG(dev, USB_INT_EN_REG, 0x00);
//clear intterupt flag
WRITE_REG(dev, EP_INT_REG, 0x1f);
WRITE_REG(dev, USB_INT_REG, 0x07);
PULL_DOWN();
dev->gadget.speed = USB_SPEED_UNKNOWN;
}
static void s3c2440_udc_enable(struct s3c2440_udc *dev)
{
int i;
dev->gadget.speed = USB_SPEED_FULL;//s3c2440 1.1
for (i = 0; i < S3C2440_ENDPOINTS; i++)//
{
WRITE_REG(dev, INDEX_REG, i);
WRITE_REG(dev, MAXP_REG, dev->ep[i].ep.maxpacket >> 3);
}
//SETB(dev, PWR_REG, 0);//enable suspend
//enable intterupt
SETB(dev, EP_INT_EN_REG, 0);
WRITE_REG(dev, USB_INT_EN_REG, 0x07);
#ifndef S3C2440_NEWSTYLE
PULL_UP();
#endif
}
static void s3c2440_usb_fifocnt(struct s3c2440_udc *dev, u32 *fifo_size)
{
*fifo_size = READ_REG(dev, OUT_FIFO_CNT1);
*fifo_size |= READ_REG(dev, OUT_FIFO_CNT2) << 8;
}
static u32 s3c2440_read_ctrlq(struct s3c2440_udc *dev, struct usb_ctrlrequest *_ctrlq)
{
u32 count;
WRITE_REG(dev, INDEX_REG, 0);
s3c2440_usb_fifocnt(dev, &count);
count = (count > sizeof(struct usb_ctrlrequest)) ? sizeof(struct usb_ctrlrequest) : count;
readsb(EP0_FIFO + dev->virl_addr, (unsigned char *)_ctrlq, count);
/*
_ctrlq->bRequest
bit7: 10 int 0: out
bit 6:5:
bit 4:0:
*/
printInfo( "Host: bRequest = %02x bRequestType = %02x \
wValue = 0x%x wIndex=0x%x wLength=0x%x
", _ctrlq->bRequest, _ctrlq->bRequestType, \
_ctrlq->wValue, _ctrlq->wIndex, _ctrlq->wLength);
return count;
}
static int s3c2440_get_status(struct s3c2440_udc *dev, struct usb_ctrlrequest *crq)
{
u16 status = 0;
u8 ep_num = crq->wIndex & 0x7F;//
u8 is_in = crq->wIndex & USB_DIR_IN;//
switch (crq->bRequestType & USB_RECIP_MASK)
{
case USB_RECIP_INTERFACE://2.0
break;
case USB_RECIP_DEVICE:// 0 ,1
status = dev->devstatus;
break;
case USB_RECIP_ENDPOINT://0 halt, ep halt, 1
if (ep_num > 4 || crq->wLength > 2)//crq->wLength 2
return 1;
WRITE_REG(dev, INDEX_REG, ep_num);
status = 0;
if (ep_num == 0)
{
status = GETB(dev, EP0_CSR, 5);
}
else
{
if (is_in)
{
status = GETB(dev, IN_CSR1_REG, 4);
}
else
{
status = GETB(dev, OUT_CSR1_REG, 5);
}
}
break;
default:
return 1;
}
WRITE_REG(dev, EP0_FIFO, status & 0xFF);
WRITE_REG(dev, EP0_FIFO, status >> 8);
EP0_SETIPR(dev);
EP0_SETDE(dev);
return 0;
}
static void s3c2440_udc_done(struct s3c2440_ep *ep, struct s3c2440_request *req, int status);
/*
1:
0:
-1:
*/
static int s3c2440_read_fifo(struct s3c2440_udc *dev, struct s3c2440_ep *ep, struct s3c2440_request *req)
{
u32 fifo_reg;
u8 *buf;
u32 idx = 0;
u32 avail, len, bufspace;
u32 fifo_count = 0;
int ret = 0;
//printInfo( "%s
", __func__);
idx = ep->bEndpointAddress & 0x7f;
if (idx > 4)
{
idx = 0;
}
WRITE_REG(dev, INDEX_REG, idx);
s3c2440_usb_fifocnt(dev, &fifo_count);
if (fifo_count == 0)
{
return 0;
}
fifo_reg = EP0_FIFO + 4 * idx;
if (req->req.length == 0)
{
return 1;
}
if (req->req.length <= req->req.actual)
{
return -1;
}
bufspace = req->req.length - req->req.actual;
buf = req->req.buf + req->req.actual;
avail = (fifo_count > ep->ep.maxpacket) ? ep->ep.maxpacket : fifo_count;// ep->ep.maxpacket
len = (bufspace < avail) ? bufspace : avail;
req->req.actual += len;
readsb(fifo_reg + dev->virl_addr, buf, len);
//req->req.actual ,req->req.length
//printInfo("read: fifo_count = %d, req->req.actual = %d, req->req.length = %d len = %d avail = %d
", fifo_count, req->req.actual, req->req.length, len, avail);
if (fifo_count < ep->ep.maxpacket)
{
ret = 1;
if (len != avail)
{
req->req.status = -EOVERFLOW;//
}
}
if (ret)
{
if (idx == 0)
{
EP0_SETDE(dev);
ep->dev->ep0state = EP0_IDLE;
}
else
{
CLRB(dev, OUT_CSR1_REG, 0);
}
s3c2440_udc_done(ep, req, 0);
}
else
{
if (idx == 0)
{
EP0_CLROPR(dev);
}
else
{
//SETB(dev, OUT_CSR1_REG, 4);
CLRB(dev, OUT_CSR1_REG, 0);
}
}
return ret;
}
#ifdef DEBUG
static int printDesc = 0;
#endif
static int s3c2440_write_fifo(struct s3c2440_udc *dev, struct s3c2440_ep *ep, struct s3c2440_request *req)
{
u32 fifo_reg;
u8 *buf;
u32 idx = 0;
u32 len;
int ret = 0;
struct usb_device_descriptor *desc;
struct usb_string_descriptor *string;
struct usb_config_descriptor *config;
u16 language;
u32 n;
u8 *tmp;
#ifdef DEBUG
//printInfo( "%s
", __func__);
switch (printDesc)
{
case USB_DT_DEVICE:
desc = (struct usb_device_descriptor*)req->req.buf;
printInfo( "Slave: length = %d Vendor = %x Product = %x Device = %x iManufacturer = %d iProduct = %d iSerialNumber = %d bNumConfigurations = %d
", \
desc->bLength, le16_to_cpu(desc->idVendor), le16_to_cpu(desc->idProduct), le16_to_cpu(desc->bcdDevice),\
desc->iManufacturer,desc->iProduct,desc->iSerialNumber,desc->bNumConfigurations);
break;
case USB_DT_DEVICE_QUALIFIER:
break;
case USB_DT_OTHER_SPEED_CONFIG:
break;
case USB_DT_CONFIG:
config = (struct usb_config_descriptor *)req->req.buf;
printInfo( "Slave: length = %d TotalLength = %d NumInterfaces = %d ConfigurationValue = %d iConfiguration = %d bMaxPower = %d
", \
config->bLength, le16_to_cpu(config->wTotalLength), config->bNumInterfaces, config->bConfigurationValue, config->iConfiguration, config->bMaxPower);
break;
case USB_DT_STRING:
string = (struct usb_string_descriptor *)req->req.buf;
printInfo( "Slave: length = %d
", string->bLength);
language = cpu_to_le16(0x0409);// , gadget
if (string->bLength == 4)//
{
break;
}
for (tmp = (u8 *)string->wData, n = 0; n < string->bLength; n++, tmp++)
{
if (*tmp == language)
{
}
else
{
printInfo( "%c", *tmp);//
}
}
printInfo("
");
break;
case USB_DT_BOS:
break;
default:
break;
}
printDesc = 0;
#endif
idx = ep->bEndpointAddress & 0x7f;
if (idx > 4)
{
idx = 0;
}
fifo_reg = EP0_FIFO + 4 * idx;
len = ((req->req.length - req->req.actual) < ep->ep.maxpacket) ? (req->req.length - req->req.actual) : ep->ep.maxpacket;
buf = req->req.buf + req->req.actual;
prefetch(buf);//prefetch cache , , writesb
req->req.actual += len;
writesb(fifo_reg + dev->virl_addr, buf, len);
//req->req.actual
//printInfo( " %dbytes ", req->req.actual);
if (len != ep->ep.maxpacket)
ret = 1;
else if (req->req.length != req->req.actual || req->req.zero)//zero 1
ret = 0;
else
ret = 2;
//printInfo( \
"Written ep%d %d.%d of %d b [last %d,z %d], max = %d
", \
idx, len, req->req.actual, req->req.length, \
ret, req->req.zero,ep->ep.maxpacket);
if (ret)
{
if (idx == 0)
{
if (!GETB(dev, USB_INT_REG, 2))
{
EP0_SETIPR(dev);
EP0_SETDE(dev);
}
ep->dev->ep0state = EP0_IDLE;
}
else
{
SETB(dev, IN_CSR1_REG, 0);
}
printInfo("write done
");
s3c2440_udc_done(ep, req, 0);
}
else
{
if (idx == 0)
{
if (!GETB(dev, USB_INT_REG, 2))
{
EP0_SETIPR(dev);
}
}
else
{
SETB(dev, IN_CSR1_REG, 0);
}
}
return ret;
}
static void s3c2440_dequeue_all(struct s3c2440_ep *ep, int status);
static int s3c2440_udc_set_halt(struct usb_ep *_ep, int value);
static void s3c2440_udc_handle_ep0_idle(struct s3c2440_udc *dev, struct s3c2440_ep *ep, u32 ep0_csr)
{
struct usb_ctrlrequest ctrlq;
int tmp;
bool config = 0;
//printInfo( "%s
", __func__);
if (!(ep0_csr & 1))//
{
return;
}
s3c2440_dequeue_all(ep, -EPROTO);
if (s3c2440_read_ctrlq(dev, &ctrlq) < sizeof(struct usb_ctrlrequest))
{
EP0_SETSST(dev);
return;
}
//EP0_CLROPR ,EP0_SETDE
switch (ctrlq.bRequest)
{
case USB_REQ_GET_STATUS: printInfo( "USB_REQ_GET_STATUS
");
EP0_CLROPR(dev);
if ((ctrlq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
{
if (!s3c2440_get_status(dev, &ctrlq))
{
return;
}
}
break;
case USB_REQ_CLEAR_FEATURE: printInfo( "USB_REQ_CLEAR_FEATURE
");
EP0_CLROPR(dev);
if (ctrlq.bRequestType != USB_RECIP_ENDPOINT)
break;
if (ctrlq.wValue != USB_ENDPOINT_HALT || ctrlq.wLength != 0)
break;
s3c2440_udc_set_halt(&dev->ep[ctrlq.wIndex & 0x7f].ep, 0);
EP0_CLROPR(dev);
EP0_SETDE(dev);
return;
case USB_REQ_SET_FEATURE: printInfo( "USB_REQ_SET_FEATURE
");
EP0_CLROPR(dev);
if (ctrlq.bRequestType != USB_RECIP_ENDPOINT)
break;
if (ctrlq.wValue != USB_ENDPOINT_HALT || ctrlq.wLength != 0)
break;
s3c2440_udc_set_halt(&dev->ep[ctrlq.wIndex & 0x7f].ep, 1);
EP0_CLROPR(dev);
EP0_SETDE(dev);
return;
case USB_REQ_SET_ADDRESS: printInfo( "USB_REQ_SET_ADDRESS
");
if (ctrlq.bRequestType == USB_RECIP_DEVICE)
{
tmp = ctrlq.wValue & 0x7F;
WRITE_REG(dev, FUNC_ADDR_REG, (1 << 7) | tmp);
EP0_CLROPR(dev);
EP0_SETDE(dev);
dev->ep0state = EP0_IDLE;
return;
}
break;
case USB_REQ_GET_DESCRIPTOR: printInfo( "USB_REQ_GET_DESCRIPTOR
");
switch (ctrlq.wValue >> 8)
{
case USB_DT_DEVICE: printInfo( "USB_DT_DEVICE
");
break;
// , USB
// , (Device_Qualifier)。
case USB_DT_DEVICE_QUALIFIER: printInfo( "USB_DT_DEVICE_QUALIFIER
");
break;
case USB_DT_OTHER_SPEED_CONFIG: printInfo( "USB_DT_OTHER_SPEED_CONFIG
");
break;
case USB_DT_CONFIG: printInfo( "USB_DT_CONFIG
");
break;
case USB_DT_STRING: printInfo( "USB_DT_STRING
");
break;
// , USB
// ,
case USB_DT_BOS: printInfo( "USB_DT_BOS
");
break;
}
EP0_CLROPR(dev);
break;
case USB_REQ_SET_DESCRIPTOR: printInfo( "USB_REQ_SET_DESCRIPTOR
");
EP0_CLROPR(dev);
break;
case USB_REQ_GET_CONFIGURATION: printInfo( "USB_REQ_GET_CONFIGURATION
");
EP0_CLROPR(dev);
break;
case USB_REQ_SET_CONFIGURATION:
if (ctrlq.bRequestType == USB_RECIP_DEVICE)
{
printInfo( "USB_REQ_SET_CONFIGURATION
");
config = 1;
EP0_CLROPR(dev);
EP0_SETDE(dev);
}
break;
case USB_REQ_GET_INTERFACE: printInfo( "USB_REQ_GET_INTERFACE
");
EP0_CLROPR(dev);
break;
case USB_REQ_SET_INTERFACE:
if (ctrlq.bRequestType == USB_RECIP_INTERFACE)
{
printInfo( "SB_REQ_SET_INTERFACE
");
config = 1;
EP0_CLROPR(dev);
EP0_SETDE(dev);
}
break;
case USB_REQ_SYNCH_FRAME: printInfo( "USB_REQ_SYNCH_FRAME
");
EP0_CLROPR(dev);
break;
}
if (config != 1)//
{
if (ctrlq.bRequestType & USB_DIR_IN)
dev->ep0state = EP0_IN;
else
dev->ep0state = EP0_OUT;
}
if (!dev->driver)
return;
#ifdef DEBUG
// queue()
switch (ctrlq.bRequest)
{
case USB_REQ_GET_DESCRIPTOR:
switch (ctrlq.wValue >> 8)
{
case USB_DT_DEVICE:printDesc = USB_DT_DEVICE;
break;
case USB_DT_DEVICE_QUALIFIER: printDesc = USB_DT_DEVICE_QUALIFIER;
break;
case USB_DT_OTHER_SPEED_CONFIG: printDesc = USB_DT_OTHER_SPEED_CONFIG;
break;
case USB_DT_CONFIG: printDesc = USB_DT_CONFIG;
break;
case USB_DT_STRING: printDesc = USB_DT_STRING;
break;
case USB_DT_BOS:
break;
}
break;
}
#endif
if (dev->driver->setup(&dev->gadget, &ctrlq) < 0)
{
if (config == 1)// , send stall,
{
return;
}
EP0_SETSST(dev);
EP0_SETDE(dev);
dev->ep0state = EP0_IDLE;
}
}
void s3c2440_handle_ep0(struct s3c2440_udc *dev)
{
struct s3c2440_ep *ep = &dev->ep[0];
struct s3c2440_request *req;
u32 ep0_csr = 0;
if (!list_empty(&ep->queue))
req = list_entry(ep->queue.next, struct s3c2440_request, queue);
else
req = NULL;
WRITE_REG(dev, INDEX_REG, 0);
ep0_csr = READ_REG(dev, EP0_CSR);
if (ep0_csr & (1 << 5))//send_stall
{
s3c2440_dequeue_all(ep, -EPIPE);// complete
EP0_CLRSST(dev);
dev->ep0state = EP0_IDLE;
return;
}
if (ep0_csr & (1 << 4))//setup_end
{
s3c2440_dequeue_all(ep, 0);
EP0_CLRSE(dev);
dev->ep0state = EP0_IDLE;
}
switch (dev->ep0state) {
case EP0_IDLE:
s3c2440_udc_handle_ep0_idle(dev, ep, ep0_csr);
break;
case EP0_IN:
if ((!(ep0_csr & (1 << 1))) && req)
{
s3c2440_write_fifo(dev, ep, req);
}
break;
case EP0_OUT:
if ((ep0_csr & 1) && req)
{
s3c2440_read_fifo(dev, ep, req);
}
break;
case EP0_STALL:
dev->ep0state = EP0_IDLE;
break;
}
}
void s3c2440_handle_ep(struct s3c2440_udc *dev, u8 n)
{
struct s3c2440_ep *ep = &dev->ep[n];
struct s3c2440_request *req;
u32 ep_csr1;
if (!list_empty(&ep->queue))
req = list_entry(ep->queue.next, struct s3c2440_request, queue);
else
req = NULL;
WRITE_REG(dev, INDEX_REG, n);
if (ep->bEndpointAddress & USB_DIR_IN)
{
ep_csr1 = READ_REG(dev, IN_CSR1_REG);
if (ep_csr1 & (1 << 5))//SENT_STALL
{
CLRB(dev, IN_CSR1_REG, 5);
return;
}
if ((!(ep_csr1 & 1)) && req)
{
s3c2440_write_fifo(dev, ep, req);
}
}
else
{
ep_csr1 = READ_REG(dev, OUT_CSR1_REG);
if (ep_csr1 & (1 << 6)) //SENT_STALL
{
CLRB(dev, OUT_CSR1_REG, 6);
return;
}
if ((ep_csr1 & 1) && req)
{
s3c2440_read_fifo(dev, ep, req);
}
}
}
//udc , ,
// , ,
// , ep0, fifo usb_ctrlrequest
// ,
static irqreturn_t s3c2440_udc_irq(int dummy, void *_dev)
{
struct s3c2440_udc *dev = (struct s3c2440_udc *)_dev;
unsigned long flags;
int usb_status;
int ep_status;
int pwr_reg;
int ep0csr;
u8 n;
//printInfo( "enter irq
");
spin_lock_irqsave(&dev->lock, flags);
usb_status = READ_REG(dev, USB_INT_REG);
ep_status = READ_REG(dev, EP_INT_REG);
//printInfo( "USB_INT_REG = 0x%x
", usb_status);
//printInfo( "EP_INT_REG = 0x%x
", ep_status);
/* Driver connected ? */
if (!dev->driver)
{
/* Clear interrupts */
WRITE_REG(dev, USB_INT_REG, READ_REG(dev, USB_INT_REG));
WRITE_REG(dev, EP_INT_REG, READ_REG(dev, EP_INT_REG));
}
//reset
if (usb_status & (1 << 2))
{
printInfo( "USB reset
");
WRITE_REG(dev, INDEX_REG, 0);
WRITE_REG(dev, MAXP_REG, (dev->ep[0].ep.maxpacket & 0x7ff) >> 3);
dev->ep0state = EP0_IDLE;
dev->gadget.speed = USB_SPEED_FULL;
s3c2440_dequeue_all(&dev->ep[0], -EPROTO);
SETB(dev, USB_INT_REG, 2);
spin_unlock_irqrestore(&dev->lock, flags);
return IRQ_HANDLED;
}
//resume
if (usb_status & (1 << 1))
{
printInfo( "USB resume
");
SETB(dev, USB_INT_REG, 1);
if (dev->gadget.speed != USB_SPEED_UNKNOWN
&& dev->driver
&& dev->driver->resume)
dev->driver->resume(&dev->gadget);
}
//suspend
if (usb_status & 1)
{
printInfo( "USB suspend
");
SETB(dev, USB_INT_REG, 0);
if (dev->gadget.speed != USB_SPEED_UNKNOWN
&& dev->driver
&& dev->driver->suspend)
dev->driver->suspend(&dev->gadget);
dev->ep0state = EP0_IDLE;
}
if (ep_status & 1)
{
//printInfo( "USB ep0 irq
");
SETB(dev, EP_INT_REG, 0);
s3c2440_handle_ep0(dev);
}
for (n = 1; n < S3C2440_ENDPOINTS; n++)
{
if (ep_status & (1 << n))
{
//printInfo( "USB ep%d irq
", n);
SETB(dev, EP_INT_REG, n);//
s3c2440_handle_ep(dev, n);
}
}
pwr_reg = READ_REG(dev, PWR_REG);
WRITE_REG(dev, INDEX_REG, 0);
ep0csr = READ_REG(dev, EP0_CSR);
if (!usb_status && !ep_status && !pwr_reg && !ep0csr)
{
for (n = 1; n < S3C2440_ENDPOINTS; n++)
{
WRITE_REG(dev, INDEX_REG, n);
if (GETB(dev, OUT_CSR1_REG, 0))
{
s3c2440_handle_ep(dev, n);
}
}
}
spin_unlock_irqrestore(&dev->lock, flags);
return IRQ_HANDLED;
}
/***************************************************************/
/***********************queue***********************************/
// usb , list
// list , gadget
static void s3c2440_usb_reinit(struct s3c2440_udc *dev)
{
u8 i;
/* device/ep0 records init */
INIT_LIST_HEAD (&dev->gadget.ep_list);
dev->gadget.ep0 = &dev->ep[0].ep;//ep0
dev->ep0state = EP0_IDLE;
INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
for (i = 0; i < S3C2440_ENDPOINTS; i++) {
struct s3c2440_ep *ep = &dev->ep[i];
if (i != 0)
list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
ep->dev = dev;
ep->desc = NULL;
ep->stopped = 0;
INIT_LIST_HEAD (&ep->queue);
}
}
static void s3c2440_udc_done(struct s3c2440_ep *ep, struct s3c2440_request *req, int status)
{
struct s3c2440_udc *dev;
unsigned stopped = ep->stopped;
list_del_init(&req->queue);
if (likely (req->req.status == -EINPROGRESS))//
req->req.status = status;
else
status = req->req.status;
dev = ep->dev;
/* don't modify queue heads during completion callback */
ep->stopped = 1;
// , dequeue_all
spin_unlock(&dev->lock);
req->req.complete(&ep->ep, &req->req);
spin_lock(&dev->lock);
ep->stopped = stopped;
}
static void s3c2440_dequeue_all(struct s3c2440_ep *ep, int status)
{
struct s3c2440_request *req;
if (&ep->queue == NULL)
return;
while (!list_empty(&ep->queue)) //list_del_init
{
req = list_entry(ep->queue.next, struct s3c2440_request, queue);
s3c2440_udc_done(ep, req, status);
}
}
/***************************************************************/
//may not be the endpoint named "ep0". gadget.h
/**************************ep_ops*******************************/
//
// , enable disable
static int s3c2440_udc_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
{
struct s3c2440_udc *dev;
struct s3c2440_ep *ep;
u32 max;
unsigned long flags;
printInfo( "%s
", __func__);
ep = to_s3c2440_ep(_ep);
if (!_ep || !desc || ep->desc
|| (desc->bDescriptorType != USB_DT_ENDPOINT)
|| (_ep->name == ep0name))
return -EINVAL;
dev = ep->dev;
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
max = usb_endpoint_maxp(desc) & 0x1fff;
spin_lock_irqsave(&dev->lock, flags);
_ep->maxpacket = max & 0x7fff;
ep->desc = desc;
ep->stopped = 0;
#ifdef S3C2440_SETWEDHE
ep->wedged = 0;
#endif
ep->bEndpointAddress = desc->bEndpointAddress;
WRITE_REG(dev, INDEX_REG, ep->num);
WRITE_REG(dev, MAXP_REG, max >> 3);
if (desc->bEndpointAddress & USB_DIR_IN)
{
//SETB(dev, IN_CSR1_REG, 0);// IN_PKT_RDY
SETB(dev, IN_CSR1_REG, 3);//FLUSH fifo
SETB(dev, IN_CSR1_REG, 6);//CLR DATA
SETB(dev, IN_CSR2_REG, 4);// in dma , dma
//CLRB(dev, IN_CSR2_REG, 4);// in dma
SETB(dev, IN_CSR2_REG, 5);//in
CLRB(dev, IN_CSR2_REG, 6);//
}
else
{
SETB(dev, IN_CSR1_REG, 6);//CLR DATA
SETB(dev, IN_CSR2_REG, 4);// in dma , dma
CLRB(dev, IN_CSR2_REG, 5);//out
//SETB(dev, OUT_CSR1_REG, 0);// IN_PKT_RDY
SETB(dev, OUT_CSR1_REG, 7);
SETB(dev, OUT_CSR1_REG, 4);//FLUSH fifo
CLRB(dev, OUT_CSR2_REG, 6);//
SETB(dev, OUT_CSR2_REG, 5);// out dma , dma
//CLRB(dev, OUT_CSR2_REG, 5);// out dma
}
SETB(dev, EP_INT_EN_REG, ep->num);//
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
static int s3c2440_udc_ep_disable(struct usb_ep *_ep)
{
struct s3c2440_udc *dev;
struct s3c2440_ep *ep = to_s3c2440_ep(_ep);
unsigned long flags;
printInfo( "%s
", __func__);
if (!_ep || !ep->desc) {
return -EINVAL;
}
local_irq_save(flags);
ep->desc = NULL;
ep->stopped = 1;
dev = ep->dev;
// list ep
s3c2440_dequeue_all(ep, -ESHUTDOWN);//
CLRB(dev, EP_INT_REG, ep->num);// ep
local_irq_restore(flags);
return 0;
}
//
static struct usb_request *s3c2440_udc_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
{
struct s3c2440_request *req;
printInfo( "%s
", __func__);
if (!_ep)
return NULL;
req = kzalloc (sizeof(struct s3c2440_request), gfp_flags);
if (!req)
return NULL;
INIT_LIST_HEAD (&req->queue);
return &req->req;
}
//
static void s3c2440_udc_free_request(struct usb_ep *_ep, struct usb_request *_req)
{
//struct s3c2440_ep *ep = to_s3c2440_ep(_ep);
struct s3c2440_request *req = to_s3c2440_req(_req);
printInfo( "%s
", __func__);
if (!_ep || !_req)
return;
WARN_ON (!list_empty (&req->queue));
kfree(req);
req = NULL;
}
// queue
//dequeue
static int s3c2440_udc_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
{
struct s3c2440_udc *dev;
unsigned long flags;
struct s3c2440_request *req = to_s3c2440_req(_req);
struct s3c2440_ep *ep = to_s3c2440_ep(_ep);
u32 ep_csr;
printInfo( "%s
", __func__);
if (unlikely (!_ep || (!ep->desc && ep->num != 0))) // (_ep [ep->desc 0 ])
{
return -EINVAL;
}
dev = ep->dev;
if (unlikely (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))
{
return -ESHUTDOWN;
}
local_irq_save (flags); // queue , list_empty
if (unlikely(!_req || !_req->complete
|| !_req->buf || !list_empty(&req->queue))) //_req _req->buf 、complete 、req->queue
{
local_irq_restore(flags);
return -EINVAL;
}
_req->status = -EINPROGRESS;
_req->actual = 0;
WRITE_REG(dev, INDEX_REG, ep->bEndpointAddress & 0x7F);
//s3c2440_usb_fifocnt(dev, &fifo_count);
if ((ep->bEndpointAddress & 0x7F) == 0)
{
ep_csr = READ_REG(dev, EP0_CSR);
}
else
{
if (ep->bEndpointAddress & USB_DIR_IN)
{
ep_csr = READ_REG(dev, IN_CSR1_REG);
}
else
{
ep_csr = READ_REG(dev, OUT_CSR1_REG);
}
//(ep->bEndpointAddress & USB_DIR_IN) ? IN_CSR1_REG : OUT_CSR1_REG);
}
if (list_empty(&ep->queue) && !ep->stopped)
{
if (ep->bEndpointAddress == 0)
{
switch(dev->ep0state)
{
case EP0_IN:
if (!(ep_csr & (1 << 1)))
{
if (s3c2440_write_fifo(dev, ep, req))
{
dev->ep0state = EP0_IDLE;
req = NULL;
}
}
break;
case EP0_OUT:
if (ep_csr & 1)
{
if (s3c2440_read_fifo(dev, ep, req))
{
dev->ep0state = EP0_IDLE;
req = NULL;
}
}
break;
default:
local_irq_restore(flags);
return -EL2HLT;
}
}
else if ((ep->bEndpointAddress & USB_DIR_IN) != 0)
{
if ((!(ep_csr & 1)) && req)
{
if (s3c2440_write_fifo(dev, ep, req))
{
req = NULL;
}
}
}
else
{
if ((ep_csr & 1) && req)
{
if (s3c2440_read_fifo(dev, ep, req))
{
req = NULL;
}
}
}
}
if (likely(req != 0))
list_add_tail(&req->queue, &ep->queue);// list
local_irq_restore(flags);
return 0;
}
static int s3c2440_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct s3c2440_ep *ep = to_s3c2440_ep(_ep);
struct s3c2440_udc *dev;
int retval = -EINVAL;
unsigned long flags;
struct s3c2440_request *req = NULL;
printInfo( "%s
", __func__);
if (!_ep || !_req)
return retval;
dev = ep->dev;
if (!dev->driver)
return -ESHUTDOWN;
local_irq_save (flags);
list_for_each_entry (req, &ep->queue, queue)
{
if (&req->req == _req)
{
list_del_init (&req->queue);
_req->status = -ECONNRESET;//Connection reset by peer
retval = 0;
break;
}
}
if (retval == 0)
{
s3c2440_udc_done(ep, req, -ECONNRESET);
}
local_irq_restore (flags);
return retval;
}
#ifdef S3C2440_FIFO_STATUS
//fifo , fifo 。
// usb_ep_fifo_statu() fifo -EOPNOTSUPP
//net2272 EP_AVAIL fifo 。
//s3c2440 , , -EOPNOTSUPP
static int s3c2440_udc_fifo_status(struct usb_ep *_ep)
{
struct s3c2440_ep *ep;
u16 retval = 0;
printInfo( "%s
", __func__);
ep = to_s3c2440_ep(_ep);
if (!_ep || (!ep->desc && ep->num != 0))
return -ENODEV;
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
//retval =
return retval;
}
#endif
#ifdef S3C2440_FIFO_FLUSH
// fifo , ,
static void s3c2440_udc_fifo_flush(struct usb_ep *_ep)
{
struct s3c2440_ep *ep;
printInfo( "%s
", __func__);
ep = to_s3c2440_ep(_ep);
if (!_ep || (!ep->desc && ep->num != 0))
return;
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
return;
//
}
#endif
/*
usb_ep_set_wedge
CLEAR_FEATURE 。 Gadget , Unwedge
wedge
set_wedge 。 set_halt(ep, 1);
( file_storage.c )
Bulk-only
CBW
Bulk-only Spec IN 。 reset,
CLEAR_FEATURE 。 , !
net2272 ,
value=1:set_halt
= 0:clear_halt
*/
static int s3c2440_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
{
struct s3c2440_ep *ep;
unsigned long flags;
int ret = 0;
struct s3c2440_udc *dev;
ep = container_of(_ep, struct s3c2440_ep, ep);
if (!_ep || (!ep->desc && ep->num != 0))
return -EINVAL;
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
return -ESHUTDOWN;
if (ep->desc /* not ep0 */ && usb_endpoint_xfer_isoc(ep->desc))// ,
return -EINVAL;
dev = ep->dev;
spin_lock_irqsave(&ep->dev->lock, flags);
if (!list_empty(&ep->queue))
ret = -EAGAIN;
#ifdef S3C2440_FIFO_STATUS
else if ((ep->bEndpointAddress & USB_DIR_IN) && value && s3c2440_udc_fifo_status(_ep) != 0)//fifo_status
ret = -EAGAIN;
#endif
else {
WRITE_REG(dev, INDEX_REG, ep->num);
/* set/clear */
if (value) {
if (ep->num == 0)
{
ep->dev->ep0state = EP0_STALL;
//net2272 0 setup , 。s3c2440
//ep->dev->protocol_stall = 1;
//ep0 set_halt
EP0_SETSST(dev);
}
else
{
//epx(x != 0) set_halt
if ((ep->bEndpointAddress & USB_DIR_IN) != 0)
{
SETB(dev, IN_CSR1_REG, 4);
}
else
{
SETB(dev, OUT_CSR1_REG, 5);
}
}
if (wedged)// wedged
ep->wedged = 1;
} else {
if (ep->num == 0)
{
ep->dev->ep0state = EP0_IDLE;
EP0_CLRSST(dev);
}
else
{
//epx(x != 0) set_halt
if ((ep->bEndpointAddress & USB_DIR_IN) != 0)
{
CLRB(dev, IN_CSR1_REG, 4);
}
else
{
CLRB(dev, OUT_CSR1_REG, 5);
}
}
//ep clear_halt
ep->wedged = 0;
}
}
ep->stopped = value ? 1 : 0;
spin_unlock_irqrestore(&ep->dev->lock, flags);
return ret;
}
//_ep , 。
static int s3c2440_udc_set_halt(struct usb_ep *_ep, int value)
{
printInfo( "%s
", __func__);
return s3c2440_set_halt_and_wedge(_ep, value, 0);
}
#ifdef S3C2440_SETWEDHE
static int s3c2440_udc_set_wedge(struct usb_ep *_ep)
{
printInfo( "%s
", __func__);
if (!_ep || _ep->name == ep0name)// 0
return -EINVAL;
return s3c2440_set_halt_and_wedge(_ep, 1, 1);
}
#endif
static const struct usb_ep_ops s3c2440_ep_ops =
{
.enable = s3c2440_udc_ep_enable,
.disable = s3c2440_udc_ep_disable,
.alloc_request = s3c2440_udc_alloc_request,
.free_request = s3c2440_udc_free_request,
.queue = s3c2440_udc_queue,
.dequeue = s3c2440_udc_dequeue,
.set_halt = s3c2440_udc_set_halt,
#ifdef S3C2440_SETWEDHE
.set_wedge = s3c2440_udc_set_wedge,
#endif
#ifdef S3C2440_FIFO_STATUS
.fifo_status = s3c2440_udc_fifo_status,
#endif
#ifdef S3C2440_FIFO_FLUSH
.fifo_flush = s3c2440_udc_fifo_flush,
#endif
};
/***************************************************************/
//USB : 、 、 、 、 、
// 、 。
/**************************usb_gadget_ops***********************/
//
// , USB , (SOF) 。
// ,
//
static int s3c2440_udc_get_frame(struct usb_gadget *usb_gdt_p)
{
printInfo( "%s
", __func__);
#ifdef S3C2440_GET_FRAME
struct s3c2440_udc *dev = to_s3c2440_udc(usb_gdt_p);
int retval = 0;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
retval = READ_REG(dev, S3C2410_UDC_FRAME_NUM2_REG) << 8;
retval |= READ_REG(dev, S3C2410_UDC_FRAME_NUM1_REG);
spin_unlock_irqrestore(&dev->lock, flags);
return retval;
#else
return -EOPNOTSUPP;
#endif
}
#ifdef S3C2440_WAKEUP
// , net2272。 usbctl0
// usbctl1 1 resume,s3c2440 PWR_REG
static int s3c2440_udc_wakeup(struct usb_gadget *usb_gdt_p)
{
struct s3c2440_udc *dev = to_s3c2440_udc(usb_gdt_p);
unsigned long flags;
printInfo( "%s
", __func__);
spin_lock_irqsave(&dev->lock, flags);
if (GETB(dev, PWR_REG, 0))//
{
SETB(dev, PWR_REG, 2);
}
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
#endif
#ifdef S3C2440_SELFPOWERED
// (selfpowered feature), 。USB_RECIP_DEVICE
static int s3c2440_udc_set_selfpowered (struct usb_gadget *usb_gdt_p, int is_selfpowered)
{
struct s3c2440_udc *dev = to_s3c2440_udc(usb_gdt_p);
printInfo( "%s
", __func__);
if (is_selfpowered)
dev->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
else
dev->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
return 0;
}
#endif
#ifdef S3C2440_VBUS_SESSION
//vbus usb , 。 gpio
// vbus , s3c2410 at91 , usb D+ gpio
// 1 0 usb
static int s3c2440_udc_vbus_session (struct usb_gadget *usb_gdt_p, int is_active)
{
struct s3c2440_udc *dev = to_s3c2440_udc(usb_gdt_p);
unsigned long flags;
printInfo( "%s
", __func__);
spin_lock_irqsave(&dev->lock, flags);
//
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
#endif
#ifdef S3C2440_VBBUS_DRAW
// vbus , SET_CONFIGRATION , vbus
//vbus ,
// usb , gta02 , pcf50633( )
static int s3c2440_udc_vbus_draw (struct usb_gadget *usb_gdt_p, unsigned mA)
{
return 0;
}
#endif
#ifdef S3C2440X_PULLUP
// vbus_session
//vbus_session vbus
//pullup usb
// udc-core.c newstyle probe , udc_start udc_stop,
// , sysfs 。 newstyle 。
//composite.c
// is_on connect disconnect usb
//net2272 USBCTL0 ,s3c2440 gpio vbus_session
//
static int s3c2440_udc_pullup (struct usb_gadget *usb_gdt_p, int is_on)
{
struct s3c2440_udc *dev = to_s3c2440_udc(usb_gdt_p);
unsigned long flags;
printInfo( "%s
", __func__);
spin_lock_irqsave(&dev->lock, flags);
if (is_on)
{
PULL_UP();
}
else
{
PULL_DOWN();
}
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
#endif
// , linux-3.2.36 /gadget
static int s3c2440_udc_ioctl(struct usb_gadget *usb_gdt_p, unsigned code, unsigned long param)
{
return 0;
}
// , , struct usb_dcd_config_params
/*
struct usb_dcd_config_params {
__u8 bU1devExitLat; // U1 Device exit Latency u1
#define USB_DEFAULT_U1_DEV_EXIT_LAT 0x01 // Less then 1 microsec 1
__le16 bU2DevExitLat; // U2 Device exit Latency
#define USB_DEFAULT_U2_DEV_EXIT_LAT 0x1F4 // Less then 500 microsec
};
struct usb_ss_cap_descriptor
I/O
*/
static void s3c2440_udc_get_config_params(struct usb_dcd_config_params *usb_dc_cfg_pm)
{
}
// udc-core.c start udc_start , bind() ,
// start bind
//udc_start non-control , ,net2272 r8a66597
#ifdef S3C2440_NEWSTYLE
static int s3c2440_udc_start(struct usb_gadget *usb_gdt_p, struct usb_gadget_driver *driver);
static int s3c2440_udc_stop(struct usb_gadget *usb_gdt_p, struct usb_gadget_driver *driver);
#else
//s3c2410 s3c2440
static int s3c2440_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *));
static int s3c2440_stop(struct usb_gadget_driver *driver);
#endif
static const struct usb_gadget_ops s3c2440_ops =
{
.get_frame = s3c2440_udc_get_frame,
#ifdef S3C2440_WAKEUP
.wakeup = s3c2440_udc_wakeup,
#endif
#ifdef S3C2440_SELFPOWERED
.set_selfpowered = s3c2440_udc_set_selfpowered,
#endif
#ifdef S3C2440_VBUS_SESSION
.vbus_session = s3c2440_udc_vbus_session,
#endif
#ifdef S3C2440_VBBUS_DRAW
.vbus_draw = s3c2440_udc_vbus_draw,
#endif
#ifdef S3C2440X_PULLUP
.pullup = s3c2440_udc_pullup,
#endif
.ioctl = s3c2440_udc_ioctl,
.get_config_params = s3c2440_udc_get_config_params,
#ifdef S3C2440_NEWSTYLE
.udc_start = s3c2440_udc_start,
.udc_stop = s3c2440_udc_stop,
#else
.start = s3c2440_start,
.stop = s3c2440_stop,
#endif
};
/***************************************************************/
/***************************************************************/
static struct s3c2440_udc udc_info = {
.gadget = {
.ops = &s3c2440_ops,
.ep0 = &udc_info.ep[0].ep,
.name = gadget_name,
.dev = {
.init_name = "gadget",
},
/*
unsigned is_dualspeed:1;
unsigned is_otg:1;
unsigned is_a_peripheral:1;
unsigned b_hnp_enable:1; //hnp: otg
unsigned a_hnp_support:1;
unsigned a_alt_hnp_support:1;
*/
},
/* control endpoint */
.ep[0] = {
.num = 0,
.ep =
{
.name = "ep0",
.ops = &s3c2440_ep_ops,
.maxpacket = EP0_FIFO_SIZE,
},
.dev = &udc_info,
.fifo_size = S3C2440_EP0_FIFO_SIZE,
},
/* first group of endpoints */
.ep[1] = {
.num = 1,
.ep =
{
.name = "ep1-bulk",
.ops = &s3c2440_ep_ops,
.maxpacket = EP1_FIFO_SIZE,
},
.dev = &udc_info,
.fifo_size = S3C2440_EP1_FIFO_SIZE,
.bEndpointAddress = EP1_ADDRESS,
.bmAttributes = EP1_ATTR,
},
.ep[2] = {
.num = 2,
.ep =
{
.name = "ep2-bulk",
.ops = &s3c2440_ep_ops,
.maxpacket = EP2_FIFO_SIZE,
},
.dev = &udc_info,
.fifo_size = S3C2440_EP2_FIFO_SIZE,
.bEndpointAddress = EP2_ADDRESS,
.bmAttributes = EP2_ATTR,
},
.ep[3] = {
.num = 3,
.ep =
{
.name = "ep3-bulk",
.ops = &s3c2440_ep_ops,
.maxpacket = EP3_FIFO_SIZE,
},
.dev = &udc_info,
.fifo_size = S3C2440_EP3_FIFO_SIZE,
.bEndpointAddress = EP3_ADDRESS,
.bmAttributes = EP3_ATTR,
},
.ep[4] = {
.num = 4,
.ep =
{
.name = "ep4-bulk",
.ops = &s3c2440_ep_ops,
.maxpacket = EP4_FIFO_SIZE,
},
.dev = &udc_info,
.fifo_size = S3C2440_EP4_FIFO_SIZE,
.bEndpointAddress = EP4_ADDRESS,
.bmAttributes = EP4_ATTR,
},
};
static void stop_activity(struct s3c2440_udc *dev, struct usb_gadget_driver *driver)
{
unsigned i;
if (dev->gadget.speed == USB_SPEED_UNKNOWN)
driver = NULL;
/* disconnect gadget driver after quiesceing hw and the driver */
s3c2440_usb_reset(dev);// disable
for (i = 0; i < S3C2440_ENDPOINTS; i++)
{
s3c2440_dequeue_all(&dev->ep[i], -ECONNABORTED);
}
#ifndef S3C2440_NEWSTYLE
/*
if (udc_is_newstyle(udc)) {
udc->driver->disconnect(udc->gadget);
udc->driver->unbind(udc->gadget);
usb_gadget_udc_stop(udc->gadget, udc->driver);
usb_gadget_disconnect(udc->gadget);// pull_up
} else {
usb_gadget_stop(udc->gadget, udc->driver);// newstyle disconnect
}
*/
if (driver)
{
spin_unlock(&dev->lock);
driver->disconnect(&dev->gadget);
spin_lock(&dev->lock);
}
#endif
if (dev->driver)
{
s3c2440_usb_reinit(dev);//
}
}
#ifdef S3C2440_NEWSTYLE
/*
udc probe
if (udc_is_newstyle(udc)) {// udc_start and udc_stop
ret = bind(udc->gadget);
if (ret)
goto err1;
ret = usb_gadget_udc_start(udc->gadget, driver);// ,bind gadget
if (ret) {
driver->unbind(udc->gadget);
goto err1;
}
usb_gadget_connect(udc->gadget);// pullup
} else {
ret = usb_gadget_start(udc->gadget, driver, bind);
if (ret)
goto err1;
}
*/
//net2272 r8a66597
// net2272
static int s3c2440_udc_start(struct usb_gadget *usb_gdt_p, struct usb_gadget_driver *driver)
{
struct s3c2440_udc *dev;
printInfo( "%s
", __func__);
if (!driver || !driver->unbind || !driver->setup ||
driver->speed != USB_SPEED_HIGH)
return -EINVAL;
dev = container_of(usb_gdt_p, struct s3c2440_udc, gadget);
/* hook up the driver ... */
driver->driver.bus = NULL;
dev->driver = driver;
dev->gadget.dev.driver = &driver->driver;
s3c2440_udc_enable(dev);
return 0;
}
static int s3c2440_udc_stop(struct usb_gadget *usb_gdt_p, struct usb_gadget_driver *driver)
{
struct s3c2440_udc *dev;
unsigned long flags;
printInfo( "%s
", __func__);
dev = container_of(usb_gdt_p, struct s3c2440_udc, gadget);
spin_lock_irqsave(&dev->lock, flags);
stop_activity(dev, driver);
spin_unlock_irqrestore(&dev->lock, flags);
dev->gadget.dev.driver = NULL;
dev->driver = NULL;
return 0;
}
#else
//s3c2410 s3c2440
// s3c2440
static int s3c2440_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *usb_gdt_p))
{
struct s3c2440_udc *dev = &udc_info;
int retval = 0;
printInfo( "%s
", __func__);
if (!driver
|| driver->speed < USB_SPEED_FULL
|| !bind
|| !driver->disconnect
|| !driver->setup)
return -EINVAL;
if (!dev)
return -ENODEV;
if (dev->driver)
return -EBUSY;
/* hook up the driver */
driver->driver.bus = NULL;
dev->driver = driver;
dev->gadget.dev.driver = &driver->driver;
if ((retval = device_add(&dev->gadget.dev)) != 0)
{
goto register_error;
}
retval = bind(&dev->gadget);
if (retval)
{
device_del(&dev->gadget.dev);
goto register_error;
}
s3c2440_udc_enable(dev);
return 0;
register_error:
dev->driver = NULL;
dev->gadget.dev.driver = NULL;
return retval;
}
static int s3c2440_stop(struct usb_gadget_driver *driver)
{
struct s3c2440_udc *dev = &udc_info;
unsigned long flags;
printInfo( "%s
", __func__);
if (!dev)
return -ENODEV;
if (!driver || driver != dev->driver || !driver->unbind)
return -EINVAL;
spin_lock_irqsave(&dev->lock, flags);
dev->driver = NULL;
stop_activity(dev, driver);
spin_unlock_irqrestore(&dev->lock, flags);
driver->unbind(&dev->gadget);
dev->gadget.dev.driver = NULL;
dev->driver = NULL;
device_del(&dev->gadget.dev);
return 0;
}
#endif
/***************************************************************/
static int s3c2440_udc_probe(struct platform_device *pdev)
{
struct s3c2440_udc *udc = &udc_info;
struct device *dev = &pdev->dev;
int retval;
struct resource *res;
#ifdef S3C2440_USE_IRQ
struct resource *resirq;
#endif
resource_size_t res_size;
dev_dbg(dev, "%s()
", __func__);
#ifdef S3C2440_HAVE_CLK
udc->s3c2440_clk_upll = clk_get(NULL, "usb-bus-gadget");
if (IS_ERR(udc->s3c2440_clk_upll))
{
dev_err(dev, "failed to get usb bus clock source
");
return PTR_ERR(udc->s3c2440_clk_upll);
}
clk_enable(udc->s3c2440_clk_upll);
udc->s3c2440_clk_udc = clk_get(NULL, "usb-device");
if (IS_ERR(udc->s3c2440_clk_udc)) {
dev_err(dev, "failed to get udc clock source
");
retval = PTR_ERR(udc->s3c2440_clk_udc);
goto err_clk_upll;
}
clk_enable(udc->s3c2440_clk_udc);
#if (CLK_DELAY_TIME != 0)
mdelay(CLK_DELAY_TIME);
#endif
dev_dbg(dev, "got and enabled clocks
");
#endif //S3C2440_HAVE_CLK
spin_lock_init (&udc->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
{
dev_err(&pdev->dev, "can't get device resources
");
retval = -ENODEV;
goto err_clk_udc;
}
res_size = resource_size(res);
if (!request_mem_region(res->start, res_size, res->name))
{
dev_err(&pdev->dev, "can't allocate %d bytes at %d address
",
res_size, res->start);
retval = -ENOMEM;
goto err_clk_udc;
}
udc->virl_addr = ioremap(res->start, res_size);
if (!udc->virl_addr)
{
retval = -ENOMEM;
goto err_mem;
}
udc->phy_addr = res->start;
udc->reg_size = res_size;
device_initialize(&udc->gadget.dev);
udc->gadget.dev.parent = &pdev->dev;
udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
platform_set_drvdata(pdev, udc);
//
s3c2440_usb_reset(udc);
s3c2440_usb_reinit(udc);
#ifdef S3C2440_USE_IRQ
resirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!resirq)
{
dev_err(&pdev->dev, "can't get device irq resources
");
retval = -ENODEV;
goto err_map;
}
udc->irq_num = resirq->start;
retval = request_irq(udc->irq_num, s3c2440_udc_irq, 0, gadget_name, (void*)udc);
if (retval != 0)
{
dev_err(dev, "cannot get irq %i, err %d
", udc->irq_num, retval);
retval = -EBUSY;
goto err_map;
}
dev_dbg(dev, "got irq %i
", udc->irq_num);
#endif
retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (retval)
goto err_int;
#ifdef S3C2440_DEBUG_FS
if (s3c2440_udc_debugfs_root)
{
udc->debug_info = debugfs_create_file("registers", S_IRUGO, s3c2440_udc_debugfs_root,
udc, &s3c2440_udc_debugfs_fops);
if (!udc->debug_info)
dev_warn(dev, "debugfs file creation failed
");
}
#endif
dev_dbg(dev, "probe ok
");
return 0;
err_int:
#ifdef S3C2440_USE_IRQ
free_irq(udc->irq_num, udc);
#endif
err_map:
iounmap(udc->virl_addr);
err_mem:
release_mem_region(res->start, res_size);
err_clk_udc:
#ifdef S3C2440_HAVE_CLK
clk_put(udc->s3c2440_clk_udc);
clk_disable(udc->s3c2440_clk_udc);
#endif
err_clk_upll:
#ifdef S3C2440_HAVE_CLK
clk_put(udc->s3c2440_clk_upll);
clk_disable(udc->s3c2440_clk_upll);
#endif
return retval;
}
static int s3c2440_udc_remove(struct platform_device *pdev)
{
struct s3c2440_udc *udc = platform_get_drvdata(pdev);
dev_dbg(&pdev->dev, "%s()
", __func__);
usb_del_gadget_udc(&udc->gadget);
if (udc->driver)
return -EBUSY;
#ifdef S3C2440_DEBUG_FS
debugfs_remove(udc->debug_info);
#endif
#ifdef S3C2440_USE_IRQ
free_irq(udc->irq_num, udc);
#endif
iounmap(udc->virl_addr);
release_mem_region(udc->phy_addr, udc->reg_size);
platform_set_drvdata(pdev, NULL);
#ifdef S3C2440_HAVE_CLK
if (!IS_ERR(udc->s3c2440_clk_udc) && udc->s3c2440_clk_udc != NULL) {
clk_disable(udc->s3c2440_clk_udc);
clk_put(udc->s3c2440_clk_udc);
udc->s3c2440_clk_udc = NULL;
}
if (!IS_ERR(udc->s3c2440_clk_upll) && udc->s3c2440_clk_upll != NULL) {
clk_disable(udc->s3c2440_clk_upll);
clk_put(udc->s3c2440_clk_upll);
udc->s3c2440_clk_upll = NULL;
}
#endif
dev_dbg(&pdev->dev, "%s: remove ok
", __func__);
return 0;
}
#ifdef CONFIG_PM
static int s3c2440_udc_suspend(struct platform_device *pdev, pm_message_t message)
{
return 0;
}
static int s3c2440_udc_resume(struct platform_device *pdev)
{
return 0;
}
#else
#define s3c2440_udc_suspend NULL
#define s3c2440_udc_resume NULL
#endif
/***************************************************************/
// struct pci_driver, 。
static struct platform_driver udc_driver_s3c2440 = {
.driver = {
.name = "s3c2440-usbgadget",
.owner = THIS_MODULE,
},
.probe = s3c2440_udc_probe,
.remove = __exit_p(s3c2440_udc_remove),
.suspend = s3c2440_udc_suspend,
.resume = s3c2440_udc_resume,
};
static int __init udc_init(void)
{
int retval;
s3c2440_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL);
if (IS_ERR(s3c2440_udc_debugfs_root)) {
printInfo( "%s: debugfs dir creation failed %ld
",
gadget_name, PTR_ERR(s3c2440_udc_debugfs_root));
s3c2440_udc_debugfs_root = NULL;
}
retval = platform_driver_register(&udc_driver_s3c2440);
if (retval)
goto err;
return 0;
err:
debugfs_remove(s3c2440_udc_debugfs_root);
return retval;
}
static void __exit udc_exit(void)
{
platform_driver_unregister(&udc_driver_s3c2440);
debugfs_remove(s3c2440_udc_debugfs_root);
}
module_init(udc_init);
module_exit(udc_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");