diff options
| author | Daniel Walker <dwalker@codeaurora.org> | 2010-05-11 18:56:44 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-05-14 16:21:30 -0400 |
| commit | bf597e99d2fd4c5d25485fd4e4877bbae2be816c (patch) | |
| tree | da7ae43a21dd8f0dc1d2904ccfc7e8370d148201 /drivers/staging | |
| parent | e64354c0be3b7134c85571a525b2e37fc4a95eef (diff) | |
staging: dream: smd: remove all smd related code
Part of this code is already in my MSM tree. I'll move the rest
forward through my tree also.
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
CC: Pavel Machek <pavel@ucw.cz>
CC: linux-arm-msm@vger.kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging')
| -rw-r--r-- | drivers/staging/dream/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/staging/dream/Makefile | 2 | ||||
| -rw-r--r-- | drivers/staging/dream/smd/Kconfig | 26 | ||||
| -rw-r--r-- | drivers/staging/dream/smd/Makefile | 7 | ||||
| -rw-r--r-- | drivers/staging/dream/smd/rpc_server_dog_keepalive.c | 68 | ||||
| -rw-r--r-- | drivers/staging/dream/smd/rpc_server_time_remote.c | 77 | ||||
| -rw-r--r-- | drivers/staging/dream/smd/smd.c | 1330 | ||||
| -rw-r--r-- | drivers/staging/dream/smd/smd_private.h | 164 | ||||
| -rw-r--r-- | drivers/staging/dream/smd/smd_qmi.c | 851 | ||||
| -rw-r--r-- | drivers/staging/dream/smd/smd_rpcrouter.c | 1261 | ||||
| -rw-r--r-- | drivers/staging/dream/smd/smd_rpcrouter.h | 193 | ||||
| -rw-r--r-- | drivers/staging/dream/smd/smd_rpcrouter_device.c | 377 | ||||
| -rw-r--r-- | drivers/staging/dream/smd/smd_rpcrouter_servers.c | 226 | ||||
| -rw-r--r-- | drivers/staging/dream/smd/smd_tty.c | 208 |
14 files changed, 1 insertions, 4790 deletions
diff --git a/drivers/staging/dream/Kconfig b/drivers/staging/dream/Kconfig index 707cc71a8a6a..0c30b19a5a7c 100644 --- a/drivers/staging/dream/Kconfig +++ b/drivers/staging/dream/Kconfig | |||
| @@ -3,7 +3,6 @@ config DREAM | |||
| 3 | depends on MACH_TROUT | 3 | depends on MACH_TROUT |
| 4 | 4 | ||
| 5 | if DREAM | 5 | if DREAM |
| 6 | source "drivers/staging/dream/smd/Kconfig" | ||
| 7 | 6 | ||
| 8 | source "drivers/staging/dream/camera/Kconfig" | 7 | source "drivers/staging/dream/camera/Kconfig" |
| 9 | 8 | ||
diff --git a/drivers/staging/dream/Makefile b/drivers/staging/dream/Makefile index 43d1eec8e257..fbea0abcc864 100644 --- a/drivers/staging/dream/Makefile +++ b/drivers/staging/dream/Makefile | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | EXTRA_CFLAGS=-Idrivers/staging/dream/include | 1 | EXTRA_CFLAGS=-Idrivers/staging/dream/include |
| 2 | obj-$(CONFIG_MSM_ADSP) += qdsp5/ smd/ | 2 | obj-$(CONFIG_MSM_ADSP) += qdsp5/ |
| 3 | obj-$(CONFIG_MSM_CAMERA) += camera/ | 3 | obj-$(CONFIG_MSM_CAMERA) += camera/ |
| 4 | obj-$(CONFIG_INPUT_GPIO) += gpio_axis.o gpio_event.o gpio_input.o gpio_matrix.o gpio_output.o | 4 | obj-$(CONFIG_INPUT_GPIO) += gpio_axis.o gpio_event.o gpio_input.o gpio_matrix.o gpio_output.o |
| 5 | 5 | ||
diff --git a/drivers/staging/dream/smd/Kconfig b/drivers/staging/dream/smd/Kconfig deleted file mode 100644 index 17b8bdc7b9b7..000000000000 --- a/drivers/staging/dream/smd/Kconfig +++ /dev/null | |||
| @@ -1,26 +0,0 @@ | |||
| 1 | config MSM_SMD | ||
| 2 | depends on ARCH_MSM | ||
| 3 | default y | ||
| 4 | bool "MSM Shared Memory Driver (SMD)" | ||
| 5 | help | ||
| 6 | Support for the shared memory interface between the apps | ||
| 7 | processor and the baseband processor. Provides access to | ||
| 8 | the "shared heap", as well as virtual serial channels | ||
| 9 | used to communicate with various services on the baseband | ||
| 10 | processor. | ||
| 11 | |||
| 12 | config MSM_ONCRPCROUTER | ||
| 13 | depends on MSM_SMD | ||
| 14 | default y | ||
| 15 | bool "MSM ONCRPC router support" | ||
| 16 | help | ||
| 17 | Support for the MSM ONCRPC router for communication between | ||
| 18 | the ARM9 and ARM11 | ||
| 19 | |||
| 20 | config MSM_RPCSERVERS | ||
| 21 | depends on MSM_ONCRPCROUTER | ||
| 22 | default y | ||
| 23 | bool "Kernel side RPC server bundle" | ||
| 24 | help | ||
| 25 | none | ||
| 26 | |||
diff --git a/drivers/staging/dream/smd/Makefile b/drivers/staging/dream/smd/Makefile deleted file mode 100644 index 1c87618366a7..000000000000 --- a/drivers/staging/dream/smd/Makefile +++ /dev/null | |||
| @@ -1,7 +0,0 @@ | |||
| 1 | EXTRA_CFLAGS=-Idrivers/staging/dream/include | ||
| 2 | obj-$(CONFIG_MSM_SMD) += smd.o smd_tty.o smd_qmi.o | ||
| 3 | obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter.o | ||
| 4 | obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o | ||
| 5 | obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o | ||
| 6 | obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o | ||
| 7 | obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o | ||
diff --git a/drivers/staging/dream/smd/rpc_server_dog_keepalive.c b/drivers/staging/dream/smd/rpc_server_dog_keepalive.c deleted file mode 100644 index b23fccfa87e2..000000000000 --- a/drivers/staging/dream/smd/rpc_server_dog_keepalive.c +++ /dev/null | |||
| @@ -1,68 +0,0 @@ | |||
| 1 | /* arch/arm/mach-msm/rpc_server_dog_keepalive.c | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 Google, Inc. | ||
| 4 | * Author: Iliyan Malchev <ibm@android.com> | ||
| 5 | * | ||
| 6 | * This software is licensed under the terms of the GNU General Public | ||
| 7 | * License version 2, as published by the Free Software Foundation, and | ||
| 8 | * may be copied, distributed, and modified under those terms. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/kernel.h> | ||
| 19 | #include <mach/msm_rpcrouter.h> | ||
| 20 | |||
| 21 | /* dog_keepalive server definitions */ | ||
| 22 | |||
| 23 | #define DOG_KEEPALIVE_PROG 0x30000015 | ||
| 24 | #if CONFIG_MSM_AMSS_VERSION==6210 | ||
| 25 | #define DOG_KEEPALIVE_VERS 0 | ||
| 26 | #define RPC_DOG_KEEPALIVE_BEACON 1 | ||
| 27 | #elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225) | ||
| 28 | #define DOG_KEEPALIVE_VERS 0x731fa727 | ||
| 29 | #define RPC_DOG_KEEPALIVE_BEACON 2 | ||
| 30 | #elif CONFIG_MSM_AMSS_VERSION==6350 | ||
| 31 | #define DOG_KEEPALIVE_VERS 0x00010000 | ||
| 32 | #define RPC_DOG_KEEPALIVE_BEACON 2 | ||
| 33 | #else | ||
| 34 | #error "Unsupported AMSS version" | ||
| 35 | #endif | ||
| 36 | #define RPC_DOG_KEEPALIVE_NULL 0 | ||
| 37 | |||
| 38 | |||
| 39 | /* TODO: Remove server registration with _VERS when modem is upated with _COMP*/ | ||
| 40 | |||
| 41 | static int handle_rpc_call(struct msm_rpc_server *server, | ||
| 42 | struct rpc_request_hdr *req, unsigned len) | ||
| 43 | { | ||
| 44 | switch (req->procedure) { | ||
| 45 | case RPC_DOG_KEEPALIVE_NULL: | ||
| 46 | return 0; | ||
| 47 | case RPC_DOG_KEEPALIVE_BEACON: | ||
| 48 | printk(KERN_INFO "DOG KEEPALIVE PING\n"); | ||
| 49 | return 0; | ||
| 50 | default: | ||
| 51 | return -ENODEV; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | static struct msm_rpc_server rpc_server = { | ||
| 56 | .prog = DOG_KEEPALIVE_PROG, | ||
| 57 | .vers = DOG_KEEPALIVE_VERS, | ||
| 58 | .rpc_call = handle_rpc_call, | ||
| 59 | }; | ||
| 60 | |||
| 61 | static int __init rpc_server_init(void) | ||
| 62 | { | ||
| 63 | /* Dual server registration to support backwards compatibility vers */ | ||
| 64 | return msm_rpc_create_server(&rpc_server); | ||
| 65 | } | ||
| 66 | |||
| 67 | |||
| 68 | module_init(rpc_server_init); | ||
diff --git a/drivers/staging/dream/smd/rpc_server_time_remote.c b/drivers/staging/dream/smd/rpc_server_time_remote.c deleted file mode 100644 index 2f90fc88c385..000000000000 --- a/drivers/staging/dream/smd/rpc_server_time_remote.c +++ /dev/null | |||
| @@ -1,77 +0,0 @@ | |||
| 1 | /* arch/arm/mach-msm/rpc_server_time_remote.c | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 Google, Inc. | ||
| 4 | * Author: Iliyan Malchev <ibm@android.com> | ||
| 5 | * | ||
| 6 | * This software is licensed under the terms of the GNU General Public | ||
| 7 | * License version 2, as published by the Free Software Foundation, and | ||
| 8 | * may be copied, distributed, and modified under those terms. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/kernel.h> | ||
| 19 | #include <mach/msm_rpcrouter.h> | ||
| 20 | |||
| 21 | /* time_remote_mtoa server definitions. */ | ||
| 22 | |||
| 23 | #define TIME_REMOTE_MTOA_PROG 0x3000005d | ||
| 24 | #if CONFIG_MSM_AMSS_VERSION==6210 | ||
| 25 | #define TIME_REMOTE_MTOA_VERS 0 | ||
| 26 | #elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225) | ||
| 27 | #define TIME_REMOTE_MTOA_VERS 0x9202a8e4 | ||
| 28 | #elif CONFIG_MSM_AMSS_VERSION==6350 | ||
| 29 | #define TIME_REMOTE_MTOA_VERS 0x00010000 | ||
| 30 | #else | ||
| 31 | #error "Unknown AMSS version" | ||
| 32 | #endif | ||
| 33 | #define RPC_TIME_REMOTE_MTOA_NULL 0 | ||
| 34 | #define RPC_TIME_TOD_SET_APPS_BASES 2 | ||
| 35 | |||
| 36 | struct rpc_time_tod_set_apps_bases_args { | ||
| 37 | uint32_t tick; | ||
| 38 | uint64_t stamp; | ||
| 39 | }; | ||
| 40 | |||
| 41 | static int handle_rpc_call(struct msm_rpc_server *server, | ||
| 42 | struct rpc_request_hdr *req, unsigned len) | ||
| 43 | { | ||
| 44 | switch (req->procedure) { | ||
| 45 | case RPC_TIME_REMOTE_MTOA_NULL: | ||
| 46 | return 0; | ||
| 47 | |||
| 48 | case RPC_TIME_TOD_SET_APPS_BASES: { | ||
| 49 | struct rpc_time_tod_set_apps_bases_args *args; | ||
| 50 | args = (struct rpc_time_tod_set_apps_bases_args *)(req + 1); | ||
| 51 | args->tick = be32_to_cpu(args->tick); | ||
| 52 | args->stamp = be64_to_cpu(args->stamp); | ||
| 53 | printk(KERN_INFO "RPC_TIME_TOD_SET_APPS_BASES:\n" | ||
| 54 | "\ttick = %d\n" | ||
| 55 | "\tstamp = %lld\n", | ||
| 56 | args->tick, args->stamp); | ||
| 57 | return 0; | ||
| 58 | } | ||
| 59 | default: | ||
| 60 | return -ENODEV; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | static struct msm_rpc_server rpc_server = { | ||
| 65 | .prog = TIME_REMOTE_MTOA_PROG, | ||
| 66 | .vers = TIME_REMOTE_MTOA_VERS, | ||
| 67 | .rpc_call = handle_rpc_call, | ||
| 68 | }; | ||
| 69 | |||
| 70 | static int __init rpc_server_init(void) | ||
| 71 | { | ||
| 72 | /* Dual server registration to support backwards compatibility vers */ | ||
| 73 | return msm_rpc_create_server(&rpc_server); | ||
| 74 | } | ||
| 75 | |||
| 76 | |||
| 77 | module_init(rpc_server_init); | ||
diff --git a/drivers/staging/dream/smd/smd.c b/drivers/staging/dream/smd/smd.c deleted file mode 100644 index 8f35be7193fb..000000000000 --- a/drivers/staging/dream/smd/smd.c +++ /dev/null | |||
| @@ -1,1330 +0,0 @@ | |||
| 1 | /* arch/arm/mach-msm/smd.c | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 Google, Inc. | ||
| 4 | * Author: Brian Swetland <swetland@google.com> | ||
| 5 | * | ||
| 6 | * This software is licensed under the terms of the GNU General Public | ||
| 7 | * License version 2, as published by the Free Software Foundation, and | ||
| 8 | * may be copied, distributed, and modified under those terms. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/fs.h> | ||
| 20 | #include <linux/cdev.h> | ||
| 21 | #include <linux/device.h> | ||
| 22 | #include <linux/wait.h> | ||
| 23 | #include <linux/interrupt.h> | ||
| 24 | #include <linux/irq.h> | ||
| 25 | #include <linux/list.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/debugfs.h> | ||
| 28 | #include <linux/delay.h> | ||
| 29 | #include <linux/io.h> | ||
| 30 | |||
| 31 | #include <mach/msm_smd.h> | ||
| 32 | #include <mach/msm_iomap.h> | ||
| 33 | #include <mach/system.h> | ||
| 34 | |||
| 35 | #include "smd_private.h" | ||
| 36 | #include "../../../../arch/arm/mach-msm/proc_comm.h" | ||
| 37 | |||
| 38 | void (*msm_hw_reset_hook)(void); | ||
| 39 | |||
| 40 | #define MODULE_NAME "msm_smd" | ||
| 41 | |||
| 42 | enum { | ||
| 43 | MSM_SMD_DEBUG = 1U << 0, | ||
| 44 | MSM_SMSM_DEBUG = 1U << 0, | ||
| 45 | }; | ||
| 46 | |||
| 47 | static int msm_smd_debug_mask; | ||
| 48 | |||
| 49 | module_param_named(debug_mask, msm_smd_debug_mask, | ||
| 50 | int, S_IRUGO | S_IWUSR | S_IWGRP); | ||
| 51 | |||
| 52 | void *smem_find(unsigned id, unsigned size); | ||
| 53 | static void smd_diag(void); | ||
| 54 | |||
| 55 | static unsigned last_heap_free = 0xffffffff; | ||
| 56 | |||
| 57 | #define MSM_A2M_INT(n) (MSM_CSR_BASE + 0x400 + (n) * 4) | ||
| 58 | |||
| 59 | static inline void notify_other_smsm(void) | ||
| 60 | { | ||
| 61 | writel(1, MSM_A2M_INT(5)); | ||
| 62 | } | ||
| 63 | |||
| 64 | static inline void notify_other_smd(void) | ||
| 65 | { | ||
| 66 | writel(1, MSM_A2M_INT(0)); | ||
| 67 | } | ||
| 68 | |||
| 69 | static void smd_diag(void) | ||
| 70 | { | ||
| 71 | char *x; | ||
| 72 | |||
| 73 | x = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG); | ||
| 74 | if (x != 0) { | ||
| 75 | x[SZ_DIAG_ERR_MSG - 1] = 0; | ||
| 76 | pr_info("smem: DIAG '%s'\n", x); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | /* call when SMSM_RESET flag is set in the A9's smsm_state */ | ||
| 81 | static void handle_modem_crash(void) | ||
| 82 | { | ||
| 83 | pr_err("ARM9 has CRASHED\n"); | ||
| 84 | smd_diag(); | ||
| 85 | |||
| 86 | /* hard reboot if possible */ | ||
| 87 | if (msm_hw_reset_hook) | ||
| 88 | msm_hw_reset_hook(); | ||
| 89 | |||
| 90 | /* in this case the modem or watchdog should reboot us */ | ||
| 91 | for (;;) | ||
| 92 | ; | ||
| 93 | } | ||
| 94 | |||
| 95 | extern int (*msm_check_for_modem_crash)(void); | ||
| 96 | |||
| 97 | static int check_for_modem_crash(void) | ||
| 98 | { | ||
| 99 | struct smsm_shared *smsm; | ||
| 100 | |||
| 101 | smsm = smem_find(ID_SHARED_STATE, 2 * sizeof(struct smsm_shared)); | ||
| 102 | |||
| 103 | /* if the modem's not ready yet, we have to hope for the best */ | ||
| 104 | if (!smsm) | ||
| 105 | return 0; | ||
| 106 | |||
| 107 | if (smsm[1].state & SMSM_RESET) { | ||
| 108 | handle_modem_crash(); | ||
| 109 | return -1; | ||
| 110 | } else { | ||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | #define SMD_SS_CLOSED 0x00000000 | ||
| 116 | #define SMD_SS_OPENING 0x00000001 | ||
| 117 | #define SMD_SS_OPENED 0x00000002 | ||
| 118 | #define SMD_SS_FLUSHING 0x00000003 | ||
| 119 | #define SMD_SS_CLOSING 0x00000004 | ||
| 120 | #define SMD_SS_RESET 0x00000005 | ||
| 121 | #define SMD_SS_RESET_OPENING 0x00000006 | ||
| 122 | |||
| 123 | #define SMD_BUF_SIZE 8192 | ||
| 124 | #define SMD_CHANNELS 64 | ||
| 125 | |||
| 126 | #define SMD_HEADER_SIZE 20 | ||
| 127 | |||
| 128 | |||
| 129 | /* the spinlock is used to synchronize between the | ||
| 130 | ** irq handler and code that mutates the channel | ||
| 131 | ** list or fiddles with channel state | ||
| 132 | */ | ||
| 133 | static DEFINE_SPINLOCK(smd_lock); | ||
| 134 | static DEFINE_SPINLOCK(smem_lock); | ||
| 135 | |||
| 136 | /* the mutex is used during open() and close() | ||
| 137 | ** operations to avoid races while creating or | ||
| 138 | ** destroying smd_channel structures | ||
| 139 | */ | ||
| 140 | static DEFINE_MUTEX(smd_creation_mutex); | ||
| 141 | |||
| 142 | static int smd_initialized; | ||
| 143 | |||
| 144 | struct smd_alloc_elm { | ||
| 145 | char name[20]; | ||
| 146 | uint32_t cid; | ||
| 147 | uint32_t ctype; | ||
| 148 | uint32_t ref_count; | ||
| 149 | }; | ||
| 150 | |||
| 151 | struct smd_half_channel { | ||
| 152 | unsigned state; | ||
| 153 | unsigned char fDSR; | ||
| 154 | unsigned char fCTS; | ||
| 155 | unsigned char fCD; | ||
| 156 | unsigned char fRI; | ||
| 157 | unsigned char fHEAD; | ||
| 158 | unsigned char fTAIL; | ||
| 159 | unsigned char fSTATE; | ||
| 160 | unsigned char fUNUSED; | ||
| 161 | unsigned tail; | ||
| 162 | unsigned head; | ||
| 163 | unsigned char data[SMD_BUF_SIZE]; | ||
| 164 | }; | ||
| 165 | |||
| 166 | struct smd_shared { | ||
| 167 | struct smd_half_channel ch0; | ||
| 168 | struct smd_half_channel ch1; | ||
| 169 | }; | ||
| 170 | |||
| 171 | struct smd_channel { | ||
| 172 | volatile struct smd_half_channel *send; | ||
| 173 | volatile struct smd_half_channel *recv; | ||
| 174 | struct list_head ch_list; | ||
| 175 | |||
| 176 | unsigned current_packet; | ||
| 177 | unsigned n; | ||
| 178 | void *priv; | ||
| 179 | void (*notify)(void *priv, unsigned flags); | ||
| 180 | |||
| 181 | int (*read)(smd_channel_t *ch, void *data, int len); | ||
| 182 | int (*write)(smd_channel_t *ch, const void *data, int len); | ||
| 183 | int (*read_avail)(smd_channel_t *ch); | ||
| 184 | int (*write_avail)(smd_channel_t *ch); | ||
| 185 | |||
| 186 | void (*update_state)(smd_channel_t *ch); | ||
| 187 | unsigned last_state; | ||
| 188 | |||
| 189 | char name[32]; | ||
| 190 | struct platform_device pdev; | ||
| 191 | }; | ||
| 192 | |||
| 193 | static LIST_HEAD(smd_ch_closed_list); | ||
| 194 | static LIST_HEAD(smd_ch_list); | ||
| 195 | |||
| 196 | static unsigned char smd_ch_allocated[64]; | ||
| 197 | static struct work_struct probe_work; | ||
| 198 | |||
| 199 | static void smd_alloc_channel(const char *name, uint32_t cid, uint32_t type); | ||
| 200 | |||
| 201 | static void smd_channel_probe_worker(struct work_struct *work) | ||
| 202 | { | ||
| 203 | struct smd_alloc_elm *shared; | ||
| 204 | unsigned n; | ||
| 205 | |||
| 206 | shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64); | ||
| 207 | |||
| 208 | for (n = 0; n < 64; n++) { | ||
| 209 | if (smd_ch_allocated[n]) | ||
| 210 | continue; | ||
| 211 | if (!shared[n].ref_count) | ||
| 212 | continue; | ||
| 213 | if (!shared[n].name[0]) | ||
| 214 | continue; | ||
| 215 | smd_alloc_channel(shared[n].name, | ||
| 216 | shared[n].cid, | ||
| 217 | shared[n].ctype); | ||
| 218 | smd_ch_allocated[n] = 1; | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | static char *chstate(unsigned n) | ||
| 223 | { | ||
| 224 | switch (n) { | ||
| 225 | case SMD_SS_CLOSED: | ||
| 226 | return "CLOSED"; | ||
| 227 | case SMD_SS_OPENING: | ||
| 228 | return "OPENING"; | ||
| 229 | case SMD_SS_OPENED: | ||
| 230 | return "OPENED"; | ||
| 231 | case SMD_SS_FLUSHING: | ||
| 232 | return "FLUSHING"; | ||
| 233 | case SMD_SS_CLOSING: | ||
| 234 | return "CLOSING"; | ||
| 235 | case SMD_SS_RESET: | ||
| 236 | return "RESET"; | ||
| 237 | case SMD_SS_RESET_OPENING: | ||
| 238 | return "ROPENING"; | ||
| 239 | default: | ||
| 240 | return "UNKNOWN"; | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | /* how many bytes are available for reading */ | ||
| 245 | static int smd_stream_read_avail(struct smd_channel *ch) | ||
| 246 | { | ||
| 247 | return (ch->recv->head - ch->recv->tail) & (SMD_BUF_SIZE - 1); | ||
| 248 | } | ||
| 249 | |||
| 250 | /* how many bytes we are free to write */ | ||
| 251 | static int smd_stream_write_avail(struct smd_channel *ch) | ||
| 252 | { | ||
| 253 | return (SMD_BUF_SIZE - 1) - | ||
| 254 | ((ch->send->head - ch->send->tail) & (SMD_BUF_SIZE - 1)); | ||
| 255 | } | ||
| 256 | |||
| 257 | static int smd_packet_read_avail(struct smd_channel *ch) | ||
| 258 | { | ||
| 259 | if (ch->current_packet) { | ||
| 260 | int n = smd_stream_read_avail(ch); | ||
| 261 | if (n > ch->current_packet) | ||
| 262 | n = ch->current_packet; | ||
| 263 | return n; | ||
| 264 | } else { | ||
| 265 | return 0; | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | static int smd_packet_write_avail(struct smd_channel *ch) | ||
| 270 | { | ||
| 271 | int n = smd_stream_write_avail(ch); | ||
| 272 | return n > SMD_HEADER_SIZE ? n - SMD_HEADER_SIZE : 0; | ||
| 273 | } | ||
| 274 | |||
| 275 | static int ch_is_open(struct smd_channel *ch) | ||
| 276 | { | ||
| 277 | return (ch->recv->state == SMD_SS_OPENED) && | ||
| 278 | (ch->send->state == SMD_SS_OPENED); | ||
| 279 | } | ||
| 280 | |||
| 281 | /* provide a pointer and length to readable data in the fifo */ | ||
| 282 | static unsigned ch_read_buffer(struct smd_channel *ch, void **ptr) | ||
| 283 | { | ||
| 284 | unsigned head = ch->recv->head; | ||
| 285 | unsigned tail = ch->recv->tail; | ||
| 286 | *ptr = (void *) (ch->recv->data + tail); | ||
| 287 | |||
| 288 | if (tail <= head) | ||
| 289 | return head - tail; | ||
| 290 | else | ||
| 291 | return SMD_BUF_SIZE - tail; | ||
| 292 | } | ||
| 293 | |||
| 294 | /* advance the fifo read pointer after data from ch_read_buffer is consumed */ | ||
| 295 | static void ch_read_done(struct smd_channel *ch, unsigned count) | ||
| 296 | { | ||
| 297 | BUG_ON(count > smd_stream_read_avail(ch)); | ||
| 298 | ch->recv->tail = (ch->recv->tail + count) & (SMD_BUF_SIZE - 1); | ||
| 299 | ch->recv->fTAIL = 1; | ||
| 300 | } | ||
| 301 | |||
| 302 | /* basic read interface to ch_read_{buffer,done} used | ||
| 303 | ** by smd_*_read() and update_packet_state() | ||
| 304 | ** will read-and-discard if the _data pointer is null | ||
| 305 | */ | ||
| 306 | static int ch_read(struct smd_channel *ch, void *_data, int len) | ||
| 307 | { | ||
| 308 | void *ptr; | ||
| 309 | unsigned n; | ||
| 310 | unsigned char *data = _data; | ||
| 311 | int orig_len = len; | ||
| 312 | |||
| 313 | while (len > 0) { | ||
| 314 | n = ch_read_buffer(ch, &ptr); | ||
| 315 | if (n == 0) | ||
| 316 | break; | ||
| 317 | |||
| 318 | if (n > len) | ||
| 319 | n = len; | ||
| 320 | if (_data) | ||
| 321 | memcpy(data, ptr, n); | ||
| 322 | |||
| 323 | data += n; | ||
| 324 | len -= n; | ||
| 325 | ch_read_done(ch, n); | ||
| 326 | } | ||
| 327 | |||
| 328 | return orig_len - len; | ||
| 329 | } | ||
| 330 | |||
| 331 | static void update_stream_state(struct smd_channel *ch) | ||
| 332 | { | ||
| 333 | /* streams have no special state requiring updating */ | ||
| 334 | } | ||
| 335 | |||
| 336 | static void update_packet_state(struct smd_channel *ch) | ||
| 337 | { | ||
| 338 | unsigned hdr[5]; | ||
| 339 | int r; | ||
| 340 | |||
| 341 | /* can't do anything if we're in the middle of a packet */ | ||
| 342 | if (ch->current_packet != 0) | ||
| 343 | return; | ||
| 344 | |||
| 345 | /* don't bother unless we can get the full header */ | ||
| 346 | if (smd_stream_read_avail(ch) < SMD_HEADER_SIZE) | ||
| 347 | return; | ||
| 348 | |||
| 349 | r = ch_read(ch, hdr, SMD_HEADER_SIZE); | ||
| 350 | BUG_ON(r != SMD_HEADER_SIZE); | ||
| 351 | |||
| 352 | ch->current_packet = hdr[0]; | ||
| 353 | } | ||
| 354 | |||
| 355 | /* provide a pointer and length to next free space in the fifo */ | ||
| 356 | static unsigned ch_write_buffer(struct smd_channel *ch, void **ptr) | ||
| 357 | { | ||
| 358 | unsigned head = ch->send->head; | ||
| 359 | unsigned tail = ch->send->tail; | ||
| 360 | *ptr = (void *) (ch->send->data + head); | ||
| 361 | |||
| 362 | if (head < tail) { | ||
| 363 | return tail - head - 1; | ||
| 364 | } else { | ||
| 365 | if (tail == 0) | ||
| 366 | return SMD_BUF_SIZE - head - 1; | ||
| 367 | else | ||
| 368 | return SMD_BUF_SIZE - head; | ||
| 369 | } | ||
| 370 | } | ||
| 371 | |||
| 372 | /* advace the fifo write pointer after freespace | ||
| 373 | * from ch_write_buffer is filled | ||
| 374 | */ | ||
| 375 | static void ch_write_done(struct smd_channel *ch, unsigned count) | ||
| 376 | { | ||
| 377 | BUG_ON(count > smd_stream_write_avail(ch)); | ||
| 378 | ch->send->head = (ch->send->head + count) & (SMD_BUF_SIZE - 1); | ||
| 379 | ch->send->fHEAD = 1; | ||
| 380 | } | ||
| 381 | |||
| 382 | static void hc_set_state(volatile struct smd_half_channel *hc, unsigned n) | ||
| 383 | { | ||
| 384 | if (n == SMD_SS_OPENED) { | ||
| 385 | hc->fDSR = 1; | ||
| 386 | hc->fCTS = 1; | ||
| 387 | hc->fCD = 1; | ||
| 388 | } else { | ||
| 389 | hc->fDSR = 0; | ||
| 390 | hc->fCTS = 0; | ||
| 391 | hc->fCD = 0; | ||
| 392 | } | ||
| 393 | hc->state = n; | ||
| 394 | hc->fSTATE = 1; | ||
| 395 | notify_other_smd(); | ||
| 396 | } | ||
| 397 | |||
| 398 | static void do_smd_probe(void) | ||
| 399 | { | ||
| 400 | struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; | ||
| 401 | if (shared->heap_info.free_offset != last_heap_free) { | ||
| 402 | last_heap_free = shared->heap_info.free_offset; | ||
| 403 | schedule_work(&probe_work); | ||
| 404 | } | ||
| 405 | } | ||
| 406 | |||
| 407 | static void smd_state_change(struct smd_channel *ch, | ||
| 408 | unsigned last, unsigned next) | ||
| 409 | { | ||
| 410 | ch->last_state = next; | ||
| 411 | |||
| 412 | pr_info("SMD: ch %d %s -> %s\n", ch->n, | ||
| 413 | chstate(last), chstate(next)); | ||
| 414 | |||
| 415 | switch (next) { | ||
| 416 | case SMD_SS_OPENING: | ||
| 417 | ch->recv->tail = 0; | ||
| 418 | case SMD_SS_OPENED: | ||
| 419 | if (ch->send->state != SMD_SS_OPENED) | ||
| 420 | hc_set_state(ch->send, SMD_SS_OPENED); | ||
| 421 | ch->notify(ch->priv, SMD_EVENT_OPEN); | ||
| 422 | break; | ||
| 423 | case SMD_SS_FLUSHING: | ||
| 424 | case SMD_SS_RESET: | ||
| 425 | /* we should force them to close? */ | ||
| 426 | default: | ||
| 427 | ch->notify(ch->priv, SMD_EVENT_CLOSE); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | static irqreturn_t smd_irq_handler(int irq, void *data) | ||
| 432 | { | ||
| 433 | unsigned long flags; | ||
| 434 | struct smd_channel *ch; | ||
| 435 | int do_notify = 0; | ||
| 436 | unsigned ch_flags; | ||
| 437 | unsigned tmp; | ||
| 438 | |||
| 439 | spin_lock_irqsave(&smd_lock, flags); | ||
| 440 | list_for_each_entry(ch, &smd_ch_list, ch_list) { | ||
| 441 | ch_flags = 0; | ||
| 442 | if (ch_is_open(ch)) { | ||
| 443 | if (ch->recv->fHEAD) { | ||
| 444 | ch->recv->fHEAD = 0; | ||
| 445 | ch_flags |= 1; | ||
| 446 | do_notify |= 1; | ||
| 447 | } | ||
| 448 | if (ch->recv->fTAIL) { | ||
| 449 | ch->recv->fTAIL = 0; | ||
| 450 | ch_flags |= 2; | ||
| 451 | do_notify |= 1; | ||
| 452 | } | ||
| 453 | if (ch->recv->fSTATE) { | ||
| 454 | ch->recv->fSTATE = 0; | ||
| 455 | ch_flags |= 4; | ||
| 456 | do_notify |= 1; | ||
| 457 | } | ||
| 458 | } | ||
| 459 | tmp = ch->recv->state; | ||
| 460 | if (tmp != ch->last_state) | ||
| 461 | smd_state_change(ch, ch->last_state, tmp); | ||
| 462 | if (ch_flags) { | ||
| 463 | ch->update_state(ch); | ||
| 464 | ch->notify(ch->priv, SMD_EVENT_DATA); | ||
| 465 | } | ||
| 466 | } | ||
| 467 | if (do_notify) | ||
| 468 | notify_other_smd(); | ||
| 469 | spin_unlock_irqrestore(&smd_lock, flags); | ||
| 470 | do_smd_probe(); | ||
| 471 | return IRQ_HANDLED; | ||
| 472 | } | ||
| 473 | |||
| 474 | static void smd_fake_irq_handler(unsigned long arg) | ||
| 475 | { | ||
| 476 | smd_irq_handler(0, NULL); | ||
| 477 | } | ||
| 478 | |||
| 479 | static DECLARE_TASKLET(smd_fake_irq_tasklet, smd_fake_irq_handler, 0); | ||
| 480 | |||
| 481 | void smd_sleep_exit(void) | ||
| 482 | { | ||
| 483 | unsigned long flags; | ||
| 484 | struct smd_channel *ch; | ||
| 485 | unsigned tmp; | ||
| 486 | int need_int = 0; | ||
| 487 | |||
| 488 | spin_lock_irqsave(&smd_lock, flags); | ||
| 489 | list_for_each_entry(ch, &smd_ch_list, ch_list) { | ||
| 490 | if (ch_is_open(ch)) { | ||
| 491 | if (ch->recv->fHEAD) { | ||
| 492 | if (msm_smd_debug_mask & MSM_SMD_DEBUG) | ||
| 493 | pr_info("smd_sleep_exit ch %d fHEAD " | ||
| 494 | "%x %x %x\n", | ||
| 495 | ch->n, ch->recv->fHEAD, | ||
| 496 | ch->recv->head, ch->recv->tail); | ||
| 497 | need_int = 1; | ||
| 498 | break; | ||
| 499 | } | ||
| 500 | if (ch->recv->fTAIL) { | ||
| 501 | if (msm_smd_debug_mask & MSM_SMD_DEBUG) | ||
| 502 | pr_info("smd_sleep_exit ch %d fTAIL " | ||
| 503 | "%x %x %x\n", | ||
| 504 | ch->n, ch->recv->fTAIL, | ||
| 505 | ch->send->head, ch->send->tail); | ||
| 506 | need_int = 1; | ||
| 507 | break; | ||
| 508 | } | ||
| 509 | if (ch->recv->fSTATE) { | ||
| 510 | if (msm_smd_debug_mask & MSM_SMD_DEBUG) | ||
| 511 | pr_info("smd_sleep_exit ch %d fSTATE %x" | ||
| 512 | "\n", ch->n, ch->recv->fSTATE); | ||
| 513 | need_int = 1; | ||
| 514 | break; | ||
| 515 | } | ||
| 516 | tmp = ch->recv->state; | ||
| 517 | if (tmp != ch->last_state) { | ||
| 518 | if (msm_smd_debug_mask & MSM_SMD_DEBUG) | ||
| 519 | pr_info("smd_sleep_exit ch %d " | ||
| 520 | "state %x != %x\n", | ||
| 521 | ch->n, tmp, ch->last_state); | ||
| 522 | need_int = 1; | ||
| 523 | break; | ||
| 524 | } | ||
| 525 | } | ||
| 526 | } | ||
| 527 | spin_unlock_irqrestore(&smd_lock, flags); | ||
| 528 | do_smd_probe(); | ||
| 529 | if (need_int) { | ||
| 530 | if (msm_smd_debug_mask & MSM_SMD_DEBUG) | ||
| 531 | pr_info("smd_sleep_exit need interrupt\n"); | ||
| 532 | tasklet_schedule(&smd_fake_irq_tasklet); | ||
| 533 | } | ||
| 534 | } | ||
| 535 | |||
| 536 | |||
| 537 | void smd_kick(smd_channel_t *ch) | ||
| 538 | { | ||
| 539 | unsigned long flags; | ||
| 540 | unsigned tmp; | ||
| 541 | |||
| 542 | spin_lock_irqsave(&smd_lock, flags); | ||
| 543 | ch->update_state(ch); | ||
| 544 | tmp = ch->recv->state; | ||
| 545 | if (tmp != ch->last_state) { | ||
| 546 | ch->last_state = tmp; | ||
| 547 | if (tmp == SMD_SS_OPENED) | ||
| 548 | ch->notify(ch->priv, SMD_EVENT_OPEN); | ||
| 549 | else | ||
| 550 | ch->notify(ch->priv, SMD_EVENT_CLOSE); | ||
| 551 | } | ||
| 552 | ch->notify(ch->priv, SMD_EVENT_DATA); | ||
| 553 | notify_other_smd(); | ||
| 554 | spin_unlock_irqrestore(&smd_lock, flags); | ||
| 555 | } | ||
| 556 | |||
| 557 | static int smd_is_packet(int chn) | ||
| 558 | { | ||
| 559 | if ((chn > 4) || (chn == 1)) | ||
| 560 | return 1; | ||
| 561 | else | ||
| 562 | return 0; | ||
| 563 | } | ||
| 564 | |||
| 565 | static int smd_stream_write(smd_channel_t *ch, const void *_data, int len) | ||
| 566 | { | ||
| 567 | void *ptr; | ||
| 568 | const unsigned char *buf = _data; | ||
| 569 | unsigned xfer; | ||
| 570 | int orig_len = len; | ||
| 571 | |||
| 572 | if (len < 0) | ||
| 573 | return -EINVAL; | ||
| 574 | |||
| 575 | while ((xfer = ch_write_buffer(ch, &ptr)) != 0) { | ||
| 576 | if (!ch_is_open(ch)) | ||
| 577 | break; | ||
| 578 | if (xfer > len) | ||
| 579 | xfer = len; | ||
| 580 | memcpy(ptr, buf, xfer); | ||
| 581 | ch_write_done(ch, xfer); | ||
| 582 | len -= xfer; | ||
| 583 | buf += xfer; | ||
| 584 | if (len == 0) | ||
| 585 | break; | ||
| 586 | } | ||
| 587 | |||
| 588 | notify_other_smd(); | ||
| 589 | |||
| 590 | return orig_len - len; | ||
| 591 | } | ||
| 592 | |||
| 593 | static int smd_packet_write(smd_channel_t *ch, const void *_data, int len) | ||
| 594 | { | ||
| 595 | unsigned hdr[5]; | ||
| 596 | |||
| 597 | if (len < 0) | ||
| 598 | return -EINVAL; | ||
| 599 | |||
| 600 | if (smd_stream_write_avail(ch) < (len + SMD_HEADER_SIZE)) | ||
| 601 | return -ENOMEM; | ||
| 602 | |||
| 603 | hdr[0] = len; | ||
| 604 | hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0; | ||
| 605 | |||
| 606 | smd_stream_write(ch, hdr, sizeof(hdr)); | ||
| 607 | smd_stream_write(ch, _data, len); | ||
| 608 | |||
| 609 | return len; | ||
| 610 | } | ||
| 611 | |||
| 612 | static int smd_stream_read(smd_channel_t *ch, void *data, int len) | ||
| 613 | { | ||
| 614 | int r; | ||
| 615 | |||
| 616 | if (len < 0) | ||
| 617 | return -EINVAL; | ||
| 618 | |||
| 619 | r = ch_read(ch, data, len); | ||
| 620 | if (r > 0) | ||
| 621 | notify_other_smd(); | ||
| 622 | |||
| 623 | return r; | ||
| 624 | } | ||
| 625 | |||
| 626 | static int smd_packet_read(smd_channel_t *ch, void *data, int len) | ||
| 627 | { | ||
| 628 | unsigned long flags; | ||
| 629 | int r; | ||
| 630 | |||
| 631 | if (len < 0) | ||
| 632 | return -EINVAL; | ||
| 633 | |||
| 634 | if (len > ch->current_packet) | ||
| 635 | len = ch->current_packet; | ||
| 636 | |||
| 637 | r = ch_read(ch, data, len); | ||
| 638 | if (r > 0) | ||
| 639 | notify_other_smd(); | ||
| 640 | |||
| 641 | spin_lock_irqsave(&smd_lock, flags); | ||
| 642 | ch->current_packet -= r; | ||
| 643 | update_packet_state(ch); | ||
| 644 | spin_unlock_irqrestore(&smd_lock, flags); | ||
| 645 | |||
| 646 | return r; | ||
| 647 | } | ||
| 648 | |||
| 649 | static void smd_alloc_channel(const char *name, uint32_t cid, uint32_t type) | ||
| 650 | { | ||
| 651 | struct smd_channel *ch; | ||
| 652 | struct smd_shared *shared; | ||
| 653 | |||
| 654 | shared = smem_alloc(ID_SMD_CHANNELS + cid, sizeof(*shared)); | ||
| 655 | if (!shared) { | ||
| 656 | pr_err("smd_alloc_channel() cid %d does not exist\n", cid); | ||
| 657 | return; | ||
| 658 | } | ||
| 659 | |||
| 660 | ch = kzalloc(sizeof(struct smd_channel), GFP_KERNEL); | ||
| 661 | if (ch == 0) { | ||
| 662 | pr_err("smd_alloc_channel() out of memory\n"); | ||
| 663 | return; | ||
| 664 | } | ||
| 665 | |||
| 666 | ch->send = &shared->ch0; | ||
| 667 | ch->recv = &shared->ch1; | ||
| 668 | ch->n = cid; | ||
| 669 | |||
| 670 | if (smd_is_packet(cid)) { | ||
| 671 | ch->read = smd_packet_read; | ||
| 672 | ch->write = smd_packet_write; | ||
| 673 | ch->read_avail = smd_packet_read_avail; | ||
| 674 | ch->write_avail = smd_packet_write_avail; | ||
| 675 | ch->update_state = update_packet_state; | ||
| 676 | } else { | ||
| 677 | ch->read = smd_stream_read; | ||
| 678 | ch->write = smd_stream_write; | ||
| 679 | ch->read_avail = smd_stream_read_avail; | ||
| 680 | ch->write_avail = smd_stream_write_avail; | ||
| 681 | ch->update_state = update_stream_state; | ||
| 682 | } | ||
| 683 | |||
| 684 | memcpy(ch->name, "SMD_", 4); | ||
| 685 | memcpy(ch->name + 4, name, 20); | ||
| 686 | ch->name[23] = 0; | ||
| 687 | ch->pdev.name = ch->name; | ||
| 688 | ch->pdev.id = -1; | ||
| 689 | |||
| 690 | pr_info("smd_alloc_channel() '%s' cid=%d, shared=%p\n", | ||
| 691 | ch->name, ch->n, shared); | ||
| 692 | |||
| 693 | mutex_lock(&smd_creation_mutex); | ||
| 694 | list_add(&ch->ch_list, &smd_ch_closed_list); | ||
| 695 | mutex_unlock(&smd_creation_mutex); | ||
| 696 | |||
| 697 | platform_device_register(&ch->pdev); | ||
| 698 | } | ||
| 699 | |||
| 700 | static void do_nothing_notify(void *priv, unsigned flags) | ||
| 701 | { | ||
| 702 | } | ||
| 703 | |||
| 704 | struct smd_channel *smd_get_channel(const char *name) | ||
| 705 | { | ||
| 706 | struct smd_channel *ch; | ||
| 707 | |||
| 708 | mutex_lock(&smd_creation_mutex); | ||
| 709 | list_for_each_entry(ch, &smd_ch_closed_list, ch_list) { | ||
| 710 | if (!strcmp(name, ch->name)) { | ||
| 711 | list_del(&ch->ch_list); | ||
| 712 | mutex_unlock(&smd_creation_mutex); | ||
| 713 | return ch; | ||
| 714 | } | ||
| 715 | } | ||
| 716 | mutex_unlock(&smd_creation_mutex); | ||
| 717 | |||
| 718 | return NULL; | ||
| 719 | } | ||
| 720 | |||
| 721 | int smd_open(const char *name, smd_channel_t **_ch, | ||
| 722 | void *priv, void (*notify)(void *, unsigned)) | ||
| 723 | { | ||
| 724 | struct smd_channel *ch; | ||
| 725 | unsigned long flags; | ||
| 726 | |||
| 727 | if (smd_initialized == 0) { | ||
| 728 | pr_info("smd_open() before smd_init()\n"); | ||
| 729 | return -ENODEV; | ||
| 730 | } | ||
| 731 | |||
| 732 | ch = smd_get_channel(name); | ||
| 733 | if (!ch) | ||
| 734 | return -ENODEV; | ||
| 735 | |||
| 736 | if (notify == 0) | ||
| 737 | notify = do_nothing_notify; | ||
| 738 | |||
| 739 | ch->notify = notify; | ||
| 740 | ch->current_packet = 0; | ||
| 741 | ch->last_state = SMD_SS_CLOSED; | ||
| 742 | ch->priv = priv; | ||
| 743 | |||
| 744 | *_ch = ch; | ||
| 745 | |||
| 746 | spin_lock_irqsave(&smd_lock, flags); | ||
| 747 | list_add(&ch->ch_list, &smd_ch_list); | ||
| 748 | |||
| 749 | /* If the remote side is CLOSING, we need to get it to | ||
| 750 | * move to OPENING (which we'll do by moving from CLOSED to | ||
| 751 | * OPENING) and then get it to move from OPENING to | ||
| 752 | * OPENED (by doing the same state change ourselves). | ||
| 753 | * | ||
| 754 | * Otherwise, it should be OPENING and we can move directly | ||
| 755 | * to OPENED so that it will follow. | ||
| 756 | */ | ||
| 757 | if (ch->recv->state == SMD_SS_CLOSING) { | ||
| 758 | ch->send->head = 0; | ||
| 759 | hc_set_state(ch->send, SMD_SS_OPENING); | ||
| 760 | } else { | ||
| 761 | hc_set_state(ch->send, SMD_SS_OPENED); | ||
| 762 | } | ||
| 763 | spin_unlock_irqrestore(&smd_lock, flags); | ||
| 764 | smd_kick(ch); | ||
| 765 | |||
| 766 | return 0; | ||
| 767 | } | ||
| 768 | |||
| 769 | int smd_close(smd_channel_t *ch) | ||
| 770 | { | ||
| 771 | unsigned long flags; | ||
| 772 | |||
| 773 | pr_info("smd_close(%p)\n", ch); | ||
| 774 | |||
| 775 | if (ch == 0) | ||
| 776 | return -1; | ||
| 777 | |||
| 778 | spin_lock_irqsave(&smd_lock, flags); | ||
| 779 | ch->notify = do_nothing_notify; | ||
| 780 | list_del(&ch->ch_list); | ||
| 781 | hc_set_state(ch->send, SMD_SS_CLOSED); | ||
| 782 | spin_unlock_irqrestore(&smd_lock, flags); | ||
| 783 | |||
| 784 | mutex_lock(&smd_creation_mutex); | ||
| 785 | list_add(&ch->ch_list, &smd_ch_closed_list); | ||
| 786 | mutex_unlock(&smd_creation_mutex); | ||
| 787 | |||
| 788 | return 0; | ||
| 789 | } | ||
| 790 | |||
| 791 | int smd_read(smd_channel_t *ch, void *data, int len) | ||
| 792 | { | ||
| 793 | return ch->read(ch, data, len); | ||
| 794 | } | ||
| 795 | |||
| 796 | int smd_write(smd_channel_t *ch, const void *data, int len) | ||
| 797 | { | ||
| 798 | return ch->write(ch, data, len); | ||
| 799 | } | ||
| 800 | |||
| 801 | int smd_read_avail(smd_channel_t *ch) | ||
| 802 | { | ||
| 803 | return ch->read_avail(ch); | ||
| 804 | } | ||
| 805 | |||
| 806 | int smd_write_avail(smd_channel_t *ch) | ||
| 807 | { | ||
| 808 | return ch->write_avail(ch); | ||
| 809 | } | ||
| 810 | |||
| 811 | int smd_wait_until_readable(smd_channel_t *ch, int bytes) | ||
| 812 | { | ||
| 813 | return -1; | ||
| 814 | } | ||
| 815 | |||
| 816 | int smd_wait_until_writable(smd_channel_t *ch, int bytes) | ||
| 817 | { | ||
| 818 | return -1; | ||
| 819 | } | ||
| 820 | |||
| 821 | int smd_cur_packet_size(smd_channel_t *ch) | ||
| 822 | { | ||
| 823 | return ch->current_packet; | ||
| 824 | } | ||
| 825 | |||
| 826 | |||
| 827 | /* ------------------------------------------------------------------------- */ | ||
| 828 | |||
| 829 | void *smem_alloc(unsigned id, unsigned size) | ||
| 830 | { | ||
| 831 | return smem_find(id, size); | ||
| 832 | } | ||
| 833 | |||
| 834 | static void *_smem_find(unsigned id, unsigned *size) | ||
| 835 | { | ||
| 836 | struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; | ||
| 837 | struct smem_heap_entry *toc = shared->heap_toc; | ||
| 838 | |||
| 839 | if (id >= SMEM_NUM_ITEMS) | ||
| 840 | return 0; | ||
| 841 | |||
| 842 | if (toc[id].allocated) { | ||
| 843 | *size = toc[id].size; | ||
| 844 | return (void *) (MSM_SHARED_RAM_BASE + toc[id].offset); | ||
| 845 | } | ||
| 846 | |||
| 847 | return 0; | ||
| 848 | } | ||
| 849 | |||
| 850 | void *smem_find(unsigned id, unsigned size_in) | ||
| 851 | { | ||
| 852 | unsigned size; | ||
| 853 | void *ptr; | ||
| 854 | |||
| 855 | ptr = _smem_find(id, &size); | ||
| 856 | if (!ptr) | ||
| 857 | return 0; | ||
| 858 | |||
| 859 | size_in = ALIGN(size_in, 8); | ||
| 860 | if (size_in != size) { | ||
| 861 | pr_err("smem_find(%d, %d): wrong size %d\n", | ||
| 862 | id, size_in, size); | ||
| 863 | return 0; | ||
| 864 | } | ||
| 865 | |||
| 866 | return ptr; | ||
| 867 | } | ||
| 868 | |||
| 869 | static irqreturn_t smsm_irq_handler(int irq, void *data) | ||
| 870 | { | ||
| 871 | unsigned long flags; | ||
| 872 | struct smsm_shared *smsm; | ||
| 873 | |||
| 874 | spin_lock_irqsave(&smem_lock, flags); | ||
| 875 | smsm = smem_alloc(ID_SHARED_STATE, | ||
| 876 | 2 * sizeof(struct smsm_shared)); | ||
| 877 | |||
| 878 | if (smsm == 0) { | ||
| 879 | pr_info("<SM NO STATE>\n"); | ||
| 880 | } else { | ||
| 881 | unsigned apps = smsm[0].state; | ||
| 882 | unsigned modm = smsm[1].state; | ||
| 883 | |||
| 884 | if (msm_smd_debug_mask & MSM_SMSM_DEBUG) | ||
| 885 | pr_info("<SM %08x %08x>\n", apps, modm); | ||
| 886 | if (modm & SMSM_RESET) { | ||
| 887 | handle_modem_crash(); | ||
| 888 | } else { | ||
| 889 | apps |= SMSM_INIT; | ||
| 890 | if (modm & SMSM_SMDINIT) | ||
| 891 | apps |= SMSM_SMDINIT; | ||
| 892 | if (modm & SMSM_RPCINIT) | ||
| 893 | apps |= SMSM_RPCINIT; | ||
| 894 | } | ||
| 895 | |||
| 896 | if (smsm[0].state != apps) { | ||
| 897 | if (msm_smd_debug_mask & MSM_SMSM_DEBUG) | ||
| 898 | pr_info("<SM %08x NOTIFY>\n", apps); | ||
| 899 | smsm[0].state = apps; | ||
| 900 | do_smd_probe(); | ||
| 901 | notify_other_smsm(); | ||
| 902 | } | ||
| 903 | } | ||
| 904 | spin_unlock_irqrestore(&smem_lock, flags); | ||
| 905 | return IRQ_HANDLED; | ||
| 906 | } | ||
| 907 | |||
| 908 | int smsm_change_state(uint32_t clear_mask, uint32_t set_mask) | ||
| 909 | { | ||
| 910 | unsigned long flags; | ||
| 911 | struct smsm_shared *smsm; | ||
| 912 | |||
| 913 | spin_lock_irqsave(&smem_lock, flags); | ||
| 914 | |||
| 915 | smsm = smem_alloc(ID_SHARED_STATE, | ||
| 916 | 2 * sizeof(struct smsm_shared)); | ||
| 917 | |||
| 918 | if (smsm) { | ||
| 919 | if (smsm[1].state & SMSM_RESET) | ||
| 920 | handle_modem_crash(); | ||
| 921 | smsm[0].state = (smsm[0].state & ~clear_mask) | set_mask; | ||
| 922 | if (msm_smd_debug_mask & MSM_SMSM_DEBUG) | ||
| 923 | pr_info("smsm_change_state %x\n", | ||
| 924 | smsm[0].state); | ||
| 925 | notify_other_smsm(); | ||
| 926 | } | ||
| 927 | |||
| 928 | spin_unlock_irqrestore(&smem_lock, flags); | ||
| 929 | |||
| 930 | if (smsm == NULL) { | ||
| 931 | pr_err("smsm_change_state <SM NO STATE>\n"); | ||
| 932 | return -EIO; | ||
| 933 | } | ||
| 934 | return 0; | ||
| 935 | } | ||
| 936 | |||
| 937 | uint32_t smsm_get_state(void) | ||
| 938 | { | ||
| 939 | unsigned long flags; | ||
| 940 | struct smsm_shared *smsm; | ||
| 941 | uint32_t rv; | ||
| 942 | |||
| 943 | spin_lock_irqsave(&smem_lock, flags); | ||
| 944 | |||
| 945 | smsm = smem_alloc(ID_SHARED_STATE, | ||
| 946 | 2 * sizeof(struct smsm_shared)); | ||
| 947 | |||
| 948 | if (smsm) | ||
| 949 | rv = smsm[1].state; | ||
| 950 | else | ||
| 951 | rv = 0; | ||
| 952 | |||
| 953 | if (rv & SMSM_RESET) | ||
| 954 | handle_modem_crash(); | ||
| 955 | |||
| 956 | spin_unlock_irqrestore(&smem_lock, flags); | ||
| 957 | |||
| 958 | if (smsm == NULL) | ||
| 959 | pr_err("smsm_get_state <SM NO STATE>\n"); | ||
| 960 | return rv; | ||
| 961 | } | ||
| 962 | |||
| 963 | int smsm_set_sleep_duration(uint32_t delay) | ||
| 964 | { | ||
| 965 | uint32_t *ptr; | ||
| 966 | |||
| 967 | ptr = smem_alloc(SMEM_SMSM_SLEEP_DELAY, sizeof(*ptr)); | ||
| 968 | if (ptr == NULL) { | ||
| 969 | pr_err("smsm_set_sleep_duration <SM NO SLEEP_DELAY>\n"); | ||
| 970 | return -EIO; | ||
| 971 | } | ||
| 972 | if (msm_smd_debug_mask & MSM_SMSM_DEBUG) | ||
| 973 | pr_info("smsm_set_sleep_duration %d -> %d\n", | ||
| 974 | *ptr, delay); | ||
| 975 | *ptr = delay; | ||
| 976 | return 0; | ||
| 977 | } | ||
| 978 | |||
| 979 | int smsm_set_interrupt_info(struct smsm_interrupt_info *info) | ||
| 980 | { | ||
| 981 | struct smsm_interrupt_info *ptr; | ||
| 982 | |||
| 983 | ptr = smem_alloc(SMEM_SMSM_INT_INFO, sizeof(*ptr)); | ||
| 984 | if (ptr == NULL) { | ||
| 985 | pr_err("smsm_set_sleep_duration <SM NO INT_INFO>\n"); | ||
| 986 | return -EIO; | ||
| 987 | } | ||
| 988 | if (msm_smd_debug_mask & MSM_SMSM_DEBUG) | ||
| 989 | pr_info("smsm_set_interrupt_info %x %x -> %x %x\n", | ||
| 990 | ptr->aArm_en_mask, ptr->aArm_interrupts_pending, | ||
| 991 | info->aArm_en_mask, info->aArm_interrupts_pending); | ||
| 992 | *ptr = *info; | ||
| 993 | return 0; | ||
| 994 | } | ||
| 995 | |||
| 996 | #define MAX_NUM_SLEEP_CLIENTS 64 | ||
| 997 | #define MAX_SLEEP_NAME_LEN 8 | ||
| 998 | |||
| 999 | #define NUM_GPIO_INT_REGISTERS 6 | ||
| 1000 | #define GPIO_SMEM_NUM_GROUPS 2 | ||
| 1001 | #define GPIO_SMEM_MAX_PC_INTERRUPTS 8 | ||
| 1002 | |||
| 1003 | struct tramp_gpio_save { | ||
| 1004 | unsigned int enable; | ||
| 1005 | unsigned int detect; | ||
| 1006 | unsigned int polarity; | ||
| 1007 | }; | ||
| 1008 | |||
| 1009 | struct tramp_gpio_smem { | ||
| 1010 | uint16_t num_fired[GPIO_SMEM_NUM_GROUPS]; | ||
| 1011 | uint16_t fired[GPIO_SMEM_NUM_GROUPS][GPIO_SMEM_MAX_PC_INTERRUPTS]; | ||
| 1012 | uint32_t enabled[NUM_GPIO_INT_REGISTERS]; | ||
| 1013 | uint32_t detection[NUM_GPIO_INT_REGISTERS]; | ||
| 1014 | uint32_t polarity[NUM_GPIO_INT_REGISTERS]; | ||
| 1015 | }; | ||
| 1016 | |||
| 1017 | |||
| 1018 | void smsm_print_sleep_info(void) | ||
| 1019 | { | ||
| 1020 | unsigned long flags; | ||
| 1021 | uint32_t *ptr; | ||
| 1022 | struct tramp_gpio_smem *gpio; | ||
| 1023 | struct smsm_interrupt_info *int_info; | ||
| 1024 | |||
| 1025 | |||
| 1026 | spin_lock_irqsave(&smem_lock, flags); | ||
| 1027 | |||
| 1028 | ptr = smem_alloc(SMEM_SMSM_SLEEP_DELAY, sizeof(*ptr)); | ||
| 1029 | if (ptr) | ||
| 1030 | pr_info("SMEM_SMSM_SLEEP_DELAY: %x\n", *ptr); | ||
| 1031 | |||
| 1032 | ptr = smem_alloc(SMEM_SMSM_LIMIT_SLEEP, sizeof(*ptr)); | ||
| 1033 | if (ptr) | ||
| 1034 | pr_info("SMEM_SMSM_LIMIT_SLEEP: %x\n", *ptr); | ||
| 1035 | |||
| 1036 | ptr = smem_alloc(SMEM_SLEEP_POWER_COLLAPSE_DISABLED, sizeof(*ptr)); | ||
| 1037 | if (ptr) | ||
| 1038 | pr_info("SMEM_SLEEP_POWER_COLLAPSE_DISABLED: %x\n", *ptr); | ||
| 1039 | |||
| 1040 | int_info = smem_alloc(SMEM_SMSM_INT_INFO, sizeof(*int_info)); | ||
| 1041 | if (int_info) | ||
| 1042 | pr_info("SMEM_SMSM_INT_INFO %x %x %x\n", | ||
| 1043 | int_info->aArm_en_mask, | ||
| 1044 | int_info->aArm_interrupts_pending, | ||
| 1045 | int_info->aArm_wakeup_reason); | ||
| 1046 | |||
| 1047 | gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*gpio)); | ||
| 1048 | if (gpio) { | ||
| 1049 | int i; | ||
| 1050 | for (i = 0; i < NUM_GPIO_INT_REGISTERS; i++) | ||
| 1051 | pr_info("SMEM_GPIO_INT: %d: e %x d %x p %x\n", | ||
| 1052 | i, gpio->enabled[i], gpio->detection[i], | ||
| 1053 | gpio->polarity[i]); | ||
| 1054 | |||
| 1055 | for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++) | ||
| 1056 | pr_info("SMEM_GPIO_INT: %d: f %d: %d %d...\n", | ||
| 1057 | i, gpio->num_fired[i], gpio->fired[i][0], | ||
| 1058 | gpio->fired[i][1]); | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | spin_unlock_irqrestore(&smem_lock, flags); | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | int smd_core_init(void) | ||
| 1065 | { | ||
| 1066 | int r; | ||
| 1067 | pr_info("smd_core_init()\n"); | ||
| 1068 | |||
| 1069 | r = request_irq(INT_A9_M2A_0, smd_irq_handler, | ||
| 1070 | IRQF_TRIGGER_RISING, "smd_dev", 0); | ||
| 1071 | if (r < 0) | ||
| 1072 | return r; | ||
| 1073 | r = enable_irq_wake(INT_A9_M2A_0); | ||
| 1074 | if (r < 0) | ||
| 1075 | pr_err("smd_core_init: enable_irq_wake failed for A9_M2A_0\n"); | ||
| 1076 | |||
| 1077 | r = request_irq(INT_A9_M2A_5, smsm_irq_handler, | ||
| 1078 | IRQF_TRIGGER_RISING, "smsm_dev", 0); | ||
| 1079 | if (r < 0) { | ||
| 1080 | free_irq(INT_A9_M2A_0, 0); | ||
| 1081 | return r; | ||
| 1082 | } | ||
| 1083 | r = enable_irq_wake(INT_A9_M2A_5); | ||
| 1084 | if (r < 0) | ||
| 1085 | pr_err("smd_core_init: enable_irq_wake failed for A9_M2A_5\n"); | ||
| 1086 | |||
| 1087 | /* we may have missed a signal while booting -- fake | ||
| 1088 | * an interrupt to make sure we process any existing | ||
| 1089 | * state | ||
| 1090 | */ | ||
| 1091 | smsm_irq_handler(0, 0); | ||
| 1092 | |||
| 1093 | pr_info("smd_core_init() done\n"); | ||
| 1094 | |||
| 1095 | return 0; | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | #if defined(CONFIG_DEBUG_FS) | ||
| 1099 | |||
| 1100 | static int dump_ch(char *buf, int max, int n, | ||
| 1101 | struct smd_half_channel *s, | ||
| 1102 | struct smd_half_channel *r) | ||
| 1103 | { | ||
| 1104 | return scnprintf( | ||
| 1105 | buf, max, | ||
| 1106 | "ch%02d:" | ||
| 1107 | " %8s(%04d/%04d) %c%c%c%c%c%c%c <->" | ||
| 1108 | " %8s(%04d/%04d) %c%c%c%c%c%c%c\n", n, | ||
| 1109 | chstate(s->state), s->tail, s->head, | ||
| 1110 | s->fDSR ? 'D' : 'd', | ||
| 1111 | s->fCTS ? 'C' : 'c', | ||
| 1112 | s->fCD ? 'C' : 'c', | ||
| 1113 | s->fRI ? 'I' : 'i', | ||
| 1114 | s->fHEAD ? 'W' : 'w', | ||
| 1115 | s->fTAIL ? 'R' : 'r', | ||
| 1116 | s->fSTATE ? 'S' : 's', | ||
| 1117 | chstate(r->state), r->tail, r->head, | ||
| 1118 | r->fDSR ? 'D' : 'd', | ||
| 1119 | r->fCTS ? 'R' : 'r', | ||
| 1120 | r->fCD ? 'C' : 'c', | ||
| 1121 | r->fRI ? 'I' : 'i', | ||
| 1122 | r->fHEAD ? 'W' : 'w', | ||
| 1123 | r->fTAIL ? 'R' : 'r', | ||
| 1124 | r->fSTATE ? 'S' : 's' | ||
| 1125 | ); | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | static int debug_read_stat(char *buf, int max) | ||
| 1129 | { | ||
| 1130 | struct smsm_shared *smsm; | ||
| 1131 | char *msg; | ||
| 1132 | int i = 0; | ||
| 1133 | |||
| 1134 | smsm = smem_find(ID_SHARED_STATE, | ||
| 1135 | 2 * sizeof(struct smsm_shared)); | ||
| 1136 | |||
| 1137 | msg = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG); | ||
| 1138 | |||
| 1139 | if (smsm) { | ||
| 1140 | if (smsm[1].state & SMSM_RESET) | ||
| 1141 | i += scnprintf(buf + i, max - i, | ||
| 1142 | "smsm: ARM9 HAS CRASHED\n"); | ||
| 1143 | i += scnprintf(buf + i, max - i, "smsm: a9: %08x a11: %08x\n", | ||
| 1144 | smsm[0].state, smsm[1].state); | ||
| 1145 | } else { | ||
| 1146 | i += scnprintf(buf + i, max - i, "smsm: cannot find\n"); | ||
| 1147 | } | ||
| 1148 | if (msg) { | ||
| 1149 | msg[SZ_DIAG_ERR_MSG - 1] = 0; | ||
| 1150 | i += scnprintf(buf + i, max - i, "diag: '%s'\n", msg); | ||
| 1151 | } | ||
| 1152 | return i; | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | static int debug_read_mem(char *buf, int max) | ||
| 1156 | { | ||
| 1157 | unsigned n; | ||
| 1158 | struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; | ||
| 1159 | struct smem_heap_entry *toc = shared->heap_toc; | ||
| 1160 | int i = 0; | ||
| 1161 | |||
| 1162 | i += scnprintf(buf + i, max - i, | ||
| 1163 | "heap: init=%d free=%d remain=%d\n", | ||
| 1164 | shared->heap_info.initialized, | ||
| 1165 | shared->heap_info.free_offset, | ||
| 1166 | shared->heap_info.heap_remaining); | ||
| 1167 | |||
| 1168 | for (n = 0; n < SMEM_NUM_ITEMS; n++) { | ||
| 1169 | if (toc[n].allocated == 0) | ||
| 1170 | continue; | ||
| 1171 | i += scnprintf(buf + i, max - i, | ||
| 1172 | "%04d: offsed %08x size %08x\n", | ||
| 1173 | n, toc[n].offset, toc[n].size); | ||
| 1174 | } | ||
| 1175 | return i; | ||
| 1176 | } | ||
| 1177 | |||
| 1178 | static int debug_read_ch(char *buf, int max) | ||
| 1179 | { | ||
| 1180 | struct smd_shared *shared; | ||
| 1181 | int n, i = 0; | ||
| 1182 | |||
| 1183 | for (n = 0; n < SMD_CHANNELS; n++) { | ||
| 1184 | shared = smem_find(ID_SMD_CHANNELS + n, | ||
| 1185 | sizeof(struct smd_shared)); | ||
| 1186 | if (shared == 0) | ||
| 1187 | continue; | ||
| 1188 | i += dump_ch(buf + i, max - i, n, &shared->ch0, &shared->ch1); | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | return i; | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | static int debug_read_version(char *buf, int max) | ||
| 1195 | { | ||
| 1196 | struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; | ||
| 1197 | unsigned version = shared->version[VERSION_MODEM]; | ||
| 1198 | return sprintf(buf, "%d.%d\n", version >> 16, version & 0xffff); | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | static int debug_read_build_id(char *buf, int max) | ||
| 1202 | { | ||
| 1203 | unsigned size; | ||
| 1204 | void *data; | ||
| 1205 | |||
| 1206 | data = _smem_find(SMEM_HW_SW_BUILD_ID, &size); | ||
| 1207 | if (!data) | ||
| 1208 | return 0; | ||
| 1209 | |||
| 1210 | if (size >= max) | ||
| 1211 | size = max; | ||
| 1212 | memcpy(buf, data, size); | ||
| 1213 | |||
| 1214 | return size; | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | static int debug_read_alloc_tbl(char *buf, int max) | ||
| 1218 | { | ||
| 1219 | struct smd_alloc_elm *shared; | ||
| 1220 | int n, i = 0; | ||
| 1221 | |||
| 1222 | shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64); | ||
| 1223 | |||
| 1224 | for (n = 0; n < 64; n++) { | ||
| 1225 | if (shared[n].ref_count == 0) | ||
| 1226 | continue; | ||
| 1227 | i += scnprintf(buf + i, max - i, | ||
| 1228 | "%03d: %20s cid=%02d ctype=%d ref_count=%d\n", | ||
| 1229 | n, shared[n].name, shared[n].cid, | ||
| 1230 | shared[n].ctype, shared[n].ref_count); | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | return i; | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | static int debug_boom(char *buf, int max) | ||
| 1237 | { | ||
| 1238 | unsigned ms = 5000; | ||
| 1239 | msm_proc_comm(PCOM_RESET_MODEM, &ms, 0); | ||
| 1240 | return 0; | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | #define DEBUG_BUFMAX 4096 | ||
| 1244 | static char debug_buffer[DEBUG_BUFMAX]; | ||
| 1245 | |||
| 1246 | static ssize_t debug_read(struct file *file, char __user *buf, | ||
| 1247 | size_t count, loff_t *ppos) | ||
| 1248 | { | ||
| 1249 | int (*fill)(char *buf, int max) = file->private_data; | ||
| 1250 | int bsize = fill(debug_buffer, DEBUG_BUFMAX); | ||
| 1251 | return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); | ||
| 1252 | } | ||
| 1253 | |||
| 1254 | static int debug_open(struct inode *inode, struct file *file) | ||
| 1255 | { | ||
| 1256 | file->private_data = inode->i_private; | ||
| 1257 | return 0; | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | static const struct file_operations debug_ops = { | ||
| 1261 | .read = debug_read, | ||
| 1262 | .open = debug_open, | ||
| 1263 | }; | ||
| 1264 | |||
| 1265 | static void debug_create(const char *name, mode_t mode, | ||
| 1266 | struct dentry *dent, | ||
| 1267 | int (*fill)(char *buf, int max)) | ||
| 1268 | { | ||
| 1269 | debugfs_create_file(name, mode, dent, fill, &debug_ops); | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | static void smd_debugfs_init(void) | ||
| 1273 | { | ||
| 1274 | struct dentry *dent; | ||
| 1275 | |||
| 1276 | dent = debugfs_create_dir("smd", 0); | ||
| 1277 | if (IS_ERR(dent)) | ||
| 1278 | return; | ||
| 1279 | |||
| 1280 | debug_create("ch", 0444, dent, debug_read_ch); | ||
| 1281 | debug_create("stat", 0444, dent, debug_read_stat); | ||
| 1282 | debug_create("mem", 0444, dent, debug_read_mem); | ||
| 1283 | debug_create("version", 0444, dent, debug_read_version); | ||
| 1284 | debug_create("tbl", 0444, dent, debug_read_alloc_tbl); | ||
| 1285 | debug_create("build", 0444, dent, debug_read_build_id); | ||
| 1286 | debug_create("boom", 0444, dent, debug_boom); | ||
| 1287 | } | ||
| 1288 | #else | ||
| 1289 | static void smd_debugfs_init(void) {} | ||
| 1290 | #endif | ||
| 1291 | |||
| 1292 | static int __init msm_smd_probe(struct platform_device *pdev) | ||
| 1293 | { | ||
| 1294 | pr_info("smd_init()\n"); | ||
| 1295 | |||
| 1296 | INIT_WORK(&probe_work, smd_channel_probe_worker); | ||
| 1297 | |||
| 1298 | if (smd_core_init()) { | ||
| 1299 | pr_err("smd_core_init() failed\n"); | ||
| 1300 | return -1; | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | do_smd_probe(); | ||
| 1304 | |||
| 1305 | msm_check_for_modem_crash = check_for_modem_crash; | ||
| 1306 | |||
| 1307 | smd_debugfs_init(); | ||
| 1308 | smd_initialized = 1; | ||
| 1309 | |||
| 1310 | return 0; | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | static struct platform_driver msm_smd_driver = { | ||
| 1314 | .probe = msm_smd_probe, | ||
| 1315 | .driver = { | ||
| 1316 | .name = MODULE_NAME, | ||
| 1317 | .owner = THIS_MODULE, | ||
| 1318 | }, | ||
| 1319 | }; | ||
| 1320 | |||
| 1321 | static int __init msm_smd_init(void) | ||
| 1322 | { | ||
| 1323 | return platform_driver_register(&msm_smd_driver); | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | module_init(msm_smd_init); | ||
| 1327 | |||
| 1328 | MODULE_DESCRIPTION("MSM Shared Memory Core"); | ||
| 1329 | MODULE_AUTHOR("Brian Swetland <swetland@google.com>"); | ||
| 1330 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/staging/dream/smd/smd_private.h b/drivers/staging/dream/smd/smd_private.h deleted file mode 100644 index 1b2e1c89ea2e..000000000000 --- a/drivers/staging/dream/smd/smd_private.h +++ /dev/null | |||
| @@ -1,164 +0,0 @@ | |||
| 1 | /* arch/arm/mach-msm/smd_private.h | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 Google, Inc. | ||
| 4 | * Copyright (c) 2007 QUALCOMM Incorporated | ||
| 5 | * | ||
| 6 | * This software is licensed under the terms of the GNU General Public | ||
| 7 | * License version 2, as published by the Free Software Foundation, and | ||
| 8 | * may be copied, distributed, and modified under those terms. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | #ifndef _ARCH_ARM_MACH_MSM_MSM_SMD_PRIVATE_H_ | ||
| 17 | #define _ARCH_ARM_MACH_MSM_MSM_SMD_PRIVATE_H_ | ||
| 18 | |||
| 19 | struct smem_heap_info { | ||
| 20 | unsigned initialized; | ||
| 21 | unsigned free_offset; | ||
| 22 | unsigned heap_remaining; | ||
| 23 | unsigned reserved; | ||
| 24 | }; | ||
| 25 | |||
| 26 | struct smem_heap_entry { | ||
| 27 | unsigned allocated; | ||
| 28 | unsigned offset; | ||
| 29 | unsigned size; | ||
| 30 | unsigned reserved; | ||
| 31 | }; | ||
| 32 | |||
| 33 | struct smem_proc_comm { | ||
| 34 | unsigned command; | ||
| 35 | unsigned status; | ||
| 36 | unsigned data1; | ||
| 37 | unsigned data2; | ||
| 38 | }; | ||
| 39 | |||
| 40 | #define PC_APPS 0 | ||
| 41 | #define PC_MODEM 1 | ||
| 42 | |||
| 43 | #define VERSION_QDSP6 4 | ||
| 44 | #define VERSION_APPS_SBL 6 | ||
| 45 | #define VERSION_MODEM_SBL 7 | ||
| 46 | #define VERSION_APPS 8 | ||
| 47 | #define VERSION_MODEM 9 | ||
| 48 | |||
| 49 | struct smem_shared { | ||
| 50 | struct smem_proc_comm proc_comm[4]; | ||
| 51 | unsigned version[32]; | ||
| 52 | struct smem_heap_info heap_info; | ||
| 53 | struct smem_heap_entry heap_toc[128]; | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct smsm_shared { | ||
| 57 | unsigned host; | ||
| 58 | unsigned state; | ||
| 59 | }; | ||
| 60 | |||
| 61 | struct smsm_interrupt_info { | ||
| 62 | uint32_t aArm_en_mask; | ||
| 63 | uint32_t aArm_interrupts_pending; | ||
| 64 | uint32_t aArm_wakeup_reason; | ||
| 65 | }; | ||
| 66 | |||
| 67 | #define SZ_DIAG_ERR_MSG 0xC8 | ||
| 68 | #define ID_DIAG_ERR_MSG SMEM_DIAG_ERR_MESSAGE | ||
| 69 | #define ID_SMD_CHANNELS SMEM_SMD_BASE_ID | ||
| 70 | #define ID_SHARED_STATE SMEM_SMSM_SHARED_STATE | ||
| 71 | #define ID_CH_ALLOC_TBL SMEM_CHANNEL_ALLOC_TBL | ||
| 72 | |||
| 73 | #define SMSM_INIT 0x000001 | ||
| 74 | #define SMSM_SMDINIT 0x000008 | ||
| 75 | #define SMSM_RPCINIT 0x000020 | ||
| 76 | #define SMSM_RESET 0x000040 | ||
| 77 | #define SMSM_RSA 0x0080 | ||
| 78 | #define SMSM_RUN 0x000100 | ||
| 79 | #define SMSM_PWRC 0x0200 | ||
| 80 | #define SMSM_TIMEWAIT 0x0400 | ||
| 81 | #define SMSM_TIMEINIT 0x0800 | ||
| 82 | #define SMSM_PWRC_EARLY_EXIT 0x1000 | ||
| 83 | #define SMSM_WFPI 0x2000 | ||
| 84 | #define SMSM_SLEEP 0x4000 | ||
| 85 | #define SMSM_SLEEPEXIT 0x8000 | ||
| 86 | #define SMSM_OEMSBL_RELEASE 0x10000 | ||
| 87 | #define SMSM_PWRC_SUSPEND 0x200000 | ||
| 88 | |||
| 89 | #define SMSM_WKUP_REASON_RPC 0x00000001 | ||
| 90 | #define SMSM_WKUP_REASON_INT 0x00000002 | ||
| 91 | #define SMSM_WKUP_REASON_GPIO 0x00000004 | ||
| 92 | #define SMSM_WKUP_REASON_TIMER 0x00000008 | ||
| 93 | #define SMSM_WKUP_REASON_ALARM 0x00000010 | ||
| 94 | #define SMSM_WKUP_REASON_RESET 0x00000020 | ||
| 95 | |||
| 96 | void *smem_alloc(unsigned id, unsigned size); | ||
| 97 | int smsm_change_state(uint32_t clear_mask, uint32_t set_mask); | ||
| 98 | uint32_t smsm_get_state(void); | ||
| 99 | int smsm_set_sleep_duration(uint32_t delay); | ||
| 100 | int smsm_set_interrupt_info(struct smsm_interrupt_info *info); | ||
| 101 | void smsm_print_sleep_info(void); | ||
| 102 | |||
| 103 | #define SMEM_NUM_SMD_CHANNELS 64 | ||
| 104 | |||
| 105 | typedef enum { | ||
| 106 | /* fixed items */ | ||
| 107 | SMEM_PROC_COMM = 0, | ||
| 108 | SMEM_HEAP_INFO, | ||
| 109 | SMEM_ALLOCATION_TABLE, | ||
| 110 | SMEM_VERSION_INFO, | ||
| 111 | SMEM_HW_RESET_DETECT, | ||
| 112 | SMEM_AARM_WARM_BOOT, | ||
| 113 | SMEM_DIAG_ERR_MESSAGE, | ||
| 114 | SMEM_SPINLOCK_ARRAY, | ||
| 115 | SMEM_MEMORY_BARRIER_LOCATION, | ||
| 116 | |||
| 117 | /* dynamic items */ | ||
| 118 | SMEM_AARM_PARTITION_TABLE, | ||
| 119 | SMEM_AARM_BAD_BLOCK_TABLE, | ||
| 120 | SMEM_RESERVE_BAD_BLOCKS, | ||
| 121 | SMEM_WM_UUID, | ||
| 122 | SMEM_CHANNEL_ALLOC_TBL, | ||
| 123 | SMEM_SMD_BASE_ID, | ||
| 124 | SMEM_SMEM_LOG_IDX = SMEM_SMD_BASE_ID + SMEM_NUM_SMD_CHANNELS, | ||
| 125 | SMEM_SMEM_LOG_EVENTS, | ||
| 126 | SMEM_SMEM_STATIC_LOG_IDX, | ||
| 127 | SMEM_SMEM_STATIC_LOG_EVENTS, | ||
| 128 | SMEM_SMEM_SLOW_CLOCK_SYNC, | ||
| 129 | SMEM_SMEM_SLOW_CLOCK_VALUE, | ||
| 130 | SMEM_BIO_LED_BUF, | ||
| 131 | SMEM_SMSM_SHARED_STATE, | ||
| 132 | SMEM_SMSM_INT_INFO, | ||
| 133 | SMEM_SMSM_SLEEP_DELAY, | ||
| 134 | SMEM_SMSM_LIMIT_SLEEP, | ||
| 135 | SMEM_SLEEP_POWER_COLLAPSE_DISABLED, | ||
| 136 | SMEM_KEYPAD_KEYS_PRESSED, | ||
| 137 | SMEM_KEYPAD_STATE_UPDATED, | ||
| 138 | SMEM_KEYPAD_STATE_IDX, | ||
| 139 | SMEM_GPIO_INT, | ||
| 140 | SMEM_MDDI_LCD_IDX, | ||
| 141 | SMEM_MDDI_HOST_DRIVER_STATE, | ||
| 142 | SMEM_MDDI_LCD_DISP_STATE, | ||
| 143 | SMEM_LCD_CUR_PANEL, | ||
| 144 | SMEM_MARM_BOOT_SEGMENT_INFO, | ||
| 145 | SMEM_AARM_BOOT_SEGMENT_INFO, | ||
| 146 | SMEM_SLEEP_STATIC, | ||
| 147 | SMEM_SCORPION_FREQUENCY, | ||
| 148 | SMEM_SMD_PROFILES, | ||
| 149 | SMEM_TSSC_BUSY, | ||
| 150 | SMEM_HS_SUSPEND_FILTER_INFO, | ||
| 151 | SMEM_BATT_INFO, | ||
| 152 | SMEM_APPS_BOOT_MODE, | ||
| 153 | SMEM_VERSION_FIRST, | ||
| 154 | SMEM_VERSION_LAST = SMEM_VERSION_FIRST + 24, | ||
| 155 | SMEM_OSS_RRCASN1_BUF1, | ||
| 156 | SMEM_OSS_RRCASN1_BUF2, | ||
| 157 | SMEM_ID_VENDOR0, | ||
| 158 | SMEM_ID_VENDOR1, | ||
| 159 | SMEM_ID_VENDOR2, | ||
| 160 | SMEM_HW_SW_BUILD_ID, | ||
| 161 | SMEM_NUM_ITEMS, | ||
| 162 | } smem_mem_type; | ||
| 163 | |||
| 164 | #endif | ||
diff --git a/drivers/staging/dream/smd/smd_qmi.c b/drivers/staging/dream/smd/smd_qmi.c deleted file mode 100644 index 76fce5142d9e..000000000000 --- a/drivers/staging/dream/smd/smd_qmi.c +++ /dev/null | |||
| @@ -1,851 +0,0 @@ | |||
| 1 | /* arch/arm/mach-msm/smd_qmi.c | ||
| 2 | * | ||
| 3 | * QMI Control Driver -- Manages network data connections. | ||
| 4 | * | ||
| 5 | * Copyright (C) 2007 Google, Inc. | ||
| 6 | * Author: Brian Swetland <swetland@google.com> | ||
| 7 | * | ||
| 8 | * This software is licensed under the terms of the GNU General Public | ||
| 9 | * License version 2, as published by the Free Software Foundation, and | ||
| 10 | * may be copied, distributed, and modified under those terms. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/fs.h> | ||
| 21 | #include <linux/cdev.h> | ||
| 22 | #include <linux/device.h> | ||
| 23 | #include <linux/sched.h> | ||
| 24 | #include <linux/wait.h> | ||
| 25 | #include <linux/miscdevice.h> | ||
| 26 | #include <linux/workqueue.h> | ||
| 27 | |||
| 28 | #include <linux/uaccess.h> | ||
| 29 | #include <mach/msm_smd.h> | ||
| 30 | |||
| 31 | #define QMI_CTL 0x00 | ||
| 32 | #define QMI_WDS 0x01 | ||
| 33 | #define QMI_DMS 0x02 | ||
| 34 | #define QMI_NAS 0x03 | ||
| 35 | |||
| 36 | #define QMI_RESULT_SUCCESS 0x0000 | ||
| 37 | #define QMI_RESULT_FAILURE 0x0001 | ||
| 38 | |||
| 39 | struct qmi_msg { | ||
| 40 | unsigned char service; | ||
| 41 | unsigned char client_id; | ||
| 42 | unsigned short txn_id; | ||
| 43 | unsigned short type; | ||
| 44 | unsigned short size; | ||
| 45 | unsigned char *tlv; | ||
| 46 | }; | ||
| 47 | |||
| 48 | #define qmi_ctl_client_id 0 | ||
| 49 | |||
| 50 | #define STATE_OFFLINE 0 | ||
| 51 | #define STATE_QUERYING 1 | ||
| 52 | #define STATE_ONLINE 2 | ||
| 53 | |||
| 54 | struct qmi_ctxt { | ||
| 55 | struct miscdevice misc; | ||
| 56 | |||
| 57 | struct mutex lock; | ||
| 58 | |||
| 59 | unsigned char ctl_txn_id; | ||
| 60 | unsigned char wds_client_id; | ||
| 61 | unsigned short wds_txn_id; | ||
| 62 | |||
| 63 | unsigned wds_busy; | ||
| 64 | unsigned wds_handle; | ||
| 65 | unsigned state_dirty; | ||
| 66 | unsigned state; | ||
| 67 | |||
| 68 | unsigned char addr[4]; | ||
| 69 | unsigned char mask[4]; | ||
| 70 | unsigned char gateway[4]; | ||
| 71 | unsigned char dns1[4]; | ||
| 72 | unsigned char dns2[4]; | ||
| 73 | |||
| 74 | smd_channel_t *ch; | ||
| 75 | const char *ch_name; | ||
| 76 | |||
| 77 | struct work_struct open_work; | ||
| 78 | struct work_struct read_work; | ||
| 79 | }; | ||
| 80 | |||
| 81 | static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n); | ||
| 82 | |||
| 83 | static void qmi_read_work(struct work_struct *ws); | ||
| 84 | static void qmi_open_work(struct work_struct *work); | ||
| 85 | |||
| 86 | void qmi_ctxt_init(struct qmi_ctxt *ctxt, unsigned n) | ||
| 87 | { | ||
| 88 | mutex_init(&ctxt->lock); | ||
| 89 | INIT_WORK(&ctxt->read_work, qmi_read_work); | ||
| 90 | INIT_WORK(&ctxt->open_work, qmi_open_work); | ||
| 91 | ctxt->ctl_txn_id = 1; | ||
| 92 | ctxt->wds_txn_id = 1; | ||
| 93 | ctxt->wds_busy = 1; | ||
| 94 | ctxt->state = STATE_OFFLINE; | ||
| 95 | |||
| 96 | } | ||
| 97 | |||
| 98 | static struct workqueue_struct *qmi_wq; | ||
| 99 | |||
| 100 | static int verbose = 0; | ||
| 101 | |||
| 102 | /* anyone waiting for a state change waits here */ | ||
| 103 | static DECLARE_WAIT_QUEUE_HEAD(qmi_wait_queue); | ||
| 104 | |||
| 105 | |||
| 106 | static void qmi_dump_msg(struct qmi_msg *msg, const char *prefix) | ||
| 107 | { | ||
| 108 | unsigned sz, n; | ||
| 109 | unsigned char *x; | ||
| 110 | |||
| 111 | if (!verbose) | ||
| 112 | return; | ||
| 113 | |||
| 114 | printk(KERN_INFO | ||
| 115 | "qmi: %s: svc=%02x cid=%02x tid=%04x type=%04x size=%04x\n", | ||
| 116 | prefix, msg->service, msg->client_id, | ||
| 117 | msg->txn_id, msg->type, msg->size); | ||
| 118 | |||
| 119 | x = msg->tlv; | ||
| 120 | sz = msg->size; | ||
| 121 | |||
| 122 | while (sz >= 3) { | ||
| 123 | sz -= 3; | ||
| 124 | |||
| 125 | n = x[1] | (x[2] << 8); | ||
| 126 | if (n > sz) | ||
| 127 | break; | ||
| 128 | |||
| 129 | printk(KERN_INFO "qmi: %s: tlv: %02x %04x { ", | ||
| 130 | prefix, x[0], n); | ||
| 131 | x += 3; | ||
| 132 | sz -= n; | ||
| 133 | while (n-- > 0) | ||
| 134 | printk("%02x ", *x++); | ||
| 135 | printk("}\n"); | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | int qmi_add_tlv(struct qmi_msg *msg, | ||
| 140 | unsigned type, unsigned size, const void *data) | ||
| 141 | { | ||
| 142 | unsigned char *x = msg->tlv + msg->size; | ||
| 143 | |||
| 144 | x[0] = type; | ||
| 145 | x[1] = size; | ||
| 146 | x[2] = size >> 8; | ||
| 147 | |||
| 148 | memcpy(x + 3, data, size); | ||
| 149 | |||
| 150 | msg->size += (size + 3); | ||
| 151 | |||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | /* Extract a tagged item from a qmi message buffer, | ||
| 156 | ** taking care not to overrun the buffer. | ||
| 157 | */ | ||
| 158 | static int qmi_get_tlv(struct qmi_msg *msg, | ||
| 159 | unsigned type, unsigned size, void *data) | ||
| 160 | { | ||
| 161 | unsigned char *x = msg->tlv; | ||
| 162 | unsigned len = msg->size; | ||
| 163 | unsigned n; | ||
| 164 | |||
| 165 | while (len >= 3) { | ||
| 166 | len -= 3; | ||
| 167 | |||
| 168 | /* size of this item */ | ||
| 169 | n = x[1] | (x[2] << 8); | ||
| 170 | if (n > len) | ||
| 171 | break; | ||
| 172 | |||
| 173 | if (x[0] == type) { | ||
| 174 | if (n != size) | ||
| 175 | return -1; | ||
| 176 | memcpy(data, x + 3, size); | ||
| 177 | return 0; | ||
| 178 | } | ||
| 179 | |||
| 180 | x += (n + 3); | ||
| 181 | len -= n; | ||
| 182 | } | ||
| 183 | |||
| 184 | return -1; | ||
| 185 | } | ||
| 186 | |||
| 187 | static unsigned qmi_get_status(struct qmi_msg *msg, unsigned *error) | ||
| 188 | { | ||
| 189 | unsigned short status[2]; | ||
| 190 | if (qmi_get_tlv(msg, 0x02, sizeof(status), status)) { | ||
| 191 | *error = 0; | ||
| 192 | return QMI_RESULT_FAILURE; | ||
| 193 | } else { | ||
| 194 | *error = status[1]; | ||
| 195 | return status[0]; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | /* 0x01 <qmux-header> <payload> */ | ||
| 200 | #define QMUX_HEADER 13 | ||
| 201 | |||
| 202 | /* should be >= HEADER + FOOTER */ | ||
| 203 | #define QMUX_OVERHEAD 16 | ||
| 204 | |||
| 205 | static int qmi_send(struct qmi_ctxt *ctxt, struct qmi_msg *msg) | ||
| 206 | { | ||
| 207 | unsigned char *data; | ||
| 208 | unsigned hlen; | ||
| 209 | unsigned len; | ||
| 210 | int r; | ||
| 211 | |||
| 212 | qmi_dump_msg(msg, "send"); | ||
| 213 | |||
| 214 | if (msg->service == QMI_CTL) | ||
| 215 | hlen = QMUX_HEADER - 1; | ||
| 216 | else | ||
| 217 | hlen = QMUX_HEADER; | ||
| 218 | |||
| 219 | /* QMUX length is total header + total payload - IFC selector */ | ||
| 220 | len = hlen + msg->size - 1; | ||
| 221 | if (len > 0xffff) | ||
| 222 | return -1; | ||
| 223 | |||
| 224 | data = msg->tlv - hlen; | ||
| 225 | |||
| 226 | /* prepend encap and qmux header */ | ||
| 227 | *data++ = 0x01; /* ifc selector */ | ||
| 228 | |||
| 229 | /* qmux header */ | ||
| 230 | *data++ = len; | ||
| 231 | *data++ = len >> 8; | ||
| 232 | *data++ = 0x00; /* flags: client */ | ||
| 233 | *data++ = msg->service; | ||
| 234 | *data++ = msg->client_id; | ||
| 235 | |||
| 236 | /* qmi header */ | ||
| 237 | *data++ = 0x00; /* flags: send */ | ||
| 238 | *data++ = msg->txn_id; | ||
| 239 | if (msg->service != QMI_CTL) | ||
| 240 | *data++ = msg->txn_id >> 8; | ||
| 241 | |||
| 242 | *data++ = msg->type; | ||
| 243 | *data++ = msg->type >> 8; | ||
| 244 | *data++ = msg->size; | ||
| 245 | *data++ = msg->size >> 8; | ||
| 246 | |||
| 247 | /* len + 1 takes the interface selector into account */ | ||
| 248 | r = smd_write(ctxt->ch, msg->tlv - hlen, len + 1); | ||
| 249 | |||
| 250 | if (r != len) | ||
| 251 | return -1; | ||
| 252 | else | ||
| 253 | return 0; | ||
| 254 | } | ||
| 255 | |||
| 256 | static void qmi_process_ctl_msg(struct qmi_ctxt *ctxt, struct qmi_msg *msg) | ||
| 257 | { | ||
| 258 | unsigned err; | ||
| 259 | if (msg->type == 0x0022) { | ||
| 260 | unsigned char n[2]; | ||
| 261 | if (qmi_get_status(msg, &err)) | ||
| 262 | return; | ||
| 263 | if (qmi_get_tlv(msg, 0x01, sizeof(n), n)) | ||
| 264 | return; | ||
| 265 | if (n[0] == QMI_WDS) { | ||
| 266 | printk(KERN_INFO | ||
| 267 | "qmi: ctl: wds use client_id 0x%02x\n", n[1]); | ||
| 268 | ctxt->wds_client_id = n[1]; | ||
| 269 | ctxt->wds_busy = 0; | ||
| 270 | } | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | static int qmi_network_get_profile(struct qmi_ctxt *ctxt); | ||
| 275 | |||
| 276 | static void swapaddr(unsigned char *src, unsigned char *dst) | ||
| 277 | { | ||
| 278 | dst[0] = src[3]; | ||
| 279 | dst[1] = src[2]; | ||
| 280 | dst[2] = src[1]; | ||
| 281 | dst[3] = src[0]; | ||
| 282 | } | ||
| 283 | |||
| 284 | static unsigned char zero[4]; | ||
| 285 | static void qmi_read_runtime_profile(struct qmi_ctxt *ctxt, struct qmi_msg *msg) | ||
| 286 | { | ||
| 287 | unsigned char tmp[4]; | ||
| 288 | unsigned r; | ||
| 289 | |||
| 290 | r = qmi_get_tlv(msg, 0x1e, 4, tmp); | ||
| 291 | swapaddr(r ? zero : tmp, ctxt->addr); | ||
| 292 | r = qmi_get_tlv(msg, 0x21, 4, tmp); | ||
| 293 | swapaddr(r ? zero : tmp, ctxt->mask); | ||
| 294 | r = qmi_get_tlv(msg, 0x20, 4, tmp); | ||
| 295 | swapaddr(r ? zero : tmp, ctxt->gateway); | ||
| 296 | r = qmi_get_tlv(msg, 0x15, 4, tmp); | ||
| 297 | swapaddr(r ? zero : tmp, ctxt->dns1); | ||
| 298 | r = qmi_get_tlv(msg, 0x16, 4, tmp); | ||
| 299 | swapaddr(r ? zero : tmp, ctxt->dns2); | ||
| 300 | } | ||
| 301 | |||
| 302 | static void qmi_process_unicast_wds_msg(struct qmi_ctxt *ctxt, | ||
| 303 | struct qmi_msg *msg) | ||
| 304 | { | ||
| 305 | unsigned err; | ||
| 306 | switch (msg->type) { | ||
| 307 | case 0x0021: | ||
| 308 | if (qmi_get_status(msg, &err)) { | ||
| 309 | printk(KERN_ERR | ||
| 310 | "qmi: wds: network stop failed (%04x)\n", err); | ||
| 311 | } else { | ||
| 312 | printk(KERN_INFO | ||
| 313 | "qmi: wds: network stopped\n"); | ||
| 314 | ctxt->state = STATE_OFFLINE; | ||
| 315 | ctxt->state_dirty = 1; | ||
| 316 | } | ||
| 317 | break; | ||
| 318 | case 0x0020: | ||
| 319 | if (qmi_get_status(msg, &err)) { | ||
| 320 | printk(KERN_ERR | ||
| 321 | "qmi: wds: network start failed (%04x)\n", err); | ||
| 322 | } else if (qmi_get_tlv(msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle)) { | ||
| 323 | printk(KERN_INFO | ||
| 324 | "qmi: wds no handle?\n"); | ||
| 325 | } else { | ||
| 326 | printk(KERN_INFO | ||
| 327 | "qmi: wds: got handle 0x%08x\n", | ||
| 328 | ctxt->wds_handle); | ||
| 329 | } | ||
| 330 | break; | ||
| 331 | case 0x002D: | ||
| 332 | printk("qmi: got network profile\n"); | ||
| 333 | if (ctxt->state == STATE_QUERYING) { | ||
| 334 | qmi_read_runtime_profile(ctxt, msg); | ||
| 335 | ctxt->state = STATE_ONLINE; | ||
| 336 | ctxt->state_dirty = 1; | ||
| 337 | } | ||
| 338 | break; | ||
| 339 | default: | ||
| 340 | printk(KERN_ERR "qmi: unknown msg type 0x%04x\n", msg->type); | ||
| 341 | } | ||
| 342 | ctxt->wds_busy = 0; | ||
| 343 | } | ||
| 344 | |||
| 345 | static void qmi_process_broadcast_wds_msg(struct qmi_ctxt *ctxt, | ||
| 346 | struct qmi_msg *msg) | ||
| 347 | { | ||
| 348 | if (msg->type == 0x0022) { | ||
| 349 | unsigned char n[2]; | ||
| 350 | if (qmi_get_tlv(msg, 0x01, sizeof(n), n)) | ||
| 351 | return; | ||
| 352 | switch (n[0]) { | ||
| 353 | case 1: | ||
| 354 | printk(KERN_INFO "qmi: wds: DISCONNECTED\n"); | ||
| 355 | ctxt->state = STATE_OFFLINE; | ||
| 356 | ctxt->state_dirty = 1; | ||
| 357 | break; | ||
| 358 | case 2: | ||
| 359 | printk(KERN_INFO "qmi: wds: CONNECTED\n"); | ||
| 360 | ctxt->state = STATE_QUERYING; | ||
| 361 | ctxt->state_dirty = 1; | ||
| 362 | qmi_network_get_profile(ctxt); | ||
| 363 | break; | ||
| 364 | case 3: | ||
| 365 | printk(KERN_INFO "qmi: wds: SUSPENDED\n"); | ||
| 366 | ctxt->state = STATE_OFFLINE; | ||
| 367 | ctxt->state_dirty = 1; | ||
| 368 | } | ||
| 369 | } else { | ||
| 370 | printk(KERN_ERR "qmi: unknown bcast msg type 0x%04x\n", msg->type); | ||
| 371 | } | ||
| 372 | } | ||
| 373 | |||
| 374 | static void qmi_process_wds_msg(struct qmi_ctxt *ctxt, | ||
| 375 | struct qmi_msg *msg) | ||
| 376 | { | ||
| 377 | printk(KERN_INFO "wds: %04x @ %02x\n", msg->type, msg->client_id); | ||
| 378 | if (msg->client_id == ctxt->wds_client_id) { | ||
| 379 | qmi_process_unicast_wds_msg(ctxt, msg); | ||
| 380 | } else if (msg->client_id == 0xff) { | ||
| 381 | qmi_process_broadcast_wds_msg(ctxt, msg); | ||
| 382 | } else { | ||
| 383 | printk(KERN_ERR | ||
| 384 | "qmi_process_wds_msg client id 0x%02x unknown\n", | ||
| 385 | msg->client_id); | ||
| 386 | } | ||
| 387 | } | ||
| 388 | |||
| 389 | static void qmi_process_qmux(struct qmi_ctxt *ctxt, | ||
| 390 | unsigned char *buf, unsigned sz) | ||
| 391 | { | ||
| 392 | struct qmi_msg msg; | ||
| 393 | |||
| 394 | /* require a full header */ | ||
| 395 | if (sz < 5) | ||
| 396 | return; | ||
| 397 | |||
| 398 | /* require a size that matches the buffer size */ | ||
| 399 | if (sz != (buf[0] | (buf[1] << 8))) | ||
| 400 | return; | ||
| 401 | |||
| 402 | /* only messages from a service (bit7=1) are allowed */ | ||
| 403 | if (buf[2] != 0x80) | ||
| 404 | return; | ||
| 405 | |||
| 406 | msg.service = buf[3]; | ||
| 407 | msg.client_id = buf[4]; | ||
| 408 | |||
| 409 | /* annoyingly, CTL messages have a shorter TID */ | ||
| 410 | if (buf[3] == 0) { | ||
| 411 | if (sz < 7) | ||
| 412 | return; | ||
| 413 | msg.txn_id = buf[6]; | ||
| 414 | buf += 7; | ||
| 415 | sz -= 7; | ||
| 416 | } else { | ||
| 417 | if (sz < 8) | ||
| 418 | return; | ||
| 419 | msg.txn_id = buf[6] | (buf[7] << 8); | ||
| 420 | buf += 8; | ||
| 421 | sz -= 8; | ||
| 422 | } | ||
| 423 | |||
| 424 | /* no type and size!? */ | ||
| 425 | if (sz < 4) | ||
| 426 | return; | ||
| 427 | sz -= 4; | ||
| 428 | |||
| 429 | msg.type = buf[0] | (buf[1] << 8); | ||
| 430 | msg.size = buf[2] | (buf[3] << 8); | ||
| 431 | msg.tlv = buf + 4; | ||
| 432 | |||
| 433 | if (sz != msg.size) | ||
| 434 | return; | ||
| 435 | |||
| 436 | qmi_dump_msg(&msg, "recv"); | ||
| 437 | |||
| 438 | mutex_lock(&ctxt->lock); | ||
| 439 | switch (msg.service) { | ||
| 440 | case QMI_CTL: | ||
| 441 | qmi_process_ctl_msg(ctxt, &msg); | ||
| 442 | break; | ||
| 443 | case QMI_WDS: | ||
| 444 | qmi_process_wds_msg(ctxt, &msg); | ||
| 445 | break; | ||
| 446 | default: | ||
| 447 | printk(KERN_ERR "qmi: msg from unknown svc 0x%02x\n", | ||
| 448 | msg.service); | ||
| 449 | break; | ||
| 450 | } | ||
| 451 | mutex_unlock(&ctxt->lock); | ||
| 452 | wake_up(&qmi_wait_queue); | ||
| 453 | } | ||
| 454 | |||
| 455 | #define QMI_MAX_PACKET (256 + QMUX_OVERHEAD) | ||
| 456 | |||
| 457 | static void qmi_read_work(struct work_struct *ws) | ||
| 458 | { | ||
| 459 | struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, read_work); | ||
| 460 | struct smd_channel *ch = ctxt->ch; | ||
| 461 | unsigned char buf[QMI_MAX_PACKET]; | ||
| 462 | int sz; | ||
| 463 | |||
| 464 | for (;;) { | ||
| 465 | sz = smd_cur_packet_size(ch); | ||
| 466 | if (sz == 0) | ||
| 467 | break; | ||
| 468 | if (sz < smd_read_avail(ch)) | ||
| 469 | break; | ||
| 470 | if (sz > QMI_MAX_PACKET) { | ||
| 471 | smd_read(ch, 0, sz); | ||
| 472 | continue; | ||
| 473 | } | ||
| 474 | if (smd_read(ch, buf, sz) != sz) { | ||
| 475 | printk(KERN_ERR "qmi: not enough data?!\n"); | ||
| 476 | continue; | ||
| 477 | } | ||
| 478 | |||
| 479 | /* interface selector must be 1 */ | ||
| 480 | if (buf[0] != 0x01) | ||
| 481 | continue; | ||
| 482 | |||
| 483 | qmi_process_qmux(ctxt, buf + 1, sz - 1); | ||
| 484 | } | ||
| 485 | } | ||
| 486 | |||
| 487 | static int qmi_request_wds_cid(struct qmi_ctxt *ctxt); | ||
| 488 | |||
| 489 | static void qmi_open_work(struct work_struct *ws) | ||
| 490 | { | ||
| 491 | struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, open_work); | ||
| 492 | mutex_lock(&ctxt->lock); | ||
| 493 | qmi_request_wds_cid(ctxt); | ||
| 494 | mutex_unlock(&ctxt->lock); | ||
| 495 | } | ||
| 496 | |||
| 497 | static void qmi_notify(void *priv, unsigned event) | ||
| 498 | { | ||
| 499 | struct qmi_ctxt *ctxt = priv; | ||
| 500 | |||
| 501 | switch (event) { | ||
| 502 | case SMD_EVENT_DATA: { | ||
| 503 | int sz; | ||
| 504 | sz = smd_cur_packet_size(ctxt->ch); | ||
| 505 | if ((sz > 0) && (sz <= smd_read_avail(ctxt->ch))) | ||
| 506 | queue_work(qmi_wq, &ctxt->read_work); | ||
| 507 | break; | ||
| 508 | } | ||
| 509 | case SMD_EVENT_OPEN: | ||
| 510 | printk(KERN_INFO "qmi: smd opened\n"); | ||
| 511 | queue_work(qmi_wq, &ctxt->open_work); | ||
| 512 | break; | ||
| 513 | case SMD_EVENT_CLOSE: | ||
| 514 | printk(KERN_INFO "qmi: smd closed\n"); | ||
| 515 | break; | ||
| 516 | } | ||
| 517 | } | ||
| 518 | |||
| 519 | static int qmi_request_wds_cid(struct qmi_ctxt *ctxt) | ||
| 520 | { | ||
| 521 | unsigned char data[64 + QMUX_OVERHEAD]; | ||
| 522 | struct qmi_msg msg; | ||
| 523 | unsigned char n; | ||
| 524 | |||
| 525 | msg.service = QMI_CTL; | ||
| 526 | msg.client_id = qmi_ctl_client_id; | ||
| 527 | msg.txn_id = ctxt->ctl_txn_id; | ||
| 528 | msg.type = 0x0022; | ||
| 529 | msg.size = 0; | ||
| 530 | msg.tlv = data + QMUX_HEADER; | ||
| 531 | |||
| 532 | ctxt->ctl_txn_id += 2; | ||
| 533 | |||
| 534 | n = QMI_WDS; | ||
| 535 | qmi_add_tlv(&msg, 0x01, 0x01, &n); | ||
| 536 | |||
| 537 | return qmi_send(ctxt, &msg); | ||
| 538 | } | ||
| 539 | |||
| 540 | static int qmi_network_get_profile(struct qmi_ctxt *ctxt) | ||
| 541 | { | ||
| 542 | unsigned char data[96 + QMUX_OVERHEAD]; | ||
| 543 | struct qmi_msg msg; | ||
| 544 | |||
| 545 | msg.service = QMI_WDS; | ||
| 546 | msg.client_id = ctxt->wds_client_id; | ||
| 547 | msg.txn_id = ctxt->wds_txn_id; | ||
| 548 | msg.type = 0x002D; | ||
| 549 | msg.size = 0; | ||
| 550 | msg.tlv = data + QMUX_HEADER; | ||
| 551 | |||
| 552 | ctxt->wds_txn_id += 2; | ||
| 553 | |||
| 554 | return qmi_send(ctxt, &msg); | ||
| 555 | } | ||
| 556 | |||
| 557 | static int qmi_network_up(struct qmi_ctxt *ctxt, char *apn) | ||
| 558 | { | ||
| 559 | unsigned char data[96 + QMUX_OVERHEAD]; | ||
| 560 | struct qmi_msg msg; | ||
| 561 | char *auth_type; | ||
| 562 | char *user; | ||
| 563 | char *pass; | ||
| 564 | |||
| 565 | for (user = apn; *user; user++) { | ||
| 566 | if (*user == ' ') { | ||
| 567 | *user++ = 0; | ||
| 568 | break; | ||
| 569 | } | ||
| 570 | } | ||
| 571 | for (pass = user; *pass; pass++) { | ||
| 572 | if (*pass == ' ') { | ||
| 573 | *pass++ = 0; | ||
| 574 | break; | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | for (auth_type = pass; *auth_type; auth_type++) { | ||
| 579 | if (*auth_type == ' ') { | ||
| 580 | *auth_type++ = 0; | ||
| 581 | break; | ||
| 582 | } | ||
| 583 | } | ||
| 584 | |||
| 585 | msg.service = QMI_WDS; | ||
| 586 | msg.client_id = ctxt->wds_client_id; | ||
| 587 | msg.txn_id = ctxt->wds_txn_id; | ||
| 588 | msg.type = 0x0020; | ||
| 589 | msg.size = 0; | ||
| 590 | msg.tlv = data + QMUX_HEADER; | ||
| 591 | |||
| 592 | ctxt->wds_txn_id += 2; | ||
| 593 | |||
| 594 | qmi_add_tlv(&msg, 0x14, strlen(apn), apn); | ||
| 595 | if (*auth_type) | ||
| 596 | qmi_add_tlv(&msg, 0x16, strlen(auth_type), auth_type); | ||
| 597 | if (*user) { | ||
| 598 | if (!*auth_type) { | ||
| 599 | unsigned char x; | ||
| 600 | x = 3; | ||
| 601 | qmi_add_tlv(&msg, 0x16, 1, &x); | ||
| 602 | } | ||
| 603 | qmi_add_tlv(&msg, 0x17, strlen(user), user); | ||
| 604 | if (*pass) | ||
| 605 | qmi_add_tlv(&msg, 0x18, strlen(pass), pass); | ||
| 606 | } | ||
| 607 | return qmi_send(ctxt, &msg); | ||
| 608 | } | ||
| 609 | |||
| 610 | static int qmi_network_down(struct qmi_ctxt *ctxt) | ||
| 611 | { | ||
| 612 | unsigned char data[16 + QMUX_OVERHEAD]; | ||
| 613 | struct qmi_msg msg; | ||
| 614 | |||
| 615 | msg.service = QMI_WDS; | ||
| 616 | msg.client_id = ctxt->wds_client_id; | ||
| 617 | msg.txn_id = ctxt->wds_txn_id; | ||
| 618 | msg.type = 0x0021; | ||
| 619 | msg.size = 0; | ||
| 620 | msg.tlv = data + QMUX_HEADER; | ||
| 621 | |||
| 622 | ctxt->wds_txn_id += 2; | ||
| 623 | |||
| 624 | qmi_add_tlv(&msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle); | ||
| 625 | |||
| 626 | return qmi_send(ctxt, &msg); | ||
| 627 | } | ||
| 628 | |||
| 629 | static int qmi_print_state(struct qmi_ctxt *ctxt, char *buf, int max) | ||
| 630 | { | ||
| 631 | int i; | ||
| 632 | char *statename; | ||
| 633 | |||
| 634 | if (ctxt->state == STATE_ONLINE) { | ||
| 635 | statename = "up"; | ||
| 636 | } else if (ctxt->state == STATE_OFFLINE) { | ||
| 637 | statename = "down"; | ||
| 638 | } else { | ||
| 639 | statename = "busy"; | ||
| 640 | } | ||
| 641 | |||
| 642 | i = scnprintf(buf, max, "STATE=%s\n", statename); | ||
| 643 | i += scnprintf(buf + i, max - i, "CID=%d\n", ctxt->wds_client_id); | ||
| 644 | |||
| 645 | if (ctxt->state != STATE_ONLINE) | ||
| 646 | return i; | ||
| 647 | |||
| 648 | i += scnprintf(buf + i, max - i, "ADDR=%d.%d.%d.%d\n", | ||
| 649 | ctxt->addr[0], ctxt->addr[1], ctxt->addr[2], ctxt->addr[3]); | ||
| 650 | i += scnprintf(buf + i, max - i, "MASK=%d.%d.%d.%d\n", | ||
| 651 | ctxt->mask[0], ctxt->mask[1], ctxt->mask[2], ctxt->mask[3]); | ||
| 652 | i += scnprintf(buf + i, max - i, "GATEWAY=%d.%d.%d.%d\n", | ||
| 653 | ctxt->gateway[0], ctxt->gateway[1], ctxt->gateway[2], | ||
| 654 | ctxt->gateway[3]); | ||
| 655 | i += scnprintf(buf + i, max - i, "DNS1=%d.%d.%d.%d\n", | ||
| 656 | ctxt->dns1[0], ctxt->dns1[1], ctxt->dns1[2], ctxt->dns1[3]); | ||
| 657 | i += scnprintf(buf + i, max - i, "DNS2=%d.%d.%d.%d\n", | ||
| 658 | ctxt->dns2[0], ctxt->dns2[1], ctxt->dns2[2], ctxt->dns2[3]); | ||
| 659 | |||
| 660 | return i; | ||
| 661 | } | ||
| 662 | |||
| 663 | static ssize_t qmi_read(struct file *fp, char __user *buf, | ||
| 664 | size_t count, loff_t *pos) | ||
| 665 | { | ||
| 666 | struct qmi_ctxt *ctxt = fp->private_data; | ||
| 667 | char msg[256]; | ||
| 668 | int len; | ||
| 669 | int r; | ||
| 670 | |||
| 671 | mutex_lock(&ctxt->lock); | ||
| 672 | for (;;) { | ||
| 673 | if (ctxt->state_dirty) { | ||
| 674 | ctxt->state_dirty = 0; | ||
| 675 | len = qmi_print_state(ctxt, msg, 256); | ||
| 676 | break; | ||
| 677 | } | ||
| 678 | mutex_unlock(&ctxt->lock); | ||
| 679 | r = wait_event_interruptible(qmi_wait_queue, ctxt->state_dirty); | ||
| 680 | if (r < 0) | ||
| 681 | return r; | ||
| 682 | mutex_lock(&ctxt->lock); | ||
| 683 | } | ||
| 684 | mutex_unlock(&ctxt->lock); | ||
| 685 | |||
| 686 | if (len > count) | ||
| 687 | len = count; | ||
| 688 | |||
| 689 | if (copy_to_user(buf, msg, len)) | ||
| 690 | return -EFAULT; | ||
| 691 | |||
| 692 | return len; | ||
| 693 | } | ||
| 694 | |||
| 695 | |||
| 696 | static ssize_t qmi_write(struct file *fp, const char __user *buf, | ||
| 697 | size_t count, loff_t *pos) | ||
| 698 | { | ||
| 699 | struct qmi_ctxt *ctxt = fp->private_data; | ||
| 700 | unsigned char cmd[64]; | ||
| 701 | int len; | ||
| 702 | int r; | ||
| 703 | |||
| 704 | if (count < 1) | ||
| 705 | return 0; | ||
| 706 | |||
| 707 | len = count > 63 ? 63 : count; | ||
| 708 | |||
| 709 | if (copy_from_user(cmd, buf, len)) | ||
| 710 | return -EFAULT; | ||
| 711 | |||
| 712 | cmd[len] = 0; | ||
| 713 | |||
| 714 | /* lazy */ | ||
| 715 | if (cmd[len-1] == '\n') { | ||
| 716 | cmd[len-1] = 0; | ||
| 717 | len--; | ||
| 718 | } | ||
| 719 | |||
| 720 | if (!strncmp(cmd, "verbose", 7)) { | ||
| 721 | verbose = 1; | ||
| 722 | } else if (!strncmp(cmd, "terse", 5)) { | ||
| 723 | verbose = 0; | ||
| 724 | } else if (!strncmp(cmd, "poll", 4)) { | ||
| 725 | ctxt->state_dirty = 1; | ||
| 726 | wake_up(&qmi_wait_queue); | ||
| 727 | } else if (!strncmp(cmd, "down", 4)) { | ||
| 728 | retry_down: | ||
| 729 | mutex_lock(&ctxt->lock); | ||
| 730 | if (ctxt->wds_busy) { | ||
| 731 | mutex_unlock(&ctxt->lock); | ||
| 732 | r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy); | ||
| 733 | if (r < 0) | ||
| 734 | return r; | ||
| 735 | goto retry_down; | ||
| 736 | } | ||
| 737 | ctxt->wds_busy = 1; | ||
| 738 | qmi_network_down(ctxt); | ||
| 739 | mutex_unlock(&ctxt->lock); | ||
| 740 | } else if (!strncmp(cmd, "up:", 3)) { | ||
| 741 | retry_up: | ||
| 742 | mutex_lock(&ctxt->lock); | ||
| 743 | if (ctxt->wds_busy) { | ||
| 744 | mutex_unlock(&ctxt->lock); | ||
| 745 | r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy); | ||
| 746 | if (r < 0) | ||
| 747 | return r; | ||
| 748 | goto retry_up; | ||
| 749 | } | ||
| 750 | ctxt->wds_busy = 1; | ||
| 751 | qmi_network_up(ctxt, cmd+3); | ||
| 752 | mutex_unlock(&ctxt->lock); | ||
| 753 | } else { | ||
| 754 | return -EINVAL; | ||
| 755 | } | ||
| 756 | |||
| 757 | return count; | ||
| 758 | } | ||
| 759 | |||
| 760 | static int qmi_open(struct inode *ip, struct file *fp) | ||
| 761 | { | ||
| 762 | struct qmi_ctxt *ctxt = qmi_minor_to_ctxt(MINOR(ip->i_rdev)); | ||
| 763 | int r = 0; | ||
| 764 | |||
| 765 | if (!ctxt) { | ||
| 766 | printk(KERN_ERR "unknown qmi misc %d\n", MINOR(ip->i_rdev)); | ||
| 767 | return -ENODEV; | ||
| 768 | } | ||
| 769 | |||
| 770 | fp->private_data = ctxt; | ||
| 771 | |||
| 772 | mutex_lock(&ctxt->lock); | ||
| 773 | if (ctxt->ch == 0) | ||
| 774 | r = smd_open(ctxt->ch_name, &ctxt->ch, ctxt, qmi_notify); | ||
| 775 | if (r == 0) | ||
| 776 | wake_up(&qmi_wait_queue); | ||
| 777 | mutex_unlock(&ctxt->lock); | ||
| 778 | |||
| 779 | return r; | ||
| 780 | } | ||
| 781 | |||
| 782 | static int qmi_release(struct inode *ip, struct file *fp) | ||
| 783 | { | ||
| 784 | return 0; | ||
| 785 | } | ||
| 786 | |||
| 787 | static struct file_operations qmi_fops = { | ||
| 788 | .owner = THIS_MODULE, | ||
| 789 | .read = qmi_read, | ||
| 790 | .write = qmi_write, | ||
| 791 | .open = qmi_open, | ||
| 792 | .release = qmi_release, | ||
| 793 | }; | ||
| 794 | |||
| 795 | static struct qmi_ctxt qmi_device0 = { | ||
| 796 | .ch_name = "SMD_DATA5_CNTL", | ||
| 797 | .misc = { | ||
| 798 | .minor = MISC_DYNAMIC_MINOR, | ||
| 799 | .name = "qmi0", | ||
| 800 | .fops = &qmi_fops, | ||
| 801 | } | ||
| 802 | }; | ||
| 803 | static struct qmi_ctxt qmi_device1 = { | ||
| 804 | .ch_name = "SMD_DATA6_CNTL", | ||
| 805 | .misc = { | ||
| 806 | .minor = MISC_DYNAMIC_MINOR, | ||
| 807 | .name = "qmi1", | ||
| 808 | .fops = &qmi_fops, | ||
| 809 | } | ||
| 810 | }; | ||
| 811 | static struct qmi_ctxt qmi_device2 = { | ||
| 812 | .ch_name = "SMD_DATA7_CNTL", | ||
| 813 | .misc = { | ||
| 814 | .minor = MISC_DYNAMIC_MINOR, | ||
| 815 | .name = "qmi2", | ||
| 816 | .fops = &qmi_fops, | ||
| 817 | } | ||
| 818 | }; | ||
| 819 | |||
| 820 | static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n) | ||
| 821 | { | ||
| 822 | if (n == qmi_device0.misc.minor) | ||
| 823 | return &qmi_device0; | ||
| 824 | if (n == qmi_device1.misc.minor) | ||
| 825 | return &qmi_device1; | ||
| 826 | if (n == qmi_device2.misc.minor) | ||
| 827 | return &qmi_device2; | ||
| 828 | return 0; | ||
| 829 | } | ||
| 830 | |||
| 831 | static int __init qmi_init(void) | ||
| 832 | { | ||
| 833 | int ret; | ||
| 834 | |||
| 835 | qmi_wq = create_singlethread_workqueue("qmi"); | ||
| 836 | if (qmi_wq == 0) | ||
| 837 | return -ENOMEM; | ||
| 838 | |||
| 839 | qmi_ctxt_init(&qmi_device0, 0); | ||
| 840 | qmi_ctxt_init(&qmi_device1, 1); | ||
| 841 | qmi_ctxt_init(&qmi_device2, 2); | ||
| 842 | |||
| 843 | ret = misc_register(&qmi_device0.misc); | ||
| 844 | if (ret == 0) | ||
| 845 | ret = misc_register(&qmi_device1.misc); | ||
| 846 | if (ret == 0) | ||
| 847 | ret = misc_register(&qmi_device2.misc); | ||
| 848 | return ret; | ||
| 849 | } | ||
| 850 | |||
| 851 | module_init(qmi_init); | ||
diff --git a/drivers/staging/dream/smd/smd_rpcrouter.c b/drivers/staging/dream/smd/smd_rpcrouter.c deleted file mode 100644 index 8744a6e499cb..000000000000 --- a/drivers/staging/dream/smd/smd_rpcrouter.c +++ /dev/null | |||
| @@ -1,1261 +0,0 @@ | |||
| 1 | /* arch/arm/mach-msm/smd_rpcrouter.c | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 Google, Inc. | ||
| 4 | * Copyright (c) 2007-2009 QUALCOMM Incorporated. | ||
| 5 | * Author: San Mehat <san@android.com> | ||
| 6 | * | ||
| 7 | * This software is licensed under the terms of the GNU General Public | ||
| 8 | * License version 2, as published by the Free Software Foundation, and | ||
| 9 | * may be copied, distributed, and modified under those terms. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | /* TODO: handle cases where smd_write() will tempfail due to full fifo */ | ||
| 19 | /* TODO: thread priority? schedule a work to bump it? */ | ||
| 20 | /* TODO: maybe make server_list_lock a mutex */ | ||
| 21 | /* TODO: pool fragments to avoid kmalloc/kfree churn */ | ||
| 22 | |||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/kernel.h> | ||
| 25 | #include <linux/string.h> | ||
| 26 | #include <linux/errno.h> | ||
| 27 | #include <linux/cdev.h> | ||
| 28 | #include <linux/init.h> | ||
| 29 | #include <linux/device.h> | ||
| 30 | #include <linux/types.h> | ||
| 31 | #include <linux/delay.h> | ||
| 32 | #include <linux/fs.h> | ||
| 33 | #include <linux/err.h> | ||
| 34 | #include <linux/sched.h> | ||
| 35 | #include <linux/poll.h> | ||
| 36 | #include <linux/slab.h> | ||
| 37 | #include <asm/uaccess.h> | ||
| 38 | #include <asm/byteorder.h> | ||
| 39 | #include <linux/platform_device.h> | ||
| 40 | #include <linux/uaccess.h> | ||
| 41 | |||
| 42 | #include <mach/msm_smd.h> | ||
| 43 | #include "smd_rpcrouter.h" | ||
| 44 | |||
| 45 | #define TRACE_R2R_MSG 0 | ||
| 46 | #define TRACE_R2R_RAW 0 | ||
| 47 | #define TRACE_RPC_MSG 0 | ||
| 48 | #define TRACE_NOTIFY_MSG 0 | ||
| 49 | |||
| 50 | #define MSM_RPCROUTER_DEBUG 0 | ||
| 51 | #define MSM_RPCROUTER_DEBUG_PKT 0 | ||
| 52 | #define MSM_RPCROUTER_R2R_DEBUG 0 | ||
| 53 | #define DUMP_ALL_RECEIVED_HEADERS 0 | ||
| 54 | |||
| 55 | #define DIAG(x...) printk("[RR] ERROR " x) | ||
| 56 | |||
| 57 | #if MSM_RPCROUTER_DEBUG | ||
| 58 | #define D(x...) printk(x) | ||
| 59 | #else | ||
| 60 | #define D(x...) do {} while (0) | ||
| 61 | #endif | ||
| 62 | |||
| 63 | #if TRACE_R2R_MSG | ||
| 64 | #define RR(x...) printk("[RR] "x) | ||
| 65 | #else | ||
| 66 | #define RR(x...) do {} while (0) | ||
| 67 | #endif | ||
| 68 | |||
| 69 | #if TRACE_RPC_MSG | ||
| 70 | #define IO(x...) printk("[RPC] "x) | ||
| 71 | #else | ||
| 72 | #define IO(x...) do {} while (0) | ||
| 73 | #endif | ||
| 74 | |||
| 75 | #if TRACE_NOTIFY_MSG | ||
| 76 | #define NTFY(x...) printk(KERN_ERR "[NOTIFY] "x) | ||
| 77 | #else | ||
| 78 | #define NTFY(x...) do {} while (0) | ||
| 79 | #endif | ||
| 80 | |||
| 81 | static LIST_HEAD(local_endpoints); | ||
| 82 | static LIST_HEAD(remote_endpoints); | ||
| 83 | |||
| 84 | static LIST_HEAD(server_list); | ||
| 85 | |||
| 86 | static smd_channel_t *smd_channel; | ||
| 87 | static int initialized; | ||
| 88 | static wait_queue_head_t newserver_wait; | ||
| 89 | static wait_queue_head_t smd_wait; | ||
| 90 | |||
| 91 | static DEFINE_SPINLOCK(local_endpoints_lock); | ||
| 92 | static DEFINE_SPINLOCK(remote_endpoints_lock); | ||
| 93 | static DEFINE_SPINLOCK(server_list_lock); | ||
| 94 | static DEFINE_SPINLOCK(smd_lock); | ||
| 95 | |||
| 96 | static struct workqueue_struct *rpcrouter_workqueue; | ||
| 97 | static int rpcrouter_need_len; | ||
| 98 | |||
| 99 | static atomic_t next_xid = ATOMIC_INIT(1); | ||
| 100 | static uint8_t next_pacmarkid; | ||
| 101 | |||
| 102 | static void do_read_data(struct work_struct *work); | ||
| 103 | static void do_create_pdevs(struct work_struct *work); | ||
| 104 | static void do_create_rpcrouter_pdev(struct work_struct *work); | ||
| 105 | |||
| 106 | static DECLARE_WORK(work_read_data, do_read_data); | ||
| 107 | static DECLARE_WORK(work_create_pdevs, do_create_pdevs); | ||
| 108 | static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev); | ||
| 109 | |||
| 110 | #define RR_STATE_IDLE 0 | ||
| 111 | #define RR_STATE_HEADER 1 | ||
| 112 | #define RR_STATE_BODY 2 | ||
| 113 | #define RR_STATE_ERROR 3 | ||
| 114 | |||
| 115 | struct rr_context { | ||
| 116 | struct rr_packet *pkt; | ||
| 117 | uint8_t *ptr; | ||
| 118 | uint32_t state; /* current assembly state */ | ||
| 119 | uint32_t count; /* bytes needed in this state */ | ||
| 120 | }; | ||
| 121 | |||
| 122 | static struct rr_context the_rr_context; | ||
| 123 | |||
| 124 | static struct platform_device rpcrouter_pdev = { | ||
| 125 | .name = "oncrpc_router", | ||
| 126 | .id = -1, | ||
| 127 | }; | ||
| 128 | |||
| 129 | |||
| 130 | static int rpcrouter_send_control_msg(union rr_control_msg *msg) | ||
| 131 | { | ||
| 132 | struct rr_header hdr; | ||
| 133 | unsigned long flags; | ||
| 134 | int need; | ||
| 135 | |||
| 136 | if (!(msg->cmd == RPCROUTER_CTRL_CMD_HELLO) && !initialized) { | ||
| 137 | printk(KERN_ERR "rpcrouter_send_control_msg(): Warning, " | ||
| 138 | "router not initialized\n"); | ||
| 139 | return -EINVAL; | ||
| 140 | } | ||
| 141 | |||
| 142 | hdr.version = RPCROUTER_VERSION; | ||
| 143 | hdr.type = msg->cmd; | ||
| 144 | hdr.src_pid = RPCROUTER_PID_LOCAL; | ||
| 145 | hdr.src_cid = RPCROUTER_ROUTER_ADDRESS; | ||
| 146 | hdr.confirm_rx = 0; | ||
| 147 | hdr.size = sizeof(*msg); | ||
| 148 | hdr.dst_pid = 0; | ||
| 149 | hdr.dst_cid = RPCROUTER_ROUTER_ADDRESS; | ||
| 150 | |||
| 151 | /* TODO: what if channel is full? */ | ||
| 152 | |||
| 153 | need = sizeof(hdr) + hdr.size; | ||
| 154 | spin_lock_irqsave(&smd_lock, flags); | ||
| 155 | while (smd_write_avail(smd_channel) < need) { | ||
| 156 | spin_unlock_irqrestore(&smd_lock, flags); | ||
| 157 | msleep(250); | ||
| 158 | spin_lock_irqsave(&smd_lock, flags); | ||
| 159 | } | ||
| 160 | smd_write(smd_channel, &hdr, sizeof(hdr)); | ||
| 161 | smd_write(smd_channel, msg, hdr.size); | ||
| 162 | spin_unlock_irqrestore(&smd_lock, flags); | ||
| 163 | return 0; | ||
| 164 | } | ||
| 165 | |||
| 166 | static struct rr_server *rpcrouter_create_server(uint32_t pid, | ||
| 167 | uint32_t cid, | ||
| 168 | uint32_t prog, | ||
| 169 | uint32_t ver) | ||
| 170 | { | ||
| 171 | struct rr_server *server; | ||
| 172 | unsigned long flags; | ||
| 173 | int rc; | ||
| 174 | |||
| 175 | server = kmalloc(sizeof(struct rr_server), GFP_KERNEL); | ||
| 176 | if (!server) | ||
| 177 | return ERR_PTR(-ENOMEM); | ||
| 178 | |||
| 179 | memset(server, 0, sizeof(struct rr_server)); | ||
| 180 | server->pid = pid; | ||
| 181 | server->cid = cid; | ||
| 182 | server->prog = prog; | ||
| 183 | server->vers = ver; | ||
| 184 | |||
| 185 | spin_lock_irqsave(&server_list_lock, flags); | ||
| 186 | list_add_tail(&server->list, &server_list); | ||
| 187 | spin_unlock_irqrestore(&server_list_lock, flags); | ||
| 188 | |||
| 189 | if (pid == RPCROUTER_PID_REMOTE) { | ||
| 190 | rc = msm_rpcrouter_create_server_cdev(server); | ||
| 191 | if (rc < 0) | ||
| 192 | goto out_fail; | ||
| 193 | } | ||
| 194 | return server; | ||
| 195 | out_fail: | ||
| 196 | spin_lock_irqsave(&server_list_lock, flags); | ||
| 197 | list_del(&server->list); | ||
| 198 | spin_unlock_irqrestore(&server_list_lock, flags); | ||
| 199 | kfree(server); | ||
| 200 | return ERR_PTR(rc); | ||
| 201 | } | ||
| 202 | |||
| 203 | static void rpcrouter_destroy_server(struct rr_server *server) | ||
| 204 | { | ||
| 205 | unsigned long flags; | ||
| 206 | |||
| 207 | spin_lock_irqsave(&server_list_lock, flags); | ||
| 208 | list_del(&server->list); | ||
| 209 | spin_unlock_irqrestore(&server_list_lock, flags); | ||
| 210 | device_destroy(msm_rpcrouter_class, server->device_number); | ||
| 211 | kfree(server); | ||
| 212 | } | ||
| 213 | |||
| 214 | static struct rr_server *rpcrouter_lookup_server(uint32_t prog, uint32_t ver) | ||
| 215 | { | ||
| 216 | struct rr_server *server; | ||
| 217 | unsigned long flags; | ||
| 218 | |||
| 219 | spin_lock_irqsave(&server_list_lock, flags); | ||
| 220 | list_for_each_entry(server, &server_list, list) { | ||
| 221 | if (server->prog == prog | ||
| 222 | && server->vers == ver) { | ||
| 223 | spin_unlock_irqrestore(&server_list_lock, flags); | ||
| 224 | return server; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | spin_unlock_irqrestore(&server_list_lock, flags); | ||
| 228 | return NULL; | ||
| 229 | } | ||
| 230 | |||
| 231 | static struct rr_server *rpcrouter_lookup_server_by_dev(dev_t dev) | ||
| 232 | { | ||
| 233 | struct rr_server *server; | ||
| 234 | unsigned long flags; | ||
| 235 | |||
| 236 | spin_lock_irqsave(&server_list_lock, flags); | ||
| 237 | list_for_each_entry(server, &server_list, list) { | ||
| 238 | if (server->device_number == dev) { | ||
| 239 | spin_unlock_irqrestore(&server_list_lock, flags); | ||
| 240 | return server; | ||
| 241 | } | ||
| 242 | } | ||
| 243 | spin_unlock_irqrestore(&server_list_lock, flags); | ||
| 244 | return NULL; | ||
| 245 | } | ||
| 246 | |||
| 247 | struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev) | ||
| 248 | { | ||
| 249 | struct msm_rpc_endpoint *ept; | ||
| 250 | unsigned long flags; | ||
| 251 | |||
| 252 | ept = kmalloc(sizeof(struct msm_rpc_endpoint), GFP_KERNEL); | ||
| 253 | if (!ept) | ||
| 254 | return NULL; | ||
| 255 | memset(ept, 0, sizeof(struct msm_rpc_endpoint)); | ||
| 256 | |||
| 257 | /* mark no reply outstanding */ | ||
| 258 | ept->reply_pid = 0xffffffff; | ||
| 259 | |||
| 260 | ept->cid = (uint32_t) ept; | ||
| 261 | ept->pid = RPCROUTER_PID_LOCAL; | ||
| 262 | ept->dev = dev; | ||
| 263 | |||
| 264 | if ((dev != msm_rpcrouter_devno) && (dev != MKDEV(0, 0))) { | ||
| 265 | struct rr_server *srv; | ||
| 266 | /* | ||
| 267 | * This is a userspace client which opened | ||
| 268 | * a program/ver devicenode. Bind the client | ||
| 269 | * to that destination | ||
| 270 | */ | ||
| 271 | srv = rpcrouter_lookup_server_by_dev(dev); | ||
| 272 | /* TODO: bug? really? */ | ||
| 273 | BUG_ON(!srv); | ||
| 274 | |||
| 275 | ept->dst_pid = srv->pid; | ||
| 276 | ept->dst_cid = srv->cid; | ||
| 277 | ept->dst_prog = cpu_to_be32(srv->prog); | ||
| 278 | ept->dst_vers = cpu_to_be32(srv->vers); | ||
| 279 | |||
| 280 | D("Creating local ept %p @ %08x:%08x\n", ept, srv->prog, srv->vers); | ||
| 281 | } else { | ||
| 282 | /* mark not connected */ | ||
| 283 | ept->dst_pid = 0xffffffff; | ||
| 284 | D("Creating a master local ept %p\n", ept); | ||
| 285 | } | ||
| 286 | |||
| 287 | init_waitqueue_head(&ept->wait_q); | ||
| 288 | INIT_LIST_HEAD(&ept->read_q); | ||
| 289 | spin_lock_init(&ept->read_q_lock); | ||
| 290 | INIT_LIST_HEAD(&ept->incomplete); | ||
| 291 | |||
| 292 | spin_lock_irqsave(&local_endpoints_lock, flags); | ||
| 293 | list_add_tail(&ept->list, &local_endpoints); | ||
| 294 | spin_unlock_irqrestore(&local_endpoints_lock, flags); | ||
| 295 | return ept; | ||
| 296 | } | ||
| 297 | |||
| 298 | int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept) | ||
| 299 | { | ||
| 300 | int rc; | ||
| 301 | union rr_control_msg msg; | ||
| 302 | |||
| 303 | msg.cmd = RPCROUTER_CTRL_CMD_REMOVE_CLIENT; | ||
| 304 | msg.cli.pid = ept->pid; | ||
| 305 | msg.cli.cid = ept->cid; | ||
| 306 | |||
| 307 | RR("x REMOVE_CLIENT id=%d:%08x\n", ept->pid, ept->cid); | ||
| 308 | rc = rpcrouter_send_control_msg(&msg); | ||
| 309 | if (rc < 0) | ||
| 310 | return rc; | ||
| 311 | |||
| 312 | list_del(&ept->list); | ||
| 313 | kfree(ept); | ||
| 314 | return 0; | ||
| 315 | } | ||
| 316 | |||
| 317 | static int rpcrouter_create_remote_endpoint(uint32_t cid) | ||
| 318 | { | ||
| 319 | struct rr_remote_endpoint *new_c; | ||
| 320 | unsigned long flags; | ||
| 321 | |||
| 322 | new_c = kmalloc(sizeof(struct rr_remote_endpoint), GFP_KERNEL); | ||
| 323 | if (!new_c) | ||
| 324 | return -ENOMEM; | ||
| 325 | memset(new_c, 0, sizeof(struct rr_remote_endpoint)); | ||
| 326 | |||
| 327 | new_c->cid = cid; | ||
| 328 | new_c->pid = RPCROUTER_PID_REMOTE; | ||
| 329 | init_waitqueue_head(&new_c->quota_wait); | ||
| 330 | spin_lock_init(&new_c->quota_lock); | ||
| 331 | |||
| 332 | spin_lock_irqsave(&remote_endpoints_lock, flags); | ||
| 333 | list_add_tail(&new_c->list, &remote_endpoints); | ||
| 334 | spin_unlock_irqrestore(&remote_endpoints_lock, flags); | ||
| 335 | return 0; | ||
| 336 | } | ||
| 337 | |||
| 338 | static struct msm_rpc_endpoint *rpcrouter_lookup_local_endpoint(uint32_t cid) | ||
| 339 | { | ||
| 340 | struct msm_rpc_endpoint *ept; | ||
| 341 | unsigned long flags; | ||
| 342 | |||
| 343 | spin_lock_irqsave(&local_endpoints_lock, flags); | ||
| 344 | list_for_each_entry(ept, &local_endpoints, list) { | ||
| 345 | if (ept->cid == cid) { | ||
| 346 | spin_unlock_irqrestore(&local_endpoints_lock, flags); | ||
| 347 | return ept; | ||
| 348 | } | ||
| 349 | } | ||
| 350 | spin_unlock_irqrestore(&local_endpoints_lock, flags); | ||
| 351 | return NULL; | ||
| 352 | } | ||
| 353 | |||
| 354 | static struct rr_remote_endpoint *rpcrouter_lookup_remote_endpoint(uint32_t cid) | ||
| 355 | { | ||
| 356 | struct rr_remote_endpoint *ept; | ||
| 357 | unsigned long flags; | ||
| 358 | |||
| 359 | spin_lock_irqsave(&remote_endpoints_lock, flags); | ||
| 360 | list_for_each_entry(ept, &remote_endpoints, list) { | ||
| 361 | if (ept->cid == cid) { | ||
| 362 | spin_unlock_irqrestore(&remote_endpoints_lock, flags); | ||
| 363 | return ept; | ||
| 364 | } | ||
| 365 | } | ||
| 366 | spin_unlock_irqrestore(&remote_endpoints_lock, flags); | ||
| 367 | return NULL; | ||
| 368 | } | ||
| 369 | |||
| 370 | static int process_control_msg(union rr_control_msg *msg, int len) | ||
| 371 | { | ||
| 372 | union rr_control_msg ctl; | ||
| 373 | struct rr_server *server; | ||
| 374 | struct rr_remote_endpoint *r_ept; | ||
| 375 | int rc = 0; | ||
| 376 | unsigned long flags; | ||
| 377 | |||
| 378 | if (len != sizeof(*msg)) { | ||
| 379 | printk(KERN_ERR "rpcrouter: r2r msg size %d != %d\n", | ||
| 380 | len, sizeof(*msg)); | ||
| 381 | return -EINVAL; | ||
| 382 | } | ||
| 383 | |||
| 384 | switch (msg->cmd) { | ||
| 385 | case RPCROUTER_CTRL_CMD_HELLO: | ||
| 386 | RR("o HELLO\n"); | ||
| 387 | |||
| 388 | RR("x HELLO\n"); | ||
| 389 | memset(&ctl, 0, sizeof(ctl)); | ||
| 390 | ctl.cmd = RPCROUTER_CTRL_CMD_HELLO; | ||
| 391 | rpcrouter_send_control_msg(&ctl); | ||
| 392 | |||
| 393 | initialized = 1; | ||
| 394 | |||
| 395 | /* Send list of servers one at a time */ | ||
| 396 | ctl.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER; | ||
| 397 | |||
| 398 | /* TODO: long time to hold a spinlock... */ | ||
| 399 | spin_lock_irqsave(&server_list_lock, flags); | ||
| 400 | list_for_each_entry(server, &server_list, list) { | ||
| 401 | ctl.srv.pid = server->pid; | ||
| 402 | ctl.srv.cid = server->cid; | ||
| 403 | ctl.srv.prog = server->prog; | ||
| 404 | ctl.srv.vers = server->vers; | ||
| 405 | |||
| 406 | RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n", | ||
| 407 | server->pid, server->cid, | ||
| 408 | server->prog, server->vers); | ||
| 409 | |||
| 410 | rpcrouter_send_control_msg(&ctl); | ||
| 411 | } | ||
| 412 | spin_unlock_irqrestore(&server_list_lock, flags); | ||
| 413 | |||
| 414 | queue_work(rpcrouter_workqueue, &work_create_rpcrouter_pdev); | ||
| 415 | break; | ||
| 416 | |||
| 417 | case RPCROUTER_CTRL_CMD_RESUME_TX: | ||
| 418 | RR("o RESUME_TX id=%d:%08x\n", msg->cli.pid, msg->cli.cid); | ||
| 419 | |||
| 420 | r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid); | ||
| 421 | if (!r_ept) { | ||
| 422 | printk(KERN_ERR | ||
| 423 | "rpcrouter: Unable to resume client\n"); | ||
| 424 | break; | ||
| 425 | } | ||
| 426 | spin_lock_irqsave(&r_ept->quota_lock, flags); | ||
| 427 | r_ept->tx_quota_cntr = 0; | ||
| 428 | spin_unlock_irqrestore(&r_ept->quota_lock, flags); | ||
| 429 | wake_up(&r_ept->quota_wait); | ||
| 430 | break; | ||
| 431 | |||
| 432 | case RPCROUTER_CTRL_CMD_NEW_SERVER: | ||
| 433 | RR("o NEW_SERVER id=%d:%08x prog=%08x:%08x\n", | ||
| 434 | msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers); | ||
| 435 | |||
| 436 | server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers); | ||
| 437 | |||
| 438 | if (!server) { | ||
| 439 | server = rpcrouter_create_server( | ||
| 440 | msg->srv.pid, msg->srv.cid, | ||
| 441 | msg->srv.prog, msg->srv.vers); | ||
| 442 | if (!server) | ||
| 443 | return -ENOMEM; | ||
| 444 | /* | ||
| 445 | * XXX: Verify that its okay to add the | ||
| 446 | * client to our remote client list | ||
| 447 | * if we get a NEW_SERVER notification | ||
| 448 | */ | ||
| 449 | if (!rpcrouter_lookup_remote_endpoint(msg->srv.cid)) { | ||
| 450 | rc = rpcrouter_create_remote_endpoint( | ||
| 451 | msg->srv.cid); | ||
| 452 | if (rc < 0) | ||
| 453 | printk(KERN_ERR | ||
| 454 | "rpcrouter:Client create" | ||
| 455 | "error (%d)\n", rc); | ||
| 456 | } | ||
| 457 | schedule_work(&work_create_pdevs); | ||
| 458 | wake_up(&newserver_wait); | ||
| 459 | } else { | ||
| 460 | if ((server->pid == msg->srv.pid) && | ||
| 461 | (server->cid == msg->srv.cid)) { | ||
| 462 | printk(KERN_ERR "rpcrouter: Duplicate svr\n"); | ||
| 463 | } else { | ||
| 464 | server->pid = msg->srv.pid; | ||
| 465 | server->cid = msg->srv.cid; | ||
| 466 | } | ||
| 467 | } | ||
| 468 | break; | ||
| 469 | |||
| 470 | case RPCROUTER_CTRL_CMD_REMOVE_SERVER: | ||
| 471 | RR("o REMOVE_SERVER prog=%08x:%d\n", | ||
| 472 | msg->srv.prog, msg->srv.vers); | ||
| 473 | server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers); | ||
| 474 | if (server) | ||
| 475 | rpcrouter_destroy_server(server); | ||
| 476 | break; | ||
| 477 | |||
| 478 | case RPCROUTER_CTRL_CMD_REMOVE_CLIENT: | ||
| 479 | RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.pid, msg->cli.cid); | ||
| 480 | if (msg->cli.pid != RPCROUTER_PID_REMOTE) { | ||
| 481 | printk(KERN_ERR | ||
| 482 | "rpcrouter: Denying remote removal of " | ||
| 483 | "local client\n"); | ||
| 484 | break; | ||
| 485 | } | ||
| 486 | r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid); | ||
| 487 | if (r_ept) { | ||
| 488 | spin_lock_irqsave(&remote_endpoints_lock, flags); | ||
| 489 | list_del(&r_ept->list); | ||
| 490 | spin_unlock_irqrestore(&remote_endpoints_lock, flags); | ||
| 491 | kfree(r_ept); | ||
| 492 | } | ||
| 493 | |||
| 494 | /* Notify local clients of this event */ | ||
| 495 | printk(KERN_ERR "rpcrouter: LOCAL NOTIFICATION NOT IMP\n"); | ||
| 496 | rc = -ENOSYS; | ||
| 497 | |||
| 498 | break; | ||
| 499 | default: | ||
| 500 | RR("o UNKNOWN(%08x)\n", msg->cmd); | ||
| 501 | rc = -ENOSYS; | ||
| 502 | } | ||
| 503 | |||
| 504 | return rc; | ||
| 505 | } | ||
| 506 | |||
| 507 | static void do_create_rpcrouter_pdev(struct work_struct *work) | ||
| 508 | { | ||
| 509 | platform_device_register(&rpcrouter_pdev); | ||
| 510 | } | ||
| 511 | |||
| 512 | static void do_create_pdevs(struct work_struct *work) | ||
| 513 | { | ||
| 514 | unsigned long flags; | ||
| 515 | struct rr_server *server; | ||
| 516 | |||
| 517 | /* TODO: race if destroyed while being registered */ | ||
| 518 | spin_lock_irqsave(&server_list_lock, flags); | ||
| 519 | list_for_each_entry(server, &server_list, list) { | ||
| 520 | if (server->pid == RPCROUTER_PID_REMOTE) { | ||
| 521 | if (server->pdev_name[0] == 0) { | ||
| 522 | spin_unlock_irqrestore(&server_list_lock, | ||
| 523 | flags); | ||
| 524 | msm_rpcrouter_create_server_pdev(server); | ||
| 525 | schedule_work(&work_create_pdevs); | ||
| 526 | return; | ||
| 527 | } | ||
| 528 | } | ||
| 529 | } | ||
| 530 | spin_unlock_irqrestore(&server_list_lock, flags); | ||
| 531 | } | ||
| 532 | |||
| 533 | static void rpcrouter_smdnotify(void *_dev, unsigned event) | ||
| 534 | { | ||
| 535 | if (event != SMD_EVENT_DATA) | ||
| 536 | return; | ||
| 537 | |||
| 538 | wake_up(&smd_wait); | ||
| 539 | } | ||
| 540 | |||
| 541 | static void *rr_malloc(unsigned sz) | ||
| 542 | { | ||
| 543 | void *ptr = kmalloc(sz, GFP_KERNEL); | ||
| 544 | if (ptr) | ||
| 545 | return ptr; | ||
| 546 | |||
| 547 | printk(KERN_ERR "rpcrouter: kmalloc of %d failed, retrying...\n", sz); | ||
| 548 | do { | ||
| 549 | ptr = kmalloc(sz, GFP_KERNEL); | ||
| 550 | } while (!ptr); | ||
| 551 | |||
| 552 | return ptr; | ||
| 553 | } | ||
| 554 | |||
| 555 | /* TODO: deal with channel teardown / restore */ | ||
| 556 | static int rr_read(void *data, int len) | ||
| 557 | { | ||
| 558 | int rc; | ||
| 559 | unsigned long flags; | ||
| 560 | // printk("rr_read() %d\n", len); | ||
| 561 | for(;;) { | ||
| 562 | spin_lock_irqsave(&smd_lock, flags); | ||
| 563 | if (smd_read_avail(smd_channel) >= len) { | ||
| 564 | rc = smd_read(smd_channel, data, len); | ||
| 565 | spin_unlock_irqrestore(&smd_lock, flags); | ||
| 566 | if (rc == len) | ||
| 567 | return 0; | ||
| 568 | else | ||
| 569 | return -EIO; | ||
| 570 | } | ||
| 571 | rpcrouter_need_len = len; | ||
| 572 | spin_unlock_irqrestore(&smd_lock, flags); | ||
| 573 | |||
| 574 | // printk("rr_read: waiting (%d)\n", len); | ||
| 575 | wait_event(smd_wait, smd_read_avail(smd_channel) >= len); | ||
| 576 | } | ||
| 577 | return 0; | ||
| 578 | } | ||
| 579 | |||
| 580 | static uint32_t r2r_buf[RPCROUTER_MSGSIZE_MAX]; | ||
| 581 | |||
| 582 | static void do_read_data(struct work_struct *work) | ||
| 583 | { | ||
| 584 | struct rr_header hdr; | ||
| 585 | struct rr_packet *pkt; | ||
| 586 | struct rr_fragment *frag; | ||
| 587 | struct msm_rpc_endpoint *ept; | ||
| 588 | uint32_t pm, mid; | ||
| 589 | unsigned long flags; | ||
| 590 | |||
| 591 | if (rr_read(&hdr, sizeof(hdr))) | ||
| 592 | goto fail_io; | ||
| 593 | |||
| 594 | #if TRACE_R2R_RAW | ||
| 595 | RR("- ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n", | ||
| 596 | hdr.version, hdr.type, hdr.src_pid, hdr.src_cid, | ||
| 597 | hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid); | ||
| 598 | #endif | ||
| 599 | |||
| 600 | if (hdr.version != RPCROUTER_VERSION) { | ||
| 601 | DIAG("version %d != %d\n", hdr.version, RPCROUTER_VERSION); | ||
| 602 | goto fail_data; | ||
| 603 | } | ||
| 604 | if (hdr.size > RPCROUTER_MSGSIZE_MAX) { | ||
| 605 | DIAG("msg size %d > max %d\n", hdr.size, RPCROUTER_MSGSIZE_MAX); | ||
| 606 | goto fail_data; | ||
| 607 | } | ||
| 608 | |||
| 609 | if (hdr.dst_cid == RPCROUTER_ROUTER_ADDRESS) { | ||
| 610 | if (rr_read(r2r_buf, hdr.size)) | ||
| 611 | goto fail_io; | ||
| 612 | process_control_msg((void*) r2r_buf, hdr.size); | ||
| 613 | goto done; | ||
| 614 | } | ||
| 615 | |||
| 616 | if (hdr.size < sizeof(pm)) { | ||
| 617 | DIAG("runt packet (no pacmark)\n"); | ||
| 618 | goto fail_data; | ||
| 619 | } | ||
| 620 | if (rr_read(&pm, sizeof(pm))) | ||
| 621 | goto fail_io; | ||
| 622 | |||
| 623 | hdr.size -= sizeof(pm); | ||
| 624 | |||
| 625 | frag = rr_malloc(hdr.size + sizeof(*frag)); | ||
| 626 | frag->next = NULL; | ||
| 627 | frag->length = hdr.size; | ||
| 628 | if (rr_read(frag->data, hdr.size)) | ||
| 629 | goto fail_io; | ||
| 630 | |||
| 631 | ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid); | ||
| 632 | if (!ept) { | ||
| 633 | DIAG("no local ept for cid %08x\n", hdr.dst_cid); | ||
| 634 | kfree(frag); | ||
| 635 | goto done; | ||
| 636 | } | ||
| 637 | |||
| 638 | /* See if there is already a partial packet that matches our mid | ||
| 639 | * and if so, append this fragment to that packet. | ||
| 640 | */ | ||
| 641 | mid = PACMARK_MID(pm); | ||
| 642 | list_for_each_entry(pkt, &ept->incomplete, list) { | ||
| 643 | if (pkt->mid == mid) { | ||
| 644 | pkt->last->next = frag; | ||
| 645 | pkt->last = frag; | ||
| 646 | pkt->length += frag->length; | ||
| 647 | if (PACMARK_LAST(pm)) { | ||
| 648 | list_del(&pkt->list); | ||
| 649 | goto packet_complete; | ||
| 650 | } | ||
| 651 | goto done; | ||
| 652 | } | ||
| 653 | } | ||
| 654 | /* This mid is new -- create a packet for it, and put it on | ||
| 655 | * the incomplete list if this fragment is not a last fragment, | ||
| 656 | * otherwise put it on the read queue. | ||
| 657 | */ | ||
| 658 | pkt = rr_malloc(sizeof(struct rr_packet)); | ||
| 659 | pkt->first = frag; | ||
| 660 | pkt->last = frag; | ||
| 661 | memcpy(&pkt->hdr, &hdr, sizeof(hdr)); | ||
| 662 | pkt->mid = mid; | ||
| 663 | pkt->length = frag->length; | ||
| 664 | if (!PACMARK_LAST(pm)) { | ||
| 665 | list_add_tail(&pkt->list, &ept->incomplete); | ||
| 666 | goto done; | ||
| 667 | } | ||
| 668 | |||
| 669 | packet_complete: | ||
| 670 | spin_lock_irqsave(&ept->read_q_lock, flags); | ||
| 671 | list_add_tail(&pkt->list, &ept->read_q); | ||
| 672 | wake_up(&ept->wait_q); | ||
| 673 | spin_unlock_irqrestore(&ept->read_q_lock, flags); | ||
| 674 | done: | ||
| 675 | |||
| 676 | if (hdr.confirm_rx) { | ||
| 677 | union rr_control_msg msg; | ||
| 678 | |||
| 679 | msg.cmd = RPCROUTER_CTRL_CMD_RESUME_TX; | ||
| 680 | msg.cli.pid = hdr.dst_pid; | ||
| 681 | msg.cli.cid = hdr.dst_cid; | ||
| 682 | |||
| 683 | RR("x RESUME_TX id=%d:%08x\n", msg.cli.pid, msg.cli.cid); | ||
| 684 | rpcrouter_send_control_msg(&msg); | ||
| 685 | } | ||
| 686 | |||
| 687 | queue_work(rpcrouter_workqueue, &work_read_data); | ||
| 688 | return; | ||
| 689 | |||
| 690 | fail_io: | ||
| 691 | fail_data: | ||
| 692 | printk(KERN_ERR "rpc_router has died\n"); | ||
| 693 | } | ||
| 694 | |||
| 695 | void msm_rpc_setup_req(struct rpc_request_hdr *hdr, uint32_t prog, | ||
| 696 | uint32_t vers, uint32_t proc) | ||
| 697 | { | ||
| 698 | memset(hdr, 0, sizeof(struct rpc_request_hdr)); | ||
| 699 | hdr->xid = cpu_to_be32(atomic_add_return(1, &next_xid)); | ||
| 700 | hdr->rpc_vers = cpu_to_be32(2); | ||
| 701 | hdr->prog = cpu_to_be32(prog); | ||
| 702 | hdr->vers = cpu_to_be32(vers); | ||
| 703 | hdr->procedure = cpu_to_be32(proc); | ||
| 704 | } | ||
| 705 | |||
| 706 | struct msm_rpc_endpoint *msm_rpc_open(void) | ||
| 707 | { | ||
| 708 | struct msm_rpc_endpoint *ept; | ||
| 709 | |||
| 710 | ept = msm_rpcrouter_create_local_endpoint(MKDEV(0, 0)); | ||
| 711 | if (ept == NULL) | ||
| 712 | return ERR_PTR(-ENOMEM); | ||
| 713 | |||
| 714 | return ept; | ||
| 715 | } | ||
| 716 | |||
| 717 | int msm_rpc_close(struct msm_rpc_endpoint *ept) | ||
| 718 | { | ||
| 719 | return msm_rpcrouter_destroy_local_endpoint(ept); | ||
| 720 | } | ||
| 721 | EXPORT_SYMBOL(msm_rpc_close); | ||
| 722 | |||
| 723 | int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) | ||
| 724 | { | ||
| 725 | struct rr_header hdr; | ||
| 726 | uint32_t pacmark; | ||
| 727 | struct rpc_request_hdr *rq = buffer; | ||
| 728 | struct rr_remote_endpoint *r_ept; | ||
| 729 | unsigned long flags; | ||
| 730 | int needed; | ||
| 731 | DEFINE_WAIT(__wait); | ||
| 732 | |||
| 733 | /* TODO: fragmentation for large outbound packets */ | ||
| 734 | if (count > (RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t)) || !count) | ||
| 735 | return -EINVAL; | ||
| 736 | |||
| 737 | /* snoop the RPC packet and enforce permissions */ | ||
| 738 | |||
| 739 | /* has to have at least the xid and type fields */ | ||
| 740 | if (count < (sizeof(uint32_t) * 2)) { | ||
| 741 | printk(KERN_ERR "rr_write: rejecting runt packet\n"); | ||
| 742 | return -EINVAL; | ||
| 743 | } | ||
| 744 | |||
| 745 | if (rq->type == 0) { | ||
| 746 | /* RPC CALL */ | ||
| 747 | if (count < (sizeof(uint32_t) * 6)) { | ||
| 748 | printk(KERN_ERR | ||
| 749 | "rr_write: rejecting runt call packet\n"); | ||
| 750 | return -EINVAL; | ||
| 751 | } | ||
| 752 | if (ept->dst_pid == 0xffffffff) { | ||
| 753 | printk(KERN_ERR "rr_write: not connected\n"); | ||
| 754 | return -ENOTCONN; | ||
| 755 | } | ||
| 756 | |||
| 757 | #if CONFIG_MSM_AMSS_VERSION >= 6350 | ||
| 758 | if ((ept->dst_prog != rq->prog) || | ||
| 759 | !msm_rpc_is_compatible_version( | ||
| 760 | be32_to_cpu(ept->dst_vers), | ||
| 761 | be32_to_cpu(rq->vers))) { | ||
| 762 | #else | ||
| 763 | if (ept->dst_prog != rq->prog || ept->dst_vers != rq->vers) { | ||
| 764 | #endif | ||
| 765 | printk(KERN_ERR | ||
| 766 | "rr_write: cannot write to %08x:%d " | ||
| 767 | "(bound to %08x:%d)\n", | ||
| 768 | be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), | ||
| 769 | be32_to_cpu(ept->dst_prog), | ||
| 770 | be32_to_cpu(ept->dst_vers)); | ||
| 771 | return -EINVAL; | ||
| 772 | } | ||
| 773 | hdr.dst_pid = ept->dst_pid; | ||
| 774 | hdr.dst_cid = ept->dst_cid; | ||
| 775 | IO("CALL on ept %p to %08x:%08x @ %d:%08x (%d bytes) (xid %x proc %x)\n", | ||
| 776 | ept, | ||
| 777 | be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), | ||
| 778 | ept->dst_pid, ept->dst_cid, count, | ||
| 779 | be32_to_cpu(rq->xid), be32_to_cpu(rq->procedure)); | ||
| 780 | } else { | ||
| 781 | /* RPC REPLY */ | ||
| 782 | /* TODO: locking */ | ||
| 783 | if (ept->reply_pid == 0xffffffff) { | ||
| 784 | printk(KERN_ERR | ||
| 785 | "rr_write: rejecting unexpected reply\n"); | ||
| 786 | return -EINVAL; | ||
| 787 | } | ||
| 788 | if (ept->reply_xid != rq->xid) { | ||
| 789 | printk(KERN_ERR | ||
| 790 | "rr_write: rejecting packet w/ bad xid\n"); | ||
| 791 | return -EINVAL; | ||
| 792 | } | ||
| 793 | |||
| 794 | hdr.dst_pid = ept->reply_pid; | ||
| 795 | hdr.dst_cid = ept->reply_cid; | ||
| 796 | |||
| 797 | /* consume this reply */ | ||
| 798 | ept->reply_pid = 0xffffffff; | ||
| 799 | |||
| 800 | IO("REPLY on ept %p to xid=%d @ %d:%08x (%d bytes)\n", | ||
| 801 | ept, | ||
| 802 | be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count); | ||
| 803 | } | ||
| 804 | |||
| 805 | r_ept = rpcrouter_lookup_remote_endpoint(hdr.dst_cid); | ||
| 806 | |||
| 807 | if (!r_ept) { | ||
| 808 | printk(KERN_ERR | ||
| 809 | "msm_rpc_write(): No route to ept " | ||
| 810 | "[PID %x CID %x]\n", hdr.dst_pid, hdr.dst_cid); | ||
| 811 | return -EHOSTUNREACH; | ||
| 812 | } | ||
| 813 | |||
| 814 | /* Create routing header */ | ||
| 815 | hdr.type = RPCROUTER_CTRL_CMD_DATA; | ||
| 816 | hdr.version = RPCROUTER_VERSION; | ||
| 817 | hdr.src_pid = ept->pid; | ||
| 818 | hdr.src_cid = ept->cid; | ||
| 819 | hdr.confirm_rx = 0; | ||
| 820 | hdr.size = count + sizeof(uint32_t); | ||
| 821 | |||
| 822 | for (;;) { | ||
| 823 | prepare_to_wait(&r_ept->quota_wait, &__wait, | ||
| 824 | TASK_INTERRUPTIBLE); | ||
| 825 | spin_lock_irqsave(&r_ept->quota_lock, flags); | ||
| 826 | if (r_ept->tx_quota_cntr < RPCROUTER_DEFAULT_RX_QUOTA) | ||
| 827 | break; | ||
| 828 | if (signal_pending(current) && | ||
| 829 | (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) | ||
| 830 | break; | ||
| 831 | spin_unlock_irqrestore(&r_ept->quota_lock, flags); | ||
| 832 | schedule(); | ||
| 833 | } | ||
| 834 | finish_wait(&r_ept->quota_wait, &__wait); | ||
| 835 | |||
| 836 | if (signal_pending(current) && | ||
| 837 | (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) { | ||
| 838 | spin_unlock_irqrestore(&r_ept->quota_lock, flags); | ||
| 839 | return -ERESTARTSYS; | ||
| 840 | } | ||
| 841 | r_ept->tx_quota_cntr++; | ||
| 842 | if (r_ept->tx_quota_cntr == RPCROUTER_DEFAULT_RX_QUOTA) | ||
| 843 | hdr.confirm_rx = 1; | ||
| 844 | |||
| 845 | /* bump pacmark while interrupts disabled to avoid race | ||
| 846 | * probably should be atomic op instead | ||
| 847 | */ | ||
| 848 | pacmark = PACMARK(count, ++next_pacmarkid, 0, 1); | ||
| 849 | |||
| 850 | spin_unlock_irqrestore(&r_ept->quota_lock, flags); | ||
| 851 | |||
| 852 | spin_lock_irqsave(&smd_lock, flags); | ||
| 853 | |||
| 854 | needed = sizeof(hdr) + hdr.size; | ||
| 855 | while (smd_write_avail(smd_channel) < needed) { | ||
| 856 | spin_unlock_irqrestore(&smd_lock, flags); | ||
| 857 | msleep(250); | ||
| 858 | spin_lock_irqsave(&smd_lock, flags); | ||
| 859 | } | ||
| 860 | |||
| 861 | /* TODO: deal with full fifo */ | ||
| 862 | smd_write(smd_channel, &hdr, sizeof(hdr)); | ||
| 863 | smd_write(smd_channel, &pacmark, sizeof(pacmark)); | ||
| 864 | smd_write(smd_channel, buffer, count); | ||
| 865 | |||
| 866 | spin_unlock_irqrestore(&smd_lock, flags); | ||
| 867 | |||
| 868 | return count; | ||
| 869 | } | ||
| 870 | EXPORT_SYMBOL(msm_rpc_write); | ||
| 871 | |||
| 872 | /* | ||
| 873 | * NOTE: It is the responsibility of the caller to kfree buffer | ||
| 874 | */ | ||
| 875 | int msm_rpc_read(struct msm_rpc_endpoint *ept, void **buffer, | ||
| 876 | unsigned user_len, long timeout) | ||
| 877 | { | ||
| 878 | struct rr_fragment *frag, *next; | ||
| 879 | char *buf; | ||
| 880 | int rc; | ||
| 881 | |||
| 882 | rc = __msm_rpc_read(ept, &frag, user_len, timeout); | ||
| 883 | if (rc <= 0) | ||
| 884 | return rc; | ||
| 885 | |||
| 886 | /* single-fragment messages conveniently can be | ||
| 887 | * returned as-is (the buffer is at the front) | ||
| 888 | */ | ||
| 889 | if (frag->next == 0) { | ||
| 890 | *buffer = (void*) frag; | ||
| 891 | return rc; | ||
| 892 | } | ||
| 893 | |||
| 894 | /* multi-fragment messages, we have to do it the | ||
| 895 | * hard way, which is rather disgusting right now | ||
| 896 | */ | ||
| 897 | buf = rr_malloc(rc); | ||
| 898 | *buffer = buf; | ||
| 899 | |||
| 900 | while (frag != NULL) { | ||
| 901 | memcpy(buf, frag->data, frag->length); | ||
| 902 | next = frag->next; | ||
| 903 | buf += frag->length; | ||
| 904 | kfree(frag); | ||
| 905 | frag = next; | ||
| 906 | } | ||
| 907 | |||
| 908 | return rc; | ||
| 909 | } | ||
| 910 | |||
| 911 | int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc, | ||
| 912 | void *_request, int request_size, | ||
| 913 | long timeout) | ||
| 914 | { | ||
| 915 | return msm_rpc_call_reply(ept, proc, | ||
| 916 | _request, request_size, | ||
| 917 | NULL, 0, timeout); | ||
| 918 | } | ||
| 919 | EXPORT_SYMBOL(msm_rpc_call); | ||
| 920 | |||
| 921 | int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, | ||
| 922 | void *_request, int request_size, | ||
| 923 | void *_reply, int reply_size, | ||
| 924 | long timeout) | ||
| 925 | { | ||
| 926 | struct rpc_request_hdr *req = _request; | ||
| 927 | struct rpc_reply_hdr *reply; | ||
| 928 | int rc; | ||
| 929 | |||
| 930 | if (request_size < sizeof(*req)) | ||
| 931 | return -ETOOSMALL; | ||
| 932 | |||
| 933 | if (ept->dst_pid == 0xffffffff) | ||
| 934 | return -ENOTCONN; | ||
| 935 | |||
| 936 | /* We can't use msm_rpc_setup_req() here, because dst_prog and | ||
| 937 | * dst_vers here are already in BE. | ||
| 938 | */ | ||
| 939 | memset(req, 0, sizeof(*req)); | ||
| 940 | req->xid = cpu_to_be32(atomic_add_return(1, &next_xid)); | ||
| 941 | req->rpc_vers = cpu_to_be32(2); | ||
| 942 | req->prog = ept->dst_prog; | ||
| 943 | req->vers = ept->dst_vers; | ||
| 944 | req->procedure = cpu_to_be32(proc); | ||
| 945 | |||
| 946 | rc = msm_rpc_write(ept, req, request_size); | ||
| 947 | if (rc < 0) | ||
| 948 | return rc; | ||
| 949 | |||
| 950 | for (;;) { | ||
| 951 | rc = msm_rpc_read(ept, (void*) &reply, -1, timeout); | ||
| 952 | if (rc < 0) | ||
| 953 | return rc; | ||
| 954 | if (rc < (3 * sizeof(uint32_t))) { | ||
| 955 | rc = -EIO; | ||
| 956 | break; | ||
| 957 | } | ||
| 958 | /* we should not get CALL packets -- ignore them */ | ||
| 959 | if (reply->type == 0) { | ||
| 960 | kfree(reply); | ||
| 961 | continue; | ||
| 962 | } | ||
| 963 | /* If an earlier call timed out, we could get the (no | ||
| 964 | * longer wanted) reply for it. Ignore replies that | ||
| 965 | * we don't expect. | ||
| 966 | */ | ||
| 967 | if (reply->xid != req->xid) { | ||
| 968 | kfree(reply); | ||
| 969 | continue; | ||
| 970 | } | ||
| 971 | if (reply->reply_stat != 0) { | ||
| 972 | rc = -EPERM; | ||
| 973 | break; | ||
| 974 | } | ||
| 975 | if (reply->data.acc_hdr.accept_stat != 0) { | ||
| 976 | rc = -EINVAL; | ||
| 977 | break; | ||
| 978 | } | ||
| 979 | if (_reply == NULL) { | ||
| 980 | rc = 0; | ||
| 981 | break; | ||
| 982 | } | ||
| 983 | if (rc > reply_size) { | ||
| 984 | rc = -ENOMEM; | ||
| 985 | } else { | ||
| 986 | memcpy(_reply, reply, rc); | ||
| 987 | } | ||
| 988 | break; | ||
| 989 | } | ||
| 990 | kfree(reply); | ||
| 991 | return rc; | ||
| 992 | } | ||
| 993 | EXPORT_SYMBOL(msm_rpc_call_reply); | ||
| 994 | |||
| 995 | |||
| 996 | static inline int ept_packet_available(struct msm_rpc_endpoint *ept) | ||
| 997 | { | ||
| 998 | unsigned long flags; | ||
| 999 | int ret; | ||
| 1000 | spin_lock_irqsave(&ept->read_q_lock, flags); | ||
| 1001 | ret = !list_empty(&ept->read_q); | ||
| 1002 | spin_unlock_irqrestore(&ept->read_q_lock, flags); | ||
| 1003 | return ret; | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | int __msm_rpc_read(struct msm_rpc_endpoint *ept, | ||
| 1007 | struct rr_fragment **frag_ret, | ||
| 1008 | unsigned len, long timeout) | ||
| 1009 | { | ||
| 1010 | struct rr_packet *pkt; | ||
| 1011 | struct rpc_request_hdr *rq; | ||
| 1012 | DEFINE_WAIT(__wait); | ||
| 1013 | unsigned long flags; | ||
| 1014 | int rc; | ||
| 1015 | |||
| 1016 | IO("READ on ept %p\n", ept); | ||
| 1017 | |||
| 1018 | if (ept->flags & MSM_RPC_UNINTERRUPTIBLE) { | ||
| 1019 | if (timeout < 0) { | ||
| 1020 | wait_event(ept->wait_q, ept_packet_available(ept)); | ||
| 1021 | } else { | ||
| 1022 | rc = wait_event_timeout( | ||
| 1023 | ept->wait_q, ept_packet_available(ept), | ||
| 1024 | timeout); | ||
| 1025 | if (rc == 0) | ||
| 1026 | return -ETIMEDOUT; | ||
| 1027 | } | ||
| 1028 | } else { | ||
| 1029 | if (timeout < 0) { | ||
| 1030 | rc = wait_event_interruptible( | ||
| 1031 | ept->wait_q, ept_packet_available(ept)); | ||
| 1032 | if (rc < 0) | ||
| 1033 | return rc; | ||
| 1034 | } else { | ||
| 1035 | rc = wait_event_interruptible_timeout( | ||
| 1036 | ept->wait_q, ept_packet_available(ept), | ||
| 1037 | timeout); | ||
| 1038 | if (rc == 0) | ||
| 1039 | return -ETIMEDOUT; | ||
| 1040 | } | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | spin_lock_irqsave(&ept->read_q_lock, flags); | ||
| 1044 | if (list_empty(&ept->read_q)) { | ||
| 1045 | spin_unlock_irqrestore(&ept->read_q_lock, flags); | ||
| 1046 | return -EAGAIN; | ||
| 1047 | } | ||
| 1048 | pkt = list_first_entry(&ept->read_q, struct rr_packet, list); | ||
| 1049 | if (pkt->length > len) { | ||
| 1050 | spin_unlock_irqrestore(&ept->read_q_lock, flags); | ||
| 1051 | return -ETOOSMALL; | ||
| 1052 | } | ||
| 1053 | list_del(&pkt->list); | ||
| 1054 | spin_unlock_irqrestore(&ept->read_q_lock, flags); | ||
| 1055 | |||
| 1056 | rc = pkt->length; | ||
| 1057 | |||
| 1058 | *frag_ret = pkt->first; | ||
| 1059 | rq = (void*) pkt->first->data; | ||
| 1060 | if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 0)) { | ||
| 1061 | IO("READ on ept %p is a CALL on %08x:%08x proc %d xid %d\n", | ||
| 1062 | ept, be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), | ||
| 1063 | be32_to_cpu(rq->procedure), | ||
| 1064 | be32_to_cpu(rq->xid)); | ||
| 1065 | /* RPC CALL */ | ||
| 1066 | if (ept->reply_pid != 0xffffffff) { | ||
| 1067 | printk(KERN_WARNING | ||
| 1068 | "rr_read: lost previous reply xid...\n"); | ||
| 1069 | } | ||
| 1070 | /* TODO: locking? */ | ||
| 1071 | ept->reply_pid = pkt->hdr.src_pid; | ||
| 1072 | ept->reply_cid = pkt->hdr.src_cid; | ||
| 1073 | ept->reply_xid = rq->xid; | ||
| 1074 | } | ||
| 1075 | #if TRACE_RPC_MSG | ||
| 1076 | else if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 1)) | ||
| 1077 | IO("READ on ept %p is a REPLY\n", ept); | ||
| 1078 | else IO("READ on ept %p (%d bytes)\n", ept, rc); | ||
| 1079 | #endif | ||
| 1080 | |||
| 1081 | kfree(pkt); | ||
| 1082 | return rc; | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | #if CONFIG_MSM_AMSS_VERSION >= 6350 | ||
| 1086 | int msm_rpc_is_compatible_version(uint32_t server_version, | ||
| 1087 | uint32_t client_version) | ||
| 1088 | { | ||
| 1089 | if ((server_version & RPC_VERSION_MODE_MASK) != | ||
| 1090 | (client_version & RPC_VERSION_MODE_MASK)) | ||
| 1091 | return 0; | ||
| 1092 | |||
| 1093 | if (server_version & RPC_VERSION_MODE_MASK) | ||
| 1094 | return server_version == client_version; | ||
| 1095 | |||
| 1096 | return ((server_version & RPC_VERSION_MAJOR_MASK) == | ||
| 1097 | (client_version & RPC_VERSION_MAJOR_MASK)) && | ||
| 1098 | ((server_version & RPC_VERSION_MINOR_MASK) >= | ||
| 1099 | (client_version & RPC_VERSION_MINOR_MASK)); | ||
| 1100 | } | ||
| 1101 | EXPORT_SYMBOL(msm_rpc_is_compatible_version); | ||
| 1102 | |||
| 1103 | static int msm_rpc_get_compatible_server(uint32_t prog, | ||
| 1104 | uint32_t ver, | ||
| 1105 | uint32_t *found_vers) | ||
| 1106 | { | ||
| 1107 | struct rr_server *server; | ||
| 1108 | unsigned long flags; | ||
| 1109 | if (found_vers == NULL) | ||
| 1110 | return 0; | ||
| 1111 | |||
| 1112 | spin_lock_irqsave(&server_list_lock, flags); | ||
| 1113 | list_for_each_entry(server, &server_list, list) { | ||
| 1114 | if ((server->prog == prog) && | ||
| 1115 | msm_rpc_is_compatible_version(server->vers, ver)) { | ||
| 1116 | *found_vers = server->vers; | ||
| 1117 | spin_unlock_irqrestore(&server_list_lock, flags); | ||
| 1118 | return 0; | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | spin_unlock_irqrestore(&server_list_lock, flags); | ||
| 1122 | return -1; | ||
| 1123 | } | ||
| 1124 | #endif | ||
| 1125 | |||
| 1126 | struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags) | ||
| 1127 | { | ||
| 1128 | struct msm_rpc_endpoint *ept; | ||
| 1129 | struct rr_server *server; | ||
| 1130 | |||
| 1131 | #if CONFIG_MSM_AMSS_VERSION >= 6350 | ||
| 1132 | if (!(vers & RPC_VERSION_MODE_MASK)) { | ||
| 1133 | uint32_t found_vers; | ||
| 1134 | if (msm_rpc_get_compatible_server(prog, vers, &found_vers) < 0) | ||
| 1135 | return ERR_PTR(-EHOSTUNREACH); | ||
| 1136 | if (found_vers != vers) { | ||
| 1137 | D("RPC using new version %08x:{%08x --> %08x}\n", | ||
| 1138 | prog, vers, found_vers); | ||
| 1139 | vers = found_vers; | ||
| 1140 | } | ||
| 1141 | } | ||
| 1142 | #endif | ||
| 1143 | |||
| 1144 | server = rpcrouter_lookup_server(prog, vers); | ||
| 1145 | if (!server) | ||
| 1146 | return ERR_PTR(-EHOSTUNREACH); | ||
| 1147 | |||
| 1148 | ept = msm_rpc_open(); | ||
| 1149 | if (IS_ERR(ept)) | ||
| 1150 | return ept; | ||
| 1151 | |||
| 1152 | ept->flags = flags; | ||
| 1153 | ept->dst_pid = server->pid; | ||
| 1154 | ept->dst_cid = server->cid; | ||
| 1155 | ept->dst_prog = cpu_to_be32(prog); | ||
| 1156 | ept->dst_vers = cpu_to_be32(vers); | ||
| 1157 | |||
| 1158 | return ept; | ||
| 1159 | } | ||
| 1160 | EXPORT_SYMBOL(msm_rpc_connect); | ||
| 1161 | |||
| 1162 | uint32_t msm_rpc_get_vers(struct msm_rpc_endpoint *ept) | ||
| 1163 | { | ||
| 1164 | return be32_to_cpu(ept->dst_vers); | ||
| 1165 | } | ||
| 1166 | EXPORT_SYMBOL(msm_rpc_get_vers); | ||
| 1167 | |||
| 1168 | /* TODO: permission check? */ | ||
| 1169 | int msm_rpc_register_server(struct msm_rpc_endpoint *ept, | ||
| 1170 | uint32_t prog, uint32_t vers) | ||
| 1171 | { | ||
| 1172 | int rc; | ||
| 1173 | union rr_control_msg msg; | ||
| 1174 | struct rr_server *server; | ||
| 1175 | |||
| 1176 | server = rpcrouter_create_server(ept->pid, ept->cid, | ||
| 1177 | prog, vers); | ||
| 1178 | if (!server) | ||
| 1179 | return -ENODEV; | ||
| 1180 | |||
| 1181 | msg.srv.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER; | ||
| 1182 | msg.srv.pid = ept->pid; | ||
| 1183 | msg.srv.cid = ept->cid; | ||
| 1184 | msg.srv.prog = prog; | ||
| 1185 | msg.srv.vers = vers; | ||
| 1186 | |||
| 1187 | RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n", | ||
| 1188 | ept->pid, ept->cid, prog, vers); | ||
| 1189 | |||
| 1190 | rc = rpcrouter_send_control_msg(&msg); | ||
| 1191 | if (rc < 0) | ||
| 1192 | return rc; | ||
| 1193 | |||
| 1194 | return 0; | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | /* TODO: permission check -- disallow unreg of somebody else's server */ | ||
| 1198 | int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept, | ||
| 1199 | uint32_t prog, uint32_t vers) | ||
| 1200 | { | ||
| 1201 | struct rr_server *server; | ||
| 1202 | server = rpcrouter_lookup_server(prog, vers); | ||
| 1203 | |||
| 1204 | if (!server) | ||
| 1205 | return -ENOENT; | ||
| 1206 | rpcrouter_destroy_server(server); | ||
| 1207 | return 0; | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | static int msm_rpcrouter_probe(struct platform_device *pdev) | ||
| 1211 | { | ||
| 1212 | int rc; | ||
| 1213 | |||
| 1214 | /* Initialize what we need to start processing */ | ||
| 1215 | INIT_LIST_HEAD(&local_endpoints); | ||
| 1216 | INIT_LIST_HEAD(&remote_endpoints); | ||
| 1217 | |||
| 1218 | init_waitqueue_head(&newserver_wait); | ||
| 1219 | init_waitqueue_head(&smd_wait); | ||
| 1220 | |||
| 1221 | rpcrouter_workqueue = create_singlethread_workqueue("rpcrouter"); | ||
| 1222 | if (!rpcrouter_workqueue) | ||
| 1223 | return -ENOMEM; | ||
| 1224 | |||
| 1225 | rc = msm_rpcrouter_init_devices(); | ||
| 1226 | if (rc < 0) | ||
| 1227 | goto fail_destroy_workqueue; | ||
| 1228 | |||
| 1229 | /* Open up SMD channel 2 */ | ||
| 1230 | initialized = 0; | ||
| 1231 | rc = smd_open("SMD_RPCCALL", &smd_channel, NULL, rpcrouter_smdnotify); | ||
| 1232 | if (rc < 0) | ||
| 1233 | goto fail_remove_devices; | ||
| 1234 | |||
| 1235 | queue_work(rpcrouter_workqueue, &work_read_data); | ||
| 1236 | return 0; | ||
| 1237 | |||
| 1238 | fail_remove_devices: | ||
| 1239 | msm_rpcrouter_exit_devices(); | ||
| 1240 | fail_destroy_workqueue: | ||
| 1241 | destroy_workqueue(rpcrouter_workqueue); | ||
| 1242 | return rc; | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | static struct platform_driver msm_smd_channel2_driver = { | ||
| 1246 | .probe = msm_rpcrouter_probe, | ||
| 1247 | .driver = { | ||
| 1248 | .name = "SMD_RPCCALL", | ||
| 1249 | .owner = THIS_MODULE, | ||
| 1250 | }, | ||
| 1251 | }; | ||
| 1252 | |||
| 1253 | static int __init rpcrouter_init(void) | ||
| 1254 | { | ||
| 1255 | return platform_driver_register(&msm_smd_channel2_driver); | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | module_init(rpcrouter_init); | ||
| 1259 | MODULE_DESCRIPTION("MSM RPC Router"); | ||
| 1260 | MODULE_AUTHOR("San Mehat <san@android.com>"); | ||
| 1261 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/staging/dream/smd/smd_rpcrouter.h b/drivers/staging/dream/smd/smd_rpcrouter.h deleted file mode 100644 index 86ab997b1b79..000000000000 --- a/drivers/staging/dream/smd/smd_rpcrouter.h +++ /dev/null | |||
| @@ -1,193 +0,0 @@ | |||
| 1 | /** arch/arm/mach-msm/smd_rpcrouter.h | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 Google, Inc. | ||
| 4 | * Copyright (c) 2007-2008 QUALCOMM Incorporated. | ||
| 5 | * Author: San Mehat <san@android.com> | ||
| 6 | * | ||
| 7 | * This software is licensed under the terms of the GNU General Public | ||
| 8 | * License version 2, as published by the Free Software Foundation, and | ||
| 9 | * may be copied, distributed, and modified under those terms. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H | ||
| 19 | #define _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H | ||
| 20 | |||
| 21 | #include <linux/types.h> | ||
| 22 | #include <linux/list.h> | ||
| 23 | #include <linux/cdev.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 25 | |||
| 26 | #include <mach/msm_smd.h> | ||
| 27 | #include <mach/msm_rpcrouter.h> | ||
| 28 | |||
| 29 | /* definitions for the R2R wire protcol */ | ||
| 30 | |||
| 31 | #define RPCROUTER_VERSION 1 | ||
| 32 | #define RPCROUTER_PROCESSORS_MAX 4 | ||
| 33 | #define RPCROUTER_MSGSIZE_MAX 512 | ||
| 34 | |||
| 35 | #define RPCROUTER_CLIENT_BCAST_ID 0xffffffff | ||
| 36 | #define RPCROUTER_ROUTER_ADDRESS 0xfffffffe | ||
| 37 | |||
| 38 | #define RPCROUTER_PID_LOCAL 1 | ||
| 39 | #define RPCROUTER_PID_REMOTE 0 | ||
| 40 | |||
| 41 | #define RPCROUTER_CTRL_CMD_DATA 1 | ||
| 42 | #define RPCROUTER_CTRL_CMD_HELLO 2 | ||
| 43 | #define RPCROUTER_CTRL_CMD_BYE 3 | ||
| 44 | #define RPCROUTER_CTRL_CMD_NEW_SERVER 4 | ||
| 45 | #define RPCROUTER_CTRL_CMD_REMOVE_SERVER 5 | ||
| 46 | #define RPCROUTER_CTRL_CMD_REMOVE_CLIENT 6 | ||
| 47 | #define RPCROUTER_CTRL_CMD_RESUME_TX 7 | ||
| 48 | #define RPCROUTER_CTRL_CMD_EXIT 8 | ||
| 49 | |||
| 50 | #define RPCROUTER_DEFAULT_RX_QUOTA 5 | ||
| 51 | |||
| 52 | union rr_control_msg { | ||
| 53 | uint32_t cmd; | ||
| 54 | struct { | ||
| 55 | uint32_t cmd; | ||
| 56 | uint32_t prog; | ||
| 57 | uint32_t vers; | ||
| 58 | uint32_t pid; | ||
| 59 | uint32_t cid; | ||
| 60 | } srv; | ||
| 61 | struct { | ||
| 62 | uint32_t cmd; | ||
| 63 | uint32_t pid; | ||
| 64 | uint32_t cid; | ||
| 65 | } cli; | ||
| 66 | }; | ||
| 67 | |||
| 68 | struct rr_header { | ||
| 69 | uint32_t version; | ||
| 70 | uint32_t type; | ||
| 71 | uint32_t src_pid; | ||
| 72 | uint32_t src_cid; | ||
| 73 | uint32_t confirm_rx; | ||
| 74 | uint32_t size; | ||
| 75 | uint32_t dst_pid; | ||
| 76 | uint32_t dst_cid; | ||
| 77 | }; | ||
| 78 | |||
| 79 | /* internals */ | ||
| 80 | |||
| 81 | #define RPCROUTER_MAX_REMOTE_SERVERS 100 | ||
| 82 | |||
| 83 | struct rr_fragment { | ||
| 84 | unsigned char data[RPCROUTER_MSGSIZE_MAX]; | ||
| 85 | uint32_t length; | ||
| 86 | struct rr_fragment *next; | ||
| 87 | }; | ||
| 88 | |||
| 89 | struct rr_packet { | ||
| 90 | struct list_head list; | ||
| 91 | struct rr_fragment *first; | ||
| 92 | struct rr_fragment *last; | ||
| 93 | struct rr_header hdr; | ||
| 94 | uint32_t mid; | ||
| 95 | uint32_t length; | ||
| 96 | }; | ||
| 97 | |||
| 98 | #define PACMARK_LAST(n) ((n) & 0x80000000) | ||
| 99 | #define PACMARK_MID(n) (((n) >> 16) & 0xFF) | ||
| 100 | #define PACMARK_LEN(n) ((n) & 0xFFFF) | ||
| 101 | |||
| 102 | static inline uint32_t PACMARK(uint32_t len, uint32_t mid, uint32_t first, | ||
| 103 | uint32_t last) | ||
| 104 | { | ||
| 105 | return (len & 0xFFFF) | | ||
| 106 | ((mid & 0xFF) << 16) | | ||
| 107 | ((!!first) << 30) | | ||
| 108 | ((!!last) << 31); | ||
| 109 | } | ||
| 110 | |||
| 111 | struct rr_server { | ||
| 112 | struct list_head list; | ||
| 113 | |||
| 114 | uint32_t pid; | ||
| 115 | uint32_t cid; | ||
| 116 | uint32_t prog; | ||
| 117 | uint32_t vers; | ||
| 118 | |||
| 119 | dev_t device_number; | ||
| 120 | struct cdev cdev; | ||
| 121 | struct device *device; | ||
| 122 | struct rpcsvr_platform_device p_device; | ||
| 123 | char pdev_name[32]; | ||
| 124 | }; | ||
| 125 | |||
| 126 | struct rr_remote_endpoint { | ||
| 127 | uint32_t pid; | ||
| 128 | uint32_t cid; | ||
| 129 | |||
| 130 | int tx_quota_cntr; | ||
| 131 | spinlock_t quota_lock; | ||
| 132 | wait_queue_head_t quota_wait; | ||
| 133 | |||
| 134 | struct list_head list; | ||
| 135 | }; | ||
| 136 | |||
| 137 | struct msm_rpc_endpoint { | ||
| 138 | struct list_head list; | ||
| 139 | |||
| 140 | /* incomplete packets waiting for assembly */ | ||
| 141 | struct list_head incomplete; | ||
| 142 | |||
| 143 | /* complete packets waiting to be read */ | ||
| 144 | struct list_head read_q; | ||
| 145 | spinlock_t read_q_lock; | ||
| 146 | wait_queue_head_t wait_q; | ||
| 147 | unsigned flags; | ||
| 148 | |||
| 149 | /* endpoint address */ | ||
| 150 | uint32_t pid; | ||
| 151 | uint32_t cid; | ||
| 152 | |||
| 153 | /* bound remote address | ||
| 154 | * if not connected (dst_pid == 0xffffffff) RPC_CALL writes fail | ||
| 155 | * RPC_CALLs must be to the prog/vers below or they will fail | ||
| 156 | */ | ||
| 157 | uint32_t dst_pid; | ||
| 158 | uint32_t dst_cid; | ||
| 159 | uint32_t dst_prog; /* be32 */ | ||
| 160 | uint32_t dst_vers; /* be32 */ | ||
| 161 | |||
| 162 | /* reply remote address | ||
| 163 | * if reply_pid == 0xffffffff, none available | ||
| 164 | * RPC_REPLY writes may only go to the pid/cid/xid of the | ||
| 165 | * last RPC_CALL we received. | ||
| 166 | */ | ||
| 167 | uint32_t reply_pid; | ||
| 168 | uint32_t reply_cid; | ||
| 169 | uint32_t reply_xid; /* be32 */ | ||
| 170 | uint32_t next_pm; /* Pacmark sequence */ | ||
| 171 | |||
| 172 | /* device node if this endpoint is accessed via userspace */ | ||
| 173 | dev_t dev; | ||
| 174 | }; | ||
| 175 | |||
| 176 | /* shared between smd_rpcrouter*.c */ | ||
| 177 | |||
| 178 | int __msm_rpc_read(struct msm_rpc_endpoint *ept, | ||
| 179 | struct rr_fragment **frag, | ||
| 180 | unsigned len, long timeout); | ||
| 181 | |||
| 182 | struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev); | ||
| 183 | int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept); | ||
| 184 | |||
| 185 | int msm_rpcrouter_create_server_cdev(struct rr_server *server); | ||
| 186 | int msm_rpcrouter_create_server_pdev(struct rr_server *server); | ||
| 187 | |||
| 188 | int msm_rpcrouter_init_devices(void); | ||
| 189 | void msm_rpcrouter_exit_devices(void); | ||
| 190 | |||
| 191 | extern dev_t msm_rpcrouter_devno; | ||
| 192 | extern struct class *msm_rpcrouter_class; | ||
| 193 | #endif | ||
diff --git a/drivers/staging/dream/smd/smd_rpcrouter_device.c b/drivers/staging/dream/smd/smd_rpcrouter_device.c deleted file mode 100644 index e9c28eddce31..000000000000 --- a/drivers/staging/dream/smd/smd_rpcrouter_device.c +++ /dev/null | |||
| @@ -1,377 +0,0 @@ | |||
| 1 | /* arch/arm/mach-msm/smd_rpcrouter_device.c | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 Google, Inc. | ||
| 4 | * Copyright (c) 2007-2009 QUALCOMM Incorporated. | ||
| 5 | * Author: San Mehat <san@android.com> | ||
| 6 | * | ||
| 7 | * This software is licensed under the terms of the GNU General Public | ||
| 8 | * License version 2, as published by the Free Software Foundation, and | ||
| 9 | * may be copied, distributed, and modified under those terms. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/kernel.h> | ||
| 19 | #include <linux/string.h> | ||
| 20 | #include <linux/errno.h> | ||
| 21 | #include <linux/cdev.h> | ||
| 22 | #include <linux/init.h> | ||
| 23 | #include <linux/device.h> | ||
| 24 | #include <linux/types.h> | ||
| 25 | #include <linux/delay.h> | ||
| 26 | #include <linux/fs.h> | ||
| 27 | #include <linux/err.h> | ||
| 28 | #include <linux/sched.h> | ||
| 29 | #include <linux/poll.h> | ||
| 30 | #include <linux/platform_device.h> | ||
| 31 | #include <linux/msm_rpcrouter.h> | ||
| 32 | #include <linux/slab.h> | ||
| 33 | |||
| 34 | #include <asm/uaccess.h> | ||
| 35 | #include <asm/byteorder.h> | ||
| 36 | |||
| 37 | #include "smd_rpcrouter.h" | ||
| 38 | |||
| 39 | #define SAFETY_MEM_SIZE 65536 | ||
| 40 | |||
| 41 | /* Next minor # available for a remote server */ | ||
| 42 | static int next_minor = 1; | ||
| 43 | |||
| 44 | struct class *msm_rpcrouter_class; | ||
| 45 | dev_t msm_rpcrouter_devno; | ||
| 46 | |||
| 47 | static struct cdev rpcrouter_cdev; | ||
| 48 | static struct device *rpcrouter_device; | ||
| 49 | |||
| 50 | static int rpcrouter_open(struct inode *inode, struct file *filp) | ||
| 51 | { | ||
| 52 | int rc; | ||
| 53 | struct msm_rpc_endpoint *ept; | ||
| 54 | |||
| 55 | rc = nonseekable_open(inode, filp); | ||
| 56 | if (rc < 0) | ||
| 57 | return rc; | ||
| 58 | |||
| 59 | ept = msm_rpcrouter_create_local_endpoint(inode->i_rdev); | ||
| 60 | if (!ept) | ||
| 61 | return -ENOMEM; | ||
| 62 | |||
| 63 | filp->private_data = ept; | ||
| 64 | return 0; | ||
| 65 | } | ||
| 66 | |||
| 67 | static int rpcrouter_release(struct inode *inode, struct file *filp) | ||
| 68 | { | ||
| 69 | struct msm_rpc_endpoint *ept; | ||
| 70 | ept = (struct msm_rpc_endpoint *) filp->private_data; | ||
| 71 | |||
| 72 | return msm_rpcrouter_destroy_local_endpoint(ept); | ||
| 73 | } | ||
| 74 | |||
| 75 | static ssize_t rpcrouter_read(struct file *filp, char __user *buf, | ||
| 76 | size_t count, loff_t *ppos) | ||
| 77 | { | ||
| 78 | struct msm_rpc_endpoint *ept; | ||
| 79 | struct rr_fragment *frag, *next; | ||
| 80 | int rc; | ||
| 81 | |||
| 82 | ept = (struct msm_rpc_endpoint *) filp->private_data; | ||
| 83 | |||
| 84 | rc = __msm_rpc_read(ept, &frag, count, -1); | ||
| 85 | if (rc < 0) | ||
| 86 | return rc; | ||
| 87 | |||
| 88 | count = rc; | ||
| 89 | |||
| 90 | while (frag != NULL) { | ||
| 91 | if (copy_to_user(buf, frag->data, frag->length)) { | ||
| 92 | printk(KERN_ERR | ||
| 93 | "rpcrouter: could not copy all read data to user!\n"); | ||
| 94 | rc = -EFAULT; | ||
| 95 | } | ||
| 96 | buf += frag->length; | ||
| 97 | next = frag->next; | ||
| 98 | kfree(frag); | ||
| 99 | frag = next; | ||
| 100 | } | ||
| 101 | |||
| 102 | return rc; | ||
| 103 | } | ||
| 104 | |||
| 105 | static ssize_t rpcrouter_write(struct file *filp, const char __user *buf, | ||
| 106 | size_t count, loff_t *ppos) | ||
| 107 | { | ||
| 108 | struct msm_rpc_endpoint *ept; | ||
| 109 | int rc = 0; | ||
| 110 | void *k_buffer; | ||
| 111 | |||
| 112 | ept = (struct msm_rpc_endpoint *) filp->private_data; | ||
| 113 | |||
| 114 | /* A check for safety, this seems non-standard */ | ||
| 115 | if (count > SAFETY_MEM_SIZE) | ||
| 116 | return -EINVAL; | ||
| 117 | |||
| 118 | k_buffer = kmalloc(count, GFP_KERNEL); | ||
| 119 | if (!k_buffer) | ||
| 120 | return -ENOMEM; | ||
| 121 | |||
| 122 | if (copy_from_user(k_buffer, buf, count)) { | ||
| 123 | rc = -EFAULT; | ||
| 124 | goto write_out_free; | ||
| 125 | } | ||
| 126 | |||
| 127 | rc = msm_rpc_write(ept, k_buffer, count); | ||
| 128 | if (rc < 0) | ||
| 129 | goto write_out_free; | ||
| 130 | |||
| 131 | rc = count; | ||
| 132 | write_out_free: | ||
| 133 | kfree(k_buffer); | ||
| 134 | return rc; | ||
| 135 | } | ||
| 136 | |||
| 137 | static unsigned int rpcrouter_poll(struct file *filp, | ||
| 138 | struct poll_table_struct *wait) | ||
| 139 | { | ||
| 140 | struct msm_rpc_endpoint *ept; | ||
| 141 | unsigned mask = 0; | ||
| 142 | ept = (struct msm_rpc_endpoint *) filp->private_data; | ||
| 143 | |||
| 144 | /* If there's data already in the read queue, return POLLIN. | ||
| 145 | * Else, wait for the requested amount of time, and check again. | ||
| 146 | */ | ||
| 147 | |||
| 148 | if (!list_empty(&ept->read_q)) | ||
| 149 | mask |= POLLIN; | ||
| 150 | |||
| 151 | if (!mask) { | ||
| 152 | poll_wait(filp, &ept->wait_q, wait); | ||
| 153 | if (!list_empty(&ept->read_q)) | ||
| 154 | mask |= POLLIN; | ||
| 155 | } | ||
| 156 | |||
| 157 | return mask; | ||
| 158 | } | ||
| 159 | |||
| 160 | static long rpcrouter_ioctl(struct file *filp, unsigned int cmd, | ||
| 161 | unsigned long arg) | ||
| 162 | { | ||
| 163 | struct msm_rpc_endpoint *ept; | ||
| 164 | struct rpcrouter_ioctl_server_args server_args; | ||
| 165 | int rc = 0; | ||
| 166 | uint32_t n; | ||
| 167 | |||
| 168 | ept = (struct msm_rpc_endpoint *) filp->private_data; | ||
| 169 | switch (cmd) { | ||
| 170 | |||
| 171 | case RPC_ROUTER_IOCTL_GET_VERSION: | ||
| 172 | n = RPC_ROUTER_VERSION_V1; | ||
| 173 | rc = put_user(n, (unsigned int *) arg); | ||
| 174 | break; | ||
| 175 | |||
| 176 | case RPC_ROUTER_IOCTL_GET_MTU: | ||
| 177 | /* the pacmark word reduces the actual payload | ||
| 178 | * possible per message | ||
| 179 | */ | ||
| 180 | n = RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t); | ||
| 181 | rc = put_user(n, (unsigned int *) arg); | ||
| 182 | break; | ||
| 183 | |||
| 184 | case RPC_ROUTER_IOCTL_REGISTER_SERVER: | ||
| 185 | rc = copy_from_user(&server_args, (void *) arg, | ||
| 186 | sizeof(server_args)); | ||
| 187 | if (rc < 0) | ||
| 188 | break; | ||
| 189 | msm_rpc_register_server(ept, | ||
| 190 | server_args.prog, | ||
| 191 | server_args.vers); | ||
| 192 | break; | ||
| 193 | |||
| 194 | case RPC_ROUTER_IOCTL_UNREGISTER_SERVER: | ||
| 195 | rc = copy_from_user(&server_args, (void *) arg, | ||
| 196 | sizeof(server_args)); | ||
| 197 | if (rc < 0) | ||
| 198 | break; | ||
| 199 | |||
| 200 | msm_rpc_unregister_server(ept, | ||
| 201 | server_args.prog, | ||
| 202 | server_args.vers); | ||
| 203 | break; | ||
| 204 | |||
| 205 | case RPC_ROUTER_IOCTL_GET_MINOR_VERSION: | ||
| 206 | n = MSM_RPC_GET_MINOR(msm_rpc_get_vers(ept)); | ||
| 207 | rc = put_user(n, (unsigned int *)arg); | ||
| 208 | break; | ||
| 209 | |||
| 210 | default: | ||
| 211 | rc = -EINVAL; | ||
| 212 | break; | ||
| 213 | } | ||
| 214 | |||
| 215 | return rc; | ||
| 216 | } | ||
| 217 | |||
| 218 | static struct file_operations rpcrouter_server_fops = { | ||
| 219 | .owner = THIS_MODULE, | ||
| 220 | .open = rpcrouter_open, | ||
| 221 | .release = rpcrouter_release, | ||
| 222 | .read = rpcrouter_read, | ||
| 223 | .write = rpcrouter_write, | ||
| 224 | .poll = rpcrouter_poll, | ||
| 225 | .unlocked_ioctl = rpcrouter_ioctl, | ||
| 226 | }; | ||
| 227 | |||
| 228 | static struct file_operations rpcrouter_router_fops = { | ||
| 229 | .owner = THIS_MODULE, | ||
| 230 | .open = rpcrouter_open, | ||
| 231 | .release = rpcrouter_release, | ||
| 232 | .read = rpcrouter_read, | ||
| 233 | .write = rpcrouter_write, | ||
| 234 | .poll = rpcrouter_poll, | ||
| 235 | .unlocked_ioctl = rpcrouter_ioctl, | ||
| 236 | }; | ||
| 237 | |||
| 238 | int msm_rpcrouter_create_server_cdev(struct rr_server *server) | ||
| 239 | { | ||
| 240 | int rc; | ||
| 241 | uint32_t dev_vers; | ||
| 242 | |||
| 243 | if (next_minor == RPCROUTER_MAX_REMOTE_SERVERS) { | ||
| 244 | printk(KERN_ERR | ||
| 245 | "rpcrouter: Minor numbers exhausted - Increase " | ||
| 246 | "RPCROUTER_MAX_REMOTE_SERVERS\n"); | ||
| 247 | return -ENOBUFS; | ||
| 248 | } | ||
| 249 | |||
| 250 | #if CONFIG_MSM_AMSS_VERSION >= 6350 | ||
| 251 | /* Servers with bit 31 set are remote msm servers with hashkey version. | ||
| 252 | * Servers with bit 31 not set are remote msm servers with | ||
| 253 | * backwards compatible version type in which case the minor number | ||
| 254 | * (lower 16 bits) is set to zero. | ||
| 255 | * | ||
| 256 | */ | ||
| 257 | if ((server->vers & RPC_VERSION_MODE_MASK)) | ||
| 258 | dev_vers = server->vers; | ||
| 259 | else | ||
| 260 | dev_vers = server->vers & RPC_VERSION_MAJOR_MASK; | ||
| 261 | #else | ||
| 262 | dev_vers = server->vers; | ||
| 263 | #endif | ||
| 264 | |||
| 265 | server->device_number = | ||
| 266 | MKDEV(MAJOR(msm_rpcrouter_devno), next_minor++); | ||
| 267 | |||
| 268 | server->device = | ||
| 269 | device_create(msm_rpcrouter_class, rpcrouter_device, | ||
| 270 | server->device_number, NULL, "%.8x:%.8x", | ||
| 271 | server->prog, dev_vers); | ||
| 272 | if (IS_ERR(server->device)) { | ||
| 273 | printk(KERN_ERR | ||
| 274 | "rpcrouter: Unable to create device (%ld)\n", | ||
| 275 | PTR_ERR(server->device)); | ||
| 276 | return PTR_ERR(server->device);; | ||
| 277 | } | ||
| 278 | |||
| 279 | cdev_init(&server->cdev, &rpcrouter_server_fops); | ||
| 280 | server->cdev.owner = THIS_MODULE; | ||
| 281 | |||
| 282 | rc = cdev_add(&server->cdev, server->device_number, 1); | ||
| 283 | if (rc < 0) { | ||
| 284 | printk(KERN_ERR | ||
| 285 | "rpcrouter: Unable to add chrdev (%d)\n", rc); | ||
| 286 | device_destroy(msm_rpcrouter_class, server->device_number); | ||
| 287 | return rc; | ||
| 288 | } | ||
| 289 | return 0; | ||
| 290 | } | ||
| 291 | |||
| 292 | /* for backward compatible version type (31st bit cleared) | ||
| 293 | * clearing minor number (lower 16 bits) in device name | ||
| 294 | * is neccessary for driver binding | ||
| 295 | */ | ||
| 296 | int msm_rpcrouter_create_server_pdev(struct rr_server *server) | ||
| 297 | { | ||
| 298 | sprintf(server->pdev_name, "rs%.8x:%.8x", | ||
| 299 | server->prog, | ||
| 300 | #if CONFIG_MSM_AMSS_VERSION >= 6350 | ||
| 301 | (server->vers & RPC_VERSION_MODE_MASK) ? server->vers : | ||
| 302 | (server->vers & RPC_VERSION_MAJOR_MASK)); | ||
| 303 | #else | ||
| 304 | server->vers); | ||
| 305 | #endif | ||
| 306 | |||
| 307 | server->p_device.base.id = -1; | ||
| 308 | server->p_device.base.name = server->pdev_name; | ||
| 309 | |||
| 310 | server->p_device.prog = server->prog; | ||
| 311 | server->p_device.vers = server->vers; | ||
| 312 | |||
| 313 | platform_device_register(&server->p_device.base); | ||
| 314 | return 0; | ||
| 315 | } | ||
| 316 | |||
| 317 | int msm_rpcrouter_init_devices(void) | ||
| 318 | { | ||
| 319 | int rc; | ||
| 320 | int major; | ||
| 321 | |||
| 322 | /* Create the device nodes */ | ||
| 323 | msm_rpcrouter_class = class_create(THIS_MODULE, "oncrpc"); | ||
| 324 | if (IS_ERR(msm_rpcrouter_class)) { | ||
| 325 | rc = -ENOMEM; | ||
| 326 | printk(KERN_ERR | ||
| 327 | "rpcrouter: failed to create oncrpc class\n"); | ||
| 328 | goto fail; | ||
| 329 | } | ||
| 330 | |||
| 331 | rc = alloc_chrdev_region(&msm_rpcrouter_devno, 0, | ||
| 332 | RPCROUTER_MAX_REMOTE_SERVERS + 1, | ||
| 333 | "oncrpc"); | ||
| 334 | if (rc < 0) { | ||
| 335 | printk(KERN_ERR | ||
| 336 | "rpcrouter: Failed to alloc chardev region (%d)\n", rc); | ||
| 337 | goto fail_destroy_class; | ||
| 338 | } | ||
| 339 | |||
| 340 | major = MAJOR(msm_rpcrouter_devno); | ||
| 341 | rpcrouter_device = device_create(msm_rpcrouter_class, NULL, | ||
| 342 | msm_rpcrouter_devno, NULL, "%.8x:%d", | ||
| 343 | 0, 0); | ||
| 344 | if (IS_ERR(rpcrouter_device)) { | ||
| 345 | rc = -ENOMEM; | ||
| 346 | goto fail_unregister_cdev_region; | ||
| 347 | } | ||
| 348 | |||
| 349 | cdev_init(&rpcrouter_cdev, &rpcrouter_router_fops); | ||
| 350 | rpcrouter_cdev.owner = THIS_MODULE; | ||
| 351 | |||
| 352 | rc = cdev_add(&rpcrouter_cdev, msm_rpcrouter_devno, 1); | ||
| 353 | if (rc < 0) | ||
| 354 | goto fail_destroy_device; | ||
| 355 | |||
| 356 | return 0; | ||
| 357 | |||
| 358 | fail_destroy_device: | ||
| 359 | device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno); | ||
| 360 | fail_unregister_cdev_region: | ||
| 361 | unregister_chrdev_region(msm_rpcrouter_devno, | ||
| 362 | RPCROUTER_MAX_REMOTE_SERVERS + 1); | ||
| 363 | fail_destroy_class: | ||
| 364 | class_destroy(msm_rpcrouter_class); | ||
| 365 | fail: | ||
| 366 | return rc; | ||
| 367 | } | ||
| 368 | |||
| 369 | void msm_rpcrouter_exit_devices(void) | ||
| 370 | { | ||
| 371 | cdev_del(&rpcrouter_cdev); | ||
| 372 | device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno); | ||
| 373 | unregister_chrdev_region(msm_rpcrouter_devno, | ||
| 374 | RPCROUTER_MAX_REMOTE_SERVERS + 1); | ||
| 375 | class_destroy(msm_rpcrouter_class); | ||
| 376 | } | ||
| 377 | |||
diff --git a/drivers/staging/dream/smd/smd_rpcrouter_servers.c b/drivers/staging/dream/smd/smd_rpcrouter_servers.c deleted file mode 100644 index bec3ee9371b3..000000000000 --- a/drivers/staging/dream/smd/smd_rpcrouter_servers.c +++ /dev/null | |||
| @@ -1,226 +0,0 @@ | |||
| 1 | /* arch/arm/mach-msm/rpc_servers.c | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 Google, Inc. | ||
| 4 | * Author: Iliyan Malchev <ibm@android.com> | ||
| 5 | * | ||
| 6 | * This software is licensed under the terms of the GNU General Public | ||
| 7 | * License version 2, as published by the Free Software Foundation, and | ||
| 8 | * may be copied, distributed, and modified under those terms. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/kernel.h> | ||
| 19 | #include <linux/string.h> | ||
| 20 | #include <linux/errno.h> | ||
| 21 | #include <linux/cdev.h> | ||
| 22 | #include <linux/init.h> | ||
| 23 | #include <linux/device.h> | ||
| 24 | #include <linux/types.h> | ||
| 25 | #include <linux/fs.h> | ||
| 26 | #include <linux/kthread.h> | ||
| 27 | #include <linux/delay.h> | ||
| 28 | #include <linux/platform_device.h> | ||
| 29 | #include <linux/wakelock.h> | ||
| 30 | #include <linux/slab.h> | ||
| 31 | |||
| 32 | #include <linux/msm_rpcrouter.h> | ||
| 33 | #include <linux/uaccess.h> | ||
| 34 | |||
| 35 | #include <mach/msm_rpcrouter.h> | ||
| 36 | #include "smd_rpcrouter.h" | ||
| 37 | |||
| 38 | static struct msm_rpc_endpoint *endpoint; | ||
| 39 | |||
| 40 | #define FLAG_REGISTERED 0x0001 | ||
| 41 | |||
| 42 | static LIST_HEAD(rpc_server_list); | ||
| 43 | static DEFINE_MUTEX(rpc_server_list_lock); | ||
| 44 | static int rpc_servers_active; | ||
| 45 | |||
| 46 | static void rpc_server_register(struct msm_rpc_server *server) | ||
| 47 | { | ||
| 48 | int rc; | ||
| 49 | rc = msm_rpc_register_server(endpoint, server->prog, server->vers); | ||
| 50 | if (rc < 0) | ||
| 51 | printk(KERN_ERR "[rpcserver] error registering %p @ %08x:%d\n", | ||
| 52 | server, server->prog, server->vers); | ||
| 53 | } | ||
| 54 | |||
| 55 | static struct msm_rpc_server *rpc_server_find(uint32_t prog, uint32_t vers) | ||
| 56 | { | ||
| 57 | struct msm_rpc_server *server; | ||
| 58 | |||
| 59 | mutex_lock(&rpc_server_list_lock); | ||
| 60 | list_for_each_entry(server, &rpc_server_list, list) { | ||
| 61 | if ((server->prog == prog) && | ||
| 62 | #if CONFIG_MSM_AMSS_VERSION >= 6350 | ||
| 63 | msm_rpc_is_compatible_version(server->vers, vers)) { | ||
| 64 | #else | ||
| 65 | server->vers == vers) { | ||
| 66 | #endif | ||
| 67 | mutex_unlock(&rpc_server_list_lock); | ||
| 68 | return server; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | mutex_unlock(&rpc_server_list_lock); | ||
| 72 | return NULL; | ||
| 73 | } | ||
| 74 | |||
| 75 | static void rpc_server_register_all(void) | ||
| 76 | { | ||
| 77 | struct msm_rpc_server *server; | ||
| 78 | |||
| 79 | mutex_lock(&rpc_server_list_lock); | ||
| 80 | list_for_each_entry(server, &rpc_server_list, list) { | ||
| 81 | if (!(server->flags & FLAG_REGISTERED)) { | ||
| 82 | rpc_server_register(server); | ||
| 83 | server->flags |= FLAG_REGISTERED; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | mutex_unlock(&rpc_server_list_lock); | ||
| 87 | } | ||
| 88 | |||
| 89 | int msm_rpc_create_server(struct msm_rpc_server *server) | ||
| 90 | { | ||
| 91 | /* make sure we're in a sane state first */ | ||
| 92 | server->flags = 0; | ||
| 93 | INIT_LIST_HEAD(&server->list); | ||
| 94 | |||
| 95 | mutex_lock(&rpc_server_list_lock); | ||
| 96 | list_add(&server->list, &rpc_server_list); | ||
| 97 | if (rpc_servers_active) { | ||
| 98 | rpc_server_register(server); | ||
| 99 | server->flags |= FLAG_REGISTERED; | ||
| 100 | } | ||
| 101 | mutex_unlock(&rpc_server_list_lock); | ||
| 102 | |||
| 103 | return 0; | ||
| 104 | } | ||
| 105 | |||
| 106 | static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client, | ||
| 107 | uint32_t xid, uint32_t accept_status) | ||
| 108 | { | ||
| 109 | int rc = 0; | ||
| 110 | uint8_t reply_buf[sizeof(struct rpc_reply_hdr)]; | ||
| 111 | struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf; | ||
| 112 | |||
| 113 | reply->xid = cpu_to_be32(xid); | ||
| 114 | reply->type = cpu_to_be32(1); /* reply */ | ||
| 115 | reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); | ||
| 116 | |||
| 117 | reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); | ||
| 118 | reply->data.acc_hdr.verf_flavor = 0; | ||
| 119 | reply->data.acc_hdr.verf_length = 0; | ||
| 120 | |||
| 121 | rc = msm_rpc_write(client, reply_buf, sizeof(reply_buf)); | ||
| 122 | if (rc < 0) | ||
| 123 | printk(KERN_ERR | ||
| 124 | "%s: could not write response: %d\n", | ||
| 125 | __FUNCTION__, rc); | ||
| 126 | |||
| 127 | return rc; | ||
| 128 | } | ||
| 129 | |||
| 130 | static int rpc_servers_thread(void *data) | ||
| 131 | { | ||
| 132 | void *buffer; | ||
| 133 | struct rpc_request_hdr *req; | ||
| 134 | struct msm_rpc_server *server; | ||
| 135 | int rc; | ||
| 136 | |||
| 137 | for (;;) { | ||
| 138 | rc = wait_event_interruptible(endpoint->wait_q, | ||
| 139 | !list_empty(&endpoint->read_q)); | ||
| 140 | rc = msm_rpc_read(endpoint, &buffer, -1, -1); | ||
| 141 | if (rc < 0) { | ||
| 142 | printk(KERN_ERR "%s: could not read: %d\n", | ||
| 143 | __FUNCTION__, rc); | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | req = (struct rpc_request_hdr *)buffer; | ||
| 147 | |||
| 148 | req->type = be32_to_cpu(req->type); | ||
| 149 | req->xid = be32_to_cpu(req->xid); | ||
| 150 | req->rpc_vers = be32_to_cpu(req->rpc_vers); | ||
| 151 | req->prog = be32_to_cpu(req->prog); | ||
| 152 | req->vers = be32_to_cpu(req->vers); | ||
| 153 | req->procedure = be32_to_cpu(req->procedure); | ||
| 154 | |||
| 155 | server = rpc_server_find(req->prog, req->vers); | ||
| 156 | |||
| 157 | if (req->rpc_vers != 2) | ||
| 158 | continue; | ||
| 159 | if (req->type != 0) | ||
| 160 | continue; | ||
| 161 | if (!server) { | ||
| 162 | rpc_send_accepted_void_reply( | ||
| 163 | endpoint, req->xid, | ||
| 164 | RPC_ACCEPTSTAT_PROG_UNAVAIL); | ||
| 165 | continue; | ||
| 166 | } | ||
| 167 | |||
| 168 | rc = server->rpc_call(server, req, rc); | ||
| 169 | |||
| 170 | switch (rc) { | ||
| 171 | case 0: | ||
| 172 | rpc_send_accepted_void_reply( | ||
| 173 | endpoint, req->xid, | ||
| 174 | RPC_ACCEPTSTAT_SUCCESS); | ||
| 175 | break; | ||
| 176 | default: | ||
| 177 | rpc_send_accepted_void_reply( | ||
| 178 | endpoint, req->xid, | ||
| 179 | RPC_ACCEPTSTAT_PROG_UNAVAIL); | ||
| 180 | break; | ||
| 181 | } | ||
| 182 | |||
| 183 | kfree(buffer); | ||
| 184 | } | ||
| 185 | |||
| 186 | do_exit(0); | ||
| 187 | } | ||
| 188 | |||
| 189 | static int rpcservers_probe(struct platform_device *pdev) | ||
| 190 | { | ||
| 191 | struct task_struct *server_thread; | ||
| 192 | |||
| 193 | endpoint = msm_rpc_open(); | ||
| 194 | if (IS_ERR(endpoint)) | ||
| 195 | return PTR_ERR(endpoint); | ||
| 196 | |||
| 197 | /* we're online -- register any servers installed beforehand */ | ||
| 198 | rpc_servers_active = 1; | ||
| 199 | rpc_server_register_all(); | ||
| 200 | |||
| 201 | /* start the kernel thread */ | ||
| 202 | server_thread = kthread_run(rpc_servers_thread, NULL, "krpcserversd"); | ||
| 203 | if (IS_ERR(server_thread)) | ||
| 204 | return PTR_ERR(server_thread); | ||
| 205 | |||
| 206 | return 0; | ||
| 207 | } | ||
| 208 | |||
| 209 | static struct platform_driver rpcservers_driver = { | ||
| 210 | .probe = rpcservers_probe, | ||
| 211 | .driver = { | ||
| 212 | .name = "oncrpc_router", | ||
| 213 | .owner = THIS_MODULE, | ||
| 214 | }, | ||
| 215 | }; | ||
| 216 | |||
| 217 | static int __init rpc_servers_init(void) | ||
| 218 | { | ||
| 219 | return platform_driver_register(&rpcservers_driver); | ||
| 220 | } | ||
| 221 | |||
| 222 | module_init(rpc_servers_init); | ||
| 223 | |||
| 224 | MODULE_DESCRIPTION("MSM RPC Servers"); | ||
| 225 | MODULE_AUTHOR("Iliyan Malchev <ibm@android.com>"); | ||
| 226 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/staging/dream/smd/smd_tty.c b/drivers/staging/dream/smd/smd_tty.c deleted file mode 100644 index f40944958d44..000000000000 --- a/drivers/staging/dream/smd/smd_tty.c +++ /dev/null | |||
| @@ -1,208 +0,0 @@ | |||
| 1 | /* arch/arm/mach-msm/smd_tty.c | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 Google, Inc. | ||
| 4 | * Author: Brian Swetland <swetland@google.com> | ||
| 5 | * | ||
| 6 | * This software is licensed under the terms of the GNU General Public | ||
| 7 | * License version 2, as published by the Free Software Foundation, and | ||
| 8 | * may be copied, distributed, and modified under those terms. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/fs.h> | ||
| 19 | #include <linux/cdev.h> | ||
| 20 | #include <linux/device.h> | ||
| 21 | #include <linux/wait.h> | ||
| 22 | |||
| 23 | #include <linux/tty.h> | ||
| 24 | #include <linux/tty_driver.h> | ||
| 25 | #include <linux/tty_flip.h> | ||
| 26 | |||
| 27 | #include <mach/msm_smd.h> | ||
| 28 | |||
| 29 | #define MAX_SMD_TTYS 32 | ||
| 30 | |||
| 31 | static DEFINE_MUTEX(smd_tty_lock); | ||
| 32 | |||
| 33 | struct smd_tty_info { | ||
| 34 | smd_channel_t *ch; | ||
| 35 | struct tty_struct *tty; | ||
| 36 | int open_count; | ||
| 37 | }; | ||
| 38 | |||
| 39 | static struct smd_tty_info smd_tty[MAX_SMD_TTYS]; | ||
| 40 | |||
| 41 | |||
| 42 | static void smd_tty_notify(void *priv, unsigned event) | ||
| 43 | { | ||
| 44 | unsigned char *ptr; | ||
| 45 | int avail; | ||
| 46 | struct smd_tty_info *info = priv; | ||
| 47 | struct tty_struct *tty = info->tty; | ||
| 48 | |||
| 49 | if (!tty) | ||
| 50 | return; | ||
| 51 | |||
| 52 | if (event != SMD_EVENT_DATA) | ||
| 53 | return; | ||
| 54 | |||
| 55 | for (;;) { | ||
| 56 | if (test_bit(TTY_THROTTLED, &tty->flags)) break; | ||
| 57 | avail = smd_read_avail(info->ch); | ||
| 58 | if (avail == 0) break; | ||
| 59 | |||
| 60 | avail = tty_prepare_flip_string(tty, &ptr, avail); | ||
| 61 | |||
| 62 | if (smd_read(info->ch, ptr, avail) != avail) { | ||
| 63 | /* shouldn't be possible since we're in interrupt | ||
| 64 | ** context here and nobody else could 'steal' our | ||
| 65 | ** characters. | ||
| 66 | */ | ||
| 67 | printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!"); | ||
| 68 | } | ||
| 69 | |||
| 70 | tty_flip_buffer_push(tty); | ||
| 71 | } | ||
| 72 | |||
| 73 | /* XXX only when writable and necessary */ | ||
| 74 | tty_wakeup(tty); | ||
| 75 | } | ||
| 76 | |||
| 77 | static int smd_tty_open(struct tty_struct *tty, struct file *f) | ||
| 78 | { | ||
| 79 | int res = 0; | ||
| 80 | int n = tty->index; | ||
| 81 | struct smd_tty_info *info; | ||
| 82 | const char *name; | ||
| 83 | |||
| 84 | if (n == 0) { | ||
| 85 | name = "SMD_DS"; | ||
| 86 | } else if (n == 27) { | ||
| 87 | name = "SMD_GPSNMEA"; | ||
| 88 | } else { | ||
| 89 | return -ENODEV; | ||
| 90 | } | ||
| 91 | |||
| 92 | info = smd_tty + n; | ||
| 93 | |||
| 94 | mutex_lock(&smd_tty_lock); | ||
| 95 | tty->driver_data = info; | ||
| 96 | |||
| 97 | if (info->open_count++ == 0) { | ||
| 98 | info->tty = tty; | ||
| 99 | if (info->ch) { | ||
| 100 | smd_kick(info->ch); | ||
| 101 | } else { | ||
| 102 | res = smd_open(name, &info->ch, info, smd_tty_notify); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | mutex_unlock(&smd_tty_lock); | ||
| 106 | |||
| 107 | return res; | ||
| 108 | } | ||
| 109 | |||
| 110 | static void smd_tty_close(struct tty_struct *tty, struct file *f) | ||
| 111 | { | ||
| 112 | struct smd_tty_info *info = tty->driver_data; | ||
| 113 | |||
| 114 | if (info == 0) | ||
| 115 | return; | ||
| 116 | |||
| 117 | mutex_lock(&smd_tty_lock); | ||
| 118 | if (--info->open_count == 0) { | ||
| 119 | info->tty = 0; | ||
| 120 | tty->driver_data = 0; | ||
| 121 | if (info->ch) { | ||
| 122 | smd_close(info->ch); | ||
| 123 | info->ch = 0; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | mutex_unlock(&smd_tty_lock); | ||
| 127 | } | ||
| 128 | |||
| 129 | static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len) | ||
| 130 | { | ||
| 131 | struct smd_tty_info *info = tty->driver_data; | ||
| 132 | int avail; | ||
| 133 | |||
| 134 | /* if we're writing to a packet channel we will | ||
| 135 | ** never be able to write more data than there | ||
| 136 | ** is currently space for | ||
| 137 | */ | ||
| 138 | avail = smd_write_avail(info->ch); | ||
| 139 | if (len > avail) | ||
| 140 | len = avail; | ||
| 141 | |||
| 142 | return smd_write(info->ch, buf, len); | ||
| 143 | } | ||
| 144 | |||
| 145 | static int smd_tty_write_room(struct tty_struct *tty) | ||
| 146 | { | ||
| 147 | struct smd_tty_info *info = tty->driver_data; | ||
| 148 | return smd_write_avail(info->ch); | ||
| 149 | } | ||
| 150 | |||
| 151 | static int smd_tty_chars_in_buffer(struct tty_struct *tty) | ||
| 152 | { | ||
| 153 | struct smd_tty_info *info = tty->driver_data; | ||
| 154 | return smd_read_avail(info->ch); | ||
| 155 | } | ||
| 156 | |||
| 157 | static void smd_tty_unthrottle(struct tty_struct *tty) | ||
| 158 | { | ||
| 159 | struct smd_tty_info *info = tty->driver_data; | ||
| 160 | smd_kick(info->ch); | ||
| 161 | } | ||
| 162 | |||
| 163 | static struct tty_operations smd_tty_ops = { | ||
| 164 | .open = smd_tty_open, | ||
| 165 | .close = smd_tty_close, | ||
| 166 | .write = smd_tty_write, | ||
| 167 | .write_room = smd_tty_write_room, | ||
| 168 | .chars_in_buffer = smd_tty_chars_in_buffer, | ||
| 169 | .unthrottle = smd_tty_unthrottle, | ||
| 170 | }; | ||
| 171 | |||
| 172 | static struct tty_driver *smd_tty_driver; | ||
| 173 | |||
| 174 | static int __init smd_tty_init(void) | ||
| 175 | { | ||
| 176 | int ret; | ||
| 177 | |||
| 178 | smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS); | ||
| 179 | if (smd_tty_driver == 0) | ||
| 180 | return -ENOMEM; | ||
| 181 | |||
| 182 | smd_tty_driver->owner = THIS_MODULE; | ||
| 183 | smd_tty_driver->driver_name = "smd_tty_driver"; | ||
| 184 | smd_tty_driver->name = "smd"; | ||
| 185 | smd_tty_driver->major = 0; | ||
| 186 | smd_tty_driver->minor_start = 0; | ||
| 187 | smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; | ||
| 188 | smd_tty_driver->subtype = SERIAL_TYPE_NORMAL; | ||
| 189 | smd_tty_driver->init_termios = tty_std_termios; | ||
| 190 | smd_tty_driver->init_termios.c_iflag = 0; | ||
| 191 | smd_tty_driver->init_termios.c_oflag = 0; | ||
| 192 | smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; | ||
| 193 | smd_tty_driver->init_termios.c_lflag = 0; | ||
| 194 | smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | | ||
| 195 | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; | ||
| 196 | tty_set_operations(smd_tty_driver, &smd_tty_ops); | ||
| 197 | |||
| 198 | ret = tty_register_driver(smd_tty_driver); | ||
| 199 | if (ret) return ret; | ||
| 200 | |||
| 201 | /* this should be dynamic */ | ||
| 202 | tty_register_device(smd_tty_driver, 0, 0); | ||
| 203 | tty_register_device(smd_tty_driver, 27, 0); | ||
| 204 | |||
| 205 | return 0; | ||
| 206 | } | ||
| 207 | |||
| 208 | module_init(smd_tty_init); | ||
