Loading drivers/usb/pd/policy_engine.c +109 −70 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <linux/power_supply.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/extcon.h> #include <linux/usb/usbpd.h> Loading Loading @@ -264,6 +265,16 @@ struct vdm_tx { int size; }; struct rx_msg { u8 type; u8 len; u32 payload[7]; struct list_head entry; }; #define IS_DATA(m, t) ((m) && ((m)->len) && ((m)->type == (t))) #define IS_CTRL(m, t) ((m) && !((m)->len) && ((m)->type == (t))) struct usbpd { struct device dev; struct workqueue_struct *wq; Loading @@ -275,9 +286,8 @@ struct usbpd { enum usbpd_state current_state; bool hard_reset_recvd; u8 rx_msg_type; u8 rx_msg_len; u32 rx_payload[7]; struct list_head rx_q; spinlock_t rx_lock; u32 received_pdos[7]; int src_cap_id; Loading Loading @@ -457,14 +467,10 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos) return 0; } static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps) static int pd_eval_src_caps(struct usbpd *pd) { union power_supply_propval val; u32 first_pdo = src_caps[0]; /* save the PDOs so userspace can further evaluate */ memcpy(&pd->received_pdos, src_caps, sizeof(pd->received_pdos)); pd->src_cap_id++; u32 first_pdo = pd->received_pdos[0]; if (PD_SRC_PDO_TYPE(first_pdo) != PD_SRC_PDO_TYPE_FIXED) { usbpd_err(&pd->dev, "First src_cap invalid! %08x\n", first_pdo); Loading Loading @@ -525,6 +531,8 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, u8 *buf, size_t len) { struct rx_msg *rx_msg; unsigned long flags; u16 header; if (type != SOP_MSG) { Loading Loading @@ -567,16 +575,20 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, return; } /* block until previous message has been consumed by usbpd_sm */ if (pd->rx_msg_type) flush_work(&pd->sm_work); rx_msg = kzalloc(sizeof(*rx_msg), GFP_KERNEL); if (!rx_msg) return; pd->rx_msg_type = PD_MSG_HDR_TYPE(header); pd->rx_msg_len = PD_MSG_HDR_COUNT(header); memcpy(&pd->rx_payload, buf, len); rx_msg->type = PD_MSG_HDR_TYPE(header); rx_msg->len = PD_MSG_HDR_COUNT(header); memcpy(&rx_msg->payload, buf, len); spin_lock_irqsave(&pd->rx_lock, flags); list_add_tail(&rx_msg->entry, &pd->rx_q); spin_unlock_irqrestore(&pd->rx_lock, flags); usbpd_dbg(&pd->dev, "received message: type(%d) len(%d)\n", pd->rx_msg_type, pd->rx_msg_len); rx_msg->type, rx_msg->len); kick_sm(pd, 0); } Loading Loading @@ -607,6 +619,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) FRAME_FILTER_EN_HARD_RESET }; union power_supply_propval val = {0}; unsigned long flags; int ret; usbpd_dbg(&pd->dev, "%s -> %s\n", Loading Loading @@ -638,8 +651,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); pd->rx_msg_len = 0; pd->rx_msg_type = 0; pd->rx_msgid = -1; if (!pd->in_pr_swap) { Loading Loading @@ -804,8 +815,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) /* Reset protocol layer */ pd->tx_msgid = 0; pd->rx_msgid = -1; pd->rx_msg_len = 0; pd->rx_msg_type = 0; if (!pd->in_pr_swap) { if (pd->pd_phy_opened) { Loading Loading @@ -834,10 +843,10 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) /* fall-through */ case PE_SNK_WAIT_FOR_CAPABILITIES: if (pd->rx_msg_len && pd->rx_msg_type) kick_sm(pd, 0); else spin_lock_irqsave(&pd->rx_lock, flags); if (list_empty(&pd->rx_q)) kick_sm(pd, SINK_WAIT_CAP_TIME); spin_unlock_irqrestore(&pd->rx_lock, flags); break; case PE_SNK_EVALUATE_CAPABILITY: Loading @@ -845,7 +854,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->hard_reset_count = 0; /* evaluate PDOs and select one */ ret = pd_eval_src_caps(pd, pd->rx_payload); ret = pd_eval_src_caps(pd); if (ret < 0) { usbpd_err(&pd->dev, "Invalid src_caps received. Skipping request\n"); break; Loading Loading @@ -999,13 +1008,13 @@ int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd, } EXPORT_SYMBOL(usbpd_send_svdm); static void handle_vdm_rx(struct usbpd *pd) static void handle_vdm_rx(struct usbpd *pd, struct rx_msg *rx_msg) { u32 vdm_hdr = pd->rx_payload[0]; u32 *vdos = &pd->rx_payload[1]; u32 vdm_hdr = rx_msg->payload[0]; u32 *vdos = &rx_msg->payload[1]; u16 svid = VDM_HDR_SVID(vdm_hdr); u16 *psvid; u8 i, num_vdos = pd->rx_msg_len - 1; /* num objects minus header */ u8 i, num_vdos = rx_msg->len - 1; /* num objects minus header */ u8 cmd = SVDM_HDR_CMD(vdm_hdr); u8 cmd_type = SVDM_HDR_CMD_TYPE(vdm_hdr); struct usbpd_svid_handler *handler; Loading Loading @@ -1330,14 +1339,27 @@ static void vconn_swap(struct usbpd *pd) } } static inline void rx_msg_cleanup(struct usbpd *pd) { struct rx_msg *msg, *tmp; unsigned long flags; spin_lock_irqsave(&pd->rx_lock, flags); list_for_each_entry_safe(msg, tmp, &pd->rx_q, entry) { list_del(&msg->entry); kfree(msg); } spin_unlock_irqrestore(&pd->rx_lock, flags); } /* Handles current state and determines transitions */ static void usbpd_sm(struct work_struct *w) { struct usbpd *pd = container_of(w, struct usbpd, sm_work); union power_supply_propval val = {0}; int ret; enum usbpd_control_msg_type ctrl_recvd = 0; enum usbpd_data_msg_type data_recvd = 0; struct rx_msg *rx_msg = NULL; unsigned long flags; usbpd_dbg(&pd->dev, "handle state %s\n", usbpd_state_strings[pd->current_state]); Loading @@ -1345,10 +1367,12 @@ static void usbpd_sm(struct work_struct *w) hrtimer_cancel(&pd->timer); pd->sm_queued = false; if (pd->rx_msg_len) data_recvd = pd->rx_msg_type; else ctrl_recvd = pd->rx_msg_type; spin_lock_irqsave(&pd->rx_lock, flags); if (!list_empty(&pd->rx_q)) { rx_msg = list_first_entry(&pd->rx_q, struct rx_msg, entry); list_del(&rx_msg->entry); } spin_unlock_irqrestore(&pd->rx_lock, flags); /* Disconnect? */ if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE && !pd->in_pr_swap) { Loading @@ -1372,6 +1396,7 @@ static void usbpd_sm(struct work_struct *w) pd->requested_voltage = 0; pd->requested_current = 0; memset(&pd->received_pdos, 0, sizeof(pd->received_pdos)); rx_msg_cleanup(pd); val.intval = 0; power_supply_set_property(pd->usb_psy, Loading Loading @@ -1426,6 +1451,7 @@ static void usbpd_sm(struct work_struct *w) POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); pd->in_pr_swap = false; rx_msg_cleanup(pd); reset_vdm_state(pd); if (pd->current_pr == PR_SINK) { Loading @@ -1439,7 +1465,7 @@ static void usbpd_sm(struct work_struct *w) } /* Soft reset? */ if (ctrl_recvd == MSG_SOFT_RESET) { if (IS_CTRL(rx_msg, MSG_SOFT_RESET)) { usbpd_dbg(&pd->dev, "Handle soft reset\n"); if (pd->current_pr == PR_SRC) Loading Loading @@ -1519,10 +1545,10 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SRC_SEND_CAPABILITIES_WAIT: if (data_recvd == MSG_REQUEST) { pd->rdo = pd->rx_payload[0]; if (IS_DATA(rx_msg, MSG_REQUEST)) { pd->rdo = rx_msg->payload[0]; usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY); } else if (data_recvd || ctrl_recvd) { } else if (rx_msg) { usbpd_err(&pd->dev, "Unexpected message received\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); } else { Loading @@ -1531,7 +1557,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SRC_READY: if (ctrl_recvd == MSG_GET_SOURCE_CAP) { if (IS_CTRL(rx_msg, MSG_GET_SOURCE_CAP)) { ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, default_src_caps, ARRAY_SIZE(default_src_caps), SOP_MSG); Loading @@ -1540,7 +1566,7 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } } else if (ctrl_recvd == MSG_GET_SINK_CAP) { } else if (IS_CTRL(rx_msg, MSG_GET_SINK_CAP)) { ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES, default_snk_caps, ARRAY_SIZE(default_snk_caps), SOP_MSG); Loading @@ -1548,10 +1574,10 @@ static void usbpd_sm(struct work_struct *w) usbpd_err(&pd->dev, "Error sending Sink Caps\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); } } else if (data_recvd == MSG_REQUEST) { pd->rdo = pd->rx_payload[0]; } else if (IS_DATA(rx_msg, MSG_REQUEST)) { pd->rdo = rx_msg->payload[0]; usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY); } else if (ctrl_recvd == MSG_DR_SWAP) { } else if (IS_CTRL(rx_msg, MSG_DR_SWAP)) { if (pd->vdm_state == MODE_ENTERED) { usbpd_set_state(pd, PE_SRC_HARD_RESET); break; Loading @@ -1566,7 +1592,7 @@ static void usbpd_sm(struct work_struct *w) dr_swap(pd); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); } else if (ctrl_recvd == MSG_PR_SWAP) { } else if (IS_CTRL(rx_msg, MSG_PR_SWAP)) { /* lock in current mode */ set_power_role(pd, pd->current_pr); Loading @@ -1581,7 +1607,7 @@ static void usbpd_sm(struct work_struct *w) pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; kick_sm(pd, SRC_TRANSITION_TIME); break; } else if (ctrl_recvd == MSG_VCONN_SWAP) { } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) { ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Accept\n"); Loading @@ -1591,8 +1617,8 @@ static void usbpd_sm(struct work_struct *w) vconn_swap(pd); } else { if (data_recvd == MSG_VDM) handle_vdm_rx(pd); if (IS_DATA(rx_msg, MSG_VDM)) handle_vdm_rx(pd, rx_msg); else handle_vdm_tx(pd); } Loading Loading @@ -1637,6 +1663,7 @@ static void usbpd_sm(struct work_struct *w) pd_send_hard_reset(pd); pd->in_explicit_contract = false; rx_msg_cleanup(pd); reset_vdm_state(pd); pd->current_state = PE_SRC_TRANSITION_TO_DEFAULT; Loading @@ -1648,7 +1675,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_WAIT_FOR_CAPABILITIES: if (data_recvd == MSG_SOURCE_CAPABILITIES) { if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) { val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, Loading @@ -1658,6 +1685,11 @@ static void usbpd_sm(struct work_struct *w) power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); /* save the PDOs so userspace can further evaluate */ memcpy(&pd->received_pdos, rx_msg->payload, sizeof(pd->received_pdos)); pd->src_cap_id++; usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY); } else if (pd->hard_reset_count < 3) { usbpd_set_state(pd, PE_SNK_HARD_RESET); Loading Loading @@ -1685,7 +1717,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_SELECT_CAPABILITY: if (ctrl_recvd == MSG_ACCEPT) { if (IS_CTRL(rx_msg, MSG_ACCEPT)) { /* prepare for voltage increase/decrease */ val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, Loading @@ -1705,13 +1737,14 @@ static void usbpd_sm(struct work_struct *w) pd->selected_pdo = pd->requested_pdo; usbpd_set_state(pd, PE_SNK_TRANSITION_SINK); } else if (ctrl_recvd == MSG_REJECT || ctrl_recvd == MSG_WAIT) { } else if (IS_CTRL(rx_msg, MSG_REJECT) || IS_CTRL(rx_msg, MSG_WAIT)) { if (pd->in_explicit_contract) usbpd_set_state(pd, PE_SNK_READY); else usbpd_set_state(pd, PE_SNK_WAIT_FOR_CAPABILITIES); } else if (pd->rx_msg_type) { } else if (rx_msg) { usbpd_err(&pd->dev, "Invalid response to sink request\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } else { Loading @@ -1721,7 +1754,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_TRANSITION_SINK: if (ctrl_recvd == MSG_PS_RDY) { if (IS_CTRL(rx_msg, MSG_PS_RDY)) { val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, pd->requested_voltage >= pd->current_voltage ? Loading @@ -1742,9 +1775,14 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_READY: if (data_recvd == MSG_SOURCE_CAPABILITIES) { if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) { /* save the PDOs so userspace can further evaluate */ memcpy(&pd->received_pdos, rx_msg->payload, sizeof(pd->received_pdos)); pd->src_cap_id++; usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY); } else if (ctrl_recvd == MSG_GET_SINK_CAP) { } else if (IS_CTRL(rx_msg, MSG_GET_SINK_CAP)) { ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES, default_snk_caps, ARRAY_SIZE(default_snk_caps), SOP_MSG); Loading @@ -1752,7 +1790,7 @@ static void usbpd_sm(struct work_struct *w) usbpd_err(&pd->dev, "Error sending Sink Caps\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } } else if (ctrl_recvd == MSG_GET_SOURCE_CAP) { } else if (IS_CTRL(rx_msg, MSG_GET_SOURCE_CAP)) { ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, default_src_caps, ARRAY_SIZE(default_src_caps), SOP_MSG); Loading @@ -1761,7 +1799,7 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } } else if (ctrl_recvd == MSG_DR_SWAP) { } else if (IS_CTRL(rx_msg, MSG_DR_SWAP)) { if (pd->vdm_state == MODE_ENTERED) { usbpd_set_state(pd, PE_SNK_HARD_RESET); break; Loading @@ -1776,7 +1814,7 @@ static void usbpd_sm(struct work_struct *w) dr_swap(pd); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); } else if (ctrl_recvd == MSG_PR_SWAP) { } else if (IS_CTRL(rx_msg, MSG_PR_SWAP)) { /* lock in current mode */ set_power_role(pd, pd->current_pr); Loading @@ -1791,7 +1829,7 @@ static void usbpd_sm(struct work_struct *w) pd->in_pr_swap = true; usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; } else if (ctrl_recvd == MSG_VCONN_SWAP) { } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) { /* * if VCONN is connected to VBUS, make sure we are * not in high voltage contract, otherwise reject. Loading @@ -1818,8 +1856,8 @@ static void usbpd_sm(struct work_struct *w) vconn_swap(pd); } else { if (data_recvd == MSG_VDM) handle_vdm_rx(pd); if (IS_DATA(rx_msg, MSG_VDM)) handle_vdm_rx(pd, rx_msg); else handle_vdm_tx(pd); } Loading Loading @@ -1865,7 +1903,7 @@ static void usbpd_sm(struct work_struct *w) case PE_SRC_SEND_SOFT_RESET: case PE_SNK_SEND_SOFT_RESET: if (ctrl_recvd == MSG_ACCEPT) { if (IS_CTRL(rx_msg, MSG_ACCEPT)) { usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_SEND_CAPABILITIES : PE_SNK_WAIT_FOR_CAPABILITIES); Loading Loading @@ -1902,7 +1940,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_DRS_SEND_DR_SWAP: if (ctrl_recvd == MSG_ACCEPT) if (IS_CTRL(rx_msg, MSG_ACCEPT)) dr_swap(pd); usbpd_set_state(pd, pd->current_pr == PR_SRC ? Loading @@ -1910,7 +1948,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_PRS_SRC_SNK_SEND_SWAP: if (ctrl_recvd != MSG_ACCEPT) { if (!IS_CTRL(rx_msg, MSG_ACCEPT)) { pd->current_state = PE_SRC_READY; break; } Loading Loading @@ -1945,14 +1983,14 @@ static void usbpd_sm(struct work_struct *w) break; case PE_PRS_SRC_SNK_WAIT_SOURCE_ON: if (ctrl_recvd == MSG_PS_RDY) if (IS_CTRL(rx_msg, MSG_PS_RDY)) usbpd_set_state(pd, PE_SNK_STARTUP); else usbpd_set_state(pd, PE_ERROR_RECOVERY); break; case PE_PRS_SNK_SRC_SEND_SWAP: if (ctrl_recvd != MSG_ACCEPT) { if (!IS_CTRL(rx_msg, MSG_ACCEPT)) { pd->current_state = PE_SNK_READY; break; } Loading @@ -1962,7 +2000,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_PRS_SNK_SRC_TRANSITION_TO_OFF: if (ctrl_recvd != MSG_PS_RDY) { if (!IS_CTRL(rx_msg, MSG_PS_RDY)) { usbpd_set_state(pd, PE_ERROR_RECOVERY); break; } Loading Loading @@ -1992,7 +2030,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_VCS_WAIT_FOR_VCONN: if (ctrl_recvd == MSG_PS_RDY) { if (IS_CTRL(rx_msg, MSG_PS_RDY)) { /* * hopefully redundant check but in case not enabled * avoids unbalanced regulator disable count Loading @@ -2017,10 +2055,9 @@ static void usbpd_sm(struct work_struct *w) break; } /* Rx message should have been consumed now */ pd->rx_msg_type = pd->rx_msg_len = 0; sm_done: kfree(rx_msg); if (!pd->sm_queued) pm_relax(&pd->dev); } Loading Loading @@ -2657,6 +2694,8 @@ struct usbpd *usbpd_create(struct device *parent) pd->current_dr = DR_NONE; list_add_tail(&pd->instance, &_usbpd); spin_lock_init(&pd->rx_lock); INIT_LIST_HEAD(&pd->rx_q); INIT_LIST_HEAD(&pd->svid_handlers); /* force read initial power_supply values */ Loading Loading
drivers/usb/pd/policy_engine.c +109 −70 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <linux/power_supply.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/extcon.h> #include <linux/usb/usbpd.h> Loading Loading @@ -264,6 +265,16 @@ struct vdm_tx { int size; }; struct rx_msg { u8 type; u8 len; u32 payload[7]; struct list_head entry; }; #define IS_DATA(m, t) ((m) && ((m)->len) && ((m)->type == (t))) #define IS_CTRL(m, t) ((m) && !((m)->len) && ((m)->type == (t))) struct usbpd { struct device dev; struct workqueue_struct *wq; Loading @@ -275,9 +286,8 @@ struct usbpd { enum usbpd_state current_state; bool hard_reset_recvd; u8 rx_msg_type; u8 rx_msg_len; u32 rx_payload[7]; struct list_head rx_q; spinlock_t rx_lock; u32 received_pdos[7]; int src_cap_id; Loading Loading @@ -457,14 +467,10 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos) return 0; } static int pd_eval_src_caps(struct usbpd *pd, const u32 *src_caps) static int pd_eval_src_caps(struct usbpd *pd) { union power_supply_propval val; u32 first_pdo = src_caps[0]; /* save the PDOs so userspace can further evaluate */ memcpy(&pd->received_pdos, src_caps, sizeof(pd->received_pdos)); pd->src_cap_id++; u32 first_pdo = pd->received_pdos[0]; if (PD_SRC_PDO_TYPE(first_pdo) != PD_SRC_PDO_TYPE_FIXED) { usbpd_err(&pd->dev, "First src_cap invalid! %08x\n", first_pdo); Loading Loading @@ -525,6 +531,8 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, u8 *buf, size_t len) { struct rx_msg *rx_msg; unsigned long flags; u16 header; if (type != SOP_MSG) { Loading Loading @@ -567,16 +575,20 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, return; } /* block until previous message has been consumed by usbpd_sm */ if (pd->rx_msg_type) flush_work(&pd->sm_work); rx_msg = kzalloc(sizeof(*rx_msg), GFP_KERNEL); if (!rx_msg) return; pd->rx_msg_type = PD_MSG_HDR_TYPE(header); pd->rx_msg_len = PD_MSG_HDR_COUNT(header); memcpy(&pd->rx_payload, buf, len); rx_msg->type = PD_MSG_HDR_TYPE(header); rx_msg->len = PD_MSG_HDR_COUNT(header); memcpy(&rx_msg->payload, buf, len); spin_lock_irqsave(&pd->rx_lock, flags); list_add_tail(&rx_msg->entry, &pd->rx_q); spin_unlock_irqrestore(&pd->rx_lock, flags); usbpd_dbg(&pd->dev, "received message: type(%d) len(%d)\n", pd->rx_msg_type, pd->rx_msg_len); rx_msg->type, rx_msg->len); kick_sm(pd, 0); } Loading Loading @@ -607,6 +619,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) FRAME_FILTER_EN_HARD_RESET }; union power_supply_propval val = {0}; unsigned long flags; int ret; usbpd_dbg(&pd->dev, "%s -> %s\n", Loading Loading @@ -638,8 +651,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); pd->rx_msg_len = 0; pd->rx_msg_type = 0; pd->rx_msgid = -1; if (!pd->in_pr_swap) { Loading Loading @@ -804,8 +815,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) /* Reset protocol layer */ pd->tx_msgid = 0; pd->rx_msgid = -1; pd->rx_msg_len = 0; pd->rx_msg_type = 0; if (!pd->in_pr_swap) { if (pd->pd_phy_opened) { Loading Loading @@ -834,10 +843,10 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) /* fall-through */ case PE_SNK_WAIT_FOR_CAPABILITIES: if (pd->rx_msg_len && pd->rx_msg_type) kick_sm(pd, 0); else spin_lock_irqsave(&pd->rx_lock, flags); if (list_empty(&pd->rx_q)) kick_sm(pd, SINK_WAIT_CAP_TIME); spin_unlock_irqrestore(&pd->rx_lock, flags); break; case PE_SNK_EVALUATE_CAPABILITY: Loading @@ -845,7 +854,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->hard_reset_count = 0; /* evaluate PDOs and select one */ ret = pd_eval_src_caps(pd, pd->rx_payload); ret = pd_eval_src_caps(pd); if (ret < 0) { usbpd_err(&pd->dev, "Invalid src_caps received. Skipping request\n"); break; Loading Loading @@ -999,13 +1008,13 @@ int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd, } EXPORT_SYMBOL(usbpd_send_svdm); static void handle_vdm_rx(struct usbpd *pd) static void handle_vdm_rx(struct usbpd *pd, struct rx_msg *rx_msg) { u32 vdm_hdr = pd->rx_payload[0]; u32 *vdos = &pd->rx_payload[1]; u32 vdm_hdr = rx_msg->payload[0]; u32 *vdos = &rx_msg->payload[1]; u16 svid = VDM_HDR_SVID(vdm_hdr); u16 *psvid; u8 i, num_vdos = pd->rx_msg_len - 1; /* num objects minus header */ u8 i, num_vdos = rx_msg->len - 1; /* num objects minus header */ u8 cmd = SVDM_HDR_CMD(vdm_hdr); u8 cmd_type = SVDM_HDR_CMD_TYPE(vdm_hdr); struct usbpd_svid_handler *handler; Loading Loading @@ -1330,14 +1339,27 @@ static void vconn_swap(struct usbpd *pd) } } static inline void rx_msg_cleanup(struct usbpd *pd) { struct rx_msg *msg, *tmp; unsigned long flags; spin_lock_irqsave(&pd->rx_lock, flags); list_for_each_entry_safe(msg, tmp, &pd->rx_q, entry) { list_del(&msg->entry); kfree(msg); } spin_unlock_irqrestore(&pd->rx_lock, flags); } /* Handles current state and determines transitions */ static void usbpd_sm(struct work_struct *w) { struct usbpd *pd = container_of(w, struct usbpd, sm_work); union power_supply_propval val = {0}; int ret; enum usbpd_control_msg_type ctrl_recvd = 0; enum usbpd_data_msg_type data_recvd = 0; struct rx_msg *rx_msg = NULL; unsigned long flags; usbpd_dbg(&pd->dev, "handle state %s\n", usbpd_state_strings[pd->current_state]); Loading @@ -1345,10 +1367,12 @@ static void usbpd_sm(struct work_struct *w) hrtimer_cancel(&pd->timer); pd->sm_queued = false; if (pd->rx_msg_len) data_recvd = pd->rx_msg_type; else ctrl_recvd = pd->rx_msg_type; spin_lock_irqsave(&pd->rx_lock, flags); if (!list_empty(&pd->rx_q)) { rx_msg = list_first_entry(&pd->rx_q, struct rx_msg, entry); list_del(&rx_msg->entry); } spin_unlock_irqrestore(&pd->rx_lock, flags); /* Disconnect? */ if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE && !pd->in_pr_swap) { Loading @@ -1372,6 +1396,7 @@ static void usbpd_sm(struct work_struct *w) pd->requested_voltage = 0; pd->requested_current = 0; memset(&pd->received_pdos, 0, sizeof(pd->received_pdos)); rx_msg_cleanup(pd); val.intval = 0; power_supply_set_property(pd->usb_psy, Loading Loading @@ -1426,6 +1451,7 @@ static void usbpd_sm(struct work_struct *w) POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); pd->in_pr_swap = false; rx_msg_cleanup(pd); reset_vdm_state(pd); if (pd->current_pr == PR_SINK) { Loading @@ -1439,7 +1465,7 @@ static void usbpd_sm(struct work_struct *w) } /* Soft reset? */ if (ctrl_recvd == MSG_SOFT_RESET) { if (IS_CTRL(rx_msg, MSG_SOFT_RESET)) { usbpd_dbg(&pd->dev, "Handle soft reset\n"); if (pd->current_pr == PR_SRC) Loading Loading @@ -1519,10 +1545,10 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SRC_SEND_CAPABILITIES_WAIT: if (data_recvd == MSG_REQUEST) { pd->rdo = pd->rx_payload[0]; if (IS_DATA(rx_msg, MSG_REQUEST)) { pd->rdo = rx_msg->payload[0]; usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY); } else if (data_recvd || ctrl_recvd) { } else if (rx_msg) { usbpd_err(&pd->dev, "Unexpected message received\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); } else { Loading @@ -1531,7 +1557,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SRC_READY: if (ctrl_recvd == MSG_GET_SOURCE_CAP) { if (IS_CTRL(rx_msg, MSG_GET_SOURCE_CAP)) { ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, default_src_caps, ARRAY_SIZE(default_src_caps), SOP_MSG); Loading @@ -1540,7 +1566,7 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); break; } } else if (ctrl_recvd == MSG_GET_SINK_CAP) { } else if (IS_CTRL(rx_msg, MSG_GET_SINK_CAP)) { ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES, default_snk_caps, ARRAY_SIZE(default_snk_caps), SOP_MSG); Loading @@ -1548,10 +1574,10 @@ static void usbpd_sm(struct work_struct *w) usbpd_err(&pd->dev, "Error sending Sink Caps\n"); usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET); } } else if (data_recvd == MSG_REQUEST) { pd->rdo = pd->rx_payload[0]; } else if (IS_DATA(rx_msg, MSG_REQUEST)) { pd->rdo = rx_msg->payload[0]; usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY); } else if (ctrl_recvd == MSG_DR_SWAP) { } else if (IS_CTRL(rx_msg, MSG_DR_SWAP)) { if (pd->vdm_state == MODE_ENTERED) { usbpd_set_state(pd, PE_SRC_HARD_RESET); break; Loading @@ -1566,7 +1592,7 @@ static void usbpd_sm(struct work_struct *w) dr_swap(pd); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); } else if (ctrl_recvd == MSG_PR_SWAP) { } else if (IS_CTRL(rx_msg, MSG_PR_SWAP)) { /* lock in current mode */ set_power_role(pd, pd->current_pr); Loading @@ -1581,7 +1607,7 @@ static void usbpd_sm(struct work_struct *w) pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF; kick_sm(pd, SRC_TRANSITION_TIME); break; } else if (ctrl_recvd == MSG_VCONN_SWAP) { } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) { ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG); if (ret) { usbpd_err(&pd->dev, "Error sending Accept\n"); Loading @@ -1591,8 +1617,8 @@ static void usbpd_sm(struct work_struct *w) vconn_swap(pd); } else { if (data_recvd == MSG_VDM) handle_vdm_rx(pd); if (IS_DATA(rx_msg, MSG_VDM)) handle_vdm_rx(pd, rx_msg); else handle_vdm_tx(pd); } Loading Loading @@ -1637,6 +1663,7 @@ static void usbpd_sm(struct work_struct *w) pd_send_hard_reset(pd); pd->in_explicit_contract = false; rx_msg_cleanup(pd); reset_vdm_state(pd); pd->current_state = PE_SRC_TRANSITION_TO_DEFAULT; Loading @@ -1648,7 +1675,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_WAIT_FOR_CAPABILITIES: if (data_recvd == MSG_SOURCE_CAPABILITIES) { if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) { val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, Loading @@ -1658,6 +1685,11 @@ static void usbpd_sm(struct work_struct *w) power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_ACTIVE, &val); /* save the PDOs so userspace can further evaluate */ memcpy(&pd->received_pdos, rx_msg->payload, sizeof(pd->received_pdos)); pd->src_cap_id++; usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY); } else if (pd->hard_reset_count < 3) { usbpd_set_state(pd, PE_SNK_HARD_RESET); Loading Loading @@ -1685,7 +1717,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_SELECT_CAPABILITY: if (ctrl_recvd == MSG_ACCEPT) { if (IS_CTRL(rx_msg, MSG_ACCEPT)) { /* prepare for voltage increase/decrease */ val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, Loading @@ -1705,13 +1737,14 @@ static void usbpd_sm(struct work_struct *w) pd->selected_pdo = pd->requested_pdo; usbpd_set_state(pd, PE_SNK_TRANSITION_SINK); } else if (ctrl_recvd == MSG_REJECT || ctrl_recvd == MSG_WAIT) { } else if (IS_CTRL(rx_msg, MSG_REJECT) || IS_CTRL(rx_msg, MSG_WAIT)) { if (pd->in_explicit_contract) usbpd_set_state(pd, PE_SNK_READY); else usbpd_set_state(pd, PE_SNK_WAIT_FOR_CAPABILITIES); } else if (pd->rx_msg_type) { } else if (rx_msg) { usbpd_err(&pd->dev, "Invalid response to sink request\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } else { Loading @@ -1721,7 +1754,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_TRANSITION_SINK: if (ctrl_recvd == MSG_PS_RDY) { if (IS_CTRL(rx_msg, MSG_PS_RDY)) { val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, pd->requested_voltage >= pd->current_voltage ? Loading @@ -1742,9 +1775,14 @@ static void usbpd_sm(struct work_struct *w) break; case PE_SNK_READY: if (data_recvd == MSG_SOURCE_CAPABILITIES) { if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) { /* save the PDOs so userspace can further evaluate */ memcpy(&pd->received_pdos, rx_msg->payload, sizeof(pd->received_pdos)); pd->src_cap_id++; usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY); } else if (ctrl_recvd == MSG_GET_SINK_CAP) { } else if (IS_CTRL(rx_msg, MSG_GET_SINK_CAP)) { ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES, default_snk_caps, ARRAY_SIZE(default_snk_caps), SOP_MSG); Loading @@ -1752,7 +1790,7 @@ static void usbpd_sm(struct work_struct *w) usbpd_err(&pd->dev, "Error sending Sink Caps\n"); usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); } } else if (ctrl_recvd == MSG_GET_SOURCE_CAP) { } else if (IS_CTRL(rx_msg, MSG_GET_SOURCE_CAP)) { ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, default_src_caps, ARRAY_SIZE(default_src_caps), SOP_MSG); Loading @@ -1761,7 +1799,7 @@ static void usbpd_sm(struct work_struct *w) usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET); break; } } else if (ctrl_recvd == MSG_DR_SWAP) { } else if (IS_CTRL(rx_msg, MSG_DR_SWAP)) { if (pd->vdm_state == MODE_ENTERED) { usbpd_set_state(pd, PE_SNK_HARD_RESET); break; Loading @@ -1776,7 +1814,7 @@ static void usbpd_sm(struct work_struct *w) dr_swap(pd); kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); } else if (ctrl_recvd == MSG_PR_SWAP) { } else if (IS_CTRL(rx_msg, MSG_PR_SWAP)) { /* lock in current mode */ set_power_role(pd, pd->current_pr); Loading @@ -1791,7 +1829,7 @@ static void usbpd_sm(struct work_struct *w) pd->in_pr_swap = true; usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; } else if (ctrl_recvd == MSG_VCONN_SWAP) { } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) { /* * if VCONN is connected to VBUS, make sure we are * not in high voltage contract, otherwise reject. Loading @@ -1818,8 +1856,8 @@ static void usbpd_sm(struct work_struct *w) vconn_swap(pd); } else { if (data_recvd == MSG_VDM) handle_vdm_rx(pd); if (IS_DATA(rx_msg, MSG_VDM)) handle_vdm_rx(pd, rx_msg); else handle_vdm_tx(pd); } Loading Loading @@ -1865,7 +1903,7 @@ static void usbpd_sm(struct work_struct *w) case PE_SRC_SEND_SOFT_RESET: case PE_SNK_SEND_SOFT_RESET: if (ctrl_recvd == MSG_ACCEPT) { if (IS_CTRL(rx_msg, MSG_ACCEPT)) { usbpd_set_state(pd, pd->current_pr == PR_SRC ? PE_SRC_SEND_CAPABILITIES : PE_SNK_WAIT_FOR_CAPABILITIES); Loading Loading @@ -1902,7 +1940,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_DRS_SEND_DR_SWAP: if (ctrl_recvd == MSG_ACCEPT) if (IS_CTRL(rx_msg, MSG_ACCEPT)) dr_swap(pd); usbpd_set_state(pd, pd->current_pr == PR_SRC ? Loading @@ -1910,7 +1948,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_PRS_SRC_SNK_SEND_SWAP: if (ctrl_recvd != MSG_ACCEPT) { if (!IS_CTRL(rx_msg, MSG_ACCEPT)) { pd->current_state = PE_SRC_READY; break; } Loading Loading @@ -1945,14 +1983,14 @@ static void usbpd_sm(struct work_struct *w) break; case PE_PRS_SRC_SNK_WAIT_SOURCE_ON: if (ctrl_recvd == MSG_PS_RDY) if (IS_CTRL(rx_msg, MSG_PS_RDY)) usbpd_set_state(pd, PE_SNK_STARTUP); else usbpd_set_state(pd, PE_ERROR_RECOVERY); break; case PE_PRS_SNK_SRC_SEND_SWAP: if (ctrl_recvd != MSG_ACCEPT) { if (!IS_CTRL(rx_msg, MSG_ACCEPT)) { pd->current_state = PE_SNK_READY; break; } Loading @@ -1962,7 +2000,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_PRS_SNK_SRC_TRANSITION_TO_OFF: if (ctrl_recvd != MSG_PS_RDY) { if (!IS_CTRL(rx_msg, MSG_PS_RDY)) { usbpd_set_state(pd, PE_ERROR_RECOVERY); break; } Loading Loading @@ -1992,7 +2030,7 @@ static void usbpd_sm(struct work_struct *w) break; case PE_VCS_WAIT_FOR_VCONN: if (ctrl_recvd == MSG_PS_RDY) { if (IS_CTRL(rx_msg, MSG_PS_RDY)) { /* * hopefully redundant check but in case not enabled * avoids unbalanced regulator disable count Loading @@ -2017,10 +2055,9 @@ static void usbpd_sm(struct work_struct *w) break; } /* Rx message should have been consumed now */ pd->rx_msg_type = pd->rx_msg_len = 0; sm_done: kfree(rx_msg); if (!pd->sm_queued) pm_relax(&pd->dev); } Loading Loading @@ -2657,6 +2694,8 @@ struct usbpd *usbpd_create(struct device *parent) pd->current_dr = DR_NONE; list_add_tail(&pd->instance, &_usbpd); spin_lock_init(&pd->rx_lock); INIT_LIST_HEAD(&pd->rx_q); INIT_LIST_HEAD(&pd->svid_handlers); /* force read initial power_supply values */ Loading