musb_otg.c文件是FreeBSD中为Beaglebone Black提供的usb控制器源码,涉及到了endpoint的初始化配置,还有控制、中断、同步、批量四种传输方式的实现,非常重要。接下来详细解析该文件,不一定按照源码顺序,而是功能或者模块顺序。
1.文件头部的函数声明:能够看出文件主要的功能
static const struct usb_bus_methods musbotg_bus_methods;
static const struct usb_pipe_methods musbotg_device_bulk_methods;
static const struct usb_pipe_methods musbotg_device_ctrl_methods;
static const struct usb_pipe_methods musbotg_device_intr_methods;
static const struct usb_pipe_methods musbotg_device_isoc_methods;
/* Control transfers: Device mode */
static musbotg_cmd_t musbotg_dev_ctrl_setup_rx;
static musbotg_cmd_t musbotg_dev_ctrl_data_rx;
static musbotg_cmd_t musbotg_dev_ctrl_data_tx;
static musbotg_cmd_t musbotg_dev_ctrl_status;
/* Control transfers: Host mode */
static musbotg_cmd_t musbotg_host_ctrl_setup_tx;
static musbotg_cmd_t musbotg_host_ctrl_data_rx;
static musbotg_cmd_t musbotg_host_ctrl_data_tx;
static musbotg_cmd_t musbotg_host_ctrl_status_rx;
static musbotg_cmd_t musbotg_host_ctrl_status_tx;
/* Bulk, Interrupt, Isochronous: Device mode */
static musbotg_cmd_t musbotg_dev_data_rx;
static musbotg_cmd_t musbotg_dev_data_tx;
/* Bulk, Interrupt, Isochronous: Host mode */
static musbotg_cmd_t musbotg_host_data_rx;
static musbotg_cmd_t musbotg_host_data_tx;
static void musbotg_device_done(struct usb_xfer *, usb_error_t);
static void musbotg_do_poll(struct usb_bus *);
static void musbotg_standard_done(struct usb_xfer *);
static void musbotg_interrupt_poll(struct musbotg_softc *);
static void musbotg_root_intr(struct musbotg_softc *);
static int musbotg_channel_alloc(struct musbotg_softc *, struct musbotg_td *td, uint8_t);
static void musbotg_channel_free(struct musbotg_softc *, struct musbotg_td *td);
static void musbotg_ep_int_set(struct musbotg_softc *sc, int channel, int on);
可以看出这些都是对四种传输模式的定义和实现。
2.musbotg_init函数,该函数是对OTG控制器的初始化,非常重要,代码分条贴出,并讲解其功能
首先定义一些函数局部变量,然后设置USB bus的结构,定义USB版本为2.0。并设置bus的方法method
struct usb_hw_ep_profile *pf;
uint16_t offset;
uint8_t nrx;
uint8_t ntx;
uint8_t temp;
uint8_t fsize;
uint8_t frx;
uint8_t ftx;
uint8_t dynfifo;
DPRINTFN(1, "start\n");
/* set up the bus structure */
sc->sc_bus.usbrev = USB_REV_2_0;
sc->sc_bus.methods = &musbotg_bus_methods;
这里的musbotg_bus_methods定义如下:
static const struct usb_bus_methods musbotg_bus_methods =
{
.endpoint_init = &musbotg_ep_init,
.get_dma_delay = &musbotg_get_dma_delay,
.xfer_setup = &musbotg_xfer_setup,
.xfer_unsetup = &musbotg_xfer_unsetup,
.get_hw_ep_profile = &musbotg_get_hw_ep_profile,
.xfer_stall = &musbotg_xfer_stall,
.set_stall = &musbotg_set_stall,
.clear_stall = &musbotg_clear_stall,
.roothub_exec = &musbotg_roothub_exec,
.xfer_poll = &musbotg_do_poll,
.set_hw_power_sleep = &musbotg_set_hw_power_sleep,
};
里面有对endpoint的初始化函数:musbotg_ep_init,以及其他一些接口函数。
musbotg_ep_init函数定义如下:
static void
musbotg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc,
struct usb_endpoint *ep)
{
struct musbotg_softc *sc = MUSBOTG_BUS2SC(udev->bus);
DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d (%d)\n",
ep, udev->address,
edesc->bEndpointAddress, udev->flags.usb_mode,
sc->sc_rt_addr);
if (udev->device_index != sc->sc_rt_addr) {
switch (edesc->bmAttributes & UE_XFERTYPE) {
case UE_CONTROL:
ep->methods = &musbotg_device_ctrl_methods;
break;
case UE_INTERRUPT:
ep->methods = &musbotg_device_intr_methods;
break;
case UE_ISOCHRONOUS:
ep->methods = &musbotg_device_isoc_methods;
break;
case UE_BULK:
ep->methods = &musbotg_device_bulk_methods;
break;
default:
/* do nothing */
break;
}
}
}
我们都知道endpoint只能工作在一种模式下,比如控制传输、批量传输、同步传输以及中断传输。在endpoint 初始化函数中主要就是设置endpoint的工作模式。
接着回到musbotg_init函数,首先对USB_BUS加锁,防止其他task访问,然后打开时钟,并等待一会稳定。
USB_BUS_LOCK(&sc->sc_bus);
/* turn on clocks */
if (sc->sc_clocks_on) {
(sc->sc_clocks_on) (sc->sc_clocks_arg);
}
/* wait a little for things to stabilise */
usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
关闭所有中断和上拉,然后等待10ms
/* disable all interrupts */
temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL);
DPRINTF("pre-DEVCTL=0x%02x\n", temp);
MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0);
MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0);
MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0);
/* disable pullup */
musbotg_pull_common(sc, 0);
/* wait a little bit (10ms) */
usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
关闭双向缓存包,然后使能高速传输(USB2.0),然后判断是作为主机还是作为设备,若作为主机,则使能session。当DEVCTL[SESSION]位被置为1时,USB探测器开始探测Iddig电平信号的状态,如果
Iddig信号检测为低,USB控制器将假设为host,而当Iddig信号为高,则假设为设备,在USB控制器确定其作为主机的角色时,会驱动USBx_DRVVBUS引脚变高来使能外部电源,也就是产生5v电源,USB控制器会等待USBx_VBUSIN引脚电平变高,等待时间要大于等于100ms,若没有变高,会产生一个Vbus的错误中断,若变高了,USB控制器会等待设备的连接。
这里就是本人曾经出现bug的地方:信号变高了,USBx_DRVVBUS引脚也变好了,但卡在了等待100ms里面。原因就是RTEMS没有使能USB的中断, 这个问题在之前的博文中描述过了,不再赘述。
/* disable double packet buffering */
MUSB2_WRITE_2(sc, MUSB2_REG_RXDBDIS, 0xFFFF);
MUSB2_WRITE_2(sc, MUSB2_REG_TXDBDIS, 0xFFFF);
/* enable HighSpeed and ISO Update flags */
MUSB2_WRITE_1(sc, MUSB2_REG_POWER,
MUSB2_MASK_HSENAB | MUSB2_MASK_ISOUPD);
if (sc->sc_mode == MUSB2_DEVICE_MODE) {
/* clear Session bit, if set */
temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL);
temp &= ~MUSB2_MASK_SESS;
MUSB2_WRITE_1(sc, MUSB2_REG_DEVCTL, temp);
} else {
/* Enter session for Host mode */
temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL);
temp |= MUSB2_MASK_SESS;
MUSB2_WRITE_1(sc, MUSB2_REG_DEVCTL, temp);
}
/* wait a little for things to stabilise */
usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 10);
接着,关闭test模式,然后读取endpoint的数量,并读取配置
/* disable testmode */
MUSB2_WRITE_1(sc, MUSB2_REG_TESTMODE, 0);
/* set default value */
MUSB2_WRITE_1(sc, MUSB2_REG_MISC, 0);
/* select endpoint index 0 */
MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
/* read out number of endpoints */
nrx =
(MUSB2_READ_1(sc, MUSB2_REG_EPINFO) / 16);
ntx =
(MUSB2_READ_1(sc, MUSB2_REG_EPINFO) % 16);
/* these numbers exclude the control endpoint */
DPRINTFN(2, "RX/TX endpoints: %u/%u\n", nrx, ntx);
sc->sc_ep_max = (nrx > ntx) ? nrx : ntx;
if (sc->sc_ep_max == 0) {
DPRINTFN(2, "ERROR: Looks like the clocks are off!\n");
}
/* read out configuration data */
sc->sc_conf_data = MUSB2_READ_1(sc, MUSB2_REG_CONFDATA);
DPRINTFN(2, "Config Data: 0x%02x\n",
sc->sc_conf_data);
dynfifo = (sc->sc_conf_data & MUSB2_MASK_CD_DYNFIFOSZ) ? 1 : 0;
if (dynfifo) {
device_printf(sc->sc_bus.bdev, "Dynamic FIFO sizing detected, "
"assuming 16Kbytes of FIFO RAM\n");
}
DPRINTFN(2, "HW version: 0x%04x\n",
MUSB2_READ_1(sc, MUSB2_REG_HWVERS));
对所有发送和接收的端点进行配置,端点号为1的配置其FIFO为4k大小,对于端点号小于8的,配置FIFO为1k,大于8的,则配置FIFO为128bytes。
然后对端点的profile进行配置,分为三部分进行配置,对于既可以发送也可以接收的端点0,也就是
if (frx && ftx && (temp <= nrx) && (temp <= ntx))
将其最大的发送和接收的size都设置,并将单工标志位设置为0,表示支持发送和接收的双工模式。
其他两部分就是分别对发送和接收的endpoint进行配置。
/* initialise endpoint profiles */
offset = 0;
for (temp = 1; temp <= sc->sc_ep_max; temp++) {
pf = sc->sc_hw_ep_profile + temp;
/* select endpoint */
MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, temp);
fsize = MUSB2_READ_1(sc, MUSB2_REG_FSIZE);
frx = (fsize & MUSB2_MASK_RX_FSIZE) / 16;
ftx = (fsize & MUSB2_MASK_TX_FSIZE);
DPRINTF("Endpoint %u FIFO size: IN=%u, OUT=%u, DYN=%d\n",
temp, ftx, frx, dynfifo);
if (dynfifo) {
if (frx && (temp <= nrx)) {
if (temp == 1) {
frx = 12; /* 4K */
MUSB2_WRITE_1(sc, MUSB2_REG_RXFIFOSZ,
MUSB2_VAL_FIFOSZ_4096 |
MUSB2_MASK_FIFODB);
} else if (temp < 8) {
frx = 10; /* 1K */
MUSB2_WRITE_1(sc, MUSB2_REG_RXFIFOSZ,
MUSB2_VAL_FIFOSZ_512 |
MUSB2_MASK_FIFODB);
} else {
frx = 7; /* 128 bytes */
MUSB2_WRITE_1(sc, MUSB2_REG_RXFIFOSZ,
MUSB2_VAL_FIFOSZ_128);
}
MUSB2_WRITE_2(sc, MUSB2_REG_RXFIFOADD,
offset >> 3);
offset += (1 << frx);
}
if (ftx && (temp <= ntx)) {
if (temp == 1) {
ftx = 12; /* 4K */
MUSB2_WRITE_1(sc, MUSB2_REG_TXFIFOSZ,
MUSB2_VAL_FIFOSZ_4096 |
MUSB2_MASK_FIFODB);
} else if (temp < 8) {
ftx = 10; /* 1K */
MUSB2_WRITE_1(sc, MUSB2_REG_TXFIFOSZ,
MUSB2_VAL_FIFOSZ_512 |
MUSB2_MASK_FIFODB);
} else {
ftx = 7; /* 128 bytes */
MUSB2_WRITE_1(sc, MUSB2_REG_TXFIFOSZ,
MUSB2_VAL_FIFOSZ_128);
}
MUSB2_WRITE_2(sc, MUSB2_REG_TXFIFOADD,
offset >> 3);
offset += (1 << ftx);
}
}
if (frx && ftx && (temp <= nrx) && (temp <= ntx)) {
pf->max_in_frame_size = 1 << ftx;
pf->max_out_frame_size = 1 << frx;
pf->is_simplex = 0; /* duplex */
pf->support_multi_buffer = 1;
pf->support_bulk = 1;
pf->support_interrupt = 1;
pf->support_isochronous = 1;
pf->support_in = 1;
pf->support_out = 1;
} else if (frx && (temp <= nrx)) {
pf->max_out_frame_size = 1 << frx;
pf->max_in_frame_size = 0;
pf->is_simplex = 1; /* simplex */
pf->support_multi_buffer = 1;
pf->support_bulk = 1;
pf->support_interrupt = 1;
pf->support_isochronous = 1;
pf->support_out = 1;
} else if (ftx && (temp <= ntx)) {
pf->max_in_frame_size = 1 << ftx;
pf->max_out_frame_size = 0;
pf->is_simplex = 1; /* simplex */
pf->support_multi_buffer = 1;
pf->support_bulk = 1;
pf->support_interrupt = 1;
pf->support_isochronous = 1;
pf->support_in = 1;
}
}
最后打开所有中断。将锁解开。
DPRINTFN(2, "Dynamic FIFO size = %d bytes\n", offset);
/* turn on default interrupts */
if (sc->sc_mode == MUSB2_HOST_MODE)
MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0xff);
else
MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE,
MUSB2_MASK_IRESET);
musbotg_clocks_off(sc);
USB_BUS_UNLOCK(&sc->sc_bus);
/* catch any lost interrupts */
musbotg_do_poll(&sc->sc_bus);
return (0); /* success */
3.四个传输方式的实现:
musbotg bulk support
/*------------------------------------------------------------------------*
* musbotg bulk support
*------------------------------------------------------------------------*/
static void
musbotg_device_bulk_open(struct usb_xfer *xfer)
{
return;
}
static void
musbotg_device_bulk_close(struct usb_xfer *xfer)
{
musbotg_device_done(xfer, USB_ERR_CANCELLED);
}
static void
musbotg_device_bulk_enter(struct usb_xfer *xfer)
{
return;
}
static void
musbotg_device_bulk_start(struct usb_xfer *xfer)
{
/* setup TDs */
musbotg_setup_standard_chain(xfer);
musbotg_start_standard_chain(xfer);
}
static const struct usb_pipe_methods musbotg_device_bulk_methods =
{
.open = musbotg_device_bulk_open,
.close = musbotg_device_bulk_close,
.enter = musbotg_device_bulk_enter,
.start = musbotg_device_bulk_start,
};
musbotg control support
/*------------------------------------------------------------------------*
* musbotg control support
*------------------------------------------------------------------------*/
static void
musbotg_device_ctrl_open(struct usb_xfer *xfer)
{
return;
}
static void
musbotg_device_ctrl_close(struct usb_xfer *xfer)
{
musbotg_device_done(xfer, USB_ERR_CANCELLED);
}
static void
musbotg_device_ctrl_enter(struct usb_xfer *xfer)
{
return;
}
static void
musbotg_device_ctrl_start(struct usb_xfer *xfer)
{
/* setup TDs */
musbotg_setup_standard_chain(xfer);
musbotg_start_standard_chain(xfer);
}
static const struct usb_pipe_methods musbotg_device_ctrl_methods =
{
.open = musbotg_device_ctrl_open,
.close = musbotg_device_ctrl_close,
.enter = musbotg_device_ctrl_enter,
.start = musbotg_device_ctrl_start,
};
musbotg interrupt support
/*------------------------------------------------------------------------*
* musbotg interrupt support
*------------------------------------------------------------------------*/
static void
musbotg_device_intr_open(struct usb_xfer *xfer)
{
return;
}
static void
musbotg_device_intr_close(struct usb_xfer *xfer)
{
musbotg_device_done(xfer, USB_ERR_CANCELLED);
}
static void
musbotg_device_intr_enter(struct usb_xfer *xfer)
{
return;
}
static void
musbotg_device_intr_start(struct usb_xfer *xfer)
{
/* setup TDs */
musbotg_setup_standard_chain(xfer);
musbotg_start_standard_chain(xfer);
}
static const struct usb_pipe_methods musbotg_device_intr_methods =
{
.open = musbotg_device_intr_open,
.close = musbotg_device_intr_close,
.enter = musbotg_device_intr_enter,
.start = musbotg_device_intr_start,
};
musbotg full speed isochronous support
/*------------------------------------------------------------------------*
* musbotg full speed isochronous support
*------------------------------------------------------------------------*/
static void
musbotg_device_isoc_open(struct usb_xfer *xfer)
{
return;
}
static void
musbotg_device_isoc_close(struct usb_xfer *xfer)
{
musbotg_device_done(xfer, USB_ERR_CANCELLED);
}
static void
musbotg_device_isoc_enter(struct usb_xfer *xfer)
{
struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
uint32_t temp;
uint32_t nframes;
uint32_t fs_frames;
DPRINTFN(5, "xfer=%p next=%d nframes=%d\n",
xfer, xfer->endpoint->isoc_next, xfer->nframes);
/* get the current frame index */
nframes = MUSB2_READ_2(sc, MUSB2_REG_FRAME);
/*
* check if the frame index is within the window where the frames
* will be inserted
*/
temp = (nframes - xfer->endpoint->isoc_next) & MUSB2_MASK_FRAME;
if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH) {
fs_frames = (xfer->nframes + 7) / 8;
} else {
fs_frames = xfer->nframes;
}
if ((xfer->endpoint->is_synced == 0) ||
(temp < fs_frames)) {
/*
* If there is data underflow or the pipe queue is
* empty we schedule the transfer a few frames ahead
* of the current frame position. Else two isochronous
* transfers might overlap.
*/
xfer->endpoint->isoc_next = (nframes + 3) & MUSB2_MASK_FRAME;
xfer->endpoint->is_synced = 1;
DPRINTFN(2, "start next=%d\n", xfer->endpoint->isoc_next);
}
/*
* compute how many milliseconds the insertion is ahead of the
* current frame position:
*/
temp = (xfer->endpoint->isoc_next - nframes) & MUSB2_MASK_FRAME;
/*
* pre-compute when the isochronous transfer will be finished:
*/
xfer->isoc_time_complete =
usb_isoc_time_expand(&sc->sc_bus, nframes) + temp +
fs_frames;
/* compute frame number for next insertion */
xfer->endpoint->isoc_next += fs_frames;
/* setup TDs */
musbotg_setup_standard_chain(xfer);
}
static void
musbotg_device_isoc_start(struct usb_xfer *xfer)
{
/* start TD chain */
musbotg_start_standard_chain(xfer);
}
static const struct usb_pipe_methods musbotg_device_isoc_methods =
{
.open = musbotg_device_isoc_open,
.close = musbotg_device_isoc_close,
.enter = musbotg_device_isoc_enter,
.start = musbotg_device_isoc_start,
};
4.musbotg_setup_standard_chain函数实现,逐条分析
定义了一些结构体,重要的是temp。
struct musbotg_std_temp temp;
struct musbotg_softc *sc;
struct musbotg_td *td;
uint32_t x;
uint8_t ep_no;
uint8_t xfer_type;
enum usb_dev_speed speed;
int tx;
int dev_addr;
然后对temp进行配置,包括其地址,通道,还有传输类型
sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
ep_no = (xfer->endpointno & UE_ADDR);
temp.max_frame_size = xfer->max_frame_size;
td = xfer->td_start[0];
xfer->td_transfer_first = td;
xfer->td_transfer_cache = td;
/* setup temp */
dev_addr = xfer->address;
xfer_type = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE;
temp.pc = NULL;
temp.td = NULL;
temp.td_next = xfer->td_start[0];
temp.offset = 0;
temp.setup_alt_next = xfer->flags_int.short_frames_ok ||
xfer->flags_int.isochronous_xfr;
temp.did_stall = !xfer->flags_int.control_stall;
temp.channel = -1;
temp.dev_addr = dev_addr;
temp.haddr = xfer->xroot->udev->hs_hub_addr;
temp.hport = xfer->xroot->udev->hs_port_no;
if (xfer->flags_int.usb_mode == USB_MODE_HOST) {
speed = usbd_get_speed(xfer->xroot->udev);
switch (speed) {
case USB_SPEED_LOW:
temp.transfer_type = MUSB2_MASK_TI_SPEED_LO;
break;
case USB_SPEED_FULL:
temp.transfer_type = MUSB2_MASK_TI_SPEED_FS;
break;
case USB_SPEED_HIGH:
temp.transfer_type = MUSB2_MASK_TI_SPEED_HS;
break;
default:
temp.transfer_type = 0;
DPRINTFN(-1, "Invalid USB speed: %d\n", speed);
break;
}
switch (xfer_type) {
case UE_CONTROL:
temp.transfer_type |= MUSB2_MASK_TI_PROTO_CTRL;
break;
case UE_ISOCHRONOUS:
temp.transfer_type |= MUSB2_MASK_TI_PROTO_ISOC;
break;
case UE_BULK:
temp.transfer_type |= MUSB2_MASK_TI_PROTO_BULK;
break;
case UE_INTERRUPT:
temp.transfer_type |= MUSB2_MASK_TI_PROTO_INTR;
break;
default:
DPRINTFN(-1, "Invalid USB transfer type: %d\n",
xfer_type);
break;
}
temp.transfer_type |= ep_no;
td->toggle = xfer->endpoint->toggle_next;
}
然后判断是否要发送一个SETUP事务,这点非常重要,在控制传输中,SETUP事务是第一阶段也就是设置阶段,第一个if判断是否要进行控制传输,第二个if判断控制的header是否需要发送,如果是的话,就将musbotg_host_ctrl_setup_tx赋给temp.func,并将x置为1,否则置为0。
/* check if we should prepend a setup message */
if (xfer->flags_int.control_xfr) {
if (xfer->flags_int.control_hdr) {
if (xfer->flags_int.usb_mode == USB_MODE_DEVICE)
temp.func = &musbotg_dev_ctrl_setup_rx;
else
temp.func = &musbotg_host_ctrl_setup_tx;
temp.len = xfer->frlengths[0];
temp.pc = xfer->frbuffers + 0;
temp.short_pkt = temp.len ? 1 : 0;
musbotg_setup_standard_chain_sub(&temp);
}
x = 1;
} else {
x = 0;
}
判断x与 xfer->nframes的关系,如果endpoint是UE_DIR_IN,那么tx设置为1,然后判断如果是工作在主机模式,tx = !tx;
将TX反转,也就是tx为0,表示主机模式下的接收,然后判断是否要发送一个SETUP事务,如果是的话,就调用控制发送函数,也就是阶段一,temp.func = &musbotg_host_ctrl_data_rx; 否则调用数据发送函数,也就是阶段二,temp.func = &musbotg_host_data_rx;
tx = 0;
if (x != xfer->nframes) {
if (xfer->endpointno & UE_DIR_IN)
tx = 1;
if (xfer->flags_int.usb_mode == USB_MODE_HOST) {
tx = !tx;
if (tx) {
if (xfer->flags_int.control_xfr)
temp.func = &musbotg_host_ctrl_data_tx;
else
temp.func = &musbotg_host_data_tx;
} else {
if (xfer->flags_int.control_xfr)
temp.func = &musbotg_host_ctrl_data_rx;
else
temp.func = &musbotg_host_data_rx;
}
} else {
if (tx) {
if (xfer->flags_int.control_xfr)
temp.func = &musbotg_dev_ctrl_data_tx;
else
temp.func = &musbotg_dev_data_tx;
} else {
if (xfer->flags_int.control_xfr)
temp.func = &musbotg_dev_ctrl_data_rx;
else
temp.func = &musbotg_dev_data_rx;
}
}
/* setup "pc" pointer */
temp.pc = xfer->frbuffers + x;
}
然后是开启DATA0或DATA1的发送,也就是第二阶段
while (x != xfer->nframes) {
/* DATA0 / DATA1 message */
temp.len = xfer->frlengths[x];
x++;
if (x == xfer->nframes) {
if (xfer->flags_int.control_xfr) {
if (xfer->flags_int.control_act) {
temp.setup_alt_next = 0;
}
} else {
temp.setup_alt_next = 0;
}
}
if (temp.len == 0) {
/* make sure that we send an USB packet */
temp.short_pkt = 0;
} else {
if (xfer->flags_int.isochronous_xfr) {
/* isochronous data transfer */
/* don't force short */
temp.short_pkt = 1;
} else {
/* regular data transfer */
temp.short_pkt = (xfer->flags.force_short_xfer ? 0 : 1);
}
}
musbotg_setup_standard_chain_sub(&temp);
if (xfer->flags_int.isochronous_xfr) {
temp.offset += temp.len;
} else {
/* get next Page Cache pointer */
temp.pc = xfer->frbuffers + x;
}
}
最后就是控制传输的最后一个阶段,也就是状态阶段,用来表示整个传输过程已经全部结束,状态传输的方向必须和数据阶段的方向相反,即原来是IN令牌包,这个阶段就是OUT令牌包:
/* check for control transfer */
if (xfer->flags_int.control_xfr) {
/* always setup a valid "pc" pointer for status and sync */
temp.pc = xfer->frbuffers + 0;
temp.len = 0;
temp.short_pkt = 0;
temp.setup_alt_next = 0;
/* check if we should append a status stage */
if (!xfer->flags_int.control_act) {
/*
* Send a DATA1 message and invert the current
* endpoint direction.
*/
if (sc->sc_mode == MUSB2_DEVICE_MODE)
temp.func = &musbotg_dev_ctrl_status;
else {
if (xfer->endpointno & UE_DIR_IN)
temp.func = musbotg_host_ctrl_status_tx;
else
temp.func = musbotg_host_ctrl_status_rx;
}
musbotg_setup_standard_chain_sub(&temp);
}
}
5.musbotg_start_standard_chain函数,主要作用是开启中断以及传输:
static void
musbotg_start_standard_chain(struct usb_xfer *xfer)
{
DPRINTFN(8, "\n");
/* poll one time */
if (musbotg_xfer_do_fifo(xfer)) {
DPRINTFN(14, "enabled interrupts on endpoint\n");
/* put transfer on interrupt queue */
usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
/* start timeout, if any */
if (xfer->timeout != 0) {
usbd_transfer_timeout_ms(xfer,
&musbotg_timeout, xfer->timeout);
}
}
}