162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020-21 Intel Corporation. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "iosm_ipc_mux_codec.h" 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* At the begin of the runtime phase the IP MUX channel shall created. */ 962306a36Sopenharmony_cistatic int ipc_mux_channel_create(struct iosm_mux *ipc_mux) 1062306a36Sopenharmony_ci{ 1162306a36Sopenharmony_ci int channel_id; 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci channel_id = ipc_imem_channel_alloc(ipc_mux->imem, ipc_mux->instance_id, 1462306a36Sopenharmony_ci IPC_CTYPE_WWAN); 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci if (channel_id < 0) { 1762306a36Sopenharmony_ci dev_err(ipc_mux->dev, 1862306a36Sopenharmony_ci "allocation of the MUX channel id failed"); 1962306a36Sopenharmony_ci ipc_mux->state = MUX_S_ERROR; 2062306a36Sopenharmony_ci ipc_mux->event = MUX_E_NOT_APPLICABLE; 2162306a36Sopenharmony_ci goto no_channel; 2262306a36Sopenharmony_ci } 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci /* Establish the MUX channel in blocking mode. */ 2562306a36Sopenharmony_ci ipc_mux->channel = ipc_imem_channel_open(ipc_mux->imem, channel_id, 2662306a36Sopenharmony_ci IPC_HP_NET_CHANNEL_INIT); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (!ipc_mux->channel) { 2962306a36Sopenharmony_ci dev_err(ipc_mux->dev, "ipc_imem_channel_open failed"); 3062306a36Sopenharmony_ci ipc_mux->state = MUX_S_ERROR; 3162306a36Sopenharmony_ci ipc_mux->event = MUX_E_NOT_APPLICABLE; 3262306a36Sopenharmony_ci return -ENODEV; /* MUX channel is not available. */ 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci /* Define the MUX active state properties. */ 3662306a36Sopenharmony_ci ipc_mux->state = MUX_S_ACTIVE; 3762306a36Sopenharmony_ci ipc_mux->event = MUX_E_NO_ORDERS; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cino_channel: 4062306a36Sopenharmony_ci return channel_id; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Reset the session/if id state. */ 4462306a36Sopenharmony_cistatic void ipc_mux_session_free(struct iosm_mux *ipc_mux, int if_id) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct mux_session *if_entry; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if_entry = &ipc_mux->session[if_id]; 4962306a36Sopenharmony_ci /* Reset the session state. */ 5062306a36Sopenharmony_ci if_entry->wwan = NULL; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Create and send the session open command. */ 5462306a36Sopenharmony_cistatic struct mux_cmd_open_session_resp * 5562306a36Sopenharmony_ciipc_mux_session_open_send(struct iosm_mux *ipc_mux, int if_id) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct mux_cmd_open_session_resp *open_session_resp; 5862306a36Sopenharmony_ci struct mux_acb *acb = &ipc_mux->acb; 5962306a36Sopenharmony_ci union mux_cmd_param param; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* open_session commands to one ACB and start transmission. */ 6262306a36Sopenharmony_ci param.open_session.flow_ctrl = 0; 6362306a36Sopenharmony_ci param.open_session.ipv4v6_hints = 0; 6462306a36Sopenharmony_ci param.open_session.reserved2 = 0; 6562306a36Sopenharmony_ci param.open_session.dl_head_pad_len = cpu_to_le32(IPC_MEM_DL_ETH_OFFSET); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* Finish and transfer ACB. The user thread is suspended. 6862306a36Sopenharmony_ci * It is a blocking function call, until CP responds or timeout. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci acb->wanted_response = MUX_CMD_OPEN_SESSION_RESP; 7162306a36Sopenharmony_ci if (ipc_mux_dl_acb_send_cmds(ipc_mux, MUX_CMD_OPEN_SESSION, if_id, 0, 7262306a36Sopenharmony_ci ¶m, sizeof(param.open_session), true, 7362306a36Sopenharmony_ci false) || 7462306a36Sopenharmony_ci acb->got_response != MUX_CMD_OPEN_SESSION_RESP) { 7562306a36Sopenharmony_ci dev_err(ipc_mux->dev, "if_id %d: OPEN_SESSION send failed", 7662306a36Sopenharmony_ci if_id); 7762306a36Sopenharmony_ci return NULL; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci open_session_resp = &ipc_mux->acb.got_param.open_session_resp; 8162306a36Sopenharmony_ci if (open_session_resp->response != cpu_to_le32(MUX_CMD_RESP_SUCCESS)) { 8262306a36Sopenharmony_ci dev_err(ipc_mux->dev, 8362306a36Sopenharmony_ci "if_id %d,session open failed,response=%d", if_id, 8462306a36Sopenharmony_ci open_session_resp->response); 8562306a36Sopenharmony_ci return NULL; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return open_session_resp; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* Open the first IP session. */ 9262306a36Sopenharmony_cistatic bool ipc_mux_session_open(struct iosm_mux *ipc_mux, 9362306a36Sopenharmony_ci struct mux_session_open *session_open) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct mux_cmd_open_session_resp *open_session_resp; 9662306a36Sopenharmony_ci int if_id; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Search for a free session interface id. */ 9962306a36Sopenharmony_ci if_id = le32_to_cpu(session_open->if_id); 10062306a36Sopenharmony_ci if (if_id < 0 || if_id >= IPC_MEM_MUX_IP_SESSION_ENTRIES) { 10162306a36Sopenharmony_ci dev_err(ipc_mux->dev, "invalid interface id=%d", if_id); 10262306a36Sopenharmony_ci return false; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* Create and send the session open command. 10662306a36Sopenharmony_ci * It is a blocking function call, until CP responds or timeout. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci open_session_resp = ipc_mux_session_open_send(ipc_mux, if_id); 10962306a36Sopenharmony_ci if (!open_session_resp) { 11062306a36Sopenharmony_ci ipc_mux_session_free(ipc_mux, if_id); 11162306a36Sopenharmony_ci session_open->if_id = cpu_to_le32(-1); 11262306a36Sopenharmony_ci return false; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* Initialize the uplink skb accumulator. */ 11662306a36Sopenharmony_ci skb_queue_head_init(&ipc_mux->session[if_id].ul_list); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci ipc_mux->session[if_id].dl_head_pad_len = IPC_MEM_DL_ETH_OFFSET; 11962306a36Sopenharmony_ci ipc_mux->session[if_id].ul_head_pad_len = 12062306a36Sopenharmony_ci le32_to_cpu(open_session_resp->ul_head_pad_len); 12162306a36Sopenharmony_ci ipc_mux->session[if_id].wwan = ipc_mux->wwan; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Reset the flow ctrl stats of the session */ 12462306a36Sopenharmony_ci ipc_mux->session[if_id].flow_ctl_en_cnt = 0; 12562306a36Sopenharmony_ci ipc_mux->session[if_id].flow_ctl_dis_cnt = 0; 12662306a36Sopenharmony_ci ipc_mux->session[if_id].ul_flow_credits = 0; 12762306a36Sopenharmony_ci ipc_mux->session[if_id].net_tx_stop = false; 12862306a36Sopenharmony_ci ipc_mux->session[if_id].flow_ctl_mask = 0; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Save and return the assigned if id. */ 13162306a36Sopenharmony_ci session_open->if_id = cpu_to_le32(if_id); 13262306a36Sopenharmony_ci ipc_mux->nr_sessions++; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return true; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* Free pending session UL packet. */ 13862306a36Sopenharmony_cistatic void ipc_mux_session_reset(struct iosm_mux *ipc_mux, int if_id) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci /* Reset the session/if id state. */ 14162306a36Sopenharmony_ci ipc_mux_session_free(ipc_mux, if_id); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Empty the uplink skb accumulator. */ 14462306a36Sopenharmony_ci skb_queue_purge(&ipc_mux->session[if_id].ul_list); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void ipc_mux_session_close(struct iosm_mux *ipc_mux, 14862306a36Sopenharmony_ci struct mux_session_close *msg) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci int if_id; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Copy the session interface id. */ 15362306a36Sopenharmony_ci if_id = le32_to_cpu(msg->if_id); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (if_id < 0 || if_id >= IPC_MEM_MUX_IP_SESSION_ENTRIES) { 15662306a36Sopenharmony_ci dev_err(ipc_mux->dev, "invalid session id %d", if_id); 15762306a36Sopenharmony_ci return; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Create and send the session close command. 16162306a36Sopenharmony_ci * It is a blocking function call, until CP responds or timeout. 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci if (ipc_mux_dl_acb_send_cmds(ipc_mux, MUX_CMD_CLOSE_SESSION, if_id, 0, 16462306a36Sopenharmony_ci NULL, 0, true, false)) 16562306a36Sopenharmony_ci dev_err(ipc_mux->dev, "if_id %d: CLOSE_SESSION send failed", 16662306a36Sopenharmony_ci if_id); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Reset the flow ctrl stats of the session */ 16962306a36Sopenharmony_ci ipc_mux->session[if_id].flow_ctl_en_cnt = 0; 17062306a36Sopenharmony_ci ipc_mux->session[if_id].flow_ctl_dis_cnt = 0; 17162306a36Sopenharmony_ci ipc_mux->session[if_id].flow_ctl_mask = 0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ipc_mux_session_reset(ipc_mux, if_id); 17462306a36Sopenharmony_ci ipc_mux->nr_sessions--; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void ipc_mux_channel_close(struct iosm_mux *ipc_mux, 17862306a36Sopenharmony_ci struct mux_channel_close *channel_close_p) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci int i; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* Free pending session UL packet. */ 18362306a36Sopenharmony_ci for (i = 0; i < IPC_MEM_MUX_IP_SESSION_ENTRIES; i++) 18462306a36Sopenharmony_ci if (ipc_mux->session[i].wwan) 18562306a36Sopenharmony_ci ipc_mux_session_reset(ipc_mux, i); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci ipc_imem_channel_close(ipc_mux->imem, ipc_mux->channel_id); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Reset the MUX object. */ 19062306a36Sopenharmony_ci ipc_mux->state = MUX_S_INACTIVE; 19162306a36Sopenharmony_ci ipc_mux->event = MUX_E_INACTIVE; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* CP has interrupted AP. If AP is in IP MUX mode, execute the pending ops. */ 19562306a36Sopenharmony_cistatic int ipc_mux_schedule(struct iosm_mux *ipc_mux, union mux_msg *msg) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci enum mux_event order; 19862306a36Sopenharmony_ci bool success; 19962306a36Sopenharmony_ci int ret = -EIO; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!ipc_mux->initialized) { 20262306a36Sopenharmony_ci ret = -EAGAIN; 20362306a36Sopenharmony_ci goto out; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci order = msg->common.event; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci switch (ipc_mux->state) { 20962306a36Sopenharmony_ci case MUX_S_INACTIVE: 21062306a36Sopenharmony_ci if (order != MUX_E_MUX_SESSION_OPEN) 21162306a36Sopenharmony_ci goto out; /* Wait for the request to open a session */ 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (ipc_mux->event == MUX_E_INACTIVE) 21462306a36Sopenharmony_ci /* Establish the MUX channel and the new state. */ 21562306a36Sopenharmony_ci ipc_mux->channel_id = ipc_mux_channel_create(ipc_mux); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (ipc_mux->state != MUX_S_ACTIVE) { 21862306a36Sopenharmony_ci ret = ipc_mux->channel_id; /* Missing the MUX channel */ 21962306a36Sopenharmony_ci goto out; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Disable the TD update timer and open the first IP session. */ 22362306a36Sopenharmony_ci ipc_imem_td_update_timer_suspend(ipc_mux->imem, true); 22462306a36Sopenharmony_ci ipc_mux->event = MUX_E_MUX_SESSION_OPEN; 22562306a36Sopenharmony_ci success = ipc_mux_session_open(ipc_mux, &msg->session_open); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ipc_imem_td_update_timer_suspend(ipc_mux->imem, false); 22862306a36Sopenharmony_ci if (success) 22962306a36Sopenharmony_ci ret = ipc_mux->channel_id; 23062306a36Sopenharmony_ci goto out; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci case MUX_S_ACTIVE: 23362306a36Sopenharmony_ci switch (order) { 23462306a36Sopenharmony_ci case MUX_E_MUX_SESSION_OPEN: 23562306a36Sopenharmony_ci /* Disable the TD update timer and open a session */ 23662306a36Sopenharmony_ci ipc_imem_td_update_timer_suspend(ipc_mux->imem, true); 23762306a36Sopenharmony_ci ipc_mux->event = MUX_E_MUX_SESSION_OPEN; 23862306a36Sopenharmony_ci success = ipc_mux_session_open(ipc_mux, 23962306a36Sopenharmony_ci &msg->session_open); 24062306a36Sopenharmony_ci ipc_imem_td_update_timer_suspend(ipc_mux->imem, false); 24162306a36Sopenharmony_ci if (success) 24262306a36Sopenharmony_ci ret = ipc_mux->channel_id; 24362306a36Sopenharmony_ci goto out; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci case MUX_E_MUX_SESSION_CLOSE: 24662306a36Sopenharmony_ci /* Release an IP session. */ 24762306a36Sopenharmony_ci ipc_mux->event = MUX_E_MUX_SESSION_CLOSE; 24862306a36Sopenharmony_ci ipc_mux_session_close(ipc_mux, &msg->session_close); 24962306a36Sopenharmony_ci if (!ipc_mux->nr_sessions) { 25062306a36Sopenharmony_ci ipc_mux->event = MUX_E_MUX_CHANNEL_CLOSE; 25162306a36Sopenharmony_ci ipc_mux_channel_close(ipc_mux, 25262306a36Sopenharmony_ci &msg->channel_close); 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci ret = ipc_mux->channel_id; 25562306a36Sopenharmony_ci goto out; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci case MUX_E_MUX_CHANNEL_CLOSE: 25862306a36Sopenharmony_ci /* Close the MUX channel pipes. */ 25962306a36Sopenharmony_ci ipc_mux->event = MUX_E_MUX_CHANNEL_CLOSE; 26062306a36Sopenharmony_ci ipc_mux_channel_close(ipc_mux, &msg->channel_close); 26162306a36Sopenharmony_ci ret = ipc_mux->channel_id; 26262306a36Sopenharmony_ci goto out; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci default: 26562306a36Sopenharmony_ci /* Invalid order. */ 26662306a36Sopenharmony_ci goto out; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci default: 27062306a36Sopenharmony_ci dev_err(ipc_mux->dev, 27162306a36Sopenharmony_ci "unexpected MUX transition: state=%d, event=%d", 27262306a36Sopenharmony_ci ipc_mux->state, ipc_mux->event); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ciout: 27562306a36Sopenharmony_ci return ret; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistruct iosm_mux *ipc_mux_init(struct ipc_mux_config *mux_cfg, 27962306a36Sopenharmony_ci struct iosm_imem *imem) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct iosm_mux *ipc_mux = kzalloc(sizeof(*ipc_mux), GFP_KERNEL); 28262306a36Sopenharmony_ci int i, j, ul_tds, ul_td_size; 28362306a36Sopenharmony_ci struct sk_buff_head *free_list; 28462306a36Sopenharmony_ci struct sk_buff *skb; 28562306a36Sopenharmony_ci int qlt_size; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (!ipc_mux) 28862306a36Sopenharmony_ci return NULL; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci ipc_mux->protocol = mux_cfg->protocol; 29162306a36Sopenharmony_ci ipc_mux->ul_flow = mux_cfg->ul_flow; 29262306a36Sopenharmony_ci ipc_mux->instance_id = mux_cfg->instance_id; 29362306a36Sopenharmony_ci ipc_mux->wwan_q_offset = 0; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ipc_mux->pcie = imem->pcie; 29662306a36Sopenharmony_ci ipc_mux->imem = imem; 29762306a36Sopenharmony_ci ipc_mux->ipc_protocol = imem->ipc_protocol; 29862306a36Sopenharmony_ci ipc_mux->dev = imem->dev; 29962306a36Sopenharmony_ci ipc_mux->wwan = imem->wwan; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Get the reference to the UL ADB list. */ 30262306a36Sopenharmony_ci free_list = &ipc_mux->ul_adb.free_list; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* Initialize the list with free ADB. */ 30562306a36Sopenharmony_ci skb_queue_head_init(free_list); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci ul_td_size = IPC_MEM_MAX_DL_MUX_LITE_BUF_SIZE; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ul_tds = IPC_MEM_MAX_TDS_MUX_LITE_UL; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ipc_mux->ul_adb.dest_skb = NULL; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci ipc_mux->initialized = true; 31462306a36Sopenharmony_ci ipc_mux->adb_prep_ongoing = false; 31562306a36Sopenharmony_ci ipc_mux->size_needed = 0; 31662306a36Sopenharmony_ci ipc_mux->ul_data_pend_bytes = 0; 31762306a36Sopenharmony_ci ipc_mux->state = MUX_S_INACTIVE; 31862306a36Sopenharmony_ci ipc_mux->ev_mux_net_transmit_pending = false; 31962306a36Sopenharmony_ci ipc_mux->tx_transaction_id = 0; 32062306a36Sopenharmony_ci ipc_mux->rr_next_session = 0; 32162306a36Sopenharmony_ci ipc_mux->event = MUX_E_INACTIVE; 32262306a36Sopenharmony_ci ipc_mux->channel_id = -1; 32362306a36Sopenharmony_ci ipc_mux->channel = NULL; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (ipc_mux->protocol != MUX_LITE) { 32662306a36Sopenharmony_ci qlt_size = offsetof(struct mux_qlth, ql) + 32762306a36Sopenharmony_ci MUX_QUEUE_LEVEL * sizeof(struct mux_qlth_ql); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci for (i = 0; i < IPC_MEM_MUX_IP_SESSION_ENTRIES; i++) { 33062306a36Sopenharmony_ci ipc_mux->ul_adb.pp_qlt[i] = kzalloc(qlt_size, 33162306a36Sopenharmony_ci GFP_ATOMIC); 33262306a36Sopenharmony_ci if (!ipc_mux->ul_adb.pp_qlt[i]) { 33362306a36Sopenharmony_ci for (j = i - 1; j >= 0; j--) 33462306a36Sopenharmony_ci kfree(ipc_mux->ul_adb.pp_qlt[j]); 33562306a36Sopenharmony_ci kfree(ipc_mux); 33662306a36Sopenharmony_ci return NULL; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ul_td_size = IPC_MEM_MAX_UL_ADB_BUF_SIZE; 34162306a36Sopenharmony_ci ul_tds = IPC_MEM_MAX_TDS_MUX_AGGR_UL; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Allocate the list of UL ADB. */ 34562306a36Sopenharmony_ci for (i = 0; i < ul_tds; i++) { 34662306a36Sopenharmony_ci dma_addr_t mapping; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci skb = ipc_pcie_alloc_skb(ipc_mux->pcie, ul_td_size, GFP_ATOMIC, 34962306a36Sopenharmony_ci &mapping, DMA_TO_DEVICE, 0); 35062306a36Sopenharmony_ci if (!skb) { 35162306a36Sopenharmony_ci ipc_mux_deinit(ipc_mux); 35262306a36Sopenharmony_ci return NULL; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci /* Extend the UL ADB list. */ 35562306a36Sopenharmony_ci skb_queue_tail(free_list, skb); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return ipc_mux; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci/* Informs the network stack to restart transmission for all opened session if 36262306a36Sopenharmony_ci * Flow Control is not ON for that session. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_cistatic void ipc_mux_restart_tx_for_all_sessions(struct iosm_mux *ipc_mux) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct mux_session *session; 36762306a36Sopenharmony_ci int idx; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci for (idx = 0; idx < IPC_MEM_MUX_IP_SESSION_ENTRIES; idx++) { 37062306a36Sopenharmony_ci session = &ipc_mux->session[idx]; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (!session->wwan) 37362306a36Sopenharmony_ci continue; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* If flow control of the session is OFF and if there was tx 37662306a36Sopenharmony_ci * stop then restart. Inform the network interface to restart 37762306a36Sopenharmony_ci * sending data. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ci if (session->flow_ctl_mask == 0) { 38062306a36Sopenharmony_ci session->net_tx_stop = false; 38162306a36Sopenharmony_ci ipc_mux_netif_tx_flowctrl(session, idx, false); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/* Informs the network stack to stop sending further pkt for all opened 38762306a36Sopenharmony_ci * sessions 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_cistatic void ipc_mux_stop_netif_for_all_sessions(struct iosm_mux *ipc_mux) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct mux_session *session; 39262306a36Sopenharmony_ci int idx; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci for (idx = 0; idx < IPC_MEM_MUX_IP_SESSION_ENTRIES; idx++) { 39562306a36Sopenharmony_ci session = &ipc_mux->session[idx]; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (!session->wwan) 39862306a36Sopenharmony_ci continue; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ipc_mux_netif_tx_flowctrl(session, session->if_id, true); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_civoid ipc_mux_check_n_restart_tx(struct iosm_mux *ipc_mux) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci if (ipc_mux->ul_flow == MUX_UL) { 40762306a36Sopenharmony_ci int low_thresh = IPC_MEM_MUX_UL_FLOWCTRL_LOW_B; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (ipc_mux->ul_data_pend_bytes < low_thresh) 41062306a36Sopenharmony_ci ipc_mux_restart_tx_for_all_sessions(ipc_mux); 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ciint ipc_mux_get_max_sessions(struct iosm_mux *ipc_mux) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci return ipc_mux ? IPC_MEM_MUX_IP_SESSION_ENTRIES : -EFAULT; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cienum ipc_mux_protocol ipc_mux_get_active_protocol(struct iosm_mux *ipc_mux) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci return ipc_mux ? ipc_mux->protocol : MUX_UNKNOWN; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ciint ipc_mux_open_session(struct iosm_mux *ipc_mux, int session_nr) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct mux_session_open *session_open; 42762306a36Sopenharmony_ci union mux_msg mux_msg; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci session_open = &mux_msg.session_open; 43062306a36Sopenharmony_ci session_open->event = MUX_E_MUX_SESSION_OPEN; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci session_open->if_id = cpu_to_le32(session_nr); 43362306a36Sopenharmony_ci ipc_mux->session[session_nr].flags |= IPC_MEM_WWAN_MUX; 43462306a36Sopenharmony_ci return ipc_mux_schedule(ipc_mux, &mux_msg); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ciint ipc_mux_close_session(struct iosm_mux *ipc_mux, int session_nr) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct mux_session_close *session_close; 44062306a36Sopenharmony_ci union mux_msg mux_msg; 44162306a36Sopenharmony_ci int ret_val; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci session_close = &mux_msg.session_close; 44462306a36Sopenharmony_ci session_close->event = MUX_E_MUX_SESSION_CLOSE; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci session_close->if_id = cpu_to_le32(session_nr); 44762306a36Sopenharmony_ci ret_val = ipc_mux_schedule(ipc_mux, &mux_msg); 44862306a36Sopenharmony_ci ipc_mux->session[session_nr].flags &= ~IPC_MEM_WWAN_MUX; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return ret_val; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_civoid ipc_mux_deinit(struct iosm_mux *ipc_mux) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci struct mux_channel_close *channel_close; 45662306a36Sopenharmony_ci struct sk_buff_head *free_list; 45762306a36Sopenharmony_ci union mux_msg mux_msg; 45862306a36Sopenharmony_ci struct sk_buff *skb; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (!ipc_mux->initialized) 46162306a36Sopenharmony_ci return; 46262306a36Sopenharmony_ci ipc_mux_stop_netif_for_all_sessions(ipc_mux); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (ipc_mux->state == MUX_S_ACTIVE) { 46562306a36Sopenharmony_ci channel_close = &mux_msg.channel_close; 46662306a36Sopenharmony_ci channel_close->event = MUX_E_MUX_CHANNEL_CLOSE; 46762306a36Sopenharmony_ci ipc_mux_schedule(ipc_mux, &mux_msg); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* Empty the ADB free list. */ 47162306a36Sopenharmony_ci free_list = &ipc_mux->ul_adb.free_list; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* Remove from the head of the downlink queue. */ 47462306a36Sopenharmony_ci while ((skb = skb_dequeue(free_list))) 47562306a36Sopenharmony_ci ipc_pcie_kfree_skb(ipc_mux->pcie, skb); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (ipc_mux->channel) { 47862306a36Sopenharmony_ci ipc_mux->channel->ul_pipe.is_open = false; 47962306a36Sopenharmony_ci ipc_mux->channel->dl_pipe.is_open = false; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci kfree(ipc_mux); 48362306a36Sopenharmony_ci} 484