From: "David Lanzendörfer" <david.lanzendoerfer@o2s.ch>
To: linux-arm-msm@vger.kernel.org
Subject: [PATCH] Adding rpc server
Date: Tue, 20 Jul 2010 15:58:32 +0200 [thread overview]
Message-ID: <201007201558.33436.david.lanzendoerfer@o2s.ch> (raw)
Hi Daniel
I've tried to import the SMD server pack into the kernel.
But somehow it went wrong.
Any suggestions?
best regards
leviathan
---
arch/arm/mach-msm/Kconfig | 25 +
arch/arm/mach-msm/Makefile | 4 +-
arch/arm/mach-msm/board-trout-battery.c | 796 ++++++++++++
arch/arm/mach-msm/board-trout-hsusb.c | 352 ++++++
arch/arm/mach-msm/gpio_chip.h | 38 +
arch/arm/mach-msm/include/mach/board-hsusb.h | 68 +
arch/arm/mach-msm/include/mach/board.h | 2 +
arch/arm/mach-msm/smd_rpcrouter.c | 1310 ++++++++++++++++++++
arch/arm/mach-msm/smd_rpcrouter.h | 195 +++
arch/arm/mach-msm/smd_rpcrouter_device.c | 377 ++++++
arch/arm/mach-msm/smd_rpcrouter_servers.c | 229 ++++
drivers/staging/dream/include/mach/msm_rpcrouter.h | 1 +
12 files changed, 3395 insertions(+), 2 deletions(-)
create mode 100644 arch/arm/mach-msm/board-trout-battery.c
create mode 100644 arch/arm/mach-msm/board-trout-hsusb.c
create mode 100644 arch/arm/mach-msm/gpio_chip.h
create mode 100644 arch/arm/mach-msm/include/mach/board-hsusb.h
create mode 100644 arch/arm/mach-msm/smd_rpcrouter.c
create mode 100644 arch/arm/mach-msm/smd_rpcrouter.h
create mode 100644 arch/arm/mach-msm/smd_rpcrouter_device.c
create mode 100644 arch/arm/mach-msm/smd_rpcrouter_servers.c
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 737cf3e..7eb8722 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -1,5 +1,30 @@
if ARCH_MSM
+config MSM_AMSS_VERSION
+ int
+ default 6210 if MSM_AMSS_VERSION_6210
+ default 6220 if MSM_AMSS_VERSION_6220
+ default 6225 if MSM_AMSS_VERSION_6225
+ default 6350 if MSM_AMSS_VERSION_6350
+
+choice
+ prompt "AMSS modem firmware version"
+
+ default MSM_AMSS_VERSION_6225
+
+ config MSM_AMSS_VERSION_6210
+ bool "6.2.10"
+
+ config MSM_AMSS_VERSION_6220
+ bool "6.2.20"
+
+ config MSM_AMSS_VERSION_6225
+ bool "6.2.20 + New ADSP"
+
+ config MSM_AMSS_VERSION_6350
+ bool "6.3.50"
+endchoice
+
choice
prompt "Qualcomm MSM SoC Type"
default ARCH_MSM7X00A
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index cdd1caf..6f4da0d 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -13,10 +13,10 @@ endif
obj-$(CONFIG_ARCH_QSD8X50) += sirc.o
obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o
+obj-$(CONFIG_MSM_SMD) += smd_rpcrouter.o smd_rpcrouter_device.o smd_rpcrouter_servers.o
obj-$(CONFIG_MSM_SMD) += last_radio_log.o
-obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o
-obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o
+obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-hsusb.o board-trout-battery.o board-trout-panel.o devices-msm7x00.o
obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o
obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o
obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o
diff --git a/arch/arm/mach-msm/board-trout-battery.c b/arch/arm/mach-msm/board-trout-battery.c
new file mode 100644
index 0000000..2ea57b8
--- /dev/null
+++ b/arch/arm/mach-msm/board-trout-battery.c
@@ -0,0 +1,796 @@
+/* arch/arm/mach-msm/htc_battery.c
+ *
+ * Copyright (C) 2008 HTC Corporation.
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include "../drivers/staging/dream/include/linux/wakelock.h"
+#include <asm/gpio.h>
+#include "../drivers/staging/dream/include/mach/msm_rpcrouter.h"
+#include <mach/board.h>
+#include <asm/mach-types.h>
+#include "board-trout.h"
+
+static struct wake_lock vbus_wake_lock;
+
+#define TRACE_BATT 0
+
+#if TRACE_BATT
+#include <linux/rtc.h>
+
+#define BATT(x...) do { \
+struct timespec ts; \
+struct rtc_time tm; \
+getnstimeofday(&ts); \
+rtc_time_to_tm(ts.tv_sec, &tm); \
+printk(KERN_INFO "[BATT] " x); \
+printk(" at %lld (%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", \
+ktime_to_ns(ktime_get()), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
+tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \
+} while (0)
+#else
+#define BATT(x...) do {} while (0)
+#endif
+
+/* rpc related */
+#define APP_BATT_PDEV_NAME "rs30100001:00000000"
+#define APP_BATT_PROG 0x30100001
+#define APP_BATT_VER MSM_RPC_VERS(0,0)
+#define HTC_PROCEDURE_BATTERY_NULL 0
+#define HTC_PROCEDURE_GET_BATT_LEVEL 1
+#define HTC_PROCEDURE_GET_BATT_INFO 2
+#define HTC_PROCEDURE_GET_CABLE_STATUS 3
+#define HTC_PROCEDURE_SET_BATT_DELTA 4
+
+/* module debugger */
+#define HTC_BATTERY_DEBUG 1
+#define BATTERY_PREVENTION 1
+
+/* Enable this will shut down if no battery */
+#define ENABLE_BATTERY_DETECTION 0
+/* Sapphire pin changes:
+ * USB_ID (GPIO 90) is renamed to AC_IN (GPIO 30)
+ * CHARGER_EN (CPLD MISC2 bit[0]) is move to PMIC (MPP_14).
+ * ISET (CPLD MISC2 bit[1]) is move to PMIC (MPP_13). */
+#define GPIO_SAPPHIRE_USB_ID 30
+
+#define GPIO_BATTERY_DETECTION 21
+#define GPIO_BATTERY_CHARGER_EN 128
+
+/* Charge current selection */
+#define GPIO_BATTERY_CHARGER_CURRENT 129
+
+typedef enum {
+ DISABLE = 0,
+ ENABLE_SLOW_CHG,
+ ENABLE_FAST_CHG
+} batt_ctl_t;
+
+/* This order is the same as htc_power_supplies[]
+ * And it's also the same as htc_cable_status_update()
+ */
+typedef enum {
+ CHARGER_BATTERY = 0,
+ CHARGER_USB,
+ CHARGER_AC
+} charger_type_t;
+
+const char *charger_tags[] = {"none", "USB", "AC"};
+
+struct battery_info_reply {
+ u32 batt_id; /* Battery ID from ADC */
+ u32 batt_vol; /* Battery voltage from ADC */
+ u32 batt_temp; /* Battery Temperature (C) from formula and ADC */
+ u32 batt_current; /* Battery current from ADC */
+ u32 level; /* formula */
+ u32 charging_source; /* 0: no cable, 1:usb, 2:AC */
+ u32 charging_enabled; /* 0: Disable, 1: Enable */
+ u32 full_bat; /* Full capacity of battery (mAh) */
+};
+
+struct htc_battery_info {
+ int present;
+ unsigned long update_time;
+
+ /* lock to protect the battery info */
+ struct mutex lock;
+
+ /* lock held while calling the arm9 to query the battery info */
+ struct mutex rpc_lock;
+ struct battery_info_reply rep;
+};
+
+static struct msm_rpc_endpoint *endpoint;
+
+static struct htc_battery_info htc_batt_info;
+
+static unsigned int cache_time = 1000;
+
+static int htc_battery_initial = 0;
+
+static enum power_supply_property htc_battery_properties[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static enum power_supply_property htc_power_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *supply_list[] = {
+ "battery",
+};
+
+/* HTC dedicated attributes */
+static ssize_t htc_battery_show_property(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static int htc_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val);
+
+static int htc_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val);
+
+static struct power_supply htc_power_supplies[] = {
+ {
+ .name = "battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = htc_battery_properties,
+ .num_properties = ARRAY_SIZE(htc_battery_properties),
+ .get_property = htc_battery_get_property,
+ },
+ {
+ .name = "usb",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .supplied_to = supply_list,
+ .num_supplicants = ARRAY_SIZE(supply_list),
+ .properties = htc_power_properties,
+ .num_properties = ARRAY_SIZE(htc_power_properties),
+ .get_property = htc_power_get_property,
+ },
+ {
+ .name = "ac",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .supplied_to = supply_list,
+ .num_supplicants = ARRAY_SIZE(supply_list),
+ .properties = htc_power_properties,
+ .num_properties = ARRAY_SIZE(htc_power_properties),
+ .get_property = htc_power_get_property,
+ },
+};
+
+static int g_usb_online;
+
+/* -------------------------------------------------------------------------- */
+
+#if defined(CONFIG_DEBUG_FS)
+int htc_battery_set_charging(batt_ctl_t ctl);
+static int batt_debug_set(void *data, u64 val)
+{
+ return htc_battery_set_charging((batt_ctl_t) val);
+}
+
+static int batt_debug_get(void *data, u64 *val)
+{
+ return -ENOSYS;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(batt_debug_fops, batt_debug_get, batt_debug_set, "%llu\n");
+static int __init batt_debug_init(void)
+{
+ struct dentry *dent;
+
+ dent = debugfs_create_dir("htc_battery", 0);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+
+ debugfs_create_file("charger_state", 0644, dent, NULL, &batt_debug_fops);
+
+ return 0;
+}
+
+device_initcall(batt_debug_init);
+#endif
+
+static int init_batt_gpio(void)
+{
+ if (!machine_is_trout())
+ return 0;
+
+ if (gpio_request(GPIO_BATTERY_DETECTION, "batt_detect") < 0)
+ goto gpio_failed;
+ if (gpio_request(GPIO_BATTERY_CHARGER_EN, "charger_en") < 0)
+ goto gpio_failed;
+ if (gpio_request(GPIO_BATTERY_CHARGER_CURRENT, "charge_current") < 0)
+ goto gpio_failed;
+
+ return 0;
+
+gpio_failed:
+ return -EINVAL;
+
+}
+
+/*
+ * battery_charging_ctrl - battery charing control.
+ * @ctl: battery control command
+ *
+ */
+static int battery_charging_ctrl(batt_ctl_t ctl)
+{
+ int result = 0;
+
+ /* The charing operations are move to A9 in Sapphire. */
+ if (!machine_is_trout())
+ return result;
+
+ switch (ctl) {
+ case DISABLE:
+ BATT("charger OFF");
+ /* 0 for enable; 1 disable */
+ result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 1);
+ break;
+ case ENABLE_SLOW_CHG:
+ BATT("charger ON (SLOW)");
+ result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 0);
+ result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0);
+ break;
+ case ENABLE_FAST_CHG:
+ BATT("charger ON (FAST)");
+ result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 1);
+ result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0);
+ break;
+ default:
+ printk(KERN_ERR "Not supported battery ctr called.!\n");
+ result = -EINVAL;
+ break;
+ }
+
+ return result;
+}
+
+int htc_battery_set_charging(batt_ctl_t ctl)
+{
+ int rc;
+
+ if ((rc = battery_charging_ctrl(ctl)) < 0)
+ goto result;
+
+ if (!htc_battery_initial) {
+ htc_batt_info.rep.charging_enabled = ctl & 0x3;
+ } else {
+ mutex_lock(&htc_batt_info.lock);
+ htc_batt_info.rep.charging_enabled = ctl & 0x3;
+ mutex_unlock(&htc_batt_info.lock);
+ }
+result:
+ return rc;
+}
+
+int htc_battery_status_update(u32 curr_level)
+{
+ int notify;
+ if (!htc_battery_initial)
+ return 0;
+
+ mutex_lock(&htc_batt_info.lock);
+ notify = (htc_batt_info.rep.level != curr_level);
+ htc_batt_info.rep.level = curr_level;
+ mutex_unlock(&htc_batt_info.lock);
+
+ if (notify)
+ power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]);
+ return 0;
+}
+
+int htc_cable_status_update(int status)
+{
+ int rc = 0;
+ unsigned last_source;
+
+ if (!htc_battery_initial)
+ return 0;
+
+ if (status < CHARGER_BATTERY || status > CHARGER_AC) {
+ BATT("%s: Not supported cable status received!", __func__);
+ return -EINVAL;
+ }
+ mutex_lock(&htc_batt_info.lock);
+ /* A9 reports USB charging when helf AC cable in and China AC charger. */
+ /* Work arround: notify userspace AC charging first,
+ and notify USB charging again when receiving usb connected notificaiton from usb driver. */
+ last_source = htc_batt_info.rep.charging_source;
+ if (status == CHARGER_USB && g_usb_online == 0)
+ htc_batt_info.rep.charging_source = CHARGER_AC;
+ else {
+ htc_batt_info.rep.charging_source = status;
+ /* usb driver will not notify usb offline. */
+ if (status == CHARGER_BATTERY && g_usb_online == 1)
+ g_usb_online = 0;
+ }
+
+ /* TODO: Don't call usb driver again with the same cable status. */
+ msm_hsusb_set_vbus_state(status == CHARGER_USB);
+
+ if (htc_batt_info.rep.charging_source != last_source) {
+ if (htc_batt_info.rep.charging_source == CHARGER_USB ||
+ htc_batt_info.rep.charging_source == CHARGER_AC) {
+ wake_lock(&vbus_wake_lock);
+ } else {
+ /* give userspace some time to see the uevent and update
+ * LED state or whatnot...
+ */
+ wake_lock_timeout(&vbus_wake_lock, HZ / 2);
+ }
+ if (htc_batt_info.rep.charging_source == CHARGER_BATTERY || last_source == CHARGER_BATTERY)
+ power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]);
+ if (htc_batt_info.rep.charging_source == CHARGER_USB || last_source == CHARGER_USB)
+ power_supply_changed(&htc_power_supplies[CHARGER_USB]);
+ if (htc_batt_info.rep.charging_source == CHARGER_AC || last_source == CHARGER_AC)
+ power_supply_changed(&htc_power_supplies[CHARGER_AC]);
+ }
+ mutex_unlock(&htc_batt_info.lock);
+
+ return rc;
+}
+
+/* A9 reports USB charging when helf AC cable in and China AC charger. */
+/* Work arround: notify userspace AC charging first,
+and notify USB charging again when receiving usb connected notification from usb driver. */
+void notify_usb_connected(int online)
+{
+ mutex_lock(&htc_batt_info.lock);
+
+ BATT("%s: online=%d, g_usb_online=%d", __func__, online, g_usb_online);
+
+ if (g_usb_online != online) {
+ g_usb_online = online;
+ if (online && htc_batt_info.rep.charging_source == CHARGER_AC) {
+ mutex_unlock(&htc_batt_info.lock);
+ htc_cable_status_update(CHARGER_USB);
+ mutex_lock(&htc_batt_info.lock);
+ } else if (online) {
+ BATT("warning: usb connected but charging source=%d", htc_batt_info.rep.charging_source);
+ }
+ }
+ mutex_unlock(&htc_batt_info.lock);
+}
+
+static int htc_get_batt_info(struct battery_info_reply *buffer)
+{
+ struct rpc_request_hdr req;
+
+ struct htc_get_batt_info_rep {
+ struct rpc_reply_hdr hdr;
+ struct battery_info_reply info;
+ } rep;
+
+ int rc;
+
+ if (buffer == NULL)
+ return -EINVAL;
+
+ rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_BATT_INFO,
+ &req, sizeof(req),
+ &rep, sizeof(rep),
+ 5 * HZ);
+ if ( rc < 0 )
+ return rc;
+
+ mutex_lock(&htc_batt_info.lock);
+ buffer->batt_id = be32_to_cpu(rep.info.batt_id);
+ buffer->batt_vol = be32_to_cpu(rep.info.batt_vol);
+ buffer->batt_temp = be32_to_cpu(rep.info.batt_temp);
+ buffer->batt_current = be32_to_cpu(rep.info.batt_current);
+ buffer->level = be32_to_cpu(rep.info.level);
+ /* Move the rules of charging_source to cable_status_update. */
+ /* buffer->charging_source = be32_to_cpu(rep.info.charging_source); */
+ buffer->charging_enabled = be32_to_cpu(rep.info.charging_enabled);
+ buffer->full_bat = be32_to_cpu(rep.info.full_bat);
+ mutex_unlock(&htc_batt_info.lock);
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+static int htc_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ charger_type_t charger;
+
+ mutex_lock(&htc_batt_info.lock);
+ charger = htc_batt_info.rep.charging_source;
+ mutex_unlock(&htc_batt_info.lock);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (psy->type == POWER_SUPPLY_TYPE_MAINS)
+ val->intval = (charger == CHARGER_AC ? 1 : 0);
+ else if (psy->type == POWER_SUPPLY_TYPE_USB)
+ val->intval = (charger == CHARGER_USB ? 1 : 0);
+ else
+ val->intval = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int htc_battery_get_charging_status(void)
+{
+ u32 level;
+ charger_type_t charger;
+ int ret;
+
+ mutex_lock(&htc_batt_info.lock);
+ charger = htc_batt_info.rep.charging_source;
+
+ switch (charger) {
+ case CHARGER_BATTERY:
+ ret = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ case CHARGER_USB:
+ case CHARGER_AC:
+ level = htc_batt_info.rep.level;
+ if (level == 100)
+ ret = POWER_SUPPLY_STATUS_FULL;
+ else
+ ret = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ default:
+ ret = POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+ mutex_unlock(&htc_batt_info.lock);
+ return ret;
+}
+
+static int htc_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = htc_battery_get_charging_status();
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = htc_batt_info.present;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ mutex_lock(&htc_batt_info.lock);
+ val->intval = htc_batt_info.rep.level;
+ mutex_unlock(&htc_batt_info.lock);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define HTC_BATTERY_ATTR(_name) \
+{ \
+ .attr = { .name = #_name, .mode = S_IRUGO, .owner = THIS_MODULE }, \
+ .show = htc_battery_show_property, \
+ .store = NULL, \
+}
+
+static struct device_attribute htc_battery_attrs[] = {
+ HTC_BATTERY_ATTR(batt_id),
+ HTC_BATTERY_ATTR(batt_vol),
+ HTC_BATTERY_ATTR(batt_temp),
+ HTC_BATTERY_ATTR(batt_current),
+ HTC_BATTERY_ATTR(charging_source),
+ HTC_BATTERY_ATTR(charging_enabled),
+ HTC_BATTERY_ATTR(full_bat),
+};
+
+enum {
+ BATT_ID = 0,
+ BATT_VOL,
+ BATT_TEMP,
+ BATT_CURRENT,
+ CHARGING_SOURCE,
+ CHARGING_ENABLED,
+ FULL_BAT,
+};
+
+static int htc_rpc_set_delta(unsigned delta)
+{
+ struct set_batt_delta_req {
+ struct rpc_request_hdr hdr;
+ uint32_t data;
+ } req;
+
+ req.data = cpu_to_be32(delta);
+ return msm_rpc_call(endpoint, HTC_PROCEDURE_SET_BATT_DELTA,
+ &req, sizeof(req), 5 * HZ);
+}
+
+
+static ssize_t htc_battery_set_delta(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
+ unsigned long delta = 0;
+
+ delta = simple_strtoul(buf, NULL, 10);
+
+ if (delta > 100)
+ return -EINVAL;
+
+ mutex_lock(&htc_batt_info.rpc_lock);
+ rc = htc_rpc_set_delta(delta);
+ mutex_unlock(&htc_batt_info.rpc_lock);
+ if (rc < 0)
+ return rc;
+ return count;
+}
+
+static struct device_attribute htc_set_delta_attrs[] = {
+ __ATTR(delta, S_IWUSR | S_IWGRP, NULL, htc_battery_set_delta),
+};
+
+static int htc_battery_create_attrs(struct device * dev)
+{
+ int i, j, rc;
+
+ for (i = 0; i < ARRAY_SIZE(htc_battery_attrs); i++) {
+ rc = device_create_file(dev, &htc_battery_attrs[i]);
+ if (rc)
+ goto htc_attrs_failed;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(htc_set_delta_attrs); j++) {
+ rc = device_create_file(dev, &htc_set_delta_attrs[j]);
+ if (rc)
+ goto htc_delta_attrs_failed;
+ }
+
+ goto succeed;
+
+htc_attrs_failed:
+ while (i--)
+ device_remove_file(dev, &htc_battery_attrs[i]);
+htc_delta_attrs_failed:
+ while (j--)
+ device_remove_file(dev, &htc_set_delta_attrs[i]);
+succeed:
+ return rc;
+}
+
+static ssize_t htc_battery_show_property(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int i = 0;
+ const ptrdiff_t off = attr - htc_battery_attrs;
+
+ /* rpc lock is used to prevent two threads from calling
+ * into the get info rpc at the same time
+ */
+
+ mutex_lock(&htc_batt_info.rpc_lock);
+ /* check cache time to decide if we need to update */
+ if (htc_batt_info.update_time &&
+ time_before(jiffies, htc_batt_info.update_time +
+ msecs_to_jiffies(cache_time)))
+ goto dont_need_update;
+
+ if (htc_get_batt_info(&htc_batt_info.rep) < 0) {
+ printk(KERN_ERR "%s: rpc failed!!!\n", __FUNCTION__);
+ } else {
+ htc_batt_info.update_time = jiffies;
+ }
+dont_need_update:
+ mutex_unlock(&htc_batt_info.rpc_lock);
+
+ mutex_lock(&htc_batt_info.lock);
+ switch (off) {
+ case BATT_ID:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.batt_id);
+ break;
+ case BATT_VOL:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.batt_vol);
+ break;
+ case BATT_TEMP:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.batt_temp);
+ break;
+ case BATT_CURRENT:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.batt_current);
+ break;
+ case CHARGING_SOURCE:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.charging_source);
+ break;
+ case CHARGING_ENABLED:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.charging_enabled);
+ break;
+ case FULL_BAT:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ htc_batt_info.rep.full_bat);
+ break;
+ default:
+ i = -EINVAL;
+ }
+ mutex_unlock(&htc_batt_info.lock);
+
+ return i;
+}
+
+static int htc_battery_probe(struct platform_device *pdev)
+{
+ int i, rc;
+
+ /* init battery gpio */
+ if ((rc = init_batt_gpio()) < 0) {
+ printk(KERN_ERR "%s: init battery gpio failed!\n", __FUNCTION__);
+ return rc;
+ }
+
+ /* init structure data member */
+ htc_batt_info.update_time = jiffies;
+ /* A9 will shutdown the phone if battery is pluged out, so this value is always 1.
+ htc_batt_info.present = gpio_get_value(GPIO_TROUT_MBAT_IN);
+ */
+ htc_batt_info.present = 1;
+
+ /* init rpc */
+ endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0);
+ if (IS_ERR(endpoint)) {
+ printk(KERN_ERR "%s: init rpc failed! rc = %ld\n",
+ __FUNCTION__, PTR_ERR(endpoint));
+ return rc;
+ }
+
+ /* init power supplier framework */
+ for (i = 0; i < ARRAY_SIZE(htc_power_supplies); i++) {
+ rc = power_supply_register(&pdev->dev, &htc_power_supplies[i]);
+ if (rc)
+ printk(KERN_ERR "Failed to register power supply (%d)\n", rc);
+ }
+
+ /* create htc detail attributes */
+ htc_battery_create_attrs(htc_power_supplies[CHARGER_BATTERY].dev);
+
+ /* After battery driver gets initialized, send rpc request to inquiry
+ * the battery status in case of we lost some info
+ */
+ htc_battery_initial = 1;
+
+ mutex_lock(&htc_batt_info.rpc_lock);
+ htc_batt_info.rep.charging_source = CHARGER_BATTERY;
+ if (htc_get_batt_info(&htc_batt_info.rep) < 0)
+ printk(KERN_ERR "%s: get info failed\n", __FUNCTION__);
+
+ if (htc_rpc_set_delta(1) < 0)
+ printk(KERN_ERR "%s: set delta failed\n", __FUNCTION__);
+ htc_batt_info.update_time = jiffies;
+ mutex_unlock(&htc_batt_info.rpc_lock);
+
+ return 0;
+}
+
+static struct platform_driver htc_battery_driver = {
+ .probe = htc_battery_probe,
+ .driver = {
+ .name = APP_BATT_PDEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/* batt_mtoa server definitions */
+#define BATT_MTOA_PROG 0x30100000
+#define BATT_MTOA_VERS 0
+#define RPC_BATT_MTOA_NULL 0
+#define RPC_BATT_MTOA_SET_CHARGING_PROC 1
+#define RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC 2
+#define RPC_BATT_MTOA_LEVEL_UPDATE_PROC 3
+
+struct rpc_batt_mtoa_set_charging_args {
+ int enable;
+};
+
+struct rpc_batt_mtoa_cable_status_update_args {
+ int status;
+};
+
+struct rpc_dem_battery_update_args {
+ uint32_t level;
+};
+
+static int handle_battery_call(struct msm_rpc_server *server,
+ struct rpc_request_hdr *req, unsigned len)
+{
+ switch (req->procedure) {
+ case RPC_BATT_MTOA_NULL:
+ return 0;
+
+ case RPC_BATT_MTOA_SET_CHARGING_PROC: {
+ struct rpc_batt_mtoa_set_charging_args *args;
+ args = (struct rpc_batt_mtoa_set_charging_args *)(req + 1);
+ args->enable = be32_to_cpu(args->enable);
+ BATT("set_charging: enable=%d",args->enable);
+ htc_battery_set_charging(args->enable);
+ return 0;
+ }
+ case RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC: {
+ struct rpc_batt_mtoa_cable_status_update_args *args;
+ args = (struct rpc_batt_mtoa_cable_status_update_args *)(req + 1);
+ args->status = be32_to_cpu(args->status);
+ BATT("cable_status_update: status=%d",args->status);
+ htc_cable_status_update(args->status);
+ return 0;
+ }
+ case RPC_BATT_MTOA_LEVEL_UPDATE_PROC: {
+ struct rpc_dem_battery_update_args *args;
+ args = (struct rpc_dem_battery_update_args *)(req + 1);
+ args->level = be32_to_cpu(args->level);
+ BATT("dem_battery_update: level=%d",args->level);
+ htc_battery_status_update(args->level);
+ return 0;
+ }
+ default:
+ printk(KERN_ERR "%s: program 0x%08x:%d: unknown procedure %d\n",
+ __FUNCTION__, req->prog, req->vers, req->procedure);
+ return -ENODEV;
+ }
+}
+
+static struct msm_rpc_server battery_server = {
+ .prog = BATT_MTOA_PROG,
+ .vers = BATT_MTOA_VERS,
+ .rpc_call = handle_battery_call,
+};
+
+static int __init htc_battery_init(void)
+{
+ wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present");
+ mutex_init(&htc_batt_info.lock);
+ mutex_init(&htc_batt_info.rpc_lock);
+ msm_rpc_create_server(&battery_server);
+ platform_driver_register(&htc_battery_driver);
+ return 0;
+}
+
+module_init(htc_battery_init);
+MODULE_DESCRIPTION("HTC Battery Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/arch/arm/mach-msm/board-trout-hsusb.c b/arch/arm/mach-msm/board-trout-hsusb.c
new file mode 100644
index 0000000..a4af380
--- /dev/null
+++ b/arch/arm/mach-msm/board-trout-hsusb.c
@@ -0,0 +1,352 @@
+/* linux/arch/arm/mach-msm/devices.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2007-2009 HTC Corporation.
+ * Author: Thomas Tsai <thomas_tsai@htc.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <linux/dma-mapping.h>
+#include <mach/msm_iomap.h>
+#include <mach/dma.h>
+#include "gpio_chip.h"
+#include "devices.h"
+#include <mach/board.h>
+#include <mach/board-hsusb.h>
+#include <mach/msm_hsusb.h>
+
+#ifdef CONFIG_USB_FUNCTION
+#include <linux/usb/mass_storage_function.h>
+#endif
+
+#include <asm/mach/flash.h>
+#include <asm/setup.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/delay.h>
+#include "../drivers/staging/dream/include/mach/msm_rpcrouter.h"
+#include <mach/msm_iomap.h>
+#include <mach/mmc.h>
+
+static char *df_serialno = "000000000000";
+
+#if 0
+struct platform_device *devices[] __initdata = {
+ &msm_device_nand,
+ &msm_device_smd,
+ &msm_device_i2c,
+};
+
+void __init msm_add_devices(void)
+{
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+}
+#endif
+
+#define HSUSB_API_INIT_PHY_PROC 2
+#define HSUSB_API_PROG 0x30000064
+#define HSUSB_API_VERS MSM_RPC_VERS(1,1)
+
+static void internal_phy_reset(void)
+{
+ struct msm_rpc_endpoint *usb_ep;
+ int rc;
+ struct hsusb_phy_start_req {
+ struct rpc_request_hdr hdr;
+ } req;
+
+ printk(KERN_INFO "msm_hsusb_phy_reset\n");
+
+ usb_ep = msm_rpc_connect(HSUSB_API_PROG, HSUSB_API_VERS, 0);
+ if (IS_ERR(usb_ep)) {
+ printk(KERN_ERR "%s: init rpc failed! error: %ld\n",
+ __func__, PTR_ERR(usb_ep));
+ goto close;
+ }
+ rc = msm_rpc_call(usb_ep, HSUSB_API_INIT_PHY_PROC,
+ &req, sizeof(req), 5 * HZ);
+ if (rc < 0)
+ printk(KERN_ERR "%s: rpc call failed! (%d)\n", __func__, rc);
+
+close:
+ msm_rpc_close(usb_ep);
+}
+
+/* adjust eye diagram, disable vbusvalid interrupts */
+static int hsusb_phy_init_seq[] = { 0x40, 0x31, 0x1D, 0x0D, 0x1D, 0x10, -1 };
+
+#ifdef CONFIG_USB_FUNCTION
+static char *usb_functions[] = {
+#if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) || defined(CONFIG_USB_FUNCTION_UMS)
+ "usb_mass_storage",
+#endif
+#ifdef CONFIG_USB_FUNCTION_ADB
+ "adb",
+#endif
+};
+
+static struct msm_hsusb_product usb_products[] = {
+ {
+ .product_id = 0x0c01,
+ .functions = 0x00000001, /* usb_mass_storage */
+ },
+ {
+ .product_id = 0x0c02,
+ .functions = 0x00000003, /* usb_mass_storage + adb */
+ },
+};
+#endif
+
+struct msm_hsusb_platform_data msm_hsusb_pdata = {
+ .phy_reset = internal_phy_reset,
+ .phy_init_seq = hsusb_phy_init_seq,
+ .usb_connected = notify_usb_connected,
+#ifdef CONFIG_USB_FUNCTION
+ .vendor_id = 0x0bb4,
+ .product_id = 0x0c02,
+ .version = 0x0100,
+ .product_name = "Android Phone",
+ .manufacturer_name = "HTC",
+
+ .functions = usb_functions,
+ .num_functions = ARRAY_SIZE(usb_functions),
+ .products = usb_products,
+ .num_products = ARRAY_SIZE(usb_products),
+#endif
+};
+
+#ifdef CONFIG_USB_FUNCTION
+static struct usb_mass_storage_platform_data mass_storage_pdata = {
+ .nluns = 1,
+ .buf_size = 16384,
+ .vendor = "HTC ",
+ .product = "Android Phone ",
+ .release = 0x0100,
+};
+
+static struct platform_device usb_mass_storage_device = {
+ .name = "usb_mass_storage",
+ .id = -1,
+ .dev = {
+ .platform_data = &mass_storage_pdata,
+ },
+};
+#endif
+
+#ifdef CONFIG_USB_ANDROID
+static struct android_usb_platform_data android_usb_pdata = {
+ .vendor_id = 0x0bb4,
+ .product_id = 0x0c01,
+ .adb_product_id = 0x0c02,
+ .version = 0x0100,
+ .product_name = "Android Phone",
+ .manufacturer_name = "HTC",
+ .nluns = 1,
+};
+
+static struct platform_device android_usb_device = {
+ .name = "android_usb",
+ .id = -1,
+ .dev = {
+ .platform_data = &android_usb_pdata,
+ },
+};
+#endif
+
+void __init msm_add_usb_devices(void (*phy_reset) (void))
+{
+ /* setup */
+ if (phy_reset)
+ msm_hsusb_pdata.phy_reset = phy_reset;
+ msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata;
+ platform_device_register(&msm_device_hsusb);
+#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE
+ platform_device_register(&usb_mass_storage_device);
+#endif
+#ifdef CONFIG_USB_ANDROID
+ platform_device_register(&android_usb_device);
+#endif
+}
+
+static struct resource ram_console_resource[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device ram_console_device = {
+ .name = "ram_console",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ram_console_resource),
+ .resource = ram_console_resource,
+};
+
+#define PM_LIBPROG 0x30000061
+#if (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225)
+#define PM_LIBVERS 0xfb837d0b
+#else
+#define PM_LIBVERS 0x10001
+#endif
+
+#if 0
+static struct platform_device *msm_serial_devices[] __initdata = {
+ &msm_device_uart1,
+ &msm_device_uart2,
+ &msm_device_uart3,
+ #ifdef CONFIG_SERIAL_MSM_HS
+ &msm_device_uart_dm1,
+ &msm_device_uart_dm2,
+ #endif
+};
+
+int __init msm_add_serial_devices(unsigned num)
+{
+ if (num > MSM_SERIAL_NUM)
+ return -EINVAL;
+
+ return platform_device_register(msm_serial_devices[num]);
+}
+#endif
+
+#define ATAG_SMI 0x4d534D71
+/* setup calls mach->fixup, then parse_tags, parse_cmdline
+ * We need to setup meminfo in mach->fixup, so this function
+ * will need to traverse each tag to find smi tag.
+ */
+int __init parse_tag_smi(const struct tag *tags)
+{
+ int smi_sz = 0, find = 0;
+ struct tag *t = (struct tag *)tags;
+
+ for (; t->hdr.size; t = tag_next(t)) {
+ if (t->hdr.tag == ATAG_SMI) {
+ printk(KERN_DEBUG "find the smi tag\n");
+ find = 1;
+ break;
+ }
+ }
+ if (!find)
+ return -1;
+
+ printk(KERN_DEBUG "parse_tag_smi: smi size = %d\n", t->u.mem.size);
+ smi_sz = t->u.mem.size;
+ return smi_sz;
+}
+__tagtable(ATAG_SMI, parse_tag_smi);
+
+
+#define ATAG_HWID 0x4d534D72
+int __init parse_tag_hwid(const struct tag *tags)
+{
+ int hwid = 0, find = 0;
+ struct tag *t = (struct tag *)tags;
+
+ for (; t->hdr.size; t = tag_next(t)) {
+ if (t->hdr.tag == ATAG_HWID) {
+ printk(KERN_DEBUG "find the hwid tag\n");
+ find = 1;
+ break;
+ }
+ }
+
+ if (find)
+ hwid = t->u.revision.rev;
+ printk(KERN_DEBUG "parse_tag_hwid: hwid = 0x%x\n", hwid);
+ return hwid;
+}
+__tagtable(ATAG_HWID, parse_tag_hwid);
+
+#define ATAG_SKUID 0x4d534D73
+int __init parse_tag_skuid(const struct tag *tags)
+{
+ int skuid = 0, find = 0;
+ struct tag *t = (struct tag *)tags;
+
+ for (; t->hdr.size; t = tag_next(t)) {
+ if (t->hdr.tag == ATAG_SKUID) {
+ printk(KERN_DEBUG "find the skuid tag\n");
+ find = 1;
+ break;
+ }
+ }
+
+ if (find)
+ skuid = t->u.revision.rev;
+ printk(KERN_DEBUG "parse_tag_skuid: hwid = 0x%x\n", skuid);
+ return skuid;
+}
+__tagtable(ATAG_SKUID, parse_tag_skuid);
+
+#define ATAG_ENGINEERID 0x4d534D75
+int __init parse_tag_engineerid(const struct tag *tags)
+{
+ int engineerid = 0, find = 0;
+ struct tag *t = (struct tag *)tags;
+
+ for (; t->hdr.size; t = tag_next(t)) {
+ if (t->hdr.tag == ATAG_ENGINEERID) {
+ printk(KERN_DEBUG "find the engineer tag\n");
+ find = 1;
+ break;
+ }
+ }
+
+ if (find)
+ engineerid = t->u.revision.rev;
+ printk(KERN_DEBUG "parse_tag_engineerid: hwid = 0x%x\n", engineerid);
+ return engineerid;
+}
+__tagtable(ATAG_ENGINEERID, parse_tag_engineerid);
+
+static int mfg_mode;
+int __init board_mfg_mode_init(char *s)
+{
+ if (!strcmp(s, "normal"))
+ mfg_mode = 0;
+ else if (!strcmp(s, "factory2"))
+ mfg_mode = 1;
+ else if (!strcmp(s, "recovery"))
+ mfg_mode = 2;
+ else if (!strcmp(s, "charge"))
+ mfg_mode = 3;
+
+ return 1;
+}
+__setup("androidboot.mode=", board_mfg_mode_init);
+
+
+int board_mfg_mode(void)
+{
+ return mfg_mode;
+}
+
+static int __init board_serialno_setup(char *serialno)
+{
+ char *str;
+
+ /* use default serial number when mode is factory2 */
+ if (mfg_mode == 1 || !strlen(serialno))
+ str = df_serialno;
+ else
+ str = serialno;
+#ifdef CONFIG_USB_FUNCTION
+ msm_hsusb_pdata.serial_number = str;
+#endif
+#ifdef CONFIG_USB_ANDROID
+ android_usb_pdata.serial_number = str;
+#endif
+ return 1;
+}
+
+__setup("androidboot.serialno=", board_serialno_setup);
diff --git a/arch/arm/mach-msm/gpio_chip.h b/arch/arm/mach-msm/gpio_chip.h
new file mode 100644
index 0000000..eab9f09
--- /dev/null
+++ b/arch/arm/mach-msm/gpio_chip.h
@@ -0,0 +1,38 @@
+/* arch/arm/mach-msm/gpio_chip.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_GPIO_CHIP_H
+#define _LINUX_GPIO_CHIP_H
+
+#include <linux/list.h>
+
+struct gpio_chip {
+ struct list_head list;
+ struct gpio_state *state;
+
+ unsigned int start;
+ unsigned int end;
+
+ int (*configure)(struct gpio_chip *chip, unsigned int gpio, unsigned long flags);
+ int (*get_irq_num)(struct gpio_chip *chip, unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp);
+ int (*read)(struct gpio_chip *chip, unsigned int gpio);
+ int (*write)(struct gpio_chip *chip, unsigned int gpio, unsigned on);
+ int (*read_detect_status)(struct gpio_chip *chip, unsigned int gpio);
+ int (*clear_detect_status)(struct gpio_chip *chip, unsigned int gpio);
+};
+
+int register_gpio_chip(struct gpio_chip *gpio_chip);
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/board-hsusb.h b/arch/arm/mach-msm/include/mach/board-hsusb.h
new file mode 100644
index 0000000..994accd
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/board-hsusb.h
@@ -0,0 +1,68 @@
+/* arch/arm/mach-msm/include/mach/BOARD_HTC.h
+ * Copyright (C) 2007-2009 HTC Corporation.
+ * Author: Thomas Tsai <thomas_tsai@htc.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __ASM_ARCH_MSM_BOARD_HTC_H
+#define __ASM_ARCH_MSM_BOARD_HTC_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <asm/setup.h>
+
+struct msm_pmem_setting{
+ resource_size_t pmem_start;
+ resource_size_t pmem_size;
+ resource_size_t pmem_adsp_start;
+ resource_size_t pmem_adsp_size;
+ resource_size_t pmem_gpu0_start;
+ resource_size_t pmem_gpu0_size;
+ resource_size_t pmem_gpu1_start;
+ resource_size_t pmem_gpu1_size;
+ resource_size_t pmem_camera_start;
+ resource_size_t pmem_camera_size;
+ resource_size_t ram_console_start;
+ resource_size_t ram_console_size;
+};
+
+enum {
+ MSM_SERIAL_UART1 = 0,
+ MSM_SERIAL_UART2,
+ MSM_SERIAL_UART3,
+#ifdef CONFIG_SERIAL_MSM_HS
+ MSM_SERIAL_UART1DM,
+ MSM_SERIAL_UART2DM,
+#endif
+ MSM_SERIAL_NUM,
+};
+
+
+/* common init routines for use by arch/arm/mach-msm/board-*.c */
+
+void __init msm_add_usb_devices(void (*phy_reset) (void));
+void __init msm_add_mem_devices(struct msm_pmem_setting *setting);
+void __init msm_init_pmic_vibrator(void);
+
+struct mmc_platform_data;
+int __init msm_add_sdcc_devices(unsigned int controller, struct mmc_platform_data *plat);
+int __init msm_add_serial_devices(unsigned uart);
+
+int __init board_mfg_mode(void);
+int __init parse_tag_smi(const struct tag *tags);
+int __init parse_tag_hwid(const struct tag * tags);
+int __init parse_tag_skuid(const struct tag * tags);
+int parse_tag_engineerid(const struct tag * tags);
+
+void notify_usb_connected(int online);
+
+char *board_serialno(void);
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index e302fbd..fd2c087 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -41,4 +41,6 @@ void __init msm_init_gpio(void);
void __init msm_clock_init(struct clk *clock_tbl, unsigned num_clocks);
void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *);
+static void msm_hsusb_set_vbus_state(int online);
+
#endif
diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c
new file mode 100644
index 0000000..739ac29
--- /dev/null
+++ b/arch/arm/mach-msm/smd_rpcrouter.c
@@ -0,0 +1,1310 @@
+/* arch/arm/mach-msm/smd_rpcrouter.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2009 QUALCOMM Incorporated.
+ * Author: San Mehat <san@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* TODO: handle cases where smd_write() will tempfail due to full fifo */
+/* TODO: thread priority? schedule a work to bump it? */
+/* TODO: maybe make server_list_lock a mutex */
+/* TODO: pool fragments to avoid kmalloc/kfree churn */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include "../drivers/staging/dream/include/linux/wakelock.h"
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+
+#include <asm/byteorder.h>
+
+#include <mach/msm_smd.h>
+#include "smd_rpcrouter.h"
+
+#define TRACE_R2R_MSG 0
+#define TRACE_R2R_RAW 0
+#define TRACE_RPC_MSG 0
+#define TRACE_NOTIFY_MSG 0
+
+#define MSM_RPCROUTER_DEBUG 0
+#define MSM_RPCROUTER_DEBUG_PKT 0
+#define MSM_RPCROUTER_R2R_DEBUG 0
+#define DUMP_ALL_RECEIVED_HEADERS 0
+
+#define DIAG(x...) printk("[RR] ERROR " x)
+
+#if MSM_RPCROUTER_DEBUG
+#define D(x...) printk(x)
+#else
+#define D(x...) do {} while (0)
+#endif
+
+#if TRACE_R2R_MSG
+#define RR(x...) printk("[RR] "x)
+#else
+#define RR(x...) do {} while (0)
+#endif
+
+#if TRACE_RPC_MSG
+#define IO(x...) printk("[RPC] "x)
+#else
+#define IO(x...) do {} while (0)
+#endif
+
+#if TRACE_NOTIFY_MSG
+#define NTFY(x...) printk(KERN_ERR "[NOTIFY] "x)
+#else
+#define NTFY(x...) do {} while (0)
+#endif
+
+static LIST_HEAD(local_endpoints);
+static LIST_HEAD(remote_endpoints);
+
+static LIST_HEAD(server_list);
+
+static smd_channel_t *smd_channel;
+static int initialized;
+static wait_queue_head_t newserver_wait;
+static wait_queue_head_t smd_wait;
+static int smd_wait_count; /* odd while waiting */
+
+static DEFINE_SPINLOCK(local_endpoints_lock);
+static DEFINE_SPINLOCK(remote_endpoints_lock);
+static DEFINE_SPINLOCK(server_list_lock);
+static DEFINE_SPINLOCK(smd_lock);
+
+static struct workqueue_struct *rpcrouter_workqueue;
+static struct wake_lock rpcrouter_wake_lock;
+static int rpcrouter_need_len;
+
+static atomic_t next_xid = ATOMIC_INIT(1);
+static uint8_t next_pacmarkid;
+
+static void do_read_data(struct work_struct *work);
+static void do_create_pdevs(struct work_struct *work);
+static void do_create_rpcrouter_pdev(struct work_struct *work);
+
+static DECLARE_WORK(work_read_data, do_read_data);
+static DECLARE_WORK(work_create_pdevs, do_create_pdevs);
+static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev);
+
+#define RR_STATE_IDLE 0
+#define RR_STATE_HEADER 1
+#define RR_STATE_BODY 2
+#define RR_STATE_ERROR 3
+
+struct rr_context {
+ struct rr_packet *pkt;
+ uint8_t *ptr;
+ uint32_t state; /* current assembly state */
+ uint32_t count; /* bytes needed in this state */
+};
+
+struct rr_context the_rr_context;
+
+static struct platform_device rpcrouter_pdev = {
+ .name = "oncrpc_router",
+ .id = -1,
+};
+
+
+static int rpcrouter_send_control_msg(union rr_control_msg *msg)
+{
+ struct rr_header hdr;
+ unsigned long flags;
+ int need;
+
+ if (!(msg->cmd == RPCROUTER_CTRL_CMD_HELLO) && !initialized) {
+ printk(KERN_ERR "rpcrouter_send_control_msg(): Warning, "
+ "router not initialized\n");
+ return -EINVAL;
+ }
+
+ hdr.version = RPCROUTER_VERSION;
+ hdr.type = msg->cmd;
+ hdr.src_pid = RPCROUTER_PID_LOCAL;
+ hdr.src_cid = RPCROUTER_ROUTER_ADDRESS;
+ hdr.confirm_rx = 0;
+ hdr.size = sizeof(*msg);
+ hdr.dst_pid = 0;
+ hdr.dst_cid = RPCROUTER_ROUTER_ADDRESS;
+
+ /* TODO: what if channel is full? */
+
+ need = sizeof(hdr) + hdr.size;
+ spin_lock_irqsave(&smd_lock, flags);
+ while (smd_write_avail(smd_channel) < need) {
+ spin_unlock_irqrestore(&smd_lock, flags);
+ msleep(250);
+ spin_lock_irqsave(&smd_lock, flags);
+ }
+ smd_write(smd_channel, &hdr, sizeof(hdr));
+ smd_write(smd_channel, msg, hdr.size);
+ spin_unlock_irqrestore(&smd_lock, flags);
+ return 0;
+}
+
+static struct rr_server *rpcrouter_create_server(uint32_t pid,
+ uint32_t cid,
+ uint32_t prog,
+ uint32_t ver)
+{
+ struct rr_server *server;
+ unsigned long flags;
+ int rc;
+
+ server = kmalloc(sizeof(struct rr_server), GFP_KERNEL);
+ if (!server)
+ return ERR_PTR(-ENOMEM);
+
+ memset(server, 0, sizeof(struct rr_server));
+ server->pid = pid;
+ server->cid = cid;
+ server->prog = prog;
+ server->vers = ver;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_add_tail(&server->list, &server_list);
+ spin_unlock_irqrestore(&server_list_lock, flags);
+
+ if (pid == RPCROUTER_PID_REMOTE) {
+ rc = msm_rpcrouter_create_server_cdev(server);
+ if (rc < 0)
+ goto out_fail;
+ }
+ return server;
+out_fail:
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_del(&server->list);
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ kfree(server);
+ return ERR_PTR(rc);
+}
+
+static void rpcrouter_destroy_server(struct rr_server *server)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_del(&server->list);
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ device_destroy(msm_rpcrouter_class, server->device_number);
+ kfree(server);
+}
+
+static struct rr_server *rpcrouter_lookup_server(uint32_t prog, uint32_t ver)
+{
+ struct rr_server *server;
+ unsigned long flags;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ if (server->prog == prog
+ && server->vers == ver) {
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return server;
+ }
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return NULL;
+}
+
+static struct rr_server *rpcrouter_lookup_server_by_dev(dev_t dev)
+{
+ struct rr_server *server;
+ unsigned long flags;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ if (server->device_number == dev) {
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return server;
+ }
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return NULL;
+}
+
+struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev)
+{
+ struct msm_rpc_endpoint *ept;
+ unsigned long flags;
+
+ ept = kmalloc(sizeof(struct msm_rpc_endpoint), GFP_KERNEL);
+ if (!ept)
+ return NULL;
+ memset(ept, 0, sizeof(struct msm_rpc_endpoint));
+
+ /* mark no reply outstanding */
+ ept->reply_pid = 0xffffffff;
+
+ ept->cid = (uint32_t) ept;
+ ept->pid = RPCROUTER_PID_LOCAL;
+ ept->dev = dev;
+
+ if ((dev != msm_rpcrouter_devno) && (dev != MKDEV(0, 0))) {
+ struct rr_server *srv;
+ /*
+ * This is a userspace client which opened
+ * a program/ver devicenode. Bind the client
+ * to that destination
+ */
+ srv = rpcrouter_lookup_server_by_dev(dev);
+ /* TODO: bug? really? */
+ BUG_ON(!srv);
+
+ ept->dst_pid = srv->pid;
+ ept->dst_cid = srv->cid;
+ ept->dst_prog = cpu_to_be32(srv->prog);
+ ept->dst_vers = cpu_to_be32(srv->vers);
+ ept->flags |= MSM_RPC_ENABLE_RECEIVE;
+
+ D("Creating local ept %p @ %08x:%08x\n", ept, srv->prog, srv->vers);
+ } else {
+ /* mark not connected */
+ ept->dst_pid = 0xffffffff;
+ D("Creating a master local ept %p\n", ept);
+ }
+
+ init_waitqueue_head(&ept->wait_q);
+ INIT_LIST_HEAD(&ept->read_q);
+ spin_lock_init(&ept->read_q_lock);
+ wake_lock_init(&ept->read_q_wake_lock, WAKE_LOCK_SUSPEND, "rpc_read");
+ INIT_LIST_HEAD(&ept->incomplete);
+
+ spin_lock_irqsave(&local_endpoints_lock, flags);
+ list_add_tail(&ept->list, &local_endpoints);
+ spin_unlock_irqrestore(&local_endpoints_lock, flags);
+ return ept;
+}
+
+int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept)
+{
+ int rc;
+ union rr_control_msg msg;
+
+ msg.cmd = RPCROUTER_CTRL_CMD_REMOVE_CLIENT;
+ msg.cli.pid = ept->pid;
+ msg.cli.cid = ept->cid;
+
+ RR("x REMOVE_CLIENT id=%d:%08x\n", ept->pid, ept->cid);
+ rc = rpcrouter_send_control_msg(&msg);
+ if (rc < 0)
+ return rc;
+
+ wake_lock_destroy(&ept->read_q_wake_lock);
+ list_del(&ept->list);
+ kfree(ept);
+ return 0;
+}
+
+static int rpcrouter_create_remote_endpoint(uint32_t cid)
+{
+ struct rr_remote_endpoint *new_c;
+ unsigned long flags;
+
+ new_c = kmalloc(sizeof(struct rr_remote_endpoint), GFP_KERNEL);
+ if (!new_c)
+ return -ENOMEM;
+ memset(new_c, 0, sizeof(struct rr_remote_endpoint));
+
+ new_c->cid = cid;
+ new_c->pid = RPCROUTER_PID_REMOTE;
+ init_waitqueue_head(&new_c->quota_wait);
+ spin_lock_init(&new_c->quota_lock);
+
+ spin_lock_irqsave(&remote_endpoints_lock, flags);
+ list_add_tail(&new_c->list, &remote_endpoints);
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+ return 0;
+}
+
+static struct msm_rpc_endpoint *rpcrouter_lookup_local_endpoint(uint32_t cid)
+{
+ struct msm_rpc_endpoint *ept;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local_endpoints_lock, flags);
+ list_for_each_entry(ept, &local_endpoints, list) {
+ if (ept->cid == cid) {
+ spin_unlock_irqrestore(&local_endpoints_lock, flags);
+ return ept;
+ }
+ }
+ spin_unlock_irqrestore(&local_endpoints_lock, flags);
+ return NULL;
+}
+
+static struct rr_remote_endpoint *rpcrouter_lookup_remote_endpoint(uint32_t cid)
+{
+ struct rr_remote_endpoint *ept;
+ unsigned long flags;
+
+ spin_lock_irqsave(&remote_endpoints_lock, flags);
+ list_for_each_entry(ept, &remote_endpoints, list) {
+ if (ept->cid == cid) {
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+ return ept;
+ }
+ }
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+ return NULL;
+}
+
+static int process_control_msg(union rr_control_msg *msg, int len)
+{
+ union rr_control_msg ctl;
+ struct rr_server *server;
+ struct rr_remote_endpoint *r_ept;
+ int rc = 0;
+ unsigned long flags;
+
+ if (len != sizeof(*msg)) {
+ printk(KERN_ERR "rpcrouter: r2r msg size %d != %d\n",
+ len, sizeof(*msg));
+ return -EINVAL;
+ }
+
+ switch (msg->cmd) {
+ case RPCROUTER_CTRL_CMD_HELLO:
+ RR("o HELLO\n");
+
+ RR("x HELLO\n");
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.cmd = RPCROUTER_CTRL_CMD_HELLO;
+ rpcrouter_send_control_msg(&ctl);
+
+ initialized = 1;
+
+ /* Send list of servers one at a time */
+ ctl.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER;
+
+ /* TODO: long time to hold a spinlock... */
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ ctl.srv.pid = server->pid;
+ ctl.srv.cid = server->cid;
+ ctl.srv.prog = server->prog;
+ ctl.srv.vers = server->vers;
+
+ RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
+ server->pid, server->cid,
+ server->prog, server->vers);
+
+ rpcrouter_send_control_msg(&ctl);
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+
+ queue_work(rpcrouter_workqueue, &work_create_rpcrouter_pdev);
+ break;
+
+ case RPCROUTER_CTRL_CMD_RESUME_TX:
+ RR("o RESUME_TX id=%d:%08x\n", msg->cli.pid, msg->cli.cid);
+
+ r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid);
+ if (!r_ept) {
+ printk(KERN_ERR
+ "rpcrouter: Unable to resume client\n");
+ break;
+ }
+ spin_lock_irqsave(&r_ept->quota_lock, flags);
+ r_ept->tx_quota_cntr = 0;
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+ wake_up(&r_ept->quota_wait);
+ break;
+
+ case RPCROUTER_CTRL_CMD_NEW_SERVER:
+ RR("o NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
+ msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers);
+
+ server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers);
+
+ if (!server) {
+ server = rpcrouter_create_server(
+ msg->srv.pid, msg->srv.cid,
+ msg->srv.prog, msg->srv.vers);
+ if (!server)
+ return -ENOMEM;
+ /*
+ * XXX: Verify that its okay to add the
+ * client to our remote client list
+ * if we get a NEW_SERVER notification
+ */
+ if (!rpcrouter_lookup_remote_endpoint(msg->srv.cid)) {
+ rc = rpcrouter_create_remote_endpoint(
+ msg->srv.cid);
+ if (rc < 0)
+ printk(KERN_ERR
+ "rpcrouter:Client create"
+ "error (%d)\n", rc);
+ }
+ schedule_work(&work_create_pdevs);
+ wake_up(&newserver_wait);
+ } else {
+ if ((server->pid == msg->srv.pid) &&
+ (server->cid == msg->srv.cid)) {
+ printk(KERN_ERR "rpcrouter: Duplicate svr\n");
+ } else {
+ server->pid = msg->srv.pid;
+ server->cid = msg->srv.cid;
+ }
+ }
+ break;
+
+ case RPCROUTER_CTRL_CMD_REMOVE_SERVER:
+ RR("o REMOVE_SERVER prog=%08x:%d\n",
+ msg->srv.prog, msg->srv.vers);
+ server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers);
+ if (server)
+ rpcrouter_destroy_server(server);
+ break;
+
+ case RPCROUTER_CTRL_CMD_REMOVE_CLIENT:
+ RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.pid, msg->cli.cid);
+ if (msg->cli.pid != RPCROUTER_PID_REMOTE) {
+ printk(KERN_ERR
+ "rpcrouter: Denying remote removal of "
+ "local client\n");
+ break;
+ }
+ r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid);
+ if (r_ept) {
+ spin_lock_irqsave(&remote_endpoints_lock, flags);
+ list_del(&r_ept->list);
+ spin_unlock_irqrestore(&remote_endpoints_lock, flags);
+ kfree(r_ept);
+ }
+
+ /* Notify local clients of this event */
+ printk(KERN_ERR "rpcrouter: LOCAL NOTIFICATION NOT IMP\n");
+ rc = -ENOSYS;
+
+ break;
+ default:
+ RR("o UNKNOWN(%08x)\n", msg->cmd);
+ rc = -ENOSYS;
+ }
+
+ return rc;
+}
+
+static void do_create_rpcrouter_pdev(struct work_struct *work)
+{
+ platform_device_register(&rpcrouter_pdev);
+}
+
+static void do_create_pdevs(struct work_struct *work)
+{
+ unsigned long flags;
+ struct rr_server *server;
+
+ /* TODO: race if destroyed while being registered */
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ if (server->pid == RPCROUTER_PID_REMOTE) {
+ if (server->pdev_name[0] == 0) {
+ spin_unlock_irqrestore(&server_list_lock,
+ flags);
+ msm_rpcrouter_create_server_pdev(server);
+ schedule_work(&work_create_pdevs);
+ return;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+}
+
+static void rpcrouter_smdnotify(void *_dev, unsigned event)
+{
+ if (event != SMD_EVENT_DATA)
+ return;
+
+ if (smd_read_avail(smd_channel) >= rpcrouter_need_len)
+ wake_lock(&rpcrouter_wake_lock);
+ wake_up(&smd_wait);
+}
+
+static void *rr_malloc(unsigned sz)
+{
+ void *ptr = kmalloc(sz, GFP_KERNEL);
+ if (ptr)
+ return ptr;
+
+ printk(KERN_ERR "rpcrouter: kmalloc of %d failed, retrying...\n", sz);
+ do {
+ ptr = kmalloc(sz, GFP_KERNEL);
+ } while (!ptr);
+
+ return ptr;
+}
+
+/* TODO: deal with channel teardown / restore */
+static int rr_read(void *data, int len)
+{
+ int rc;
+ unsigned long flags;
+// printk("rr_read() %d\n", len);
+ for(;;) {
+ spin_lock_irqsave(&smd_lock, flags);
+ if (smd_read_avail(smd_channel) >= len) {
+ rc = smd_read(smd_channel, data, len);
+ spin_unlock_irqrestore(&smd_lock, flags);
+ if (rc == len)
+ return 0;
+ else
+ return -EIO;
+ }
+ rpcrouter_need_len = len;
+ wake_unlock(&rpcrouter_wake_lock);
+ spin_unlock_irqrestore(&smd_lock, flags);
+
+// printk("rr_read: waiting (%d)\n", len);
+ smd_wait_count++;
+ wake_up(&smd_wait);
+ wait_event(smd_wait, smd_read_avail(smd_channel) >= len);
+ smd_wait_count++;
+ }
+ return 0;
+}
+
+static uint32_t r2r_buf[RPCROUTER_MSGSIZE_MAX];
+
+static void do_read_data(struct work_struct *work)
+{
+ struct rr_header hdr;
+ struct rr_packet *pkt;
+ struct rr_fragment *frag;
+ struct msm_rpc_endpoint *ept;
+ uint32_t pm, mid;
+ unsigned long flags;
+
+ if (rr_read(&hdr, sizeof(hdr)))
+ goto fail_io;
+
+#if TRACE_R2R_RAW
+ RR("- ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n",
+ hdr.version, hdr.type, hdr.src_pid, hdr.src_cid,
+ hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid);
+#endif
+
+ if (hdr.version != RPCROUTER_VERSION) {
+ DIAG("version %d != %d\n", hdr.version, RPCROUTER_VERSION);
+ goto fail_data;
+ }
+ if (hdr.size > RPCROUTER_MSGSIZE_MAX) {
+ DIAG("msg size %d > max %d\n", hdr.size, RPCROUTER_MSGSIZE_MAX);
+ goto fail_data;
+ }
+
+ if (hdr.dst_cid == RPCROUTER_ROUTER_ADDRESS) {
+ if (rr_read(r2r_buf, hdr.size))
+ goto fail_io;
+ process_control_msg((void*) r2r_buf, hdr.size);
+ goto done;
+ }
+
+ if (hdr.size < sizeof(pm)) {
+ DIAG("runt packet (no pacmark)\n");
+ goto fail_data;
+ }
+ if (rr_read(&pm, sizeof(pm)))
+ goto fail_io;
+
+ hdr.size -= sizeof(pm);
+
+ frag = rr_malloc(hdr.size + sizeof(*frag));
+ frag->next = NULL;
+ frag->length = hdr.size;
+ if (rr_read(frag->data, hdr.size))
+ goto fail_io;
+
+ ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid);
+ if (!ept) {
+ DIAG("no local ept for cid %08x\n", hdr.dst_cid);
+ kfree(frag);
+ goto done;
+ }
+
+ /* See if there is already a partial packet that matches our mid
+ * and if so, append this fragment to that packet.
+ */
+ mid = PACMARK_MID(pm);
+ list_for_each_entry(pkt, &ept->incomplete, list) {
+ if (pkt->mid == mid) {
+ pkt->last->next = frag;
+ pkt->last = frag;
+ pkt->length += frag->length;
+ if (PACMARK_LAST(pm)) {
+ list_del(&pkt->list);
+ goto packet_complete;
+ }
+ goto done;
+ }
+ }
+ /* This mid is new -- create a packet for it, and put it on
+ * the incomplete list if this fragment is not a last fragment,
+ * otherwise put it on the read queue.
+ */
+ pkt = rr_malloc(sizeof(struct rr_packet));
+ pkt->first = frag;
+ pkt->last = frag;
+ memcpy(&pkt->hdr, &hdr, sizeof(hdr));
+ pkt->mid = mid;
+ pkt->length = frag->length;
+ if (!PACMARK_LAST(pm)) {
+ list_add_tail(&pkt->list, &ept->incomplete);
+ goto done;
+ }
+
+packet_complete:
+ spin_lock_irqsave(&ept->read_q_lock, flags);
+ if (ept->flags & MSM_RPC_ENABLE_RECEIVE) {
+ wake_lock(&ept->read_q_wake_lock);
+ list_add_tail(&pkt->list, &ept->read_q);
+ wake_up(&ept->wait_q);
+ } else {
+ pr_warning("smd_rpcrouter: Unexpected incoming data on %08x:%08x\n",
+ be32_to_cpu(ept->dst_prog),
+ be32_to_cpu(ept->dst_vers));
+ }
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+done:
+
+ if (hdr.confirm_rx) {
+ union rr_control_msg msg;
+
+ msg.cmd = RPCROUTER_CTRL_CMD_RESUME_TX;
+ msg.cli.pid = hdr.dst_pid;
+ msg.cli.cid = hdr.dst_cid;
+
+ RR("x RESUME_TX id=%d:%08x\n", msg.cli.pid, msg.cli.cid);
+ rpcrouter_send_control_msg(&msg);
+ }
+
+ queue_work(rpcrouter_workqueue, &work_read_data);
+ return;
+
+fail_io:
+fail_data:
+ printk(KERN_ERR "rpc_router has died\n");
+ wake_unlock(&rpcrouter_wake_lock);
+}
+
+void msm_rpc_setup_req(struct rpc_request_hdr *hdr, uint32_t prog,
+ uint32_t vers, uint32_t proc)
+{
+ memset(hdr, 0, sizeof(struct rpc_request_hdr));
+ hdr->xid = cpu_to_be32(atomic_add_return(1, &next_xid));
+ hdr->rpc_vers = cpu_to_be32(2);
+ hdr->prog = cpu_to_be32(prog);
+ hdr->vers = cpu_to_be32(vers);
+ hdr->procedure = cpu_to_be32(proc);
+}
+
+struct msm_rpc_endpoint *msm_rpc_open(void)
+{
+ struct msm_rpc_endpoint *ept;
+
+ ept = msm_rpcrouter_create_local_endpoint(MKDEV(0, 0));
+ if (ept == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ return ept;
+}
+
+int msm_rpc_close(struct msm_rpc_endpoint *ept)
+{
+ return msm_rpcrouter_destroy_local_endpoint(ept);
+}
+EXPORT_SYMBOL(msm_rpc_close);
+
+int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count)
+{
+ struct rr_header hdr;
+ uint32_t pacmark;
+ struct rpc_request_hdr *rq = buffer;
+ struct rr_remote_endpoint *r_ept;
+ unsigned long flags;
+ int needed;
+ DEFINE_WAIT(__wait);
+
+ /* TODO: fragmentation for large outbound packets */
+ if (count > (RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t)) || !count)
+ return -EINVAL;
+
+ /* snoop the RPC packet and enforce permissions */
+
+ /* has to have at least the xid and type fields */
+ if (count < (sizeof(uint32_t) * 2)) {
+ printk(KERN_ERR "rr_write: rejecting runt packet\n");
+ return -EINVAL;
+ }
+
+ if (rq->type == 0) {
+ /* RPC CALL */
+ if (count < (sizeof(uint32_t) * 6)) {
+ printk(KERN_ERR
+ "rr_write: rejecting runt call packet\n");
+ return -EINVAL;
+ }
+ if (ept->dst_pid == 0xffffffff) {
+ printk(KERN_ERR "rr_write: not connected\n");
+ return -ENOTCONN;
+ }
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ if ((ept->dst_prog != rq->prog) ||
+ !msm_rpc_is_compatible_version(
+ be32_to_cpu(ept->dst_vers),
+ be32_to_cpu(rq->vers))) {
+#else
+ if (ept->dst_prog != rq->prog || ept->dst_vers != rq->vers) {
+#endif
+ printk(KERN_ERR
+ "rr_write: cannot write to %08x:%d "
+ "(bound to %08x:%d)\n",
+ be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+ be32_to_cpu(ept->dst_prog),
+ be32_to_cpu(ept->dst_vers));
+ return -EINVAL;
+ }
+ hdr.dst_pid = ept->dst_pid;
+ hdr.dst_cid = ept->dst_cid;
+ IO("CALL on ept %p to %08x:%08x @ %d:%08x (%d bytes) (xid %x proc %x)\n",
+ ept,
+ be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+ ept->dst_pid, ept->dst_cid, count,
+ be32_to_cpu(rq->xid), be32_to_cpu(rq->procedure));
+ } else {
+ /* RPC REPLY */
+ /* TODO: locking */
+ if (ept->reply_pid == 0xffffffff) {
+ printk(KERN_ERR
+ "rr_write: rejecting unexpected reply\n");
+ return -EINVAL;
+ }
+ if (ept->reply_xid != rq->xid) {
+ printk(KERN_ERR
+ "rr_write: rejecting packet w/ bad xid\n");
+ return -EINVAL;
+ }
+
+ hdr.dst_pid = ept->reply_pid;
+ hdr.dst_cid = ept->reply_cid;
+
+ /* consume this reply */
+ ept->reply_pid = 0xffffffff;
+
+ IO("REPLY on ept %p to xid=%d @ %d:%08x (%d bytes)\n",
+ ept,
+ be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count);
+ }
+
+ r_ept = rpcrouter_lookup_remote_endpoint(hdr.dst_cid);
+
+ if (!r_ept) {
+ printk(KERN_ERR
+ "msm_rpc_write(): No route to ept "
+ "[PID %x CID %x]\n", hdr.dst_pid, hdr.dst_cid);
+ return -EHOSTUNREACH;
+ }
+
+ /* Create routing header */
+ hdr.type = RPCROUTER_CTRL_CMD_DATA;
+ hdr.version = RPCROUTER_VERSION;
+ hdr.src_pid = ept->pid;
+ hdr.src_cid = ept->cid;
+ hdr.confirm_rx = 0;
+ hdr.size = count + sizeof(uint32_t);
+
+ for (;;) {
+ prepare_to_wait(&r_ept->quota_wait, &__wait,
+ TASK_INTERRUPTIBLE);
+ spin_lock_irqsave(&r_ept->quota_lock, flags);
+ if (r_ept->tx_quota_cntr < RPCROUTER_DEFAULT_RX_QUOTA)
+ break;
+ if (signal_pending(current) &&
+ (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE)))
+ break;
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+ schedule();
+ }
+ finish_wait(&r_ept->quota_wait, &__wait);
+
+ if (signal_pending(current) &&
+ (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) {
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+ return -ERESTARTSYS;
+ }
+ r_ept->tx_quota_cntr++;
+ if (r_ept->tx_quota_cntr == RPCROUTER_DEFAULT_RX_QUOTA)
+ hdr.confirm_rx = 1;
+
+ /* bump pacmark while interrupts disabled to avoid race
+ * probably should be atomic op instead
+ */
+ pacmark = PACMARK(count, ++next_pacmarkid, 0, 1);
+
+ spin_unlock_irqrestore(&r_ept->quota_lock, flags);
+
+ spin_lock_irqsave(&smd_lock, flags);
+
+ needed = sizeof(hdr) + hdr.size;
+ while (smd_write_avail(smd_channel) < needed) {
+ spin_unlock_irqrestore(&smd_lock, flags);
+ msleep(250);
+ spin_lock_irqsave(&smd_lock, flags);
+ }
+
+ /* TODO: deal with full fifo */
+ smd_write(smd_channel, &hdr, sizeof(hdr));
+ smd_write(smd_channel, &pacmark, sizeof(pacmark));
+ smd_write(smd_channel, buffer, count);
+
+ spin_unlock_irqrestore(&smd_lock, flags);
+
+ return count;
+}
+EXPORT_SYMBOL(msm_rpc_write);
+
+/*
+ * NOTE: It is the responsibility of the caller to kfree buffer
+ */
+int msm_rpc_read(struct msm_rpc_endpoint *ept, void **buffer,
+ unsigned user_len, long timeout)
+{
+ struct rr_fragment *frag, *next;
+ char *buf;
+ int rc;
+
+ rc = __msm_rpc_read(ept, &frag, user_len, timeout);
+ if (rc <= 0)
+ return rc;
+
+ /* single-fragment messages conveniently can be
+ * returned as-is (the buffer is at the front)
+ */
+ if (frag->next == 0) {
+ *buffer = (void*) frag;
+ return rc;
+ }
+
+ /* multi-fragment messages, we have to do it the
+ * hard way, which is rather disgusting right now
+ */
+ buf = rr_malloc(rc);
+ *buffer = buf;
+
+ while (frag != NULL) {
+ memcpy(buf, frag->data, frag->length);
+ next = frag->next;
+ buf += frag->length;
+ kfree(frag);
+ frag = next;
+ }
+
+ return rc;
+}
+
+int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc,
+ void *_request, int request_size,
+ long timeout)
+{
+ return msm_rpc_call_reply(ept, proc,
+ _request, request_size,
+ NULL, 0, timeout);
+}
+EXPORT_SYMBOL(msm_rpc_call);
+
+int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc,
+ void *_request, int request_size,
+ void *_reply, int reply_size,
+ long timeout)
+{
+ struct rpc_request_hdr *req = _request;
+ struct rpc_reply_hdr *reply;
+ int rc;
+
+ if (request_size < sizeof(*req))
+ return -ETOOSMALL;
+
+ if (ept->dst_pid == 0xffffffff)
+ return -ENOTCONN;
+
+ /* We can't use msm_rpc_setup_req() here, because dst_prog and
+ * dst_vers here are already in BE.
+ */
+ memset(req, 0, sizeof(*req));
+ req->xid = cpu_to_be32(atomic_add_return(1, &next_xid));
+ req->rpc_vers = cpu_to_be32(2);
+ req->prog = ept->dst_prog;
+ req->vers = ept->dst_vers;
+ req->procedure = cpu_to_be32(proc);
+
+ /* Allow replys to be added to the queue */
+ ept->flags |= MSM_RPC_ENABLE_RECEIVE;
+
+ rc = msm_rpc_write(ept, req, request_size);
+ if (rc < 0)
+ goto error;
+
+ for (;;) {
+ rc = msm_rpc_read(ept, (void*) &reply, -1, timeout);
+ if (rc < 0)
+ goto error;
+ if (rc < (3 * sizeof(uint32_t))) {
+ rc = -EIO;
+ break;
+ }
+ /* we should not get CALL packets -- ignore them */
+ if (reply->type == 0) {
+ kfree(reply);
+ continue;
+ }
+ /* If an earlier call timed out, we could get the (no
+ * longer wanted) reply for it. Ignore replies that
+ * we don't expect.
+ */
+ if (reply->xid != req->xid) {
+ kfree(reply);
+ continue;
+ }
+ if (reply->reply_stat != 0) {
+ rc = -EPERM;
+ break;
+ }
+ if (reply->data.acc_hdr.accept_stat != 0) {
+ rc = -EINVAL;
+ break;
+ }
+ if (_reply == NULL) {
+ rc = 0;
+ break;
+ }
+ if (rc > reply_size) {
+ rc = -ENOMEM;
+ } else {
+ memcpy(_reply, reply, rc);
+ }
+ break;
+ }
+ kfree(reply);
+error:
+ ept->flags &= ~MSM_RPC_ENABLE_RECEIVE;
+ wake_unlock(&ept->read_q_wake_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(msm_rpc_call_reply);
+
+
+static inline int ept_packet_available(struct msm_rpc_endpoint *ept)
+{
+ unsigned long flags;
+ int ret;
+ spin_lock_irqsave(&ept->read_q_lock, flags);
+ ret = !list_empty(&ept->read_q);
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+ return ret;
+}
+
+int __msm_rpc_read(struct msm_rpc_endpoint *ept,
+ struct rr_fragment **frag_ret,
+ unsigned len, long timeout)
+{
+ struct rr_packet *pkt;
+ struct rpc_request_hdr *rq;
+ DEFINE_WAIT(__wait);
+ unsigned long flags;
+ int rc;
+
+ IO("READ on ept %p\n", ept);
+
+ if (ept->flags & MSM_RPC_UNINTERRUPTIBLE) {
+ if (timeout < 0) {
+ wait_event(ept->wait_q, ept_packet_available(ept));
+ } else {
+ rc = wait_event_timeout(
+ ept->wait_q, ept_packet_available(ept),
+ timeout);
+ if (rc == 0)
+ return -ETIMEDOUT;
+ }
+ } else {
+ if (timeout < 0) {
+ rc = wait_event_interruptible(
+ ept->wait_q, ept_packet_available(ept));
+ if (rc < 0)
+ return rc;
+ } else {
+ rc = wait_event_interruptible_timeout(
+ ept->wait_q, ept_packet_available(ept),
+ timeout);
+ if (rc == 0)
+ return -ETIMEDOUT;
+ }
+ }
+
+ spin_lock_irqsave(&ept->read_q_lock, flags);
+ if (list_empty(&ept->read_q)) {
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+ return -EAGAIN;
+ }
+ pkt = list_first_entry(&ept->read_q, struct rr_packet, list);
+ if (pkt->length > len) {
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+ return -ETOOSMALL;
+ }
+ list_del(&pkt->list);
+ if (list_empty(&ept->read_q))
+ wake_unlock(&ept->read_q_wake_lock);
+ spin_unlock_irqrestore(&ept->read_q_lock, flags);
+
+ rc = pkt->length;
+
+ *frag_ret = pkt->first;
+ rq = (void*) pkt->first->data;
+ if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 0)) {
+ IO("READ on ept %p is a CALL on %08x:%08x proc %d xid %d\n",
+ ept, be32_to_cpu(rq->prog), be32_to_cpu(rq->vers),
+ be32_to_cpu(rq->procedure),
+ be32_to_cpu(rq->xid));
+ /* RPC CALL */
+ if (ept->reply_pid != 0xffffffff) {
+ printk(KERN_WARNING
+ "rr_read: lost previous reply xid...\n");
+ }
+ /* TODO: locking? */
+ ept->reply_pid = pkt->hdr.src_pid;
+ ept->reply_cid = pkt->hdr.src_cid;
+ ept->reply_xid = rq->xid;
+ }
+#if TRACE_RPC_MSG
+ else if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 1))
+ IO("READ on ept %p is a REPLY\n", ept);
+ else IO("READ on ept %p (%d bytes)\n", ept, rc);
+#endif
+
+ kfree(pkt);
+ return rc;
+}
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+int msm_rpc_is_compatible_version(uint32_t server_version,
+ uint32_t client_version)
+{
+ if ((server_version & RPC_VERSION_MODE_MASK) !=
+ (client_version & RPC_VERSION_MODE_MASK))
+ return 0;
+
+ if (server_version & RPC_VERSION_MODE_MASK)
+ return server_version == client_version;
+
+ return ((server_version & RPC_VERSION_MAJOR_MASK) ==
+ (client_version & RPC_VERSION_MAJOR_MASK)) &&
+ ((server_version & RPC_VERSION_MINOR_MASK) >=
+ (client_version & RPC_VERSION_MINOR_MASK));
+}
+EXPORT_SYMBOL(msm_rpc_is_compatible_version);
+
+static int msm_rpc_get_compatible_server(uint32_t prog,
+ uint32_t ver,
+ uint32_t *found_vers)
+{
+ struct rr_server *server;
+ unsigned long flags;
+ if (found_vers == NULL)
+ return 0;
+
+ spin_lock_irqsave(&server_list_lock, flags);
+ list_for_each_entry(server, &server_list, list) {
+ if ((server->prog == prog) &&
+ msm_rpc_is_compatible_version(server->vers, ver)) {
+ *found_vers = server->vers;
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return 0;
+ }
+ }
+ spin_unlock_irqrestore(&server_list_lock, flags);
+ return -1;
+}
+#endif
+
+struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags)
+{
+ struct msm_rpc_endpoint *ept;
+ struct rr_server *server;
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ if (!(vers & RPC_VERSION_MODE_MASK)) {
+ uint32_t found_vers;
+ if (msm_rpc_get_compatible_server(prog, vers, &found_vers) < 0)
+ return ERR_PTR(-EHOSTUNREACH);
+ if (found_vers != vers) {
+ D("RPC using new version %08x:{%08x --> %08x}\n",
+ prog, vers, found_vers);
+ vers = found_vers;
+ }
+ }
+#endif
+
+ server = rpcrouter_lookup_server(prog, vers);
+ if (!server)
+ return ERR_PTR(-EHOSTUNREACH);
+
+ ept = msm_rpc_open();
+ if (IS_ERR(ept))
+ return ept;
+
+ ept->flags = flags;
+ ept->dst_pid = server->pid;
+ ept->dst_cid = server->cid;
+ ept->dst_prog = cpu_to_be32(prog);
+ ept->dst_vers = cpu_to_be32(vers);
+
+ return ept;
+}
+EXPORT_SYMBOL(msm_rpc_connect);
+
+uint32_t msm_rpc_get_vers(struct msm_rpc_endpoint *ept)
+{
+ return be32_to_cpu(ept->dst_vers);
+}
+EXPORT_SYMBOL(msm_rpc_get_vers);
+
+/* TODO: permission check? */
+int msm_rpc_register_server(struct msm_rpc_endpoint *ept,
+ uint32_t prog, uint32_t vers)
+{
+ int rc;
+ union rr_control_msg msg;
+ struct rr_server *server;
+
+ server = rpcrouter_create_server(ept->pid, ept->cid,
+ prog, vers);
+ if (!server)
+ return -ENODEV;
+
+ msg.srv.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER;
+ msg.srv.pid = ept->pid;
+ msg.srv.cid = ept->cid;
+ msg.srv.prog = prog;
+ msg.srv.vers = vers;
+
+ RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n",
+ ept->pid, ept->cid, prog, vers);
+
+ rc = rpcrouter_send_control_msg(&msg);
+ if (rc < 0)
+ return rc;
+
+ ept->flags |= MSM_RPC_ENABLE_RECEIVE;
+ return 0;
+}
+
+/* TODO: permission check -- disallow unreg of somebody else's server */
+int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept,
+ uint32_t prog, uint32_t vers)
+{
+ struct rr_server *server;
+ server = rpcrouter_lookup_server(prog, vers);
+
+ if (!server)
+ return -ENOENT;
+
+ ept->flags &= ~MSM_RPC_ENABLE_RECEIVE;
+ wake_unlock(&ept->read_q_wake_lock);
+ rpcrouter_destroy_server(server);
+ return 0;
+}
+
+static int msm_rpcrouter_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ /* Initialize what we need to start processing */
+ INIT_LIST_HEAD(&local_endpoints);
+ INIT_LIST_HEAD(&remote_endpoints);
+
+ init_waitqueue_head(&newserver_wait);
+ init_waitqueue_head(&smd_wait);
+ wake_lock_init(&rpcrouter_wake_lock, WAKE_LOCK_SUSPEND, "SMD_RPCCALL");
+
+ rpcrouter_workqueue = create_singlethread_workqueue("rpcrouter");
+ if (!rpcrouter_workqueue)
+ return -ENOMEM;
+
+ rc = msm_rpcrouter_init_devices();
+ if (rc < 0)
+ goto fail_destroy_workqueue;
+
+ /* Open up SMD channel 2 */
+ initialized = 0;
+ rc = smd_open("SMD_RPCCALL", &smd_channel, NULL, rpcrouter_smdnotify);
+ if (rc < 0)
+ goto fail_remove_devices;
+
+ queue_work(rpcrouter_workqueue, &work_read_data);
+ return 0;
+
+ fail_remove_devices:
+ msm_rpcrouter_exit_devices();
+ fail_destroy_workqueue:
+ destroy_workqueue(rpcrouter_workqueue);
+ return rc;
+}
+
+static int msm_rpcrouter_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ /* Wait until the worker thread has waited at least once so that it
+ * gets a chance to release its wakelock.
+ */
+ int wait_count = smd_wait_count;
+ if (!(smd_wait_count & 1))
+ wait_event(smd_wait, smd_wait_count != wait_count);
+ return 0;
+}
+
+static struct platform_driver msm_smd_channel2_driver = {
+ .probe = msm_rpcrouter_probe,
+ .driver = {
+ .name = "SMD_RPCCALL",
+ .owner = THIS_MODULE,
+ },
+ .suspend = msm_rpcrouter_suspend,
+};
+
+static int __init rpcrouter_init(void)
+{
+ return platform_driver_register(&msm_smd_channel2_driver);
+}
+
+module_init(rpcrouter_init);
+MODULE_DESCRIPTION("MSM RPC Router");
+MODULE_AUTHOR("San Mehat <san@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/smd_rpcrouter.h b/arch/arm/mach-msm/smd_rpcrouter.h
new file mode 100644
index 0000000..4279e6f
--- /dev/null
+++ b/arch/arm/mach-msm/smd_rpcrouter.h
@@ -0,0 +1,195 @@
+/** arch/arm/mach-msm/smd_rpcrouter.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2008 QUALCOMM Incorporated.
+ * Author: San Mehat <san@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H
+#define _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include "../drivers/staging/dream/include/linux/wakelock.h"
+
+#include <mach/msm_smd.h>
+#include "../drivers/staging/dream/include/mach/msm_rpcrouter.h"
+
+/* definitions for the R2R wire protcol */
+
+#define RPCROUTER_VERSION 1
+#define RPCROUTER_PROCESSORS_MAX 4
+#define RPCROUTER_MSGSIZE_MAX 512
+
+#define RPCROUTER_CLIENT_BCAST_ID 0xffffffff
+#define RPCROUTER_ROUTER_ADDRESS 0xfffffffe
+
+#define RPCROUTER_PID_LOCAL 1
+#define RPCROUTER_PID_REMOTE 0
+
+#define RPCROUTER_CTRL_CMD_DATA 1
+#define RPCROUTER_CTRL_CMD_HELLO 2
+#define RPCROUTER_CTRL_CMD_BYE 3
+#define RPCROUTER_CTRL_CMD_NEW_SERVER 4
+#define RPCROUTER_CTRL_CMD_REMOVE_SERVER 5
+#define RPCROUTER_CTRL_CMD_REMOVE_CLIENT 6
+#define RPCROUTER_CTRL_CMD_RESUME_TX 7
+#define RPCROUTER_CTRL_CMD_EXIT 8
+
+#define RPCROUTER_DEFAULT_RX_QUOTA 5
+
+union rr_control_msg {
+ uint32_t cmd;
+ struct {
+ uint32_t cmd;
+ uint32_t prog;
+ uint32_t vers;
+ uint32_t pid;
+ uint32_t cid;
+ } srv;
+ struct {
+ uint32_t cmd;
+ uint32_t pid;
+ uint32_t cid;
+ } cli;
+};
+
+struct rr_header {
+ uint32_t version;
+ uint32_t type;
+ uint32_t src_pid;
+ uint32_t src_cid;
+ uint32_t confirm_rx;
+ uint32_t size;
+ uint32_t dst_pid;
+ uint32_t dst_cid;
+};
+
+/* internals */
+
+#define RPCROUTER_MAX_REMOTE_SERVERS 100
+
+struct rr_fragment {
+ unsigned char data[RPCROUTER_MSGSIZE_MAX];
+ uint32_t length;
+ struct rr_fragment *next;
+};
+
+struct rr_packet {
+ struct list_head list;
+ struct rr_fragment *first;
+ struct rr_fragment *last;
+ struct rr_header hdr;
+ uint32_t mid;
+ uint32_t length;
+};
+
+#define PACMARK_LAST(n) ((n) & 0x80000000)
+#define PACMARK_MID(n) (((n) >> 16) & 0xFF)
+#define PACMARK_LEN(n) ((n) & 0xFFFF)
+
+static inline uint32_t PACMARK(uint32_t len, uint32_t mid, uint32_t first,
+ uint32_t last)
+{
+ return (len & 0xFFFF) |
+ ((mid & 0xFF) << 16) |
+ ((!!first) << 30) |
+ ((!!last) << 31);
+}
+
+struct rr_server {
+ struct list_head list;
+
+ uint32_t pid;
+ uint32_t cid;
+ uint32_t prog;
+ uint32_t vers;
+
+ dev_t device_number;
+ struct cdev cdev;
+ struct device *device;
+ struct rpcsvr_platform_device p_device;
+ char pdev_name[32];
+};
+
+struct rr_remote_endpoint {
+ uint32_t pid;
+ uint32_t cid;
+
+ int tx_quota_cntr;
+ spinlock_t quota_lock;
+ wait_queue_head_t quota_wait;
+
+ struct list_head list;
+};
+
+struct msm_rpc_endpoint {
+ struct list_head list;
+
+ /* incomplete packets waiting for assembly */
+ struct list_head incomplete;
+
+ /* complete packets waiting to be read */
+ struct list_head read_q;
+ spinlock_t read_q_lock;
+ struct wake_lock read_q_wake_lock;
+ wait_queue_head_t wait_q;
+ unsigned flags;
+
+ /* endpoint address */
+ uint32_t pid;
+ uint32_t cid;
+
+ /* bound remote address
+ * if not connected (dst_pid == 0xffffffff) RPC_CALL writes fail
+ * RPC_CALLs must be to the prog/vers below or they will fail
+ */
+ uint32_t dst_pid;
+ uint32_t dst_cid;
+ uint32_t dst_prog; /* be32 */
+ uint32_t dst_vers; /* be32 */
+
+ /* reply remote address
+ * if reply_pid == 0xffffffff, none available
+ * RPC_REPLY writes may only go to the pid/cid/xid of the
+ * last RPC_CALL we received.
+ */
+ uint32_t reply_pid;
+ uint32_t reply_cid;
+ uint32_t reply_xid; /* be32 */
+ uint32_t next_pm; /* Pacmark sequence */
+
+ /* device node if this endpoint is accessed via userspace */
+ dev_t dev;
+};
+
+/* shared between smd_rpcrouter*.c */
+
+int __msm_rpc_read(struct msm_rpc_endpoint *ept,
+ struct rr_fragment **frag,
+ unsigned len, long timeout);
+
+struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev);
+int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept);
+
+int msm_rpcrouter_create_server_cdev(struct rr_server *server);
+int msm_rpcrouter_create_server_pdev(struct rr_server *server);
+
+int msm_rpcrouter_init_devices(void);
+void msm_rpcrouter_exit_devices(void);
+
+extern dev_t msm_rpcrouter_devno;
+extern struct class *msm_rpcrouter_class;
+#endif
diff --git a/arch/arm/mach-msm/smd_rpcrouter_device.c b/arch/arm/mach-msm/smd_rpcrouter_device.c
new file mode 100644
index 0000000..4ddbcff
--- /dev/null
+++ b/arch/arm/mach-msm/smd_rpcrouter_device.c
@@ -0,0 +1,377 @@
+/* arch/arm/mach-msm/smd_rpcrouter_device.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2009 QUALCOMM Incorporated.
+ * Author: San Mehat <san@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/platform_device.h>
+#include "../drivers/staging/dream/include/linux/msm_rpcrouter.h"
+
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+#include "smd_rpcrouter.h"
+
+#define SAFETY_MEM_SIZE 65536
+
+/* Next minor # available for a remote server */
+static int next_minor = 1;
+
+struct class *msm_rpcrouter_class;
+dev_t msm_rpcrouter_devno;
+
+static struct cdev rpcrouter_cdev;
+static struct device *rpcrouter_device;
+
+static int rpcrouter_open(struct inode *inode, struct file *filp)
+{
+ int rc;
+ struct msm_rpc_endpoint *ept;
+
+ rc = nonseekable_open(inode, filp);
+ if (rc < 0)
+ return rc;
+
+ ept = msm_rpcrouter_create_local_endpoint(inode->i_rdev);
+ if (!ept)
+ return -ENOMEM;
+
+ filp->private_data = ept;
+ return 0;
+}
+
+static int rpcrouter_release(struct inode *inode, struct file *filp)
+{
+ struct msm_rpc_endpoint *ept;
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+ return msm_rpcrouter_destroy_local_endpoint(ept);
+}
+
+static ssize_t rpcrouter_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_rpc_endpoint *ept;
+ struct rr_fragment *frag, *next;
+ int rc;
+
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+ rc = __msm_rpc_read(ept, &frag, count, -1);
+ if (rc < 0)
+ return rc;
+
+ count = rc;
+
+ while (frag != NULL) {
+ if (copy_to_user(buf, frag->data, frag->length)) {
+ printk(KERN_ERR
+ "rpcrouter: could not copy all read data to user!\n");
+ rc = -EFAULT;
+ }
+ buf += frag->length;
+ next = frag->next;
+ kfree(frag);
+ frag = next;
+ }
+
+ return rc;
+}
+
+static ssize_t rpcrouter_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_rpc_endpoint *ept;
+ int rc = 0;
+ void *k_buffer;
+
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+ /* A check for safety, this seems non-standard */
+ if (count > SAFETY_MEM_SIZE)
+ return -EINVAL;
+
+ k_buffer = kmalloc(count, GFP_KERNEL);
+ if (!k_buffer)
+ return -ENOMEM;
+
+ if (copy_from_user(k_buffer, buf, count)) {
+ rc = -EFAULT;
+ goto write_out_free;
+ }
+
+ rc = msm_rpc_write(ept, k_buffer, count);
+ if (rc < 0)
+ goto write_out_free;
+
+ rc = count;
+write_out_free:
+ kfree(k_buffer);
+ return rc;
+}
+
+static unsigned int rpcrouter_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct msm_rpc_endpoint *ept;
+ unsigned mask = 0;
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+
+ /* If there's data already in the read queue, return POLLIN.
+ * Else, wait for the requested amount of time, and check again.
+ */
+
+ if (!list_empty(&ept->read_q))
+ mask |= POLLIN;
+
+ if (!mask) {
+ poll_wait(filp, &ept->wait_q, wait);
+ if (!list_empty(&ept->read_q))
+ mask |= POLLIN;
+ }
+
+ return mask;
+}
+
+static long rpcrouter_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct msm_rpc_endpoint *ept;
+ struct rpcrouter_ioctl_server_args server_args;
+ int rc = 0;
+ uint32_t n;
+
+ ept = (struct msm_rpc_endpoint *) filp->private_data;
+ switch (cmd) {
+
+ case RPC_ROUTER_IOCTL_GET_VERSION:
+ n = RPC_ROUTER_VERSION_V1;
+ rc = put_user(n, (unsigned int *) arg);
+ break;
+
+ case RPC_ROUTER_IOCTL_GET_MTU:
+ /* the pacmark word reduces the actual payload
+ * possible per message
+ */
+ n = RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t);
+ rc = put_user(n, (unsigned int *) arg);
+ break;
+
+ case RPC_ROUTER_IOCTL_REGISTER_SERVER:
+ rc = copy_from_user(&server_args, (void *) arg,
+ sizeof(server_args));
+ if (rc < 0)
+ break;
+ msm_rpc_register_server(ept,
+ server_args.prog,
+ server_args.vers);
+ break;
+
+ case RPC_ROUTER_IOCTL_UNREGISTER_SERVER:
+ rc = copy_from_user(&server_args, (void *) arg,
+ sizeof(server_args));
+ if (rc < 0)
+ break;
+
+ msm_rpc_unregister_server(ept,
+ server_args.prog,
+ server_args.vers);
+ break;
+
+ case RPC_ROUTER_IOCTL_GET_MINOR_VERSION:
+ n = MSM_RPC_GET_MINOR(msm_rpc_get_vers(ept));
+ rc = put_user(n, (unsigned int *)arg);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static struct file_operations rpcrouter_server_fops = {
+ .owner = THIS_MODULE,
+ .open = rpcrouter_open,
+ .release = rpcrouter_release,
+ .read = rpcrouter_read,
+ .write = rpcrouter_write,
+ .poll = rpcrouter_poll,
+ .unlocked_ioctl = rpcrouter_ioctl,
+};
+
+static struct file_operations rpcrouter_router_fops = {
+ .owner = THIS_MODULE,
+ .open = rpcrouter_open,
+ .release = rpcrouter_release,
+ .read = rpcrouter_read,
+ .write = rpcrouter_write,
+ .poll = rpcrouter_poll,
+ .unlocked_ioctl = rpcrouter_ioctl,
+};
+
+int msm_rpcrouter_create_server_cdev(struct rr_server *server)
+{
+ int rc;
+ uint32_t dev_vers;
+
+ if (next_minor == RPCROUTER_MAX_REMOTE_SERVERS) {
+ printk(KERN_ERR
+ "rpcrouter: Minor numbers exhausted - Increase "
+ "RPCROUTER_MAX_REMOTE_SERVERS\n");
+ return -ENOBUFS;
+ }
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ /* Servers with bit 31 set are remote msm servers with hashkey version.
+ * Servers with bit 31 not set are remote msm servers with
+ * backwards compatible version type in which case the minor number
+ * (lower 16 bits) is set to zero.
+ *
+ */
+ if ((server->vers & RPC_VERSION_MODE_MASK))
+ dev_vers = server->vers;
+ else
+ dev_vers = server->vers & RPC_VERSION_MAJOR_MASK;
+#else
+ dev_vers = server->vers;
+#endif
+
+ server->device_number =
+ MKDEV(MAJOR(msm_rpcrouter_devno), next_minor++);
+
+ server->device =
+ device_create(msm_rpcrouter_class, rpcrouter_device,
+ server->device_number, NULL, "%.8x:%.8x",
+ server->prog, dev_vers);
+ if (IS_ERR(server->device)) {
+ printk(KERN_ERR
+ "rpcrouter: Unable to create device (%ld)\n",
+ PTR_ERR(server->device));
+ return PTR_ERR(server->device);;
+ }
+
+ cdev_init(&server->cdev, &rpcrouter_server_fops);
+ server->cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&server->cdev, server->device_number, 1);
+ if (rc < 0) {
+ printk(KERN_ERR
+ "rpcrouter: Unable to add chrdev (%d)\n", rc);
+ device_destroy(msm_rpcrouter_class, server->device_number);
+ return rc;
+ }
+ return 0;
+}
+
+/* for backward compatible version type (31st bit cleared)
+ * clearing minor number (lower 16 bits) in device name
+ * is neccessary for driver binding
+ */
+int msm_rpcrouter_create_server_pdev(struct rr_server *server)
+{
+ sprintf(server->pdev_name, "rs%.8x:%.8x",
+ server->prog,
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ (server->vers & RPC_VERSION_MODE_MASK) ? server->vers :
+ (server->vers & RPC_VERSION_MAJOR_MASK));
+#else
+ server->vers);
+#endif
+
+ server->p_device.base.id = -1;
+ server->p_device.base.name = server->pdev_name;
+
+ server->p_device.prog = server->prog;
+ server->p_device.vers = server->vers;
+
+ platform_device_register(&server->p_device.base);
+ return 0;
+}
+
+int msm_rpcrouter_init_devices(void)
+{
+ int rc;
+ int major;
+
+ /* Create the device nodes */
+ msm_rpcrouter_class = class_create(THIS_MODULE, "oncrpc");
+ if (IS_ERR(msm_rpcrouter_class)) {
+ rc = -ENOMEM;
+ printk(KERN_ERR
+ "rpcrouter: failed to create oncrpc class\n");
+ goto fail;
+ }
+
+ rc = alloc_chrdev_region(&msm_rpcrouter_devno, 0,
+ RPCROUTER_MAX_REMOTE_SERVERS + 1,
+ "oncrpc");
+ if (rc < 0) {
+ printk(KERN_ERR
+ "rpcrouter: Failed to alloc chardev region (%d)\n", rc);
+ goto fail_destroy_class;
+ }
+
+ major = MAJOR(msm_rpcrouter_devno);
+ rpcrouter_device = device_create(msm_rpcrouter_class, NULL,
+ msm_rpcrouter_devno, NULL, "%.8x:%d",
+ 0, 0);
+ if (IS_ERR(rpcrouter_device)) {
+ rc = -ENOMEM;
+ goto fail_unregister_cdev_region;
+ }
+
+ cdev_init(&rpcrouter_cdev, &rpcrouter_router_fops);
+ rpcrouter_cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&rpcrouter_cdev, msm_rpcrouter_devno, 1);
+ if (rc < 0)
+ goto fail_destroy_device;
+
+ return 0;
+
+fail_destroy_device:
+ device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno);
+fail_unregister_cdev_region:
+ unregister_chrdev_region(msm_rpcrouter_devno,
+ RPCROUTER_MAX_REMOTE_SERVERS + 1);
+fail_destroy_class:
+ class_destroy(msm_rpcrouter_class);
+fail:
+ return rc;
+}
+
+void msm_rpcrouter_exit_devices(void)
+{
+ cdev_del(&rpcrouter_cdev);
+ device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno);
+ unregister_chrdev_region(msm_rpcrouter_devno,
+ RPCROUTER_MAX_REMOTE_SERVERS + 1);
+ class_destroy(msm_rpcrouter_class);
+}
+
diff --git a/arch/arm/mach-msm/smd_rpcrouter_servers.c b/arch/arm/mach-msm/smd_rpcrouter_servers.c
new file mode 100644
index 0000000..e7c1f1d
--- /dev/null
+++ b/arch/arm/mach-msm/smd_rpcrouter_servers.c
@@ -0,0 +1,229 @@
+/* arch/arm/mach-msm/rpc_servers.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include "../drivers/staging/dream/include/linux/wakelock.h"
+
+#include <linux/uaccess.h>
+
+#include "../drivers/staging/dream/include/mach/msm_rpcrouter.h"
+#include "../drivers/staging/dream/include/linux/msm_rpcrouter.h"
+
+
+static struct msm_rpc_endpoint *endpoint;
+
+#define FLAG_REGISTERED 0x0001
+
+static LIST_HEAD(rpc_server_list);
+static DEFINE_MUTEX(rpc_server_list_lock);
+static int rpc_servers_active;
+static struct wake_lock rpc_servers_wake_lock;
+
+static void rpc_server_register(struct msm_rpc_server *server)
+{
+ int rc;
+ rc = msm_rpc_register_server(endpoint, server->prog, server->vers);
+ if (rc < 0)
+ printk(KERN_ERR "[rpcserver] error registering %p @ %08x:%d\n",
+ server, server->prog, server->vers);
+}
+
+static struct msm_rpc_server *rpc_server_find(uint32_t prog, uint32_t vers)
+{
+ struct msm_rpc_server *server;
+
+ mutex_lock(&rpc_server_list_lock);
+ list_for_each_entry(server, &rpc_server_list, list) {
+ if ((server->prog == prog) &&
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+ msm_rpc_is_compatible_version(server->vers, vers)) {
+#else
+ server->vers == vers) {
+#endif
+ mutex_unlock(&rpc_server_list_lock);
+ return server;
+ }
+ }
+ mutex_unlock(&rpc_server_list_lock);
+ return NULL;
+}
+
+static void rpc_server_register_all(void)
+{
+ struct msm_rpc_server *server;
+
+ mutex_lock(&rpc_server_list_lock);
+ list_for_each_entry(server, &rpc_server_list, list) {
+ if (!(server->flags & FLAG_REGISTERED)) {
+ rpc_server_register(server);
+ server->flags |= FLAG_REGISTERED;
+ }
+ }
+ mutex_unlock(&rpc_server_list_lock);
+}
+
+int msm_rpc_create_server(struct msm_rpc_server *server)
+{
+ /* make sure we're in a sane state first */
+ server->flags = 0;
+ INIT_LIST_HEAD(&server->list);
+
+ mutex_lock(&rpc_server_list_lock);
+ list_add(&server->list, &rpc_server_list);
+ if (rpc_servers_active) {
+ rpc_server_register(server);
+ server->flags |= FLAG_REGISTERED;
+ }
+ mutex_unlock(&rpc_server_list_lock);
+
+ return 0;
+}
+
+static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client,
+ uint32_t xid, uint32_t accept_status)
+{
+ int rc = 0;
+ uint8_t reply_buf[sizeof(struct rpc_reply_hdr)];
+ struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf;
+
+ reply->xid = cpu_to_be32(xid);
+ reply->type = cpu_to_be32(1); /* reply */
+ reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+
+ reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status);
+ reply->data.acc_hdr.verf_flavor = 0;
+ reply->data.acc_hdr.verf_length = 0;
+
+ rc = msm_rpc_write(client, reply_buf, sizeof(reply_buf));
+ if (rc < 0)
+ printk(KERN_ERR
+ "%s: could not write response: %d\n",
+ __FUNCTION__, rc);
+
+ return rc;
+}
+
+static int rpc_servers_thread(void *data)
+{
+ void *buffer;
+ struct rpc_request_hdr *req;
+ struct msm_rpc_server *server;
+ int rc;
+
+ for (;;) {
+ wake_unlock(&rpc_servers_wake_lock);
+ rc = wait_event_interruptible( endpoint->wait_q, !list_empty(&endpoint->read_q) );
+ wake_lock(&rpc_servers_wake_lock);
+ rc = msm_rpc_read(endpoint, &buffer, -1, -1);
+ if (rc < 0) {
+ printk(KERN_ERR "%s: could not read: %d\n",
+ __FUNCTION__, rc);
+ break;
+ }
+ req = (struct rpc_request_hdr *)buffer;
+
+ req->type = be32_to_cpu(req->type);
+ req->xid = be32_to_cpu(req->xid);
+ req->rpc_vers = be32_to_cpu(req->rpc_vers);
+ req->prog = be32_to_cpu(req->prog);
+ req->vers = be32_to_cpu(req->vers);
+ req->procedure = be32_to_cpu(req->procedure);
+
+ server = rpc_server_find(req->prog, req->vers);
+
+ if (req->rpc_vers != 2)
+ continue;
+ if (req->type != 0)
+ continue;
+ if (!server) {
+ rpc_send_accepted_void_reply(
+ endpoint, req->xid,
+ RPC_ACCEPTSTAT_PROG_UNAVAIL);
+ continue;
+ }
+
+ rc = server->rpc_call(server, req, rc);
+
+ switch (rc) {
+ case 0:
+ rpc_send_accepted_void_reply(
+ endpoint, req->xid,
+ RPC_ACCEPTSTAT_SUCCESS);
+ break;
+ default:
+ rpc_send_accepted_void_reply(
+ endpoint, req->xid,
+ RPC_ACCEPTSTAT_PROG_UNAVAIL);
+ break;
+ }
+
+ kfree(buffer);
+ }
+
+ do_exit(0);
+}
+
+static int rpcservers_probe(struct platform_device *pdev)
+{
+ struct task_struct *server_thread;
+
+ endpoint = msm_rpc_open();
+ if (IS_ERR(endpoint))
+ return PTR_ERR(endpoint);
+
+ /* we're online -- register any servers installed beforehand */
+ rpc_servers_active = 1;
+ rpc_server_register_all();
+
+ /* start the kernel thread */
+ server_thread = kthread_run(rpc_servers_thread, NULL, "krpcserversd");
+ if (IS_ERR(server_thread))
+ return PTR_ERR(server_thread);
+
+ return 0;
+}
+
+static struct platform_driver rpcservers_driver = {
+ .probe = rpcservers_probe,
+ .driver = {
+ .name = "oncrpc_router",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init rpc_servers_init(void)
+{
+ wake_lock_init(&rpc_servers_wake_lock, WAKE_LOCK_SUSPEND, "rpc_server");
+ return platform_driver_register(&rpcservers_driver);
+}
+
+module_init(rpc_servers_init);
+
+MODULE_DESCRIPTION("MSM RPC Servers");
+MODULE_AUTHOR("Iliyan Malchev <ibm@android.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/dream/include/mach/msm_rpcrouter.h b/drivers/staging/dream/include/mach/msm_rpcrouter.h
index 9724ece..5e51705 100644
--- a/drivers/staging/dream/include/mach/msm_rpcrouter.h
+++ b/drivers/staging/dream/include/mach/msm_rpcrouter.h
@@ -125,6 +125,7 @@ struct rpc_reply_hdr
/* flags for msm_rpc_connect() */
#define MSM_RPC_UNINTERRUPTIBLE 0x0001
+#define MSM_RPC_ENABLE_RECEIVE (0x10000)
/* use IS_ERR() to check for failure */
struct msm_rpc_endpoint *msm_rpc_open(void);
--
1.6.4.4
next reply other threads:[~2010-07-20 13:58 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-07-20 13:58 David Lanzendörfer [this message]
2010-07-20 20:45 ` [PATCH] Adding rpc server Michael Bohan
2010-07-22 20:04 ` Daniel Walker
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=201007201558.33436.david.lanzendoerfer@o2s.ch \
--to=david.lanzendoerfer@o2s.ch \
--cc=linux-arm-msm@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).