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

usb: typec: tcpm: Respond Wait if VDM state machine is running

Port partner could send PR_SWAP/DR_SWAP/VCONN_SWAP/Request just after it
enters Ready states. This will cause conficts if the port is going to
send DISC_IDENT in the Ready states of TCPM. Set a flag indicating that
the state machine is processing VDM and respond Wait messages until the
VDM state machine stops.

Tested-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Kyle Tso <kyletso@google.com>
Link: https://lore.kernel.org/r/20210114145053.1952756-4-kyletso@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Kyle Tso and committed by
Greg Kroah-Hartman
8d3a0578 8dea75e1

+73 -7
+73 -7
drivers/usb/typec/tcpm/tcpm.c
··· 352 352 struct hrtimer enable_frs_timer; 353 353 struct kthread_work enable_frs; 354 354 bool state_machine_running; 355 + bool vdm_sm_running; 355 356 356 357 struct completion tx_complete; 357 358 enum tcpm_transmit_status tx_status; ··· 1527 1526 rlen = 1; 1528 1527 } else { 1529 1528 tcpm_register_partner_altmodes(port); 1529 + port->vdm_sm_running = false; 1530 1530 } 1531 1531 break; 1532 1532 case CMD_ENTER_MODE: ··· 1571 1569 rlen = 1; 1572 1570 break; 1573 1571 } 1572 + port->vdm_sm_running = false; 1574 1573 break; 1575 1574 default: 1576 1575 response[0] = p[0] | VDO_CMDT(CMDT_RSP_NAK); 1577 1576 rlen = 1; 1577 + port->vdm_sm_running = false; 1578 1578 break; 1579 1579 } 1580 1580 ··· 1743 1739 switch (PD_VDO_CMD(vdo_hdr)) { 1744 1740 case CMD_DISCOVER_IDENT: 1745 1741 res = tcpm_ams_start(port, DISCOVER_IDENTITY); 1742 + if (res == 0) 1743 + port->send_discover = false; 1746 1744 break; 1747 1745 case CMD_DISCOVER_SVID: 1748 1746 res = tcpm_ams_start(port, DISCOVER_SVIDS); ··· 1769 1763 break; 1770 1764 } 1771 1765 1772 - if (res < 0) 1766 + if (res < 0) { 1767 + port->vdm_sm_running = false; 1773 1768 return; 1769 + } 1774 1770 } 1775 1771 1776 1772 port->vdm_state = VDM_STATE_SEND_MESSAGE; ··· 1850 1842 } while (port->vdm_state != prev_state && 1851 1843 port->vdm_state != VDM_STATE_BUSY && 1852 1844 port->vdm_state != VDM_STATE_SEND_MESSAGE); 1845 + 1846 + if (port->vdm_state == VDM_STATE_ERR_TMOUT) 1847 + port->vdm_sm_running = false; 1853 1848 1854 1849 mutex_unlock(&port->lock); 1855 1850 } ··· 2237 2226 } 2238 2227 2239 2228 port->sink_request = le32_to_cpu(msg->payload[0]); 2229 + 2230 + if (port->vdm_sm_running && port->explicit_contract) { 2231 + tcpm_pd_handle_msg(port, PD_MSG_CTRL_WAIT, port->ams); 2232 + break; 2233 + } 2234 + 2240 2235 if (port->state == SRC_SEND_CAPABILITIES) 2241 2236 tcpm_set_state(port, SRC_NEGOTIATE_CAPABILITIES, 0); 2242 2237 else ··· 2345 2328 TYPEC_PWR_MODE_PD, 2346 2329 port->pps_data.active, 2347 2330 port->supply_voltage); 2331 + /* Set VDM running flag ASAP */ 2332 + if (port->data_role == TYPEC_HOST && 2333 + port->send_discover) 2334 + port->vdm_sm_running = true; 2348 2335 tcpm_set_state(port, SNK_READY, 0); 2349 2336 } else { 2350 2337 /* ··· 2386 2365 switch (port->state) { 2387 2366 case SNK_NEGOTIATE_CAPABILITIES: 2388 2367 /* USB PD specification, Figure 8-43 */ 2389 - if (port->explicit_contract) 2368 + if (port->explicit_contract) { 2390 2369 next_state = SNK_READY; 2391 - else 2370 + if (port->data_role == TYPEC_HOST && 2371 + port->send_discover) 2372 + port->vdm_sm_running = true; 2373 + } else { 2392 2374 next_state = SNK_WAIT_CAPABILITIES; 2375 + } 2393 2376 tcpm_set_state(port, next_state, 0); 2394 2377 break; 2395 2378 case SNK_NEGOTIATE_PPS_CAPABILITIES: ··· 2402 2377 port->pps_data.op_curr = port->current_limit; 2403 2378 port->pps_status = (type == PD_CTRL_WAIT ? 2404 2379 -EAGAIN : -EOPNOTSUPP); 2380 + 2381 + if (port->data_role == TYPEC_HOST && 2382 + port->send_discover) 2383 + port->vdm_sm_running = true; 2384 + 2405 2385 tcpm_set_state(port, SNK_READY, 0); 2406 2386 break; 2407 2387 case DR_SWAP_SEND: ··· 2463 2433 } 2464 2434 break; 2465 2435 case DR_SWAP_SEND: 2436 + if (port->data_role == TYPEC_DEVICE && 2437 + port->send_discover) 2438 + port->vdm_sm_running = true; 2439 + 2466 2440 tcpm_set_state(port, DR_SWAP_CHANGE_DR, 0); 2467 2441 break; 2468 2442 case PR_SWAP_SEND: ··· 2497 2463 * 6.3.9: If an alternate mode is active, a request to swap 2498 2464 * alternate modes shall trigger a port reset. 2499 2465 */ 2500 - if (port->typec_caps.data != TYPEC_PORT_DRD) 2466 + if (port->typec_caps.data != TYPEC_PORT_DRD) { 2501 2467 tcpm_pd_handle_msg(port, 2502 2468 port->negotiated_rev < PD_REV30 ? 2503 2469 PD_MSG_CTRL_REJECT : 2504 2470 PD_MSG_CTRL_NOT_SUPP, 2505 2471 NONE_AMS); 2506 - else 2472 + } else { 2473 + if (port->vdm_sm_running) { 2474 + tcpm_queue_message(port, PD_MSG_CTRL_WAIT); 2475 + break; 2476 + } 2477 + 2507 2478 tcpm_pd_handle_state(port, DR_SWAP_ACCEPT, DATA_ROLE_SWAP, 0); 2479 + } 2508 2480 break; 2509 2481 case PD_CTRL_PR_SWAP: 2510 - if (port->port_type != TYPEC_PORT_DRP) 2482 + if (port->port_type != TYPEC_PORT_DRP) { 2511 2483 tcpm_pd_handle_msg(port, 2512 2484 port->negotiated_rev < PD_REV30 ? 2513 2485 PD_MSG_CTRL_REJECT : 2514 2486 PD_MSG_CTRL_NOT_SUPP, 2515 2487 NONE_AMS); 2516 - else 2488 + } else { 2489 + if (port->vdm_sm_running) { 2490 + tcpm_queue_message(port, PD_MSG_CTRL_WAIT); 2491 + break; 2492 + } 2493 + 2517 2494 tcpm_pd_handle_state(port, PR_SWAP_ACCEPT, POWER_ROLE_SWAP, 0); 2495 + } 2518 2496 break; 2519 2497 case PD_CTRL_VCONN_SWAP: 2498 + if (port->vdm_sm_running) { 2499 + tcpm_queue_message(port, PD_MSG_CTRL_WAIT); 2500 + break; 2501 + } 2502 + 2520 2503 tcpm_pd_handle_state(port, VCONN_SWAP_ACCEPT, VCONN_SWAP, 0); 2521 2504 break; 2522 2505 case PD_CTRL_GET_SOURCE_CAP_EXT: ··· 3397 3346 } 3398 3347 port->in_ams = false; 3399 3348 port->ams = NONE_AMS; 3349 + port->vdm_sm_running = false; 3400 3350 tcpm_unregister_altmodes(port); 3401 3351 tcpm_typec_disconnect(port); 3402 3352 port->attached = false; ··· 4196 4144 break; 4197 4145 case DR_SWAP_ACCEPT: 4198 4146 tcpm_pd_send_control(port, PD_CTRL_ACCEPT); 4147 + /* Set VDM state machine running flag ASAP */ 4148 + if (port->data_role == TYPEC_DEVICE && port->send_discover) 4149 + port->vdm_sm_running = true; 4199 4150 tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0); 4200 4151 break; 4201 4152 case DR_SWAP_SEND_TIMEOUT: ··· 4354 4299 break; 4355 4300 case VCONN_SWAP_SEND_TIMEOUT: 4356 4301 tcpm_swap_complete(port, -ETIMEDOUT); 4302 + if (port->data_role == TYPEC_HOST && port->send_discover) 4303 + port->vdm_sm_running = true; 4357 4304 tcpm_set_state(port, ready_state(port), 0); 4358 4305 break; 4359 4306 case VCONN_SWAP_START: ··· 4371 4314 case VCONN_SWAP_TURN_ON_VCONN: 4372 4315 tcpm_set_vconn(port, true); 4373 4316 tcpm_pd_send_control(port, PD_CTRL_PS_RDY); 4317 + if (port->data_role == TYPEC_HOST && port->send_discover) 4318 + port->vdm_sm_running = true; 4374 4319 tcpm_set_state(port, ready_state(port), 0); 4375 4320 break; 4376 4321 case VCONN_SWAP_TURN_OFF_VCONN: 4377 4322 tcpm_set_vconn(port, false); 4323 + if (port->data_role == TYPEC_HOST && port->send_discover) 4324 + port->vdm_sm_running = true; 4378 4325 tcpm_set_state(port, ready_state(port), 0); 4379 4326 break; 4380 4327 ··· 4386 4325 case PR_SWAP_CANCEL: 4387 4326 case VCONN_SWAP_CANCEL: 4388 4327 tcpm_swap_complete(port, port->swap_status); 4328 + if (port->data_role == TYPEC_HOST && port->send_discover) 4329 + port->vdm_sm_running = true; 4389 4330 if (port->pwr_role == TYPEC_SOURCE) 4390 4331 tcpm_set_state(port, SRC_READY, 0); 4391 4332 else ··· 4717 4654 switch (port->state) { 4718 4655 case SNK_TRANSITION_SINK_VBUS: 4719 4656 port->explicit_contract = true; 4657 + /* Set the VDM flag ASAP */ 4658 + if (port->data_role == TYPEC_HOST && port->send_discover) 4659 + port->vdm_sm_running = true; 4720 4660 tcpm_set_state(port, SNK_READY, 0); 4721 4661 break; 4722 4662 case SNK_DISCOVERY: