⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lh7a40x_udc.c

📁 ARM S3C2410 USB SLAVE LINUX驱动
💻 C
📖 第 1 页 / 共 4 页
字号:

	return 1;
}

static int lh7a40x_handle_get_status(struct lh7a40x_udc *dev,
				     struct usb_ctrlrequest *ctrl)
{
	struct lh7a40x_ep *ep0 = &dev->ep[0];
	struct lh7a40x_ep *qep;
	int reqtype = (ctrl->bRequestType & USB_RECIP_MASK);
	u16 val = 0;

	if (reqtype == USB_RECIP_INTERFACE) {
		/* This is not supported.
		 * And according to the USB spec, this one does nothing..
		 * Just return 0
		 */
		DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n");
	} else if (reqtype == USB_RECIP_DEVICE) {
		DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n");
		val |= (1 << 0);	/* Self powered */
		/*val |= (1<<1); *//* Remote wakeup */
	} else if (reqtype == USB_RECIP_ENDPOINT) {
		int ep_num = (ctrl->wIndex & ~USB_DIR_IN);

		DEBUG_SETUP
		    ("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n",
		     ep_num, ctrl->wLength);

		if (ctrl->wLength > 2 || ep_num > 3)
			return -EOPNOTSUPP;

		qep = &dev->ep[ep_num];
		if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0)
		    && ep_index(qep) != 0) {
			return -EOPNOTSUPP;
		}

		usb_set_index(ep_index(qep));

		/* Return status on next IN token */
		switch (qep->ep_type) {
		case ep_control:
			val =
			    (usb_read(qep->csr1) & EP0_SEND_STALL) ==
			    EP0_SEND_STALL;
			break;
		case ep_bulk_in:
		case ep_interrupt:
			val =
			    (usb_read(qep->csr1) & USB_IN_CSR1_SEND_STALL) ==
			    USB_IN_CSR1_SEND_STALL;
			break;
		case ep_bulk_out:
			val =
			    (usb_read(qep->csr1) & USB_OUT_CSR1_SEND_STALL) ==
			    USB_OUT_CSR1_SEND_STALL;
			break;
		}

		/* Back to EP0 index */
		usb_set_index(0);

		DEBUG_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num,
			    ctrl->wIndex, val);
	} else {
		DEBUG_SETUP("Unknown REQ TYPE: %d\n", reqtype);
		return -EOPNOTSUPP;
	}

	/* Clear "out packet ready" */
	usb_set((EP0_CLR_OUT), USB_EP0_CSR);
	/* Put status to FIFO */
	lh7a40x_fifo_write(ep0, (u8 *) & val, sizeof(val));
	/* Issue "In packet ready" */
	usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR);

	return 0;
}

/*
 * WAIT_FOR_SETUP (OUT_PKT_RDY)
 *      - read data packet from EP0 FIFO
 *      - decode command
 *      - if error
 *              set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits
 *      - else
 *              set EP0_CLR_OUT | EP0_DATA_END bits
 */
static void lh7a40x_ep0_setup(struct lh7a40x_udc *dev, u32 csr)
{
	struct lh7a40x_ep *ep = &dev->ep[0];
	struct usb_ctrlrequest ctrl;
	int i, bytes, is_in;

	DEBUG_SETUP("%s: %x\n", __FUNCTION__, csr);

	/* Nuke all previous transfers */
	nuke(ep, -EPROTO);

	/* read control req from fifo (8 bytes) */
	bytes = lh7a40x_fifo_read(ep, (unsigned char *)&ctrl, 8);

	DEBUG_SETUP("Read CTRL REQ %d bytes\n", bytes);
	DEBUG_SETUP("CTRL.bRequestType = %d (is_in %d)\n", ctrl.bRequestType,
		    ctrl.bRequestType == USB_DIR_IN);
	DEBUG_SETUP("CTRL.bRequest = %d\n", ctrl.bRequest);
	DEBUG_SETUP("CTRL.wLength = %d\n", ctrl.wLength);
	DEBUG_SETUP("CTRL.wValue = %d (%d)\n", ctrl.wValue, ctrl.wValue >> 8);
	DEBUG_SETUP("CTRL.wIndex = %d\n", ctrl.wIndex);

	/* Set direction of EP0 */
	if (likely(ctrl.bRequestType & USB_DIR_IN)) {
		ep->bEndpointAddress |= USB_DIR_IN;
		is_in = 1;
	} else {
		ep->bEndpointAddress &= ~USB_DIR_IN;
		is_in = 0;
	}

	dev->req_pending = 1;

	/* Handle some SETUP packets ourselves */
	switch (ctrl.bRequest) {
	case USB_REQ_SET_ADDRESS:
		if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
			break;

		DEBUG_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue);
		udc_set_address(dev, ctrl.wValue);
		usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR);
		return;

	case USB_REQ_GET_STATUS:{
			if (lh7a40x_handle_get_status(dev, &ctrl) == 0)
				return;

	case USB_REQ_CLEAR_FEATURE:
	case USB_REQ_SET_FEATURE:
			if (ctrl.bRequestType == USB_RECIP_ENDPOINT) {
				struct lh7a40x_ep *qep;
				int ep_num = (ctrl.wIndex & 0x0f);

				/* Support only HALT feature */
				if (ctrl.wValue != 0 || ctrl.wLength != 0
				    || ep_num > 3 || ep_num < 1)
					break;

				qep = &dev->ep[ep_num];
				if (ctrl.bRequest == USB_REQ_SET_FEATURE) {
					DEBUG_SETUP("SET_FEATURE (%d)\n",
						    ep_num);
					lh7a40x_set_halt(&qep->ep, 1);
				} else {
					DEBUG_SETUP("CLR_FEATURE (%d)\n",
						    ep_num);
					lh7a40x_set_halt(&qep->ep, 0);
				}
				usb_set_index(0);

				/* Reply with a ZLP on next IN token */
				usb_set((EP0_CLR_OUT | EP0_DATA_END),
					USB_EP0_CSR);
				return;
			}
			break;
		}

	default:
		break;
	}

	if (likely(dev->driver)) {
		/* device-2-host (IN) or no data setup command, process immediately */
		spin_unlock(&dev->lock);
		i = dev->driver->setup(&dev->gadget, &ctrl);
		spin_lock(&dev->lock);

		if (i < 0) {
			/* setup processing failed, force stall */
			DEBUG_SETUP
			    ("  --> ERROR: gadget setup FAILED (stalling), setup returned %d\n",
			     i);
			usb_set_index(0);
			usb_set((EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL),
				USB_EP0_CSR);

			/* ep->stopped = 1; */
			dev->ep0state = WAIT_FOR_SETUP;
		}
	}
}

/*
 * DATA_STATE_NEED_ZLP
 */
static void lh7a40x_ep0_in_zlp(struct lh7a40x_udc *dev, u32 csr)
{
	DEBUG_EP0("%s: %x\n", __FUNCTION__, csr);

	/* c.f. Table 15-14 */
	usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR);
	dev->ep0state = WAIT_FOR_SETUP;
}

/*
 * handle ep0 interrupt
 */
static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr)
{
	struct lh7a40x_ep *ep = &dev->ep[0];
	u32 csr;

	/* Set index 0 */
	usb_set_index(0);
 	csr = usb_read(USB_EP0_CSR);

	DEBUG_EP0("%s: csr = %x\n", __FUNCTION__, csr);

	/*
	 * For overview of what we should be doing see c.f. Chapter 18.1.2.4
	 * We will follow that outline here modified by our own global state
	 * indication which provides hints as to what we think should be
	 * happening..
	 */

	/*
	 * if SENT_STALL is set
	 *      - clear the SENT_STALL bit
	 */
	if (csr & EP0_SENT_STALL) {
		DEBUG_EP0("%s: EP0_SENT_STALL is set: %x\n", __FUNCTION__, csr);
		usb_clear((EP0_SENT_STALL | EP0_SEND_STALL), USB_EP0_CSR);
		nuke(ep, -ECONNABORTED);
		dev->ep0state = WAIT_FOR_SETUP;
		return;
	}

	/*
	 * if a transfer is in progress && IN_PKT_RDY and OUT_PKT_RDY are clear
	 *      - fill EP0 FIFO
	 *      - if last packet
	 *      -       set IN_PKT_RDY | DATA_END
	 *      - else
	 *              set IN_PKT_RDY
	 */
	if (!(csr & (EP0_IN_PKT_RDY | EP0_OUT_PKT_RDY))) {
		DEBUG_EP0("%s: IN_PKT_RDY and OUT_PKT_RDY are clear\n",
			  __FUNCTION__);

		switch (dev->ep0state) {
		case DATA_STATE_XMIT:
			DEBUG_EP0("continue with DATA_STATE_XMIT\n");
			lh7a40x_ep0_in(dev, csr);
			return;
		case DATA_STATE_NEED_ZLP:
			DEBUG_EP0("continue with DATA_STATE_NEED_ZLP\n");
			lh7a40x_ep0_in_zlp(dev, csr);
			return;
		default:
			/* Stall? */
			DEBUG_EP0("Odd state!! state = %s\n",
				  state_names[dev->ep0state]);
			dev->ep0state = WAIT_FOR_SETUP;
			/* nuke(ep, 0); */
			/* usb_set(EP0_SEND_STALL, ep->csr1); */
			break;
		}
	}

	/*
	 * if SETUP_END is set
	 *      - abort the last transfer
	 *      - set SERVICED_SETUP_END_BIT
	 */
	if (csr & EP0_SETUP_END) {
		DEBUG_EP0("%s: EP0_SETUP_END is set: %x\n", __FUNCTION__, csr);

		usb_set(EP0_CLR_SETUP_END, USB_EP0_CSR);

		nuke(ep, 0);
		dev->ep0state = WAIT_FOR_SETUP;
	}

	/*
	 * if EP0_OUT_PKT_RDY is set
	 *      - read data packet from EP0 FIFO
	 *      - decode command
	 *      - if error
	 *              set SERVICED_OUT_PKT_RDY | DATA_END bits | SEND_STALL
	 *      - else
	 *              set SERVICED_OUT_PKT_RDY | DATA_END bits
	 */
	if (csr & EP0_OUT_PKT_RDY) {

		DEBUG_EP0("%s: EP0_OUT_PKT_RDY is set: %x\n", __FUNCTION__,
			  csr);

		switch (dev->ep0state) {
		case WAIT_FOR_SETUP:
			DEBUG_EP0("WAIT_FOR_SETUP\n");
			lh7a40x_ep0_setup(dev, csr);
			break;

		case DATA_STATE_RECV:
			DEBUG_EP0("DATA_STATE_RECV\n");
			lh7a40x_ep0_out(dev, csr);
			break;

		default:
			/* send stall? */
			DEBUG_EP0("strange state!! 2. send stall? state = %d\n",
				  dev->ep0state);
			break;
		}
	}
}

static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep)
{
	u32 csr;

	usb_set_index(0);
	csr = usb_read(USB_EP0_CSR);

	DEBUG_EP0("%s: %x\n", __FUNCTION__, csr);

	/* Clear "out packet ready" */
	usb_set(EP0_CLR_OUT, USB_EP0_CSR);

	if (ep_is_in(ep)) {
		dev->ep0state = DATA_STATE_XMIT;
		lh7a40x_ep0_in(dev, csr);
	} else {
		dev->ep0state = DATA_STATE_RECV;
		lh7a40x_ep0_out(dev, csr);
	}
}

/* ---------------------------------------------------------------------------
 * 	device-scoped parts of the api to the usb controller hardware
 * ---------------------------------------------------------------------------
 */

static int lh7a40x_udc_get_frame(struct usb_gadget *_gadget)
{
	u32 frame1 = usb_read(USB_FRM_NUM1);	/* Least significant 8 bits */
	u32 frame2 = usb_read(USB_FRM_NUM2);	/* Most significant 3 bits */
	DEBUG("%s, %p\n", __FUNCTION__, _gadget);
	return ((frame2 & 0x07) << 8) | (frame1 & 0xff);
}

static int lh7a40x_udc_wakeup(struct usb_gadget *_gadget)
{
	/* host may not have enabled remote wakeup */
	/*if ((UDCCS0 & UDCCS0_DRWF) == 0)
	   return -EHOSTUNREACH;
	   udc_set_mask_UDCCR(UDCCR_RSM); */
	return -ENOTSUPP;
}

static const struct usb_gadget_ops lh7a40x_udc_ops = {
	.get_frame = lh7a40x_udc_get_frame,
	.wakeup = lh7a40x_udc_wakeup,
	/* current versions must always be self-powered */
};

static void nop_release(struct device *dev)
{
	DEBUG("%s %s\n", __FUNCTION__, dev->bus_id);
}

static struct lh7a40x_udc memory = {
	.usb_address = 0,

	.gadget = {
		   .ops = &lh7a40x_udc_ops,
		   .ep0 = &memory.ep[0].ep,
		   .name = driver_name,
		   .dev = {
			   .bus_id = "gadget",
			   .release = nop_release,
			   },
		   },

	/* control endpoint */
	.ep[0] = {
		  .ep = {
			 .name = ep0name,
			 .ops = &lh7a40x_ep_ops,
			 .maxpacket = EP0_PACKETSIZE,
			 },
		  .dev = &memory,

		  .bEndpointAddress = 0,
		  .bmAttributes = 0,

		  .ep_type = ep_control,
		  .fifo = io_p2v(USB_EP0_FIFO),
		  .csr1 = USB_EP0_CSR,
		  .csr2 = USB_EP0_CSR,
		  },

	/* first group of endpoints */
	.ep[1] = {
		  .ep = {
			 .name = "ep1in-bulk",
			 .ops = &lh7a40x_ep_ops,
			 .maxpacket = 64,
			 },
		  .dev = &memory,

		  .bEndpointAddress = USB_DIR_IN | 1,
		  .bmAttributes = USB_ENDPOINT_XFER_BULK,

		  .ep_type = ep_bulk_in,
		  .fifo = io_p2v(USB_EP1_FIFO),
		  .csr1 = USB_IN_CSR1,
		  .csr2 = USB_IN_CSR2,
		  },

	.ep[2] = {
		  .ep = {
			 .name = "ep2out-bulk",
			 .ops = &lh7a40x_ep_ops,
			 .maxpacket = 64,
			 },
		  .dev = &memory,

		  .bEndpointAddress = 2,
		  .bmAttributes = USB_ENDPOINT_XFER_BULK,

		  .ep_type = ep_bulk_out,
		  .fifo = io_p2v(USB_EP2_FIFO),
		  .csr1 = USB_OUT_CSR1,
		  .csr2 = USB_OUT_CSR2,
		  },

	.ep[3] = {
		  .ep = {
			 .name = "ep3in-int",
			 .ops = &lh7a40x_ep_ops,
			 .maxpacket = 64,
			 },
		  .dev = &memory,

		  .bEndpointAddress = USB_DIR_IN | 3,
		  .bmAttributes = USB_ENDPOINT_XFER_INT,

		  .ep_type = ep_interrupt,
		  .fifo = io_p2v(USB_EP3_FIFO),
		  .csr1 = USB_IN_CSR1,
		  .csr2 = USB_IN_CSR2,
		  },
};

/*
 * 	probe - binds to the platform device
 */
static int lh7a40x_udc_probe(struct device *_dev)
{
	struct lh7a40x_udc *dev = &memory;
	int retval;

	DEBUG("%s: %p\n", __FUNCTION__, _dev);

	spin_lock_init(&dev->lock);
	dev->dev = _dev;

	device_initialize(&dev->gadget.dev);
	dev->gadget.dev.parent = _dev;

	the_controller = dev;
	dev_set_drvdata(_dev, dev);

	udc_disable(dev);
	udc_reinit(dev);

	/* irq setup after old hardware state is cleaned up */
	retval =
	    request_irq(IRQ_USBINTR, lh7a40x_udc_irq, SA_INTERRUPT, driver_name,
			dev);
	if (retval != 0) {
		DEBUG(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name,
		      IRQ_USBINTR, retval);
		return -EBUSY;
	}

	create_proc_files();

	return retval;
}

static int lh7a40x_udc_remove(struct device *_dev)
{
	struct lh7a40x_udc *dev = _dev->driver_data;

	DEBUG("%s: %p\n", __FUNCTION__, dev);

	udc_disable(dev);
	remove_proc_files();
	usb_gadget_unregister_driver(dev->driver);

	free_irq(IRQ_USBINTR, dev);

	dev_set_drvdata(_dev, 0);

	the_controller = 0;

	return 0;
}

/*-------------------------------------------------------------------------*/

static struct device_driver udc_driver = {
	.name = (char *)driver_name,
	.bus = &platform_bus_type,
	.probe = lh7a40x_udc_probe,
	.remove = lh7a40x_udc_remove
	    /* FIXME power management support */
	    /* .suspend = ... disable UDC */
	    /* .resume = ... re-enable UDC */
};

static int __init udc_init(void)
{
	DEBUG("%s: %s version %s\n", __FUNCTION__, driver_name, DRIVER_VERSION);
	return driver_register(&udc_driver);
}

static void __exit udc_exit(void)
{
	driver_unregister(&udc_driver);
}

module_init(udc_init);
module_exit(udc_exit);

MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Mikko Lahteenmaki, Bo Henriksen");
MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -