Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: chipidea: udc: using the correct stall implementation

According to spec, there are functional and protocol stalls.

For functional stall, it is for bulk and interrupt endpoints,
below are cases for it:
- Host sends SET_FEATURE request for Set-Halt, the udc driver
needs to set stall, and return true unconditionally.
- The gadget driver may call usb_ep_set_halt to stall certain
endpoints, if there is a transfer in pending, the udc driver
should not set stall, and return -EAGAIN accordingly.
These two kinds of stall need to be cleared by host using CLEAR_FEATURE
request (Clear-Halt).

For protocol stall, it is for control endpoint, this stall will
be set if the control request has failed. This stall will be
cleared by next setup request (hardware will do it).

It fixed usbtest (drivers/usb/misc/usbtest.c) Test 13 "set/clear halt"
test failure, meanwhile, this change has been verified by
USB2 CV Compliance Test and MSC Tests.

Cc: <stable@vger.kernel.org> #3.10+
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Felipe Balbi <balbi@ti.com>
Signed-off-by: Peter Chen <peter.chen@freescale.com>

+44 -40
+44 -40
drivers/usb/chipidea/udc.c
··· 656 656 return 0; 657 657 } 658 658 659 + static int _ep_set_halt(struct usb_ep *ep, int value, bool check_transfer) 660 + { 661 + struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); 662 + int direction, retval = 0; 663 + unsigned long flags; 664 + 665 + if (ep == NULL || hwep->ep.desc == NULL) 666 + return -EINVAL; 667 + 668 + if (usb_endpoint_xfer_isoc(hwep->ep.desc)) 669 + return -EOPNOTSUPP; 670 + 671 + spin_lock_irqsave(hwep->lock, flags); 672 + 673 + if (value && hwep->dir == TX && check_transfer && 674 + !list_empty(&hwep->qh.queue) && 675 + !usb_endpoint_xfer_control(hwep->ep.desc)) { 676 + spin_unlock_irqrestore(hwep->lock, flags); 677 + return -EAGAIN; 678 + } 679 + 680 + direction = hwep->dir; 681 + do { 682 + retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value); 683 + 684 + if (!value) 685 + hwep->wedge = 0; 686 + 687 + if (hwep->type == USB_ENDPOINT_XFER_CONTROL) 688 + hwep->dir = (hwep->dir == TX) ? RX : TX; 689 + 690 + } while (hwep->dir != direction); 691 + 692 + spin_unlock_irqrestore(hwep->lock, flags); 693 + return retval; 694 + } 695 + 696 + 659 697 /** 660 698 * _gadget_stop_activity: stops all USB activity, flushes & disables all endpts 661 699 * @gadget: gadget ··· 1089 1051 num += ci->hw_ep_max / 2; 1090 1052 1091 1053 spin_unlock(&ci->lock); 1092 - err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep); 1054 + err = _ep_set_halt(&ci->ci_hw_ep[num].ep, 1, false); 1093 1055 spin_lock(&ci->lock); 1094 1056 if (!err) 1095 1057 isr_setup_status_phase(ci); ··· 1155 1117 1156 1118 if (err < 0) { 1157 1119 spin_unlock(&ci->lock); 1158 - if (usb_ep_set_halt(&hwep->ep)) 1159 - dev_err(ci->dev, "error: ep_set_halt\n"); 1120 + if (_ep_set_halt(&hwep->ep, 1, false)) 1121 + dev_err(ci->dev, "error: _ep_set_halt\n"); 1160 1122 spin_lock(&ci->lock); 1161 1123 } 1162 1124 } ··· 1187 1149 err = isr_setup_status_phase(ci); 1188 1150 if (err < 0) { 1189 1151 spin_unlock(&ci->lock); 1190 - if (usb_ep_set_halt(&hwep->ep)) 1152 + if (_ep_set_halt(&hwep->ep, 1, false)) 1191 1153 dev_err(ci->dev, 1192 - "error: ep_set_halt\n"); 1154 + "error: _ep_set_halt\n"); 1193 1155 spin_lock(&ci->lock); 1194 1156 } 1195 1157 } ··· 1435 1397 */ 1436 1398 static int ep_set_halt(struct usb_ep *ep, int value) 1437 1399 { 1438 - struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); 1439 - int direction, retval = 0; 1440 - unsigned long flags; 1441 - 1442 - if (ep == NULL || hwep->ep.desc == NULL) 1443 - return -EINVAL; 1444 - 1445 - if (usb_endpoint_xfer_isoc(hwep->ep.desc)) 1446 - return -EOPNOTSUPP; 1447 - 1448 - spin_lock_irqsave(hwep->lock, flags); 1449 - 1450 - #ifndef STALL_IN 1451 - /* g_file_storage MS compliant but g_zero fails chapter 9 compliance */ 1452 - if (value && hwep->type == USB_ENDPOINT_XFER_BULK && hwep->dir == TX && 1453 - !list_empty(&hwep->qh.queue)) { 1454 - spin_unlock_irqrestore(hwep->lock, flags); 1455 - return -EAGAIN; 1456 - } 1457 - #endif 1458 - 1459 - direction = hwep->dir; 1460 - do { 1461 - retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value); 1462 - 1463 - if (!value) 1464 - hwep->wedge = 0; 1465 - 1466 - if (hwep->type == USB_ENDPOINT_XFER_CONTROL) 1467 - hwep->dir = (hwep->dir == TX) ? RX : TX; 1468 - 1469 - } while (hwep->dir != direction); 1470 - 1471 - spin_unlock_irqrestore(hwep->lock, flags); 1472 - return retval; 1400 + return _ep_set_halt(ep, value, true); 1473 1401 } 1474 1402 1475 1403 /**