123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- /* Copyright (c) 2013, 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
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- /*
- * IPC ROUTER HSIC XPRT module.
- */
- #define DEBUG
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/types.h>
- #include <mach/ipc_bridge.h>
- #include <mach/subsystem_restart.h>
- #include "ipc_router.h"
- static int msm_ipc_router_hsic_xprt_debug_mask;
- module_param_named(debug_mask, msm_ipc_router_hsic_xprt_debug_mask,
- int, S_IRUGO | S_IWUSR | S_IWGRP);
- #if defined(DEBUG)
- #define D(x...) do { \
- if (msm_ipc_router_hsic_xprt_debug_mask) \
- pr_info(x); \
- } while (0)
- #else
- #define D(x...) do { } while (0)
- #endif
- #define NUM_HSIC_XPRTS 1
- #define XPRT_NAME_LEN 32
- /**
- * msm_ipc_router_hsic_xprt - IPC Router's HSIC XPRT strucutre
- * @xprt: IPC Router XPRT structure to contain HSIC XPRT specific info.
- * @pdev: Platform device registered by IPC Bridge function driver.
- * @hsic_xprt_wq: Workqueue to queue read & other XPRT related works.
- * @read_work: Read Work to perform read operation from HSIC's ipc_bridge.
- * @in_pkt: Pointer to any partially read packet.
- * @ss_reset_lock: Lock to protect access to the ss_reset flag.
- * @sft_close_complete: Variable to indicate completion of SSR handling
- * by IPC Router.
- * @xprt_version: IPC Router header version supported by this XPRT.
- * @xprt_option: XPRT specific options to be handled by IPC Router.
- */
- struct msm_ipc_router_hsic_xprt {
- struct msm_ipc_router_xprt xprt;
- struct platform_device *pdev;
- struct workqueue_struct *hsic_xprt_wq;
- struct delayed_work read_work;
- struct rr_packet *in_pkt;
- struct mutex ss_reset_lock;
- int ss_reset;
- struct completion sft_close_complete;
- unsigned xprt_version;
- unsigned xprt_option;
- };
- struct msm_ipc_router_hsic_xprt_work {
- struct msm_ipc_router_xprt *xprt;
- struct work_struct work;
- };
- static void hsic_xprt_read_data(struct work_struct *work);
- /**
- * msm_ipc_router_hsic_xprt_config - Config. Info. of each HSIC XPRT
- * @ch_name: Name of the HSIC endpoint exported by ipc_bridge driver.
- * @xprt_name: Name of the XPRT to be registered with IPC Router.
- * @hsic_pdev_id: ID to differentiate among multiple ipc_bridge endpoints.
- * @link_id: Network Cluster ID to which this XPRT belongs to.
- * @xprt_version: IPC Router header version supported by this XPRT.
- */
- struct msm_ipc_router_hsic_xprt_config {
- char ch_name[XPRT_NAME_LEN];
- char xprt_name[XPRT_NAME_LEN];
- int hsic_pdev_id;
- uint32_t link_id;
- unsigned xprt_version;
- };
- struct msm_ipc_router_hsic_xprt_config hsic_xprt_cfg[] = {
- {"ipc_bridge", "ipc_rtr_ipc_bridge1", 1, 1, 3},
- };
- static struct msm_ipc_router_hsic_xprt hsic_remote_xprt[NUM_HSIC_XPRTS];
- /**
- * find_hsic_xprt_cfg() - Find the config info specific to an HSIC endpoint
- * @pdev: Platform device registered by HSIC's ipc_bridge driver
- *
- * @return: Index to the entry in the hsic_remote_xprt table if matching
- * endpoint is found, < 0 on error.
- *
- * This function is used to find the configuration information specific to
- * an HSIC endpoint from the hsic_remote_xprt table.
- */
- static int find_hsic_xprt_cfg(struct platform_device *pdev)
- {
- int i;
- for (i = 0; i < NUM_HSIC_XPRTS; i++) {
- /* TODO: Update the condition for multiple hsic links */
- if (!strncmp(pdev->name, hsic_xprt_cfg[i].ch_name, 32))
- return i;
- }
- return -ENODEV;
- }
- /**
- * msm_ipc_router_hsic_get_xprt_version() - Get IPC Router header version
- * supported by the XPRT
- * @xprt: XPRT for which the version information is required.
- *
- * @return: IPC Router header version supported by the XPRT.
- */
- static int msm_ipc_router_hsic_get_xprt_version(
- struct msm_ipc_router_xprt *xprt)
- {
- struct msm_ipc_router_hsic_xprt *hsic_xprtp;
- if (!xprt)
- return -EINVAL;
- hsic_xprtp = container_of(xprt, struct msm_ipc_router_hsic_xprt, xprt);
- return (int)hsic_xprtp->xprt_version;
- }
- /**
- * msm_ipc_router_hsic_get_xprt_option() - Get XPRT options
- * @xprt: XPRT for which the option information is required.
- *
- * @return: Options supported by the XPRT.
- */
- static int msm_ipc_router_hsic_get_xprt_option(
- struct msm_ipc_router_xprt *xprt)
- {
- struct msm_ipc_router_hsic_xprt *hsic_xprtp;
- if (!xprt)
- return -EINVAL;
- hsic_xprtp = container_of(xprt, struct msm_ipc_router_hsic_xprt, xprt);
- return (int)hsic_xprtp->xprt_option;
- }
- /**
- * msm_ipc_router_hsic_remote_write_avail() - Get available write space
- * @xprt: XPRT for which the available write space info. is required.
- *
- * @return: Write space in bytes on success, 0 on SSR.
- */
- static int msm_ipc_router_hsic_remote_write_avail(
- struct msm_ipc_router_xprt *xprt)
- {
- struct ipc_bridge_platform_data *pdata;
- int write_avail;
- struct msm_ipc_router_hsic_xprt *hsic_xprtp =
- container_of(xprt, struct msm_ipc_router_hsic_xprt, xprt);
- mutex_lock(&hsic_xprtp->ss_reset_lock);
- if (hsic_xprtp->ss_reset || !hsic_xprtp->pdev) {
- write_avail = 0;
- } else {
- pdata = hsic_xprtp->pdev->dev.platform_data;
- write_avail = pdata->max_write_size;
- }
- mutex_unlock(&hsic_xprtp->ss_reset_lock);
- return write_avail;
- }
- /**
- * msm_ipc_router_hsic_remote_write() - Write to XPRT
- * @data: Data to be written to the XPRT.
- * @len: Length of the data to be written.
- * @xprt: XPRT to which the data has to be written.
- *
- * @return: Data Length on success, standard Linux error codes on failure.
- */
- static int msm_ipc_router_hsic_remote_write(void *data,
- uint32_t len, struct msm_ipc_router_xprt *xprt)
- {
- struct rr_packet *pkt = (struct rr_packet *)data;
- struct sk_buff *skb;
- struct ipc_bridge_platform_data *pdata;
- struct msm_ipc_router_hsic_xprt *hsic_xprtp;
- int ret;
- if (!pkt || pkt->length != len || !xprt) {
- pr_err("%s: Invalid input parameters\n", __func__);
- return -EINVAL;
- }
- hsic_xprtp = container_of(xprt, struct msm_ipc_router_hsic_xprt, xprt);
- mutex_lock(&hsic_xprtp->ss_reset_lock);
- if (hsic_xprtp->ss_reset) {
- pr_err("%s: Trying to write on a reset link\n", __func__);
- mutex_unlock(&hsic_xprtp->ss_reset_lock);
- return -ENETRESET;
- }
- if (!hsic_xprtp->pdev) {
- pr_err("%s: Trying to write on a closed link\n", __func__);
- mutex_unlock(&hsic_xprtp->ss_reset_lock);
- return -ENODEV;
- }
- pdata = hsic_xprtp->pdev->dev.platform_data;
- if (!pdata || !pdata->write) {
- pr_err("%s on a uninitialized link\n", __func__);
- mutex_unlock(&hsic_xprtp->ss_reset_lock);
- return -EFAULT;
- }
- skb = skb_peek(pkt->pkt_fragment_q);
- if (!skb) {
- pr_err("%s SKB is NULL\n", __func__);
- mutex_unlock(&hsic_xprtp->ss_reset_lock);
- return -EINVAL;
- }
- D("%s: About to write %d bytes\n", __func__, len);
- ret = pdata->write(hsic_xprtp->pdev, skb->data, skb->len);
- if (ret == skb->len)
- ret = len;
- D("%s: Finished writing %d bytes\n", __func__, len);
- mutex_unlock(&hsic_xprtp->ss_reset_lock);
- return ret;
- }
- /**
- * msm_ipc_router_hsic_remote_close() - Close the XPRT
- * @xprt: XPRT which needs to be closed.
- *
- * @return: 0 on success, standard Linux error codes on failure.
- */
- static int msm_ipc_router_hsic_remote_close(
- struct msm_ipc_router_xprt *xprt)
- {
- struct msm_ipc_router_hsic_xprt *hsic_xprtp;
- struct ipc_bridge_platform_data *pdata;
- if (!xprt)
- return -EINVAL;
- hsic_xprtp = container_of(xprt, struct msm_ipc_router_hsic_xprt, xprt);
- mutex_lock(&hsic_xprtp->ss_reset_lock);
- hsic_xprtp->ss_reset = 1;
- mutex_unlock(&hsic_xprtp->ss_reset_lock);
- flush_workqueue(hsic_xprtp->hsic_xprt_wq);
- destroy_workqueue(hsic_xprtp->hsic_xprt_wq);
- pdata = hsic_xprtp->pdev->dev.platform_data;
- if (pdata && pdata->close)
- pdata->close(hsic_xprtp->pdev);
- hsic_xprtp->pdev = NULL;
- return 0;
- }
- /**
- * hsic_xprt_read_data() - Read work to read from the XPRT
- * @work: Read work to be executed.
- *
- * This function is a read work item queued on a XPRT specific workqueue.
- * The work parameter contains information regarding the XPRT on which this
- * read work has to be performed. The work item keeps reading from the HSIC
- * endpoint, until the endpoint returns an error.
- */
- static void hsic_xprt_read_data(struct work_struct *work)
- {
- int pkt_size;
- struct sk_buff *skb = NULL;
- void *data;
- struct ipc_bridge_platform_data *pdata;
- struct delayed_work *rwork = to_delayed_work(work);
- struct msm_ipc_router_hsic_xprt *hsic_xprtp =
- container_of(rwork, struct msm_ipc_router_hsic_xprt, read_work);
- while (1) {
- mutex_lock(&hsic_xprtp->ss_reset_lock);
- if (hsic_xprtp->ss_reset) {
- mutex_unlock(&hsic_xprtp->ss_reset_lock);
- break;
- }
- pdata = hsic_xprtp->pdev->dev.platform_data;
- mutex_unlock(&hsic_xprtp->ss_reset_lock);
- while (!hsic_xprtp->in_pkt) {
- hsic_xprtp->in_pkt = kzalloc(sizeof(struct rr_packet),
- GFP_KERNEL);
- if (hsic_xprtp->in_pkt)
- break;
- pr_err("%s: packet allocation failure\n", __func__);
- msleep(100);
- }
- while (!hsic_xprtp->in_pkt->pkt_fragment_q) {
- hsic_xprtp->in_pkt->pkt_fragment_q =
- kmalloc(sizeof(struct sk_buff_head),
- GFP_KERNEL);
- if (hsic_xprtp->in_pkt->pkt_fragment_q)
- break;
- pr_err("%s: Couldn't alloc pkt_fragment_q\n",
- __func__);
- msleep(100);
- }
- skb_queue_head_init(hsic_xprtp->in_pkt->pkt_fragment_q);
- D("%s: Allocated rr_packet\n", __func__);
- while (!skb) {
- skb = alloc_skb(pdata->max_read_size, GFP_KERNEL);
- if (skb)
- break;
- pr_err("%s: Couldn't alloc SKB\n", __func__);
- msleep(100);
- }
- data = skb_put(skb, pdata->max_read_size);
- pkt_size = pdata->read(hsic_xprtp->pdev, data,
- pdata->max_read_size);
- if (pkt_size < 0) {
- pr_err("%s: Error %d @ read operation\n",
- __func__, pkt_size);
- kfree_skb(skb);
- kfree(hsic_xprtp->in_pkt->pkt_fragment_q);
- kfree(hsic_xprtp->in_pkt);
- break;
- }
- skb_queue_tail(hsic_xprtp->in_pkt->pkt_fragment_q, skb);
- hsic_xprtp->in_pkt->length = pkt_size;
- D("%s: Packet size read %d\n", __func__, pkt_size);
- msm_ipc_router_xprt_notify(&hsic_xprtp->xprt,
- IPC_ROUTER_XPRT_EVENT_DATA, (void *)hsic_xprtp->in_pkt);
- release_pkt(hsic_xprtp->in_pkt);
- hsic_xprtp->in_pkt = NULL;
- skb = NULL;
- }
- }
- /**
- * hsic_xprt_sft_close_done() - Completion of XPRT reset
- * @xprt: XPRT on which the reset operation is complete.
- *
- * This function is used by IPC Router to signal this HSIC XPRT Abstraction
- * Layer(XAL) that the reset of XPRT is completely handled by IPC Router.
- */
- static void hsic_xprt_sft_close_done(struct msm_ipc_router_xprt *xprt)
- {
- struct msm_ipc_router_hsic_xprt *hsic_xprtp =
- container_of(xprt, struct msm_ipc_router_hsic_xprt, xprt);
- complete_all(&hsic_xprtp->sft_close_complete);
- }
- /**
- * msm_ipc_router_hsic_remote_remove() - Remove an HSIC endpoint
- * @pdev: Platform device corresponding to HSIC endpoint.
- *
- * @return: 0 on success, standard Linux error codes on error.
- *
- * This function is called when the underlying ipc_bridge driver unregisters
- * a platform device, mapped to an HSIC endpoint, during SSR.
- */
- static int msm_ipc_router_hsic_remote_remove(struct platform_device *pdev)
- {
- int id;
- struct ipc_bridge_platform_data *pdata;
- id = find_hsic_xprt_cfg(pdev);
- if (id < 0) {
- pr_err("%s: called for unknown ch %s\n",
- __func__, pdev->name);
- return id;
- }
- mutex_lock(&hsic_remote_xprt[id].ss_reset_lock);
- hsic_remote_xprt[id].ss_reset = 1;
- mutex_unlock(&hsic_remote_xprt[id].ss_reset_lock);
- flush_workqueue(hsic_remote_xprt[id].hsic_xprt_wq);
- destroy_workqueue(hsic_remote_xprt[id].hsic_xprt_wq);
- init_completion(&hsic_remote_xprt[id].sft_close_complete);
- msm_ipc_router_xprt_notify(&hsic_remote_xprt[id].xprt,
- IPC_ROUTER_XPRT_EVENT_CLOSE, NULL);
- D("%s: Notified IPC Router of %s CLOSE\n",
- __func__, hsic_remote_xprt[id].xprt.name);
- wait_for_completion(&hsic_remote_xprt[id].sft_close_complete);
- hsic_remote_xprt[id].pdev = NULL;
- pdata = pdev->dev.platform_data;
- if (pdata && pdata->close)
- pdata->close(pdev);
- return 0;
- }
- /**
- * msm_ipc_router_hsic_remote_probe() - Probe an HSIC endpoint
- * @pdev: Platform device corresponding to HSIC endpoint.
- *
- * @return: 0 on success, standard Linux error codes on error.
- *
- * This function is called when the underlying ipc_bridge driver registers
- * a platform device, mapped to an HSIC endpoint.
- */
- static int msm_ipc_router_hsic_remote_probe(struct platform_device *pdev)
- {
- int rc;
- int id; /*Index into the hsic_xprt_cfg table*/
- struct ipc_bridge_platform_data *pdata;
- pdata = pdev->dev.platform_data;
- if (!pdata || !pdata->open || !pdata->read ||
- !pdata->write || !pdata->close) {
- pr_err("%s: pdata or pdata->operations is NULL\n", __func__);
- return -EINVAL;
- }
- id = find_hsic_xprt_cfg(pdev);
- if (id < 0) {
- pr_err("%s: called for unknown ch %s\n",
- __func__, pdev->name);
- return id;
- }
- hsic_remote_xprt[id].hsic_xprt_wq =
- create_singlethread_workqueue(pdev->name);
- if (!hsic_remote_xprt[id].hsic_xprt_wq) {
- pr_err("%s: WQ creation failed for %s\n",
- __func__, pdev->name);
- return -EFAULT;
- }
- hsic_remote_xprt[id].xprt.name = hsic_xprt_cfg[id].xprt_name;
- hsic_remote_xprt[id].xprt.link_id = hsic_xprt_cfg[id].link_id;
- hsic_remote_xprt[id].xprt.get_version =
- msm_ipc_router_hsic_get_xprt_version;
- hsic_remote_xprt[id].xprt.get_option =
- msm_ipc_router_hsic_get_xprt_option;
- hsic_remote_xprt[id].xprt.read_avail = NULL;
- hsic_remote_xprt[id].xprt.read = NULL;
- hsic_remote_xprt[id].xprt.write_avail =
- msm_ipc_router_hsic_remote_write_avail;
- hsic_remote_xprt[id].xprt.write = msm_ipc_router_hsic_remote_write;
- hsic_remote_xprt[id].xprt.close = msm_ipc_router_hsic_remote_close;
- hsic_remote_xprt[id].xprt.sft_close_done = hsic_xprt_sft_close_done;
- hsic_remote_xprt[id].xprt.priv = NULL;
- hsic_remote_xprt[id].in_pkt = NULL;
- INIT_DELAYED_WORK(&hsic_remote_xprt[id].read_work, hsic_xprt_read_data);
- mutex_init(&hsic_remote_xprt[id].ss_reset_lock);
- hsic_remote_xprt[id].ss_reset = 0;
- hsic_remote_xprt[id].xprt_version = hsic_xprt_cfg[id].xprt_version;
- hsic_remote_xprt[id].xprt_option = 0;
- rc = pdata->open(pdev);
- if (rc < 0) {
- pr_err("%s: Channel open failed for %s.%d\n",
- __func__, pdev->name, pdev->id);
- destroy_workqueue(hsic_remote_xprt[id].hsic_xprt_wq);
- return rc;
- }
- hsic_remote_xprt[id].pdev = pdev;
- msm_ipc_router_xprt_notify(&hsic_remote_xprt[id].xprt,
- IPC_ROUTER_XPRT_EVENT_OPEN, NULL);
- D("%s: Notified IPC Router of %s OPEN\n",
- __func__, hsic_remote_xprt[id].xprt.name);
- queue_delayed_work(hsic_remote_xprt[id].hsic_xprt_wq,
- &hsic_remote_xprt[id].read_work, 0);
- return 0;
- }
- static struct platform_driver msm_ipc_router_hsic_remote_driver[] = {
- {
- .probe = msm_ipc_router_hsic_remote_probe,
- .remove = msm_ipc_router_hsic_remote_remove,
- .driver = {
- .name = "ipc_bridge",
- .owner = THIS_MODULE,
- },
- },
- };
- static int __init msm_ipc_router_hsic_init(void)
- {
- int i, ret, rc = 0;
- BUG_ON(ARRAY_SIZE(hsic_xprt_cfg) != NUM_HSIC_XPRTS);
- for (i = 0; i < ARRAY_SIZE(msm_ipc_router_hsic_remote_driver); i++) {
- ret = platform_driver_register(
- &msm_ipc_router_hsic_remote_driver[i]);
- if (ret) {
- pr_err("%s: Failed to register platform driver for xprt%d. Continuing...\n",
- __func__, i);
- rc = ret;
- }
- }
- return rc;
- }
- module_init(msm_ipc_router_hsic_init);
- MODULE_DESCRIPTION("IPC Router HSIC XPRT");
- MODULE_LICENSE("GPL v2");
|