当前位置: 首页 > news >正文

FreeBSD-musb_otg文件详解

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,
};


可以看到除了同步传输模式的实现,其他的实现都是调用了musbotg_setup_standard_chain和musbotg_start_standard_chain两个函数。接下来具体分析这两个函数的实现:


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);
		}
	}
}









转载于:https://www.cnblogs.com/sichenzhao/p/9320160.html

相关文章:

  • centos7 安装wps 后 演示无法启动
  • NSString属性什么时候用copy,什么时候用strong?
  • 使用Visual Studio Code对Node.js进行断点调试
  • CentOS7.2升级openSSH为7.5P1无法登录的处理过程
  • linux复盘:mysql双主与mysql-proxy实现读写分离
  • 10.28 rsync工具介绍 10.29/10.30 rsync常用选项 10.31 rsync通
  • 三角形内随机生成一个点
  • 04.spring security oauth2认证中心 集成zuul网关的代码分析
  • 2018 掌握好这几点方法学习Linux,一定比别人更快入门运维!
  • python小白项目推荐
  • 使用Jackson来实现java对象和json格式的相互转换
  • Python设计模式——观察者模式
  • 如何免费创建移动端论坛APP?
  • 3.7字典
  • css线性炫酷动画
  • 【划重点】MySQL技术内幕:InnoDB存储引擎
  • CSS相对定位
  •  D - 粉碎叛乱F - 其他起义
  • leetcode-27. Remove Element
  • SQLServer插入数据
  • Vue 2.3、2.4 知识点小结
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 排序算法学习笔记
  • 区块链技术特点之去中心化特性
  • 详解移动APP与web APP的区别
  • Hibernate主键生成策略及选择
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • ( 10 )MySQL中的外键
  • (10)ATF MMU转换表
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (2020)Java后端开发----(面试题和笔试题)
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (独孤九剑)--文件系统
  • (二开)Flink 修改源码拓展 SQL 语法
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (转)项目管理杂谈-我所期望的新人
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .NET Core 网络数据采集 -- 使用AngleSharp做html解析
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • @manytomany 保存后数据被删除_[Windows] 数据恢复软件RStudio v8.14.179675 便携特别版...
  • @test注解_Spring 自定义注解你了解过吗?
  • []sim300 GPRS数据收发程序
  • []使用 Tortoise SVN 创建 Externals 外部引用目录
  • [20181219]script使用小技巧.txt
  • [Angular] 笔记 21:@ViewChild
  • [BZOJ] 2044: 三维导弹拦截
  • [bzoj1324]Exca王者之剑_最小割
  • [C#]winform制作圆形进度条好用的圆环圆形进度条控件和使用方法
  • [Flutter]设置应用包名、名称、版本号、最低支持版本、Icon、启动页以及环境判断、平台判断和打包
  • [github全教程]github版本控制最全教学------- 大厂找工作面试必备!
  • [Json.net]快速入门
  • [LeetCode] Max Points on a Line
  • [Linux]Ubuntu noVNC使用
  • [SCOI2010]传送带
  • [SQL开发笔记]UPDATE 语句:更新表中的记录