花了半个月,才搞定驱动中的枚举部分,现在说linux的枚举,windows可能有差别。

代码我会贴在后面,现在只是实现枚举,你可能对代码不感兴趣,我就不分析代码了,你可以看看

在《自娱自乐1》中的模板,比较一下,我做了什么,这会给你写udc驱动提供个思路。我直接分析

调试打印,就是枚举过程,我们从代码看枚举。打印位置可以在下面的代码里找到。

如果你要弄懂驱动代码中涉及枚举的地方,你就仔细看看代码在那打印的,这个对你完成一个udc驱

动有帮助。

如果你只是想简单了解枚举你就看看我分析的调试打印就可以了。

http://wenku.baidu.com/view/87064d244b35eefdc8d333dc.html

这个是枚举过程,对照着上面说的看我的调试打印,提示我的是从机,他说的是主机。

# insmod s3c2440_add_udc.ko

UPLLCON = 38022, wxl add//驱动usb的时钟源

# insmod s3c2440_udc.ko

# insmod gadget_transfer.ko

s3c2440_start//调用s3c2440_start(),在此会PULL_UP

s3c2440_udc_alloc_request//分配请求结构体内存

gadget_transfer gadget: gadget_transfer ready

# USB reset//pc检测到PULL_UP 复位设备

USB ep0 irq//ep0中断

Host: bRequest = 06 bRequestType = 80 wValue = 0x100wIndex=0x0 wLength=0x40//请求信息

//bRequest = 06是请求描述符,bRequestType = 80输入方向(输入是对主机而言) 端点0,

//请求长度64(微软的策略,这里linux学微软的)。

//wValue = 0x100: 这个前面的1表示设备描述符

USB_REQ_GET_DESCRIPTOR//第一次请求主要是获取最大包长度,此值在设备描述符第8个字节

USB_DT_DEVICE//设备描述符请求

s3c2440_udc_queue//调用了s3c2440_udc_queue()

Slave: length = 18 Vendor = ff0 Product = ff0 Device =212 iManufacturer = 1 iProduct = 2 iSerialNumber = 3 bNumConfigurations = 1

// Vendor = ff0 Product = ff0 Device = 212 这个在上一篇的gadget_transfer驱动中可以看到

// iManufacturer = 1 iProduct = 2iSerialNumber = 3 这个是字符串描述符引索

// bNumConfigurations = 1 你看看我上一篇的gadget_transfar驱动写的是2,这里却是1

/*

bNumConfigurations是配置数,是用count_configs()统计的,和你在gadget驱动中赋值无关

多配置很少,不过还是有,例如multi.c可以配置为RNDIS或ECM。

*/

8bytes USB USB reset //一次发出8个字节,我的ep0最大包长度是8,还有下面的xbytes都是已发的字节数。微软的策略后面没发完的不要了,直接复位设备。没有按usb spec来做。Windows要接受16个才复位。

USB ep0 irq

Host: bRequest = 05 bRequestType = 00 wValue = 0x4wIndex=0x0 wLength=0x0

//主机的请求又来了,这次是设置地址

// bRequest = 05 就是设置地址请求,

// bRequestType = 00 方向out,地址0

//wValue=0x4 设备地址4,在驱动中看不到它的使用,应该是硬件来判断

USB_REQ_SET_ADDRESS

USB ep0 irq//这个我调试是数据没准备好

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x100wIndex=0x0 wLength=0x12

//这次是真的获取设备描述符,长度18

USB_REQ_GET_DESCRIPTOR

USB_DT_DEVICE

s3c2440_udc_queue

Slave: length = 18 Vendor = ff0 Product = ff0 Device =212 iManufacturer = 1 iProduct = 2 iSerialNumber = 3 bNumConfigurations = 1//上面已解释

8bytes USB ep0 irq

16bytes USB ep0irq

18bytes USB ep0irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x200wIndex=0x0 wLength=0x9

/*

wValue = 0x200: 这个前面的2表示配置描述符,后面是引索

*/

//获取配置描述符

USB_REQ_GET_DESCRIPTOR

USB_DT_CONFIG

s3c2440_udc_queue

Slave: length = 9 TotalLength = 32 NumInterfaces = 1ConfigurationValue = 3 iConfiguration = 4 bMaxPower = 250

/*

length = 9描述符长度为9

TotalLength = 32配置信息的长度--包括配置描述符、接口描述符、端点描述符长度的总和

NumInterfaces = 1 接口数

ConfigurationValue = 3用于表示 USB设备的配置值,主机就是根据这个选

iConfiguration = 4字符串描述符的索引值

bMaxPower = 250用于表示 USB设备运行时所需要消耗的总线电流,单位以2mA 为基准。USB设备可以从USB总线上获得最大的电流为500mA,因此 bMaxPower 字段的最大值可以设置为250。

还有个bmAttributes,没打印

*/

8bytes USB ep0 irq

9bytes USB ep0irq

USB ep0 irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x300wIndex=0x0 wLength=0xff

/*

wValue = 0x300: 这个前面的3表示字符串描述符,后面是引索0

之前并没有看到0引索的字符串描述符,不过看一下composite.c就知道了

/* 0 == report all availablelanguage codes */

可用的语言数,不细说

*/

USB_REQ_GET_DESCRIPTOR

USB_DT_STRING

s3c2440_udc_queue

Slave: length = 4

4bytes USB ep0irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x303wIndex=0x409 wLength=0xff

//3是上面的iSerialNumber = 3

USB_REQ_GET_DESCRIPTOR

USB_DT_STRING

s3c2440_udc_queue

Slave: length = 66

0123456789.0123456789.0123456789

//可以在上一篇的驱动开的这个,static charserial[] = "0123456789.0123456789.0123456789";

8bytes USB ep0 irq

16bytes USB ep0irq

24bytes USB ep0irq

32bytes USB ep0irq

40bytes USB ep0irq

48bytes USB ep0irq

56bytes USB ep0irq

64bytes USB ep0irq

66bytes USB ep0irq

//下面又来一次,就是获得的字符串描述不同而已

Host: bRequest = 06 bRequestType = 80 wValue = 0x200wIndex=0x0 wLength=0xff

USB_REQ_GET_DESCRIPTOR

USB_DT_CONFIG

s3c2440_udc_queue

Slave: length = 9 TotalLength = 32 NumInterfaces = 1ConfigurationValue = 3 iConfiguration = 4 bMaxPower = 250

8bytes USB ep0irq

16bytes USB ep0irq

24bytes USB ep0irq

32bytes USB ep0irq

32bytes USB ep0irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x300wIndex=0x0 wLength=0xff

USB_REQ_GET_DESCRIPTOR

USB_DT_STRING

s3c2440_udc_queue

Slave: length = 4

4bytes USB ep0irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x302wIndex=0x409 wLength=0xff

// iProduct = 2

USB_REQ_GET_DESCRIPTOR

USB_DT_STRING

s3c2440_udc_queue

Slave: length = 46

Gadget gadget_transfer// static const char longname[]= "Gadget gadget_transfer";

8bytes USB ep0irq

16bytes USB ep0irq

24bytes USB ep0irq

32bytes USB ep0irq

40bytes USB ep0irq

46bytes USB ep0irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x300wIndex=0x0 wLength=0xff//再一次,不清楚为什么

USB_REQ_GET_DESCRIPTOR

USB_DT_STRING

s3c2440_udc_queue

Slave: length = 4

4bytes USB ep0irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x302wIndex=0x409 wLength=0xff

USB_REQ_GET_DESCRIPTOR

USB_DT_STRING

s3c2440_udc_queue

Slave: length = 46

Gadget gadget_transfer

8bytes USB ep0irq

16bytes USB ep0irq

24bytes USB ep0irq

32bytes USB ep0irq

40bytes USB ep0irq

46bytes USB ep0irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x100wIndex=0x0 wLength=0x12

USB_REQ_GET_DESCRIPTOR

USB_DT_DEVICE//获取设备描述符

s3c2440_udc_queue

Slave: length = 18 Vendor = ff0 Product = ff0 Device =212 iManufacturer = 1 iProduct = 2 iSerialNumber = 3 bNumConfigurations = 1

8bytes USB ep0irq

16bytes USB ep0irq

18bytes USB ep0irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x100wIndex=0x0 wLength=0x40//这里有用64长度请求一次

USB_REQ_GET_DESCRIPTOR

USB_DT_DEVICE

s3c2440_udc_queue

Slave: length = 18 Vendor = ff0 Product = ff0 Device =212 iManufacturer = 1 iProduct = 2 iSerialNumber = 3 bNumConfigurations = 1

8bytes USB ep0irq

16bytes USB ep0irq

18bytes USB ep0irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x100wIndex=0x0 wLength=0x12

USB_REQ_GET_DESCRIPTOR

USB_DT_DEVICE//又来一次

s3c2440_udc_queue

Slave: length = 18 Vendor = ff0 Product = ff0 Device =212 iManufacturer = 1 iProduct = 2 iSerialNumber = 3 bNumConfigurations = 1

8bytes USB ep0irq

16bytes USB ep0irq

18bytes USB ep0irq

USB ep0 irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x600wIndex=0x0 wLength=0xa

USB_REQ_GET_DESCRIPTOR

USB_DT_DEVICE_QUALIFIER

//设备限定描述符用于指定另一传输速率下该设备的总体信息,如果高速USB设备既需要采用高速传//输又需要全速传输,则它必须支持设备限定描述符(Device_Qualifier)。全速设备不支持

//我的是全速设备用不倒QUALIFIER,composite.c用gadget_is_dualspeed()这个判断

USB ep0 irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x600wIndex=0x0 wLength=0xa

USB_REQ_GET_DESCRIPTOR

USB_DT_DEVICE_QUALIFIER

USB ep0 irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x600wIndex=0x0 wLength=0xa

USB_REQ_GET_DESCRIPTOR

USB_DT_DEVICE_QUALIFIER

USB ep0 irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x200wIndex=0x0 wLength=0x9

USB_REQ_GET_DESCRIPTOR

USB_DT_CONFIG//这次你会看到到wValue & 0xff 是0到3

// iManufacturer = 1 iProduct = 2iSerialNumber = 3

s3c2440_udc_queue

Slave: length = 9 TotalLength = 32 NumInterfaces = 1ConfigurationValue = 3 iConfiguration = 4 bMaxPower = 250

8bytes USB ep0irq

9bytes USB ep0irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x200wIndex=0x0 wLength=0x20

USB_REQ_GET_DESCRIPTOR

USB_DT_CONFIG

s3c2440_udc_queue

Slave: length = 9 TotalLength = 32 NumInterfaces = 1ConfigurationValue = 3 iConfiguration = 4 bMaxPower = 250

8bytes USB ep0irq

16bytes USB ep0irq

24bytes USB ep0irq

32bytes USB ep0irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x300wIndex=0x0 wLength=0xff

USB_REQ_GET_DESCRIPTOR

USB_DT_STRING

s3c2440_udc_queue

Slave: length = 4

4bytes USB ep0irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x302wIndex=0x409 wLength=0xff

USB_REQ_GET_DESCRIPTOR

USB_DT_STRING

s3c2440_udc_queue

Slave: length = 46

Gadget gadget_transfer

8bytes USB ep0irq

16bytes USB ep0irq

24bytes USB ep0irq

32bytes USB ep0irq

40bytes USB ep0irq

46bytes USB ep0irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x301wIndex=0x409 wLength=0xff

USB_REQ_GET_DESCRIPTOR

USB_DT_STRING

s3c2440_udc_queue

Slave: length = 58

Linux 3.2.0 with s3c2440_udc

/*

还记得我的gadget驱动开始

snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",

init_utsname()->sysname, init_utsname()->release,

gadget->name);

*/

8bytes USB ep0irq

16bytes USB ep0irq

24bytes USB ep0irq

32bytes USB ep0irq

40bytes USB ep0irq

48bytes USB ep0irq

56bytes USB ep0irq

58bytes USB ep0irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x303wIndex=0x409 wLength=0xff

USB_REQ_GET_DESCRIPTOR

USB_DT_STRING

s3c2440_udc_queue

Slave: length = 66

0123456789.0123456789.0123456789

8bytes USB ep0irq

16bytes USB ep0irq

24bytes USB ep0irq

32bytes USB ep0irq

40bytes USB ep0irq

48bytes USB ep0irq

56bytes USB ep0irq

64bytes USB ep0irq

66bytes USB ep0irq

USB ep0 irq

Host: bRequest = 09 bRequestType = 00 wValue = 0x3wIndex=0x0 wLength=0x0

USB_REQ_SET_CONFIGURATION//设置配置

// wValue = 0x3对应上面的ConfigurationValue = 3程序中是

/*

static struct usb_configurationsourcesink_driver = {

.label        = "source/sink",

.strings   = sourcesink_strings,

.setup        = sourcesink_setup,

.bConfigurationValue = 3,

.bmAttributes    = USB_CONFIG_ATT_SELFPOWER,

/* .iConfiguration = DYNAMIC */

};

*/

gadget_transfer gadget: full-speed config#3: source/sink//上面的.label        ="source/sink",

s3c2440_udc_ep_enable

s3c2440_udc_ep_enable

s3c2440_udc_queue

USB ep0 irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x304wIndex=0x409 wLength=0xff

USB_REQ_GET_DESCRIPTOR

USB_DT_STRING

s3c2440_udc_queue

Slave: length = 42

source and sink data9

8bytes USB ep0irq

16bytes USB ep0irq

24bytes USB ep0irq

32bytes USB ep0irq

40bytes USB ep0irq

42bytes USB ep0irq

USB ep0 irq

Host: bRequest = 06 bRequestType = 80 wValue = 0x304wIndex=0x409 wLength=0xff

USB_REQ_GET_DESCRIPTOR//这里引索是4就是iConfiguration = 4

USB_DT_STRING

s3c2440_udc_queue

Slave: length = 42

source and sink data9

/*代码

static struct usb_stringstrings_sourcesink[] = {

[0].s ="source and sink data",

{  }           /* end of list */

};

*/

8bytes USB ep0irq

16bytes USB ep0irq

24bytes USB ep0irq

32bytes USB ep0irq

40bytes USB ep0irq

42bytes USB ep0irq

有些重复的请求就是为了确认。

下面是代码:(现在只能枚举),可以把内核自带的驱动和我的比较一下,我的宗旨是应用我的模板。

s3c2440_udc.h

/***********************************
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
//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__

s3c2440.c

/***********************************
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>//printk
#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\n", 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: 接收者
*/
printk( "Host: bRequest = %02x bRequestType = %02x \
wValue = 0x%x wIndex=0x%x wLength=0x%x\n", _ctrlq->bRequest, _ctrlq->bRequestType, \
_ctrlq->wValue, _ctrlq->wIndex, _ctrlq->wLength); return count;
} 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 fifo_count, avail, len, bufspace;
int ret = 0; printk( "%s\n", __func__); idx = ep->bEndpointAddress & 0x7f; if (idx > 4)
{
idx = 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; WRITE_REG(dev, INDEX_REG, idx); s3c2440_usb_fifocnt(dev, &fifo_count); 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要接收的总长度
printk( "read: req->req.actual = %d, req->req.length = %d\n", req->req.actual, req->req.length); 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
{
} s3c2440_udc_done(ep, req, 0);
} else
{
if (idx == 0)
{
EP0_CLROPR(dev);
}
else
{
}
}
return ret;
} static int printDesc = 0; 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; //printk( "%s\n", __func__); struct usb_device_descriptor *desc;
struct usb_string_descriptor *string;
struct usb_config_descriptor *config;
u16 language;
u32 n;
u8 *tmp; switch (printDesc)
{
case USB_DT_DEVICE:
desc = (struct usb_device_descriptor*)req->req.buf; printk( "Slave: length = %d Vendor = %x Product = %x Device = %x iManufacturer = %d iProduct = %d iSerialNumber = %d bNumConfigurations = %d\n", \
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; printk( "Slave: length = %d TotalLength = %d NumInterfaces = %d ConfigurationValue = %d iConfiguration = %d bMaxPower = %d\n", \
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;
printk( "Slave: length = %d\n", 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
{
printk( "%c", *tmp);//没考虑大小端
}
} printk("\n"); break;
case USB_DT_BOS:
break;
default:
break;
} printDesc = 0; 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已发送长度
printk( " %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; //printk( \
"Written ep%d %d.%d of %d b [last %d,z %d], max = %d\n", \
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
{ } s3c2440_udc_done(ep, req, 0);
}
else
{
if (idx == 0)
{
if (!GETB(dev, USB_INT_REG, 2))
{
EP0_SETIPR(dev);
}
}
else
{ }
} return ret;
} static void s3c2440_dequeue_all(struct s3c2440_ep *ep, int status); 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; //printk( "%s\n", __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: printk( "USB_REQ_GET_STATUS\n");
EP0_CLROPR(dev);
if ((ctrlq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
{ } break;
case USB_REQ_CLEAR_FEATURE: printk( "USB_REQ_CLEAR_FEATURE\n");
EP0_CLROPR(dev);
break;
case USB_REQ_SET_FEATURE: printk( "USB_REQ_SET_FEATURE\n");
EP0_CLROPR(dev);
break;
case USB_REQ_SET_ADDRESS: printk( "USB_REQ_SET_ADDRESS\n");
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: printk( "USB_REQ_GET_DESCRIPTOR\n");
switch (ctrlq.wValue >> 8)
{
case USB_DT_DEVICE: printk( "USB_DT_DEVICE\n");
break;
//设备限定描述符用于指定另一传输速率下该设备的总体信息,如果高速 USB设备既需要采用高速传输又需
//要全速传输,则它必须支持设备限定描述符(Device_Qualifier)。全速设备不支持
case USB_DT_DEVICE_QUALIFIER: printk( "USB_DT_DEVICE_QUALIFIER\n");
break;
case USB_DT_OTHER_SPEED_CONFIG: printk( "USB_DT_OTHER_SPEED_CONFIG\n");
break;
case USB_DT_CONFIG: printk( "USB_DT_CONFIG\n");
break;
case USB_DT_STRING: printk( "USB_DT_STRING\n");
break;
//其他速率配置描述符用于指定另一传输速率下该设备的配置信息,如果高速USB设备既需要采用高速传输
//又需要全速传输,则它必须支持其他速率配置描述符
case USB_DT_BOS: printk( "USB_DT_BOS\n");
break;
} EP0_CLROPR(dev); break;
case USB_REQ_SET_DESCRIPTOR: printk( "USB_REQ_SET_DESCRIPTOR\n");
EP0_CLROPR(dev);
break;
case USB_REQ_GET_CONFIGURATION: printk( "USB_REQ_GET_CONFIGURATION\n");
EP0_CLROPR(dev);
break;
case USB_REQ_SET_CONFIGURATION:
if (ctrlq.bRequestType == USB_RECIP_DEVICE)
{
printk( "USB_REQ_SET_CONFIGURATION\n");
config = 1; EP0_CLROPR(dev);
EP0_SETDE(dev);
}
break;
case USB_REQ_GET_INTERFACE: printk( "USB_REQ_GET_INTERFACE\n");
EP0_CLROPR(dev);
break;
case USB_REQ_SET_INTERFACE:
if (ctrlq.bRequestType == USB_RECIP_INTERFACE)
{
printk( "SB_REQ_SET_INTERFACE\n");
config = 1; EP0_CLROPR(dev);
EP0_SETDE(dev);
} break;
case USB_REQ_SYNCH_FRAME: printk( "USB_REQ_SYNCH_FRAME\n");
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; //为了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;
} 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;
}
} //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; //printk( "enter irq\n"); spin_lock_irqsave(&dev->lock, flags); usb_status = READ_REG(dev, USB_INT_REG);
ep_status = READ_REG(dev, EP_INT_REG); //printk( "USB_INT_REG = 0x%x\n", usb_status);
//printk( "EP_INT_REG = 0x%x\n", 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))
{
printk( "USB reset\n"); 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))
{
printk( "USB resume\n"); 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)
{
printk( "USB suspend\n"); 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)
{
printk( "USB ep0 irq\n"); SETB(dev, EP_INT_REG, 0); s3c2440_handle_ep0(dev);
} 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; printk( "%s\n", __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_CSR2_REG, 0);//关闭in dma中断,先不用dma
SETB(dev, IN_CSR2_REG, 5);//in
SETB(dev, IN_CSR2_REG, 6);//批量端点
}
else
{
CLRB(dev, IN_CSR2_REG, 5);//out SETB(dev, OUT_CSR1_REG, 0);//清楚IN_PKT_RDY
SETB(dev, OUT_CSR1_REG, 4);//FLUSH fifo SETB(dev, OUT_CSR2_REG, 5);//批量端点
SETB(dev, OUT_CSR2_REG, 6);//关闭out dma中断,先不用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; printk( "%s\n", __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; printk( "%s\n", __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); printk( "%s\n", __func__); if (!_ep || !_req)
return; WARN_ON (!list_empty (&req->queue));
kfree(req);
} //下面的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 fifo_count, ep_csr; printk( "%s\n", __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 == 0)
{
ep_csr = READ_REG(dev, EP0_CSR);
}
else
{
ep_csr = READ_REG(dev, (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) == 1)
{
dev->ep0state = EP0_IDLE;
req = NULL;
}
}
break; default:
local_irq_restore(flags); return -EL2HLT;
}
}
else if ((ep->bEndpointAddress & USB_DIR_IN) != 0)
{
}
else
{
}
} 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; printk( "%s\n", __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; printk( "%s\n", __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; printk( "%s\n", __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; 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; 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 {
/* set/clear */
if (value) {
if (ep->num == 0)
{
ep->dev->ep0state = EP0_STALL;
ep->stopped = 1;
//net2272的端点0在setup时自动复位,没有什么操作。s3c2440就不是了
//ep->dev->protocol_stall = 1;
//ep0 set_halt
}
else
//epx(x != 0) set_halt
if (wedged)//维护wedged
ep->wedged = 1;
} else {
//ep clear_halt
ep->wedged = 0;
}
}
spin_unlock_irqrestore(&ep->dev->lock, flags); return ret;
}
//_ep 不能是同步端点,同步端点不支持错误重发机制。在上面判断
static int s3c2440_udc_set_halt(struct usb_ep *_ep, int value)
{
printk( "%s\n", __func__); return s3c2440_set_halt_and_wedge(_ep, value, 0);
} #ifdef S3C2440_SETWEDHE static int s3c2440_udc_set_wedge(struct usb_ep *_ep)
{ printk( "%s\n", __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)
{
printk( "%s\n", __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; printk( "%s\n", __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); printk( "%s\n", __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; printk( "%s\n", __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; printk( "%s\n", __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; printk( "%s\n", __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; printk( "%s\n", __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; printk( "%s\n", __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; printk( "%s\n", __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()\n", __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\n");
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\n");
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\n");
#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\n");
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\n",
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\n");
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\n", udc->irq_num, retval);
retval = -EBUSY;
goto err_map;
} dev_dbg(dev, "got irq %i\n", 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\n");
}
#endif dev_dbg(dev, "probe ok\n"); 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()\n", __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\n", __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)) {
printk( "%s: debugfs dir creation failed %ld\n",
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");

下期完善它,并考虑使用DMA。下期见!

[置顶] 自娱自乐7之Linux UDC驱动2(自编udc驱动,现完成枚举过程,从驱动代码分析枚举过程)的更多相关文章

  1. [置顶] 自娱自乐1之Linux UDC驱动(形式模板)

    首先,我不是做驱动的开发人员.所以只能用自娱自乐来表示我的行为. 我不知道udc和gadget驱动是不是冷门的驱动,资料真是不多.我之前买了一本书,上面说到这些,就教你如何调试已写好的驱动.这样也可以 ...

  2. [置顶] 自娱自乐6之Linux gadget驱动5(自编gadget驱动,包涵与之通讯的主机usb驱动,已调试通过)

    这个代码调试,你首先要保证你的udc驱动没用问题,这个有些矛盾,应为我本来要用gadget驱动来调试udc驱动,结果反过来了. 这是在zero基础改的,大概的改动 1. 去掉loop. 2. sink ...

  3. Linux内核中的GPIO系统之(3):pin controller driver代码分析

    一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...

  4. Linux内核中的GPIO系统之(3):pin controller driver代码分析--devm_kzalloc使用【转】

    转自:http://www.wowotech.net/linux_kenrel/pin-controller-driver.html 一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道 ...

  5. [置顶] 学习鸟哥的Linux私房菜笔记(6)——过滤器、输入输出及管道

    一.过滤器 Linux中的应用工具分为三种: 交互工具 过滤器 编辑器 能够接受数据,过滤再输出的工具,称之为过滤器 对过滤器和进程,存在着输入源与输出对象 二.输入.输出.重定向 输入:过滤器的数据 ...

  6. [置顶] getopt_long函数基本用法-linux

    一.感性认识: [c-sharp]  view plain copy   #include <stdio.h> #include <getopt.h> char * l_opt ...

  7. Linux启动过程的内核代码分析

    参考上文: http://www.cnblogs.com/long123king/p/3543872.html http://www.cnblogs.com/long123king/p/3545688 ...

  8. ahk之路:利用ahk在window7下实现窗口置顶

    操作系统:win7 64位 ahk版本:autohotkey_L1.1.24.03 今天安装了AutoHotkey_1.1.24.03.SciTE.PuloversMacroCreator,重新开始我 ...

  9. [自娱自乐] 3、超声波测距模块DIY笔记(三)

    前言 上一节我们已经研究了超声波接收模块并自己设计了一个超声波接收模块,在此基础上又尝试用单片机加反相器构成生成40KHz的超声波发射电路,可是发现采用这种设计的发射电路存在严重的发射功率太低问题,对 ...

随机推荐

  1. C++对C语言的非面向对象特性扩充(2)

    上一篇随笔写了关于C++在注释,输入输出,局部变量说明的扩充,以及const修饰符与C中的#define的比较,也得到了几位学习C++朋友们的帮助讲解,十分感谢,我也希望欢迎有更多学习C++的朋友一起 ...

  2. JavaScript基础知识----document对象

    对象属性document.title                 //设置文档标题等价于HTML的<title>标签document.bgColor               //设 ...

  3. JDBC----数据库连接池(connection pool)

    •数据库连接池的基本思想就是为数据库连接建立一个"缓冲池".预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从"缓冲池"中取出一个,使用完毕之后再 ...

  4. perl encode_utf8 和decode_utf8

    encode_utf8 等于 $octets = encode_utf8($string); 这个字符串 在$string 在Perl的内部格式,返回结果是作为一个顺序的字节. 因为所有的可能的字符串 ...

  5. wiki oi 1044 拦截导弹

    题目描述 Description 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某 ...

  6. linux命令--sysctl

    sysctl sysctl被用来在执行时配置内核参数.这些参数都存储在/proc/sys/(以键-值对形式存储)中.你可以用sysctl来读和写数据 命令参数 variable   要读的键值的名字 ...

  7. large-scale analysis of malware downloaders

    http://www.christian-rossow.de/publications/downloaders-dimva12.pdf

  8. Git简介及安装和简单配置

    首先需要清楚的是Git和GitHub的区别. Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目.Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了 ...

  9. BZOJ 1742: [Usaco2005 nov]Grazing on the Run 边跑边吃草( dp )

    dp... dp( l , r , k )  , 表示 吃了[ l , r ] 的草 , k = 1 表示最后在 r 处 , k = 0 表示最后在 l 处 . ------------------- ...

  10. WCF Publisher/Subscriber 订阅-发布模式

    本博后续将陆续整理这些年做的一些预研demo,及一些前沿技术的研究,与大家共研技术,共同进步. 关于发布订阅有很多种实现方式,下面主要介绍WCF中的发布订阅,主要参考书籍<Programming ...