|
@@ -948,6 +948,156 @@ __acquires(hwep->lock)
|
|
return retval;
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * isr_setup_packet_handler: setup packet handler
|
|
|
|
+ * @ci: UDC descriptor
|
|
|
|
+ *
|
|
|
|
+ * This function handles setup packet
|
|
|
|
+ */
|
|
|
|
+static void isr_setup_packet_handler(struct ci_hdrc *ci)
|
|
|
|
+__releases(ci->lock)
|
|
|
|
+__acquires(ci->lock)
|
|
|
|
+{
|
|
|
|
+ struct ci_hw_ep *hwep = &ci->ci_hw_ep[0];
|
|
|
|
+ struct usb_ctrlrequest req;
|
|
|
|
+ int type, num, dir, err = -EINVAL;
|
|
|
|
+ u8 tmode = 0;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Flush data and handshake transactions of previous
|
|
|
|
+ * setup packet.
|
|
|
|
+ */
|
|
|
|
+ _ep_nuke(ci->ep0out);
|
|
|
|
+ _ep_nuke(ci->ep0in);
|
|
|
|
+
|
|
|
|
+ /* read_setup_packet */
|
|
|
|
+ do {
|
|
|
|
+ hw_test_and_set_setup_guard(ci);
|
|
|
|
+ memcpy(&req, &hwep->qh.ptr->setup, sizeof(req));
|
|
|
|
+ } while (!hw_test_and_clear_setup_guard(ci));
|
|
|
|
+
|
|
|
|
+ type = req.bRequestType;
|
|
|
|
+
|
|
|
|
+ ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
|
|
|
|
+
|
|
|
|
+ switch (req.bRequest) {
|
|
|
|
+ case USB_REQ_CLEAR_FEATURE:
|
|
|
|
+ if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
|
|
|
|
+ le16_to_cpu(req.wValue) ==
|
|
|
|
+ USB_ENDPOINT_HALT) {
|
|
|
|
+ if (req.wLength != 0)
|
|
|
|
+ break;
|
|
|
|
+ num = le16_to_cpu(req.wIndex);
|
|
|
|
+ dir = num & USB_ENDPOINT_DIR_MASK;
|
|
|
|
+ num &= USB_ENDPOINT_NUMBER_MASK;
|
|
|
|
+ if (dir) /* TX */
|
|
|
|
+ num += ci->hw_ep_max / 2;
|
|
|
|
+ if (!ci->ci_hw_ep[num].wedge) {
|
|
|
|
+ spin_unlock(&ci->lock);
|
|
|
|
+ err = usb_ep_clear_halt(
|
|
|
|
+ &ci->ci_hw_ep[num].ep);
|
|
|
|
+ spin_lock(&ci->lock);
|
|
|
|
+ if (err)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ err = isr_setup_status_phase(ci);
|
|
|
|
+ } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
|
|
|
|
+ le16_to_cpu(req.wValue) ==
|
|
|
|
+ USB_DEVICE_REMOTE_WAKEUP) {
|
|
|
|
+ if (req.wLength != 0)
|
|
|
|
+ break;
|
|
|
|
+ ci->remote_wakeup = 0;
|
|
|
|
+ err = isr_setup_status_phase(ci);
|
|
|
|
+ } else {
|
|
|
|
+ goto delegate;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case USB_REQ_GET_STATUS:
|
|
|
|
+ if (type != (USB_DIR_IN|USB_RECIP_DEVICE) &&
|
|
|
|
+ type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
|
|
|
|
+ type != (USB_DIR_IN|USB_RECIP_INTERFACE))
|
|
|
|
+ goto delegate;
|
|
|
|
+ if (le16_to_cpu(req.wLength) != 2 ||
|
|
|
|
+ le16_to_cpu(req.wValue) != 0)
|
|
|
|
+ break;
|
|
|
|
+ err = isr_get_status_response(ci, &req);
|
|
|
|
+ break;
|
|
|
|
+ case USB_REQ_SET_ADDRESS:
|
|
|
|
+ if (type != (USB_DIR_OUT|USB_RECIP_DEVICE))
|
|
|
|
+ goto delegate;
|
|
|
|
+ if (le16_to_cpu(req.wLength) != 0 ||
|
|
|
|
+ le16_to_cpu(req.wIndex) != 0)
|
|
|
|
+ break;
|
|
|
|
+ ci->address = (u8)le16_to_cpu(req.wValue);
|
|
|
|
+ ci->setaddr = true;
|
|
|
|
+ err = isr_setup_status_phase(ci);
|
|
|
|
+ break;
|
|
|
|
+ case USB_REQ_SET_FEATURE:
|
|
|
|
+ if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
|
|
|
|
+ le16_to_cpu(req.wValue) ==
|
|
|
|
+ USB_ENDPOINT_HALT) {
|
|
|
|
+ if (req.wLength != 0)
|
|
|
|
+ break;
|
|
|
|
+ num = le16_to_cpu(req.wIndex);
|
|
|
|
+ dir = num & USB_ENDPOINT_DIR_MASK;
|
|
|
|
+ num &= USB_ENDPOINT_NUMBER_MASK;
|
|
|
|
+ if (dir) /* TX */
|
|
|
|
+ num += ci->hw_ep_max / 2;
|
|
|
|
+
|
|
|
|
+ spin_unlock(&ci->lock);
|
|
|
|
+ err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep);
|
|
|
|
+ spin_lock(&ci->lock);
|
|
|
|
+ if (!err)
|
|
|
|
+ isr_setup_status_phase(ci);
|
|
|
|
+ } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
|
|
|
|
+ if (req.wLength != 0)
|
|
|
|
+ break;
|
|
|
|
+ switch (le16_to_cpu(req.wValue)) {
|
|
|
|
+ case USB_DEVICE_REMOTE_WAKEUP:
|
|
|
|
+ ci->remote_wakeup = 1;
|
|
|
|
+ err = isr_setup_status_phase(ci);
|
|
|
|
+ break;
|
|
|
|
+ case USB_DEVICE_TEST_MODE:
|
|
|
|
+ tmode = le16_to_cpu(req.wIndex) >> 8;
|
|
|
|
+ switch (tmode) {
|
|
|
|
+ case TEST_J:
|
|
|
|
+ case TEST_K:
|
|
|
|
+ case TEST_SE0_NAK:
|
|
|
|
+ case TEST_PACKET:
|
|
|
|
+ case TEST_FORCE_EN:
|
|
|
|
+ ci->test_mode = tmode;
|
|
|
|
+ err = isr_setup_status_phase(
|
|
|
|
+ ci);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ default:
|
|
|
|
+ goto delegate;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ goto delegate;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+delegate:
|
|
|
|
+ if (req.wLength == 0) /* no data phase */
|
|
|
|
+ ci->ep0_dir = TX;
|
|
|
|
+
|
|
|
|
+ spin_unlock(&ci->lock);
|
|
|
|
+ err = ci->driver->setup(&ci->gadget, &req);
|
|
|
|
+ spin_lock(&ci->lock);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (err < 0) {
|
|
|
|
+ spin_unlock(&ci->lock);
|
|
|
|
+ if (usb_ep_set_halt(&hwep->ep))
|
|
|
|
+ dev_err(ci->dev, "error: ep_set_halt\n");
|
|
|
|
+ spin_lock(&ci->lock);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* isr_tr_complete_handler: transaction complete interrupt handler
|
|
* isr_tr_complete_handler: transaction complete interrupt handler
|
|
* @ci: UDC descriptor
|
|
* @ci: UDC descriptor
|
|
@@ -959,12 +1109,10 @@ __releases(ci->lock)
|
|
__acquires(ci->lock)
|
|
__acquires(ci->lock)
|
|
{
|
|
{
|
|
unsigned i;
|
|
unsigned i;
|
|
- u8 tmode = 0;
|
|
|
|
|
|
+ int err;
|
|
|
|
|
|
for (i = 0; i < ci->hw_ep_max; i++) {
|
|
for (i = 0; i < ci->hw_ep_max; i++) {
|
|
struct ci_hw_ep *hwep = &ci->ci_hw_ep[i];
|
|
struct ci_hw_ep *hwep = &ci->ci_hw_ep[i];
|
|
- int type, num, dir, err = -EINVAL;
|
|
|
|
- struct usb_ctrlrequest req;
|
|
|
|
|
|
|
|
if (hwep->ep.desc == NULL)
|
|
if (hwep->ep.desc == NULL)
|
|
continue; /* not configured */
|
|
continue; /* not configured */
|
|
@@ -985,143 +1133,9 @@ __acquires(ci->lock)
|
|
}
|
|
}
|
|
|
|
|
|
/* Only handle setup packet below */
|
|
/* Only handle setup packet below */
|
|
- if (i != 0 ||
|
|
|
|
- !hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0)))
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * Flush data and handshake transactions of previous
|
|
|
|
- * setup packet.
|
|
|
|
- */
|
|
|
|
- _ep_nuke(ci->ep0out);
|
|
|
|
- _ep_nuke(ci->ep0in);
|
|
|
|
-
|
|
|
|
- /* read_setup_packet */
|
|
|
|
- do {
|
|
|
|
- hw_test_and_set_setup_guard(ci);
|
|
|
|
- memcpy(&req, &hwep->qh.ptr->setup, sizeof(req));
|
|
|
|
- } while (!hw_test_and_clear_setup_guard(ci));
|
|
|
|
-
|
|
|
|
- type = req.bRequestType;
|
|
|
|
-
|
|
|
|
- ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
|
|
|
|
-
|
|
|
|
- switch (req.bRequest) {
|
|
|
|
- case USB_REQ_CLEAR_FEATURE:
|
|
|
|
- if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
|
|
|
|
- le16_to_cpu(req.wValue) ==
|
|
|
|
- USB_ENDPOINT_HALT) {
|
|
|
|
- if (req.wLength != 0)
|
|
|
|
- break;
|
|
|
|
- num = le16_to_cpu(req.wIndex);
|
|
|
|
- dir = num & USB_ENDPOINT_DIR_MASK;
|
|
|
|
- num &= USB_ENDPOINT_NUMBER_MASK;
|
|
|
|
- if (dir) /* TX */
|
|
|
|
- num += ci->hw_ep_max/2;
|
|
|
|
- if (!ci->ci_hw_ep[num].wedge) {
|
|
|
|
- spin_unlock(&ci->lock);
|
|
|
|
- err = usb_ep_clear_halt(
|
|
|
|
- &ci->ci_hw_ep[num].ep);
|
|
|
|
- spin_lock(&ci->lock);
|
|
|
|
- if (err)
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- err = isr_setup_status_phase(ci);
|
|
|
|
- } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
|
|
|
|
- le16_to_cpu(req.wValue) ==
|
|
|
|
- USB_DEVICE_REMOTE_WAKEUP) {
|
|
|
|
- if (req.wLength != 0)
|
|
|
|
- break;
|
|
|
|
- ci->remote_wakeup = 0;
|
|
|
|
- err = isr_setup_status_phase(ci);
|
|
|
|
- } else {
|
|
|
|
- goto delegate;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- case USB_REQ_GET_STATUS:
|
|
|
|
- if (type != (USB_DIR_IN|USB_RECIP_DEVICE) &&
|
|
|
|
- type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
|
|
|
|
- type != (USB_DIR_IN|USB_RECIP_INTERFACE))
|
|
|
|
- goto delegate;
|
|
|
|
- if (le16_to_cpu(req.wLength) != 2 ||
|
|
|
|
- le16_to_cpu(req.wValue) != 0)
|
|
|
|
- break;
|
|
|
|
- err = isr_get_status_response(ci, &req);
|
|
|
|
- break;
|
|
|
|
- case USB_REQ_SET_ADDRESS:
|
|
|
|
- if (type != (USB_DIR_OUT|USB_RECIP_DEVICE))
|
|
|
|
- goto delegate;
|
|
|
|
- if (le16_to_cpu(req.wLength) != 0 ||
|
|
|
|
- le16_to_cpu(req.wIndex) != 0)
|
|
|
|
- break;
|
|
|
|
- ci->address = (u8)le16_to_cpu(req.wValue);
|
|
|
|
- ci->setaddr = true;
|
|
|
|
- err = isr_setup_status_phase(ci);
|
|
|
|
- break;
|
|
|
|
- case USB_REQ_SET_FEATURE:
|
|
|
|
- if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
|
|
|
|
- le16_to_cpu(req.wValue) ==
|
|
|
|
- USB_ENDPOINT_HALT) {
|
|
|
|
- if (req.wLength != 0)
|
|
|
|
- break;
|
|
|
|
- num = le16_to_cpu(req.wIndex);
|
|
|
|
- dir = num & USB_ENDPOINT_DIR_MASK;
|
|
|
|
- num &= USB_ENDPOINT_NUMBER_MASK;
|
|
|
|
- if (dir) /* TX */
|
|
|
|
- num += ci->hw_ep_max/2;
|
|
|
|
-
|
|
|
|
- spin_unlock(&ci->lock);
|
|
|
|
- err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep);
|
|
|
|
- spin_lock(&ci->lock);
|
|
|
|
- if (!err)
|
|
|
|
- isr_setup_status_phase(ci);
|
|
|
|
- } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
|
|
|
|
- if (req.wLength != 0)
|
|
|
|
- break;
|
|
|
|
- switch (le16_to_cpu(req.wValue)) {
|
|
|
|
- case USB_DEVICE_REMOTE_WAKEUP:
|
|
|
|
- ci->remote_wakeup = 1;
|
|
|
|
- err = isr_setup_status_phase(ci);
|
|
|
|
- break;
|
|
|
|
- case USB_DEVICE_TEST_MODE:
|
|
|
|
- tmode = le16_to_cpu(req.wIndex) >> 8;
|
|
|
|
- switch (tmode) {
|
|
|
|
- case TEST_J:
|
|
|
|
- case TEST_K:
|
|
|
|
- case TEST_SE0_NAK:
|
|
|
|
- case TEST_PACKET:
|
|
|
|
- case TEST_FORCE_EN:
|
|
|
|
- ci->test_mode = tmode;
|
|
|
|
- err = isr_setup_status_phase(
|
|
|
|
- ci);
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- default:
|
|
|
|
- goto delegate;
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- goto delegate;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
-delegate:
|
|
|
|
- if (req.wLength == 0) /* no data phase */
|
|
|
|
- ci->ep0_dir = TX;
|
|
|
|
-
|
|
|
|
- spin_unlock(&ci->lock);
|
|
|
|
- err = ci->driver->setup(&ci->gadget, &req);
|
|
|
|
- spin_lock(&ci->lock);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (err < 0) {
|
|
|
|
- spin_unlock(&ci->lock);
|
|
|
|
- if (usb_ep_set_halt(&hwep->ep))
|
|
|
|
- dev_err(ci->dev, "error: ep_set_halt\n");
|
|
|
|
- spin_lock(&ci->lock);
|
|
|
|
- }
|
|
|
|
|
|
+ if (i == 0 &&
|
|
|
|
+ hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0)))
|
|
|
|
+ isr_setup_packet_handler(ci);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|