Loading drivers/soc/qcom/ipc_router_mhi_xprt.c +160 −16 Original line number Diff line number Diff line /* Copyright (c) 2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -38,6 +38,22 @@ if (ipc_router_mhi_xprt_debug_mask) \ #define IPC_ROUTER_MHI_XPRT_MAX_PKT_SIZE 0x1000 #define IPC_ROUTER_MHI_XPRT_NUM_TRBS 10 /** * ipc_router_mhi_addr_map - DMA to virtual address mapping for an IPC Router * packet. * @list_node: Address mapping list node used by mhi transport map list. * @virt_addr: The virtual address in mapping. * @dma_addr: The dma address in mapping. * @pkt: The IPC Router packet for which the virtual address of skbs are mapped * to DMA address during TX/RX operations. */ struct ipc_router_mhi_addr_map { struct list_head list_node; void *virt_addr; dma_addr_t dma_addr; struct rr_packet *pkt; }; /** * ipc_router_mhi_channel - MHI Channel related information * @out_chan_id: Out channel ID for use by IPC ROUTER enumerated in MHI driver. Loading @@ -49,7 +65,6 @@ if (ipc_router_mhi_xprt_debug_mask) \ * @state_lock: Lock to protect access to the state information. * @out_chan_enabled: State of the outgoing channel. * @in_chan_enabled: State of the incoming channel. * @bytes_to_tx: Remaining bytes to be transmitted in a packet. * @bytes_to_rx: Remaining bytes to be received in a packet. * @in_skbq_lock: Lock to protect access to the input skbs queue. * @in_skbq: Queue containing the input buffers. Loading @@ -69,7 +84,6 @@ struct ipc_router_mhi_channel { struct mutex state_lock; bool out_chan_enabled; bool in_chan_enabled; int bytes_to_tx; int bytes_to_rx; struct mutex in_skbq_lock; Loading @@ -93,6 +107,12 @@ struct ipc_router_mhi_channel { * by IPC Router. * @xprt_version: IPC Router header version supported by this XPRT. * @xprt_option: XPRT specific options to be handled by IPC Router. * @tx_addr_map_list_lock: The lock to protect the address mapping list for TX * operations. * @tx_addr_map_list: DMA to virtual address mapping list for TX operations. * @rx_addr_map_list_lock: The lock to protect the address mapping list for RX * operations. * @rx_addr_map_list: DMA to virtual address mapping list for RX operations. */ struct ipc_router_mhi_xprt { struct list_head list; Loading @@ -106,6 +126,10 @@ struct ipc_router_mhi_xprt { struct completion sft_close_complete; unsigned xprt_version; unsigned xprt_option; struct mutex tx_addr_map_list_lock; struct list_head tx_addr_map_list; struct mutex rx_addr_map_list_lock; struct list_head rx_addr_map_list; }; struct ipc_router_mhi_xprt_work { Loading Loading @@ -138,6 +162,90 @@ struct ipc_router_mhi_xprt_config { static DEFINE_MUTEX(mhi_xprt_list_lock_lha1); static LIST_HEAD(mhi_xprt_list); /* * ipc_router_mhi_release_pkt() - Release a cloned IPC Router packet * @ref: Reference to the kref object in the IPC Router packet. */ void ipc_router_mhi_release_pkt(struct kref *ref) { struct rr_packet *pkt = container_of(ref, struct rr_packet, ref); release_pkt(pkt); } /* * ipc_router_mhi_xprt_find_addr_map() - Search the mapped virtual address * @addr_map_list: The list of address mappings. * @addr_map_list_lock: Reference to the lock that protects the @addr_map_list. * @addr: The dma address for which mapped virtual address need to be found. * * Return: The mapped virtual Address if found, NULL otherwise. */ void *ipc_router_mhi_xprt_find_addr_map(struct list_head *addr_map_list, struct mutex *addr_map_list_lock, dma_addr_t addr) { struct ipc_router_mhi_addr_map *addr_mapping; struct ipc_router_mhi_addr_map *tmp_addr_mapping; void *virt_addr; if (!addr_map_list || !addr_map_list_lock) return NULL; mutex_lock(addr_map_list_lock); list_for_each_entry_safe(addr_mapping, tmp_addr_mapping, addr_map_list, list_node) { if (addr_mapping->dma_addr == addr) { virt_addr = addr_mapping->virt_addr; list_del(&addr_mapping->list_node); if (addr_mapping->pkt) kref_put(&addr_mapping->pkt->ref, ipc_router_mhi_release_pkt); kfree(addr_mapping); mutex_unlock(addr_map_list_lock); return virt_addr; } } mutex_unlock(addr_map_list_lock); IPC_RTR_ERR( "%s: Virtual address mapping for DMA addr [%p] not found\n", __func__, (void *)addr); return NULL; } /* * ipc_router_mhi_xprt_add_addr_map() - Add a mapping of virtual address to dma * address * @addr_map_list: The list of address mappings. * @addr_map_list_lock: Reference to the lock that protects the @addr_map_list. * @pkt: The IPC Router packet that contains the virtual address in skbs. * @addr: The virtual address which needs to be added. * @dma_addr: The dma address which needs to be added. * * Return: 0 on success, standard Linux error code otherwise. */ int ipc_router_mhi_xprt_add_addr_map(struct list_head *addr_map_list, struct mutex *addr_map_list_lock, struct rr_packet *pkt, void *virt_addr, dma_addr_t dma_addr) { struct ipc_router_mhi_addr_map *addr_mapping; if (!addr_map_list || !addr_map_list_lock) return -EINVAL; addr_mapping = kmalloc(sizeof(*addr_mapping), GFP_KERNEL); if (!addr_mapping) return -ENOMEM; addr_mapping->virt_addr = virt_addr; addr_mapping->dma_addr = dma_addr; addr_mapping->pkt = pkt; mutex_lock(addr_map_list_lock); if (addr_mapping->pkt) kref_get(&addr_mapping->pkt->ref); list_add_tail(&addr_mapping->list_node, addr_map_list); mutex_unlock(addr_map_list_lock); return 0; } /* * mhi_xprt_queue_in_buffers() - Queue input buffers * @mhi_xprtp: MHI XPRT in which the input buffer has to be queued. Loading Loading @@ -169,6 +277,14 @@ int mhi_xprt_queue_in_buffers(struct ipc_router_mhi_xprt *mhi_xprtp, kfree_skb(skb); break; } if (ipc_router_mhi_xprt_add_addr_map( &mhi_xprtp->rx_addr_map_list, &mhi_xprtp->rx_addr_map_list_lock, NULL, skb->data, dma_addr) < 0) { IPC_RTR_ERR("%s: Could not map %d SKB->DMA address\n", __func__, (i + 1)); break; } mutex_lock(&mhi_xprtp->ch_hndl.in_skbq_lock); rc_val = mhi_queue_xfer(mhi_xprtp->ch_hndl.in_handle, dma_addr, buf_size, MHI_EOT); Loading Loading @@ -251,7 +367,7 @@ static int ipc_router_mhi_write_avail(struct msm_ipc_router_xprt *xprt) * standard Linux error codes on failure. */ static int ipc_router_mhi_write_skb(struct ipc_router_mhi_xprt *mhi_xprtp, struct sk_buff *skb) struct sk_buff *skb, struct rr_packet *pkt) { size_t sz_to_write = 0; size_t offset = 0; Loading @@ -276,22 +392,30 @@ static int ipc_router_mhi_write_skb(struct ipc_router_mhi_xprt *mhi_xprtp, sz_to_write, DMA_TO_DEVICE); if (dma_mapping_error(NULL, dma_addr)) { mutex_unlock(&mhi_xprtp->ch_hndl.state_lock); IPC_RTR_ERR("%s: Failed to map DMA 0x%x\n", IPC_RTR_ERR("%s: Failed to map DMA 0x%zx\n", __func__, sz_to_write); return -ENOMEM; } if (ipc_router_mhi_xprt_add_addr_map( &mhi_xprtp->tx_addr_map_list, &mhi_xprtp->tx_addr_map_list_lock, pkt, skb->data + offset, dma_addr) < 0) { IPC_RTR_ERR("%s: Could not map SKB->DMA address\n", __func__); break; } rc = mhi_queue_xfer(mhi_xprtp->ch_hndl.out_handle, dma_addr, sz_to_write, MHI_EOT | MHI_EOB); if (rc != 0) { mutex_unlock(&mhi_xprtp->ch_hndl.state_lock); dma_unmap_single(NULL, dma_addr, sz_to_write, DMA_TO_DEVICE); IPC_RTR_ERR("%s: Error queueing mhi_xfer 0x%x\n", IPC_RTR_ERR("%s: Error queueing mhi_xfer 0x%zx\n", __func__, sz_to_write); return -EFAULT; } else { offset += sz_to_write; mhi_xprtp->ch_hndl.bytes_to_tx += sz_to_write; } mutex_unlock(&mhi_xprtp->ch_hndl.state_lock); } Loading @@ -311,6 +435,7 @@ static int ipc_router_mhi_write(void *data, { struct rr_packet *pkt = (struct rr_packet *)data; struct sk_buff *ipc_rtr_pkt; struct rr_packet *cloned_pkt; int rc; struct ipc_router_mhi_xprt *mhi_xprtp = container_of(xprt, struct ipc_router_mhi_xprt, xprt); Loading @@ -321,9 +446,15 @@ static int ipc_router_mhi_write(void *data, if (!len || pkt->length != len) return -EINVAL; cloned_pkt = clone_pkt(pkt); if (!cloned_pkt) { pr_err("%s: Error in cloning packet while tx\n", __func__); return -ENOMEM; } D("%s: Ready to write %d bytes\n", __func__, len); skb_queue_walk(pkt->pkt_fragment_q, ipc_rtr_pkt) { rc = ipc_router_mhi_write_skb(mhi_xprtp, ipc_rtr_pkt); skb_queue_walk(cloned_pkt->pkt_fragment_q, ipc_rtr_pkt) { rc = ipc_router_mhi_write_skb(mhi_xprtp, ipc_rtr_pkt, cloned_pkt); if (rc < 0) { IPC_RTR_ERR("%s: Error writing SKB %d\n", __func__, rc); Loading @@ -331,7 +462,7 @@ static int ipc_router_mhi_write(void *data, } } wait_event(mhi_xprtp->write_wait_q, !mhi_xprtp->ch_hndl.bytes_to_tx); kref_put(&cloned_pkt->ref, ipc_router_mhi_release_pkt); if (rc < 0) return rc; else Loading Loading @@ -375,7 +506,7 @@ static void mhi_xprt_read_data(struct work_struct *work) rc = mhi_poll_inbound(mhi_xprtp->ch_hndl.in_handle, &result); if (rc || !result.payload_buf || !result.bytes_xferd) { if (rc != MHI_STATUS_RING_EMPTY) IPC_RTR_ERR("%s: Poll failed %s:%d:%p:%zu\n", IPC_RTR_ERR("%s: Poll failed %s:%d:%p:%u\n", __func__, mhi_xprtp->xprt_name, rc, (void *)result.payload_buf, result.bytes_xferd); Loading @@ -395,8 +526,14 @@ static void mhi_xprt_read_data(struct work_struct *work) D("%s: Allocated rr_packet\n", __func__); } skb_data = dma_to_virt(NULL, data_addr); skb_data = ipc_router_mhi_xprt_find_addr_map( &mhi_xprtp->rx_addr_map_list, &mhi_xprtp->rx_addr_map_list_lock, data_addr); dma_unmap_single(NULL, data_addr, data_sz, DMA_BIDIRECTIONAL); if (!skb_data) continue; mutex_lock(&mhi_xprtp->ch_hndl.in_skbq_lock); skb_queue_walk(&mhi_xprtp->ch_hndl.in_skbq, skb) { if (skb->data == skb_data) { Loading Loading @@ -590,14 +727,17 @@ static void mhi_xprt_disable_event(struct work_struct *work) static void mhi_xprt_xfer_event(struct mhi_cb_info *cb_info) { struct ipc_router_mhi_xprt *mhi_xprtp; dma_addr_t out_dma_addr; mhi_xprtp = (struct ipc_router_mhi_xprt *)(cb_info->result->user_data); if (cb_info->chan == mhi_xprtp->ch_hndl.out_chan_id) { dma_unmap_single(NULL, (dma_addr_t)cb_info->result->payload_buf, out_dma_addr = (dma_addr_t)cb_info->result->payload_buf; dma_unmap_single(NULL, out_dma_addr, cb_info->result->bytes_xferd, DMA_TO_DEVICE); mutex_lock(&mhi_xprtp->ch_hndl.state_lock); mhi_xprtp->ch_hndl.bytes_to_tx -= cb_info->result->bytes_xferd; if (!mhi_xprtp->ch_hndl.bytes_to_tx) ipc_router_mhi_xprt_find_addr_map(&mhi_xprtp->tx_addr_map_list, &mhi_xprtp->tx_addr_map_list_lock, out_dma_addr); wake_up(&mhi_xprtp->write_wait_q); mutex_unlock(&mhi_xprtp->ch_hndl.state_lock); } else if (cb_info->chan == mhi_xprtp->ch_hndl.in_chan_id) { Loading Loading @@ -754,6 +894,10 @@ static int ipc_router_mhi_config_init( mhi_xprtp->ch_hndl.max_packet_size = IPC_ROUTER_MHI_XPRT_MAX_PKT_SIZE; mhi_xprtp->ch_hndl.num_trbs = IPC_ROUTER_MHI_XPRT_NUM_TRBS; mhi_xprtp->ch_hndl.mhi_xprtp = mhi_xprtp; INIT_LIST_HEAD(&mhi_xprtp->tx_addr_map_list); mutex_init(&mhi_xprtp->tx_addr_map_list_lock); INIT_LIST_HEAD(&mhi_xprtp->rx_addr_map_list); mutex_init(&mhi_xprtp->rx_addr_map_list_lock); rc = ipc_router_mhi_driver_register(mhi_xprtp); return rc; Loading include/linux/ipc_router_xprt.h +3 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/platform_device.h> #include <linux/msm_ipc.h> #include <linux/ipc_router.h> #include <linux/kref.h> #define IPC_ROUTER_XPRT_EVENT_DATA 1 #define IPC_ROUTER_XPRT_EVENT_OPEN 2 Loading Loading @@ -98,6 +99,7 @@ struct rr_opt_hdr { * @opt_hdr: Optinal header information. * @pkt_fragment_q: Queue of SKBs containing payload. * @length: Length of data in the chain of SKBs * @ref: Reference count for the packet. */ struct rr_packet { struct list_head list; Loading @@ -105,6 +107,7 @@ struct rr_packet { struct rr_opt_hdr opt_hdr; struct sk_buff_head *pkt_fragment_q; uint32_t length; struct kref ref; }; /** Loading net/ipc_router/ipc_router_core.c +2 −0 Original line number Diff line number Diff line Loading @@ -530,6 +530,7 @@ struct rr_packet *clone_pkt(struct rr_packet *pkt) return NULL; } skb_queue_head_init(pkt_fragment_q); kref_init(&cloned_pkt->ref); skb_queue_walk(pkt->pkt_fragment_q, temp_skb) { cloned_skb = skb_clone(temp_skb, GFP_KERNEL); Loading Loading @@ -585,6 +586,7 @@ struct rr_packet *create_pkt(struct sk_buff_head *data) } skb_queue_head_init(pkt->pkt_fragment_q); } kref_init(&pkt->ref); return pkt; } Loading Loading
drivers/soc/qcom/ipc_router_mhi_xprt.c +160 −16 Original line number Diff line number Diff line /* Copyright (c) 2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -38,6 +38,22 @@ if (ipc_router_mhi_xprt_debug_mask) \ #define IPC_ROUTER_MHI_XPRT_MAX_PKT_SIZE 0x1000 #define IPC_ROUTER_MHI_XPRT_NUM_TRBS 10 /** * ipc_router_mhi_addr_map - DMA to virtual address mapping for an IPC Router * packet. * @list_node: Address mapping list node used by mhi transport map list. * @virt_addr: The virtual address in mapping. * @dma_addr: The dma address in mapping. * @pkt: The IPC Router packet for which the virtual address of skbs are mapped * to DMA address during TX/RX operations. */ struct ipc_router_mhi_addr_map { struct list_head list_node; void *virt_addr; dma_addr_t dma_addr; struct rr_packet *pkt; }; /** * ipc_router_mhi_channel - MHI Channel related information * @out_chan_id: Out channel ID for use by IPC ROUTER enumerated in MHI driver. Loading @@ -49,7 +65,6 @@ if (ipc_router_mhi_xprt_debug_mask) \ * @state_lock: Lock to protect access to the state information. * @out_chan_enabled: State of the outgoing channel. * @in_chan_enabled: State of the incoming channel. * @bytes_to_tx: Remaining bytes to be transmitted in a packet. * @bytes_to_rx: Remaining bytes to be received in a packet. * @in_skbq_lock: Lock to protect access to the input skbs queue. * @in_skbq: Queue containing the input buffers. Loading @@ -69,7 +84,6 @@ struct ipc_router_mhi_channel { struct mutex state_lock; bool out_chan_enabled; bool in_chan_enabled; int bytes_to_tx; int bytes_to_rx; struct mutex in_skbq_lock; Loading @@ -93,6 +107,12 @@ struct ipc_router_mhi_channel { * by IPC Router. * @xprt_version: IPC Router header version supported by this XPRT. * @xprt_option: XPRT specific options to be handled by IPC Router. * @tx_addr_map_list_lock: The lock to protect the address mapping list for TX * operations. * @tx_addr_map_list: DMA to virtual address mapping list for TX operations. * @rx_addr_map_list_lock: The lock to protect the address mapping list for RX * operations. * @rx_addr_map_list: DMA to virtual address mapping list for RX operations. */ struct ipc_router_mhi_xprt { struct list_head list; Loading @@ -106,6 +126,10 @@ struct ipc_router_mhi_xprt { struct completion sft_close_complete; unsigned xprt_version; unsigned xprt_option; struct mutex tx_addr_map_list_lock; struct list_head tx_addr_map_list; struct mutex rx_addr_map_list_lock; struct list_head rx_addr_map_list; }; struct ipc_router_mhi_xprt_work { Loading Loading @@ -138,6 +162,90 @@ struct ipc_router_mhi_xprt_config { static DEFINE_MUTEX(mhi_xprt_list_lock_lha1); static LIST_HEAD(mhi_xprt_list); /* * ipc_router_mhi_release_pkt() - Release a cloned IPC Router packet * @ref: Reference to the kref object in the IPC Router packet. */ void ipc_router_mhi_release_pkt(struct kref *ref) { struct rr_packet *pkt = container_of(ref, struct rr_packet, ref); release_pkt(pkt); } /* * ipc_router_mhi_xprt_find_addr_map() - Search the mapped virtual address * @addr_map_list: The list of address mappings. * @addr_map_list_lock: Reference to the lock that protects the @addr_map_list. * @addr: The dma address for which mapped virtual address need to be found. * * Return: The mapped virtual Address if found, NULL otherwise. */ void *ipc_router_mhi_xprt_find_addr_map(struct list_head *addr_map_list, struct mutex *addr_map_list_lock, dma_addr_t addr) { struct ipc_router_mhi_addr_map *addr_mapping; struct ipc_router_mhi_addr_map *tmp_addr_mapping; void *virt_addr; if (!addr_map_list || !addr_map_list_lock) return NULL; mutex_lock(addr_map_list_lock); list_for_each_entry_safe(addr_mapping, tmp_addr_mapping, addr_map_list, list_node) { if (addr_mapping->dma_addr == addr) { virt_addr = addr_mapping->virt_addr; list_del(&addr_mapping->list_node); if (addr_mapping->pkt) kref_put(&addr_mapping->pkt->ref, ipc_router_mhi_release_pkt); kfree(addr_mapping); mutex_unlock(addr_map_list_lock); return virt_addr; } } mutex_unlock(addr_map_list_lock); IPC_RTR_ERR( "%s: Virtual address mapping for DMA addr [%p] not found\n", __func__, (void *)addr); return NULL; } /* * ipc_router_mhi_xprt_add_addr_map() - Add a mapping of virtual address to dma * address * @addr_map_list: The list of address mappings. * @addr_map_list_lock: Reference to the lock that protects the @addr_map_list. * @pkt: The IPC Router packet that contains the virtual address in skbs. * @addr: The virtual address which needs to be added. * @dma_addr: The dma address which needs to be added. * * Return: 0 on success, standard Linux error code otherwise. */ int ipc_router_mhi_xprt_add_addr_map(struct list_head *addr_map_list, struct mutex *addr_map_list_lock, struct rr_packet *pkt, void *virt_addr, dma_addr_t dma_addr) { struct ipc_router_mhi_addr_map *addr_mapping; if (!addr_map_list || !addr_map_list_lock) return -EINVAL; addr_mapping = kmalloc(sizeof(*addr_mapping), GFP_KERNEL); if (!addr_mapping) return -ENOMEM; addr_mapping->virt_addr = virt_addr; addr_mapping->dma_addr = dma_addr; addr_mapping->pkt = pkt; mutex_lock(addr_map_list_lock); if (addr_mapping->pkt) kref_get(&addr_mapping->pkt->ref); list_add_tail(&addr_mapping->list_node, addr_map_list); mutex_unlock(addr_map_list_lock); return 0; } /* * mhi_xprt_queue_in_buffers() - Queue input buffers * @mhi_xprtp: MHI XPRT in which the input buffer has to be queued. Loading Loading @@ -169,6 +277,14 @@ int mhi_xprt_queue_in_buffers(struct ipc_router_mhi_xprt *mhi_xprtp, kfree_skb(skb); break; } if (ipc_router_mhi_xprt_add_addr_map( &mhi_xprtp->rx_addr_map_list, &mhi_xprtp->rx_addr_map_list_lock, NULL, skb->data, dma_addr) < 0) { IPC_RTR_ERR("%s: Could not map %d SKB->DMA address\n", __func__, (i + 1)); break; } mutex_lock(&mhi_xprtp->ch_hndl.in_skbq_lock); rc_val = mhi_queue_xfer(mhi_xprtp->ch_hndl.in_handle, dma_addr, buf_size, MHI_EOT); Loading Loading @@ -251,7 +367,7 @@ static int ipc_router_mhi_write_avail(struct msm_ipc_router_xprt *xprt) * standard Linux error codes on failure. */ static int ipc_router_mhi_write_skb(struct ipc_router_mhi_xprt *mhi_xprtp, struct sk_buff *skb) struct sk_buff *skb, struct rr_packet *pkt) { size_t sz_to_write = 0; size_t offset = 0; Loading @@ -276,22 +392,30 @@ static int ipc_router_mhi_write_skb(struct ipc_router_mhi_xprt *mhi_xprtp, sz_to_write, DMA_TO_DEVICE); if (dma_mapping_error(NULL, dma_addr)) { mutex_unlock(&mhi_xprtp->ch_hndl.state_lock); IPC_RTR_ERR("%s: Failed to map DMA 0x%x\n", IPC_RTR_ERR("%s: Failed to map DMA 0x%zx\n", __func__, sz_to_write); return -ENOMEM; } if (ipc_router_mhi_xprt_add_addr_map( &mhi_xprtp->tx_addr_map_list, &mhi_xprtp->tx_addr_map_list_lock, pkt, skb->data + offset, dma_addr) < 0) { IPC_RTR_ERR("%s: Could not map SKB->DMA address\n", __func__); break; } rc = mhi_queue_xfer(mhi_xprtp->ch_hndl.out_handle, dma_addr, sz_to_write, MHI_EOT | MHI_EOB); if (rc != 0) { mutex_unlock(&mhi_xprtp->ch_hndl.state_lock); dma_unmap_single(NULL, dma_addr, sz_to_write, DMA_TO_DEVICE); IPC_RTR_ERR("%s: Error queueing mhi_xfer 0x%x\n", IPC_RTR_ERR("%s: Error queueing mhi_xfer 0x%zx\n", __func__, sz_to_write); return -EFAULT; } else { offset += sz_to_write; mhi_xprtp->ch_hndl.bytes_to_tx += sz_to_write; } mutex_unlock(&mhi_xprtp->ch_hndl.state_lock); } Loading @@ -311,6 +435,7 @@ static int ipc_router_mhi_write(void *data, { struct rr_packet *pkt = (struct rr_packet *)data; struct sk_buff *ipc_rtr_pkt; struct rr_packet *cloned_pkt; int rc; struct ipc_router_mhi_xprt *mhi_xprtp = container_of(xprt, struct ipc_router_mhi_xprt, xprt); Loading @@ -321,9 +446,15 @@ static int ipc_router_mhi_write(void *data, if (!len || pkt->length != len) return -EINVAL; cloned_pkt = clone_pkt(pkt); if (!cloned_pkt) { pr_err("%s: Error in cloning packet while tx\n", __func__); return -ENOMEM; } D("%s: Ready to write %d bytes\n", __func__, len); skb_queue_walk(pkt->pkt_fragment_q, ipc_rtr_pkt) { rc = ipc_router_mhi_write_skb(mhi_xprtp, ipc_rtr_pkt); skb_queue_walk(cloned_pkt->pkt_fragment_q, ipc_rtr_pkt) { rc = ipc_router_mhi_write_skb(mhi_xprtp, ipc_rtr_pkt, cloned_pkt); if (rc < 0) { IPC_RTR_ERR("%s: Error writing SKB %d\n", __func__, rc); Loading @@ -331,7 +462,7 @@ static int ipc_router_mhi_write(void *data, } } wait_event(mhi_xprtp->write_wait_q, !mhi_xprtp->ch_hndl.bytes_to_tx); kref_put(&cloned_pkt->ref, ipc_router_mhi_release_pkt); if (rc < 0) return rc; else Loading Loading @@ -375,7 +506,7 @@ static void mhi_xprt_read_data(struct work_struct *work) rc = mhi_poll_inbound(mhi_xprtp->ch_hndl.in_handle, &result); if (rc || !result.payload_buf || !result.bytes_xferd) { if (rc != MHI_STATUS_RING_EMPTY) IPC_RTR_ERR("%s: Poll failed %s:%d:%p:%zu\n", IPC_RTR_ERR("%s: Poll failed %s:%d:%p:%u\n", __func__, mhi_xprtp->xprt_name, rc, (void *)result.payload_buf, result.bytes_xferd); Loading @@ -395,8 +526,14 @@ static void mhi_xprt_read_data(struct work_struct *work) D("%s: Allocated rr_packet\n", __func__); } skb_data = dma_to_virt(NULL, data_addr); skb_data = ipc_router_mhi_xprt_find_addr_map( &mhi_xprtp->rx_addr_map_list, &mhi_xprtp->rx_addr_map_list_lock, data_addr); dma_unmap_single(NULL, data_addr, data_sz, DMA_BIDIRECTIONAL); if (!skb_data) continue; mutex_lock(&mhi_xprtp->ch_hndl.in_skbq_lock); skb_queue_walk(&mhi_xprtp->ch_hndl.in_skbq, skb) { if (skb->data == skb_data) { Loading Loading @@ -590,14 +727,17 @@ static void mhi_xprt_disable_event(struct work_struct *work) static void mhi_xprt_xfer_event(struct mhi_cb_info *cb_info) { struct ipc_router_mhi_xprt *mhi_xprtp; dma_addr_t out_dma_addr; mhi_xprtp = (struct ipc_router_mhi_xprt *)(cb_info->result->user_data); if (cb_info->chan == mhi_xprtp->ch_hndl.out_chan_id) { dma_unmap_single(NULL, (dma_addr_t)cb_info->result->payload_buf, out_dma_addr = (dma_addr_t)cb_info->result->payload_buf; dma_unmap_single(NULL, out_dma_addr, cb_info->result->bytes_xferd, DMA_TO_DEVICE); mutex_lock(&mhi_xprtp->ch_hndl.state_lock); mhi_xprtp->ch_hndl.bytes_to_tx -= cb_info->result->bytes_xferd; if (!mhi_xprtp->ch_hndl.bytes_to_tx) ipc_router_mhi_xprt_find_addr_map(&mhi_xprtp->tx_addr_map_list, &mhi_xprtp->tx_addr_map_list_lock, out_dma_addr); wake_up(&mhi_xprtp->write_wait_q); mutex_unlock(&mhi_xprtp->ch_hndl.state_lock); } else if (cb_info->chan == mhi_xprtp->ch_hndl.in_chan_id) { Loading Loading @@ -754,6 +894,10 @@ static int ipc_router_mhi_config_init( mhi_xprtp->ch_hndl.max_packet_size = IPC_ROUTER_MHI_XPRT_MAX_PKT_SIZE; mhi_xprtp->ch_hndl.num_trbs = IPC_ROUTER_MHI_XPRT_NUM_TRBS; mhi_xprtp->ch_hndl.mhi_xprtp = mhi_xprtp; INIT_LIST_HEAD(&mhi_xprtp->tx_addr_map_list); mutex_init(&mhi_xprtp->tx_addr_map_list_lock); INIT_LIST_HEAD(&mhi_xprtp->rx_addr_map_list); mutex_init(&mhi_xprtp->rx_addr_map_list_lock); rc = ipc_router_mhi_driver_register(mhi_xprtp); return rc; Loading
include/linux/ipc_router_xprt.h +3 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/platform_device.h> #include <linux/msm_ipc.h> #include <linux/ipc_router.h> #include <linux/kref.h> #define IPC_ROUTER_XPRT_EVENT_DATA 1 #define IPC_ROUTER_XPRT_EVENT_OPEN 2 Loading Loading @@ -98,6 +99,7 @@ struct rr_opt_hdr { * @opt_hdr: Optinal header information. * @pkt_fragment_q: Queue of SKBs containing payload. * @length: Length of data in the chain of SKBs * @ref: Reference count for the packet. */ struct rr_packet { struct list_head list; Loading @@ -105,6 +107,7 @@ struct rr_packet { struct rr_opt_hdr opt_hdr; struct sk_buff_head *pkt_fragment_q; uint32_t length; struct kref ref; }; /** Loading
net/ipc_router/ipc_router_core.c +2 −0 Original line number Diff line number Diff line Loading @@ -530,6 +530,7 @@ struct rr_packet *clone_pkt(struct rr_packet *pkt) return NULL; } skb_queue_head_init(pkt_fragment_q); kref_init(&cloned_pkt->ref); skb_queue_walk(pkt->pkt_fragment_q, temp_skb) { cloned_skb = skb_clone(temp_skb, GFP_KERNEL); Loading Loading @@ -585,6 +586,7 @@ struct rr_packet *create_pkt(struct sk_buff_head *data) } skb_queue_head_init(pkt->pkt_fragment_q); } kref_init(&pkt->ref); return pkt; } Loading