mirror of https://gitee.com/openkylin/linux.git
staging: dream: smd: remove all smd related code
Part of this code is already in my MSM tree. I'll move the rest forward through my tree also. Signed-off-by: Daniel Walker <dwalker@codeaurora.org> CC: Pavel Machek <pavel@ucw.cz> CC: linux-arm-msm@vger.kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
e64354c0be
commit
bf597e99d2
|
@ -3,7 +3,6 @@ config DREAM
|
|||
depends on MACH_TROUT
|
||||
|
||||
if DREAM
|
||||
source "drivers/staging/dream/smd/Kconfig"
|
||||
|
||||
source "drivers/staging/dream/camera/Kconfig"
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
EXTRA_CFLAGS=-Idrivers/staging/dream/include
|
||||
obj-$(CONFIG_MSM_ADSP) += qdsp5/ smd/
|
||||
obj-$(CONFIG_MSM_ADSP) += qdsp5/
|
||||
obj-$(CONFIG_MSM_CAMERA) += camera/
|
||||
obj-$(CONFIG_INPUT_GPIO) += gpio_axis.o gpio_event.o gpio_input.o gpio_matrix.o gpio_output.o
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
config MSM_SMD
|
||||
depends on ARCH_MSM
|
||||
default y
|
||||
bool "MSM Shared Memory Driver (SMD)"
|
||||
help
|
||||
Support for the shared memory interface between the apps
|
||||
processor and the baseband processor. Provides access to
|
||||
the "shared heap", as well as virtual serial channels
|
||||
used to communicate with various services on the baseband
|
||||
processor.
|
||||
|
||||
config MSM_ONCRPCROUTER
|
||||
depends on MSM_SMD
|
||||
default y
|
||||
bool "MSM ONCRPC router support"
|
||||
help
|
||||
Support for the MSM ONCRPC router for communication between
|
||||
the ARM9 and ARM11
|
||||
|
||||
config MSM_RPCSERVERS
|
||||
depends on MSM_ONCRPCROUTER
|
||||
default y
|
||||
bool "Kernel side RPC server bundle"
|
||||
help
|
||||
none
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
EXTRA_CFLAGS=-Idrivers/staging/dream/include
|
||||
obj-$(CONFIG_MSM_SMD) += smd.o smd_tty.o smd_qmi.o
|
||||
obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter.o
|
||||
obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o
|
||||
obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o
|
||||
obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o
|
||||
obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o
|
|
@ -1,68 +0,0 @@
|
|||
/* arch/arm/mach-msm/rpc_server_dog_keepalive.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 <mach/msm_rpcrouter.h>
|
||||
|
||||
/* dog_keepalive server definitions */
|
||||
|
||||
#define DOG_KEEPALIVE_PROG 0x30000015
|
||||
#if CONFIG_MSM_AMSS_VERSION==6210
|
||||
#define DOG_KEEPALIVE_VERS 0
|
||||
#define RPC_DOG_KEEPALIVE_BEACON 1
|
||||
#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225)
|
||||
#define DOG_KEEPALIVE_VERS 0x731fa727
|
||||
#define RPC_DOG_KEEPALIVE_BEACON 2
|
||||
#elif CONFIG_MSM_AMSS_VERSION==6350
|
||||
#define DOG_KEEPALIVE_VERS 0x00010000
|
||||
#define RPC_DOG_KEEPALIVE_BEACON 2
|
||||
#else
|
||||
#error "Unsupported AMSS version"
|
||||
#endif
|
||||
#define RPC_DOG_KEEPALIVE_NULL 0
|
||||
|
||||
|
||||
/* TODO: Remove server registration with _VERS when modem is upated with _COMP*/
|
||||
|
||||
static int handle_rpc_call(struct msm_rpc_server *server,
|
||||
struct rpc_request_hdr *req, unsigned len)
|
||||
{
|
||||
switch (req->procedure) {
|
||||
case RPC_DOG_KEEPALIVE_NULL:
|
||||
return 0;
|
||||
case RPC_DOG_KEEPALIVE_BEACON:
|
||||
printk(KERN_INFO "DOG KEEPALIVE PING\n");
|
||||
return 0;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
static struct msm_rpc_server rpc_server = {
|
||||
.prog = DOG_KEEPALIVE_PROG,
|
||||
.vers = DOG_KEEPALIVE_VERS,
|
||||
.rpc_call = handle_rpc_call,
|
||||
};
|
||||
|
||||
static int __init rpc_server_init(void)
|
||||
{
|
||||
/* Dual server registration to support backwards compatibility vers */
|
||||
return msm_rpc_create_server(&rpc_server);
|
||||
}
|
||||
|
||||
|
||||
module_init(rpc_server_init);
|
|
@ -1,77 +0,0 @@
|
|||
/* arch/arm/mach-msm/rpc_server_time_remote.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 <mach/msm_rpcrouter.h>
|
||||
|
||||
/* time_remote_mtoa server definitions. */
|
||||
|
||||
#define TIME_REMOTE_MTOA_PROG 0x3000005d
|
||||
#if CONFIG_MSM_AMSS_VERSION==6210
|
||||
#define TIME_REMOTE_MTOA_VERS 0
|
||||
#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225)
|
||||
#define TIME_REMOTE_MTOA_VERS 0x9202a8e4
|
||||
#elif CONFIG_MSM_AMSS_VERSION==6350
|
||||
#define TIME_REMOTE_MTOA_VERS 0x00010000
|
||||
#else
|
||||
#error "Unknown AMSS version"
|
||||
#endif
|
||||
#define RPC_TIME_REMOTE_MTOA_NULL 0
|
||||
#define RPC_TIME_TOD_SET_APPS_BASES 2
|
||||
|
||||
struct rpc_time_tod_set_apps_bases_args {
|
||||
uint32_t tick;
|
||||
uint64_t stamp;
|
||||
};
|
||||
|
||||
static int handle_rpc_call(struct msm_rpc_server *server,
|
||||
struct rpc_request_hdr *req, unsigned len)
|
||||
{
|
||||
switch (req->procedure) {
|
||||
case RPC_TIME_REMOTE_MTOA_NULL:
|
||||
return 0;
|
||||
|
||||
case RPC_TIME_TOD_SET_APPS_BASES: {
|
||||
struct rpc_time_tod_set_apps_bases_args *args;
|
||||
args = (struct rpc_time_tod_set_apps_bases_args *)(req + 1);
|
||||
args->tick = be32_to_cpu(args->tick);
|
||||
args->stamp = be64_to_cpu(args->stamp);
|
||||
printk(KERN_INFO "RPC_TIME_TOD_SET_APPS_BASES:\n"
|
||||
"\ttick = %d\n"
|
||||
"\tstamp = %lld\n",
|
||||
args->tick, args->stamp);
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
static struct msm_rpc_server rpc_server = {
|
||||
.prog = TIME_REMOTE_MTOA_PROG,
|
||||
.vers = TIME_REMOTE_MTOA_VERS,
|
||||
.rpc_call = handle_rpc_call,
|
||||
};
|
||||
|
||||
static int __init rpc_server_init(void)
|
||||
{
|
||||
/* Dual server registration to support backwards compatibility vers */
|
||||
return msm_rpc_create_server(&rpc_server);
|
||||
}
|
||||
|
||||
|
||||
module_init(rpc_server_init);
|
File diff suppressed because it is too large
Load Diff
|
@ -1,164 +0,0 @@
|
|||
/* arch/arm/mach-msm/smd_private.h
|
||||
*
|
||||
* Copyright (C) 2007 Google, Inc.
|
||||
* Copyright (c) 2007 QUALCOMM Incorporated
|
||||
*
|
||||
* 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_MSM_SMD_PRIVATE_H_
|
||||
#define _ARCH_ARM_MACH_MSM_MSM_SMD_PRIVATE_H_
|
||||
|
||||
struct smem_heap_info {
|
||||
unsigned initialized;
|
||||
unsigned free_offset;
|
||||
unsigned heap_remaining;
|
||||
unsigned reserved;
|
||||
};
|
||||
|
||||
struct smem_heap_entry {
|
||||
unsigned allocated;
|
||||
unsigned offset;
|
||||
unsigned size;
|
||||
unsigned reserved;
|
||||
};
|
||||
|
||||
struct smem_proc_comm {
|
||||
unsigned command;
|
||||
unsigned status;
|
||||
unsigned data1;
|
||||
unsigned data2;
|
||||
};
|
||||
|
||||
#define PC_APPS 0
|
||||
#define PC_MODEM 1
|
||||
|
||||
#define VERSION_QDSP6 4
|
||||
#define VERSION_APPS_SBL 6
|
||||
#define VERSION_MODEM_SBL 7
|
||||
#define VERSION_APPS 8
|
||||
#define VERSION_MODEM 9
|
||||
|
||||
struct smem_shared {
|
||||
struct smem_proc_comm proc_comm[4];
|
||||
unsigned version[32];
|
||||
struct smem_heap_info heap_info;
|
||||
struct smem_heap_entry heap_toc[128];
|
||||
};
|
||||
|
||||
struct smsm_shared {
|
||||
unsigned host;
|
||||
unsigned state;
|
||||
};
|
||||
|
||||
struct smsm_interrupt_info {
|
||||
uint32_t aArm_en_mask;
|
||||
uint32_t aArm_interrupts_pending;
|
||||
uint32_t aArm_wakeup_reason;
|
||||
};
|
||||
|
||||
#define SZ_DIAG_ERR_MSG 0xC8
|
||||
#define ID_DIAG_ERR_MSG SMEM_DIAG_ERR_MESSAGE
|
||||
#define ID_SMD_CHANNELS SMEM_SMD_BASE_ID
|
||||
#define ID_SHARED_STATE SMEM_SMSM_SHARED_STATE
|
||||
#define ID_CH_ALLOC_TBL SMEM_CHANNEL_ALLOC_TBL
|
||||
|
||||
#define SMSM_INIT 0x000001
|
||||
#define SMSM_SMDINIT 0x000008
|
||||
#define SMSM_RPCINIT 0x000020
|
||||
#define SMSM_RESET 0x000040
|
||||
#define SMSM_RSA 0x0080
|
||||
#define SMSM_RUN 0x000100
|
||||
#define SMSM_PWRC 0x0200
|
||||
#define SMSM_TIMEWAIT 0x0400
|
||||
#define SMSM_TIMEINIT 0x0800
|
||||
#define SMSM_PWRC_EARLY_EXIT 0x1000
|
||||
#define SMSM_WFPI 0x2000
|
||||
#define SMSM_SLEEP 0x4000
|
||||
#define SMSM_SLEEPEXIT 0x8000
|
||||
#define SMSM_OEMSBL_RELEASE 0x10000
|
||||
#define SMSM_PWRC_SUSPEND 0x200000
|
||||
|
||||
#define SMSM_WKUP_REASON_RPC 0x00000001
|
||||
#define SMSM_WKUP_REASON_INT 0x00000002
|
||||
#define SMSM_WKUP_REASON_GPIO 0x00000004
|
||||
#define SMSM_WKUP_REASON_TIMER 0x00000008
|
||||
#define SMSM_WKUP_REASON_ALARM 0x00000010
|
||||
#define SMSM_WKUP_REASON_RESET 0x00000020
|
||||
|
||||
void *smem_alloc(unsigned id, unsigned size);
|
||||
int smsm_change_state(uint32_t clear_mask, uint32_t set_mask);
|
||||
uint32_t smsm_get_state(void);
|
||||
int smsm_set_sleep_duration(uint32_t delay);
|
||||
int smsm_set_interrupt_info(struct smsm_interrupt_info *info);
|
||||
void smsm_print_sleep_info(void);
|
||||
|
||||
#define SMEM_NUM_SMD_CHANNELS 64
|
||||
|
||||
typedef enum {
|
||||
/* fixed items */
|
||||
SMEM_PROC_COMM = 0,
|
||||
SMEM_HEAP_INFO,
|
||||
SMEM_ALLOCATION_TABLE,
|
||||
SMEM_VERSION_INFO,
|
||||
SMEM_HW_RESET_DETECT,
|
||||
SMEM_AARM_WARM_BOOT,
|
||||
SMEM_DIAG_ERR_MESSAGE,
|
||||
SMEM_SPINLOCK_ARRAY,
|
||||
SMEM_MEMORY_BARRIER_LOCATION,
|
||||
|
||||
/* dynamic items */
|
||||
SMEM_AARM_PARTITION_TABLE,
|
||||
SMEM_AARM_BAD_BLOCK_TABLE,
|
||||
SMEM_RESERVE_BAD_BLOCKS,
|
||||
SMEM_WM_UUID,
|
||||
SMEM_CHANNEL_ALLOC_TBL,
|
||||
SMEM_SMD_BASE_ID,
|
||||
SMEM_SMEM_LOG_IDX = SMEM_SMD_BASE_ID + SMEM_NUM_SMD_CHANNELS,
|
||||
SMEM_SMEM_LOG_EVENTS,
|
||||
SMEM_SMEM_STATIC_LOG_IDX,
|
||||
SMEM_SMEM_STATIC_LOG_EVENTS,
|
||||
SMEM_SMEM_SLOW_CLOCK_SYNC,
|
||||
SMEM_SMEM_SLOW_CLOCK_VALUE,
|
||||
SMEM_BIO_LED_BUF,
|
||||
SMEM_SMSM_SHARED_STATE,
|
||||
SMEM_SMSM_INT_INFO,
|
||||
SMEM_SMSM_SLEEP_DELAY,
|
||||
SMEM_SMSM_LIMIT_SLEEP,
|
||||
SMEM_SLEEP_POWER_COLLAPSE_DISABLED,
|
||||
SMEM_KEYPAD_KEYS_PRESSED,
|
||||
SMEM_KEYPAD_STATE_UPDATED,
|
||||
SMEM_KEYPAD_STATE_IDX,
|
||||
SMEM_GPIO_INT,
|
||||
SMEM_MDDI_LCD_IDX,
|
||||
SMEM_MDDI_HOST_DRIVER_STATE,
|
||||
SMEM_MDDI_LCD_DISP_STATE,
|
||||
SMEM_LCD_CUR_PANEL,
|
||||
SMEM_MARM_BOOT_SEGMENT_INFO,
|
||||
SMEM_AARM_BOOT_SEGMENT_INFO,
|
||||
SMEM_SLEEP_STATIC,
|
||||
SMEM_SCORPION_FREQUENCY,
|
||||
SMEM_SMD_PROFILES,
|
||||
SMEM_TSSC_BUSY,
|
||||
SMEM_HS_SUSPEND_FILTER_INFO,
|
||||
SMEM_BATT_INFO,
|
||||
SMEM_APPS_BOOT_MODE,
|
||||
SMEM_VERSION_FIRST,
|
||||
SMEM_VERSION_LAST = SMEM_VERSION_FIRST + 24,
|
||||
SMEM_OSS_RRCASN1_BUF1,
|
||||
SMEM_OSS_RRCASN1_BUF2,
|
||||
SMEM_ID_VENDOR0,
|
||||
SMEM_ID_VENDOR1,
|
||||
SMEM_ID_VENDOR2,
|
||||
SMEM_HW_SW_BUILD_ID,
|
||||
SMEM_NUM_ITEMS,
|
||||
} smem_mem_type;
|
||||
|
||||
#endif
|
|
@ -1,851 +0,0 @@
|
|||
/* arch/arm/mach-msm/smd_qmi.c
|
||||
*
|
||||
* QMI Control Driver -- Manages network data connections.
|
||||
*
|
||||
* Copyright (C) 2007 Google, Inc.
|
||||
* Author: Brian Swetland <swetland@google.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/fs.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <mach/msm_smd.h>
|
||||
|
||||
#define QMI_CTL 0x00
|
||||
#define QMI_WDS 0x01
|
||||
#define QMI_DMS 0x02
|
||||
#define QMI_NAS 0x03
|
||||
|
||||
#define QMI_RESULT_SUCCESS 0x0000
|
||||
#define QMI_RESULT_FAILURE 0x0001
|
||||
|
||||
struct qmi_msg {
|
||||
unsigned char service;
|
||||
unsigned char client_id;
|
||||
unsigned short txn_id;
|
||||
unsigned short type;
|
||||
unsigned short size;
|
||||
unsigned char *tlv;
|
||||
};
|
||||
|
||||
#define qmi_ctl_client_id 0
|
||||
|
||||
#define STATE_OFFLINE 0
|
||||
#define STATE_QUERYING 1
|
||||
#define STATE_ONLINE 2
|
||||
|
||||
struct qmi_ctxt {
|
||||
struct miscdevice misc;
|
||||
|
||||
struct mutex lock;
|
||||
|
||||
unsigned char ctl_txn_id;
|
||||
unsigned char wds_client_id;
|
||||
unsigned short wds_txn_id;
|
||||
|
||||
unsigned wds_busy;
|
||||
unsigned wds_handle;
|
||||
unsigned state_dirty;
|
||||
unsigned state;
|
||||
|
||||
unsigned char addr[4];
|
||||
unsigned char mask[4];
|
||||
unsigned char gateway[4];
|
||||
unsigned char dns1[4];
|
||||
unsigned char dns2[4];
|
||||
|
||||
smd_channel_t *ch;
|
||||
const char *ch_name;
|
||||
|
||||
struct work_struct open_work;
|
||||
struct work_struct read_work;
|
||||
};
|
||||
|
||||
static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n);
|
||||
|
||||
static void qmi_read_work(struct work_struct *ws);
|
||||
static void qmi_open_work(struct work_struct *work);
|
||||
|
||||
void qmi_ctxt_init(struct qmi_ctxt *ctxt, unsigned n)
|
||||
{
|
||||
mutex_init(&ctxt->lock);
|
||||
INIT_WORK(&ctxt->read_work, qmi_read_work);
|
||||
INIT_WORK(&ctxt->open_work, qmi_open_work);
|
||||
ctxt->ctl_txn_id = 1;
|
||||
ctxt->wds_txn_id = 1;
|
||||
ctxt->wds_busy = 1;
|
||||
ctxt->state = STATE_OFFLINE;
|
||||
|
||||
}
|
||||
|
||||
static struct workqueue_struct *qmi_wq;
|
||||
|
||||
static int verbose = 0;
|
||||
|
||||
/* anyone waiting for a state change waits here */
|
||||
static DECLARE_WAIT_QUEUE_HEAD(qmi_wait_queue);
|
||||
|
||||
|
||||
static void qmi_dump_msg(struct qmi_msg *msg, const char *prefix)
|
||||
{
|
||||
unsigned sz, n;
|
||||
unsigned char *x;
|
||||
|
||||
if (!verbose)
|
||||
return;
|
||||
|
||||
printk(KERN_INFO
|
||||
"qmi: %s: svc=%02x cid=%02x tid=%04x type=%04x size=%04x\n",
|
||||
prefix, msg->service, msg->client_id,
|
||||
msg->txn_id, msg->type, msg->size);
|
||||
|
||||
x = msg->tlv;
|
||||
sz = msg->size;
|
||||
|
||||
while (sz >= 3) {
|
||||
sz -= 3;
|
||||
|
||||
n = x[1] | (x[2] << 8);
|
||||
if (n > sz)
|
||||
break;
|
||||
|
||||
printk(KERN_INFO "qmi: %s: tlv: %02x %04x { ",
|
||||
prefix, x[0], n);
|
||||
x += 3;
|
||||
sz -= n;
|
||||
while (n-- > 0)
|
||||
printk("%02x ", *x++);
|
||||
printk("}\n");
|
||||
}
|
||||
}
|
||||
|
||||
int qmi_add_tlv(struct qmi_msg *msg,
|
||||
unsigned type, unsigned size, const void *data)
|
||||
{
|
||||
unsigned char *x = msg->tlv + msg->size;
|
||||
|
||||
x[0] = type;
|
||||
x[1] = size;
|
||||
x[2] = size >> 8;
|
||||
|
||||
memcpy(x + 3, data, size);
|
||||
|
||||
msg->size += (size + 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Extract a tagged item from a qmi message buffer,
|
||||
** taking care not to overrun the buffer.
|
||||
*/
|
||||
static int qmi_get_tlv(struct qmi_msg *msg,
|
||||
unsigned type, unsigned size, void *data)
|
||||
{
|
||||
unsigned char *x = msg->tlv;
|
||||
unsigned len = msg->size;
|
||||
unsigned n;
|
||||
|
||||
while (len >= 3) {
|
||||
len -= 3;
|
||||
|
||||
/* size of this item */
|
||||
n = x[1] | (x[2] << 8);
|
||||
if (n > len)
|
||||
break;
|
||||
|
||||
if (x[0] == type) {
|
||||
if (n != size)
|
||||
return -1;
|
||||
memcpy(data, x + 3, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
x += (n + 3);
|
||||
len -= n;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static unsigned qmi_get_status(struct qmi_msg *msg, unsigned *error)
|
||||
{
|
||||
unsigned short status[2];
|
||||
if (qmi_get_tlv(msg, 0x02, sizeof(status), status)) {
|
||||
*error = 0;
|
||||
return QMI_RESULT_FAILURE;
|
||||
} else {
|
||||
*error = status[1];
|
||||
return status[0];
|
||||
}
|
||||
}
|
||||
|
||||
/* 0x01 <qmux-header> <payload> */
|
||||
#define QMUX_HEADER 13
|
||||
|
||||
/* should be >= HEADER + FOOTER */
|
||||
#define QMUX_OVERHEAD 16
|
||||
|
||||
static int qmi_send(struct qmi_ctxt *ctxt, struct qmi_msg *msg)
|
||||
{
|
||||
unsigned char *data;
|
||||
unsigned hlen;
|
||||
unsigned len;
|
||||
int r;
|
||||
|
||||
qmi_dump_msg(msg, "send");
|
||||
|
||||
if (msg->service == QMI_CTL)
|
||||
hlen = QMUX_HEADER - 1;
|
||||
else
|
||||
hlen = QMUX_HEADER;
|
||||
|
||||
/* QMUX length is total header + total payload - IFC selector */
|
||||
len = hlen + msg->size - 1;
|
||||
if (len > 0xffff)
|
||||
return -1;
|
||||
|
||||
data = msg->tlv - hlen;
|
||||
|
||||
/* prepend encap and qmux header */
|
||||
*data++ = 0x01; /* ifc selector */
|
||||
|
||||
/* qmux header */
|
||||
*data++ = len;
|
||||
*data++ = len >> 8;
|
||||
*data++ = 0x00; /* flags: client */
|
||||
*data++ = msg->service;
|
||||
*data++ = msg->client_id;
|
||||
|
||||
/* qmi header */
|
||||
*data++ = 0x00; /* flags: send */
|
||||
*data++ = msg->txn_id;
|
||||
if (msg->service != QMI_CTL)
|
||||
*data++ = msg->txn_id >> 8;
|
||||
|
||||
*data++ = msg->type;
|
||||
*data++ = msg->type >> 8;
|
||||
*data++ = msg->size;
|
||||
*data++ = msg->size >> 8;
|
||||
|
||||
/* len + 1 takes the interface selector into account */
|
||||
r = smd_write(ctxt->ch, msg->tlv - hlen, len + 1);
|
||||
|
||||
if (r != len)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qmi_process_ctl_msg(struct qmi_ctxt *ctxt, struct qmi_msg *msg)
|
||||
{
|
||||
unsigned err;
|
||||
if (msg->type == 0x0022) {
|
||||
unsigned char n[2];
|
||||
if (qmi_get_status(msg, &err))
|
||||
return;
|
||||
if (qmi_get_tlv(msg, 0x01, sizeof(n), n))
|
||||
return;
|
||||
if (n[0] == QMI_WDS) {
|
||||
printk(KERN_INFO
|
||||
"qmi: ctl: wds use client_id 0x%02x\n", n[1]);
|
||||
ctxt->wds_client_id = n[1];
|
||||
ctxt->wds_busy = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int qmi_network_get_profile(struct qmi_ctxt *ctxt);
|
||||
|
||||
static void swapaddr(unsigned char *src, unsigned char *dst)
|
||||
{
|
||||
dst[0] = src[3];
|
||||
dst[1] = src[2];
|
||||
dst[2] = src[1];
|
||||
dst[3] = src[0];
|
||||
}
|
||||
|
||||
static unsigned char zero[4];
|
||||
static void qmi_read_runtime_profile(struct qmi_ctxt *ctxt, struct qmi_msg *msg)
|
||||
{
|
||||
unsigned char tmp[4];
|
||||
unsigned r;
|
||||
|
||||
r = qmi_get_tlv(msg, 0x1e, 4, tmp);
|
||||
swapaddr(r ? zero : tmp, ctxt->addr);
|
||||
r = qmi_get_tlv(msg, 0x21, 4, tmp);
|
||||
swapaddr(r ? zero : tmp, ctxt->mask);
|
||||
r = qmi_get_tlv(msg, 0x20, 4, tmp);
|
||||
swapaddr(r ? zero : tmp, ctxt->gateway);
|
||||
r = qmi_get_tlv(msg, 0x15, 4, tmp);
|
||||
swapaddr(r ? zero : tmp, ctxt->dns1);
|
||||
r = qmi_get_tlv(msg, 0x16, 4, tmp);
|
||||
swapaddr(r ? zero : tmp, ctxt->dns2);
|
||||
}
|
||||
|
||||
static void qmi_process_unicast_wds_msg(struct qmi_ctxt *ctxt,
|
||||
struct qmi_msg *msg)
|
||||
{
|
||||
unsigned err;
|
||||
switch (msg->type) {
|
||||
case 0x0021:
|
||||
if (qmi_get_status(msg, &err)) {
|
||||
printk(KERN_ERR
|
||||
"qmi: wds: network stop failed (%04x)\n", err);
|
||||
} else {
|
||||
printk(KERN_INFO
|
||||
"qmi: wds: network stopped\n");
|
||||
ctxt->state = STATE_OFFLINE;
|
||||
ctxt->state_dirty = 1;
|
||||
}
|
||||
break;
|
||||
case 0x0020:
|
||||
if (qmi_get_status(msg, &err)) {
|
||||
printk(KERN_ERR
|
||||
"qmi: wds: network start failed (%04x)\n", err);
|
||||
} else if (qmi_get_tlv(msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle)) {
|
||||
printk(KERN_INFO
|
||||
"qmi: wds no handle?\n");
|
||||
} else {
|
||||
printk(KERN_INFO
|
||||
"qmi: wds: got handle 0x%08x\n",
|
||||
ctxt->wds_handle);
|
||||
}
|
||||
break;
|
||||
case 0x002D:
|
||||
printk("qmi: got network profile\n");
|
||||
if (ctxt->state == STATE_QUERYING) {
|
||||
qmi_read_runtime_profile(ctxt, msg);
|
||||
ctxt->state = STATE_ONLINE;
|
||||
ctxt->state_dirty = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "qmi: unknown msg type 0x%04x\n", msg->type);
|
||||
}
|
||||
ctxt->wds_busy = 0;
|
||||
}
|
||||
|
||||
static void qmi_process_broadcast_wds_msg(struct qmi_ctxt *ctxt,
|
||||
struct qmi_msg *msg)
|
||||
{
|
||||
if (msg->type == 0x0022) {
|
||||
unsigned char n[2];
|
||||
if (qmi_get_tlv(msg, 0x01, sizeof(n), n))
|
||||
return;
|
||||
switch (n[0]) {
|
||||
case 1:
|
||||
printk(KERN_INFO "qmi: wds: DISCONNECTED\n");
|
||||
ctxt->state = STATE_OFFLINE;
|
||||
ctxt->state_dirty = 1;
|
||||
break;
|
||||
case 2:
|
||||
printk(KERN_INFO "qmi: wds: CONNECTED\n");
|
||||
ctxt->state = STATE_QUERYING;
|
||||
ctxt->state_dirty = 1;
|
||||
qmi_network_get_profile(ctxt);
|
||||
break;
|
||||
case 3:
|
||||
printk(KERN_INFO "qmi: wds: SUSPENDED\n");
|
||||
ctxt->state = STATE_OFFLINE;
|
||||
ctxt->state_dirty = 1;
|
||||
}
|
||||
} else {
|
||||
printk(KERN_ERR "qmi: unknown bcast msg type 0x%04x\n", msg->type);
|
||||
}
|
||||
}
|
||||
|
||||
static void qmi_process_wds_msg(struct qmi_ctxt *ctxt,
|
||||
struct qmi_msg *msg)
|
||||
{
|
||||
printk(KERN_INFO "wds: %04x @ %02x\n", msg->type, msg->client_id);
|
||||
if (msg->client_id == ctxt->wds_client_id) {
|
||||
qmi_process_unicast_wds_msg(ctxt, msg);
|
||||
} else if (msg->client_id == 0xff) {
|
||||
qmi_process_broadcast_wds_msg(ctxt, msg);
|
||||
} else {
|
||||
printk(KERN_ERR
|
||||
"qmi_process_wds_msg client id 0x%02x unknown\n",
|
||||
msg->client_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void qmi_process_qmux(struct qmi_ctxt *ctxt,
|
||||
unsigned char *buf, unsigned sz)
|
||||
{
|
||||
struct qmi_msg msg;
|
||||
|
||||
/* require a full header */
|
||||
if (sz < 5)
|
||||
return;
|
||||
|
||||
/* require a size that matches the buffer size */
|
||||
if (sz != (buf[0] | (buf[1] << 8)))
|
||||
return;
|
||||
|
||||
/* only messages from a service (bit7=1) are allowed */
|
||||
if (buf[2] != 0x80)
|
||||
return;
|
||||
|
||||
msg.service = buf[3];
|
||||
msg.client_id = buf[4];
|
||||
|
||||
/* annoyingly, CTL messages have a shorter TID */
|
||||
if (buf[3] == 0) {
|
||||
if (sz < 7)
|
||||
return;
|
||||
msg.txn_id = buf[6];
|
||||
buf += 7;
|
||||
sz -= 7;
|
||||
} else {
|
||||
if (sz < 8)
|
||||
return;
|
||||
msg.txn_id = buf[6] | (buf[7] << 8);
|
||||
buf += 8;
|
||||
sz -= 8;
|
||||
}
|
||||
|
||||
/* no type and size!? */
|
||||
if (sz < 4)
|
||||
return;
|
||||
sz -= 4;
|
||||
|
||||
msg.type = buf[0] | (buf[1] << 8);
|
||||
msg.size = buf[2] | (buf[3] << 8);
|
||||
msg.tlv = buf + 4;
|
||||
|
||||
if (sz != msg.size)
|
||||
return;
|
||||
|
||||
qmi_dump_msg(&msg, "recv");
|
||||
|
||||
mutex_lock(&ctxt->lock);
|
||||
switch (msg.service) {
|
||||
case QMI_CTL:
|
||||
qmi_process_ctl_msg(ctxt, &msg);
|
||||
break;
|
||||
case QMI_WDS:
|
||||
qmi_process_wds_msg(ctxt, &msg);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "qmi: msg from unknown svc 0x%02x\n",
|
||||
msg.service);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&ctxt->lock);
|
||||
wake_up(&qmi_wait_queue);
|
||||
}
|
||||
|
||||
#define QMI_MAX_PACKET (256 + QMUX_OVERHEAD)
|
||||
|
||||
static void qmi_read_work(struct work_struct *ws)
|
||||
{
|
||||
struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, read_work);
|
||||
struct smd_channel *ch = ctxt->ch;
|
||||
unsigned char buf[QMI_MAX_PACKET];
|
||||
int sz;
|
||||
|
||||
for (;;) {
|
||||
sz = smd_cur_packet_size(ch);
|
||||
if (sz == 0)
|
||||
break;
|
||||
if (sz < smd_read_avail(ch))
|
||||
break;
|
||||
if (sz > QMI_MAX_PACKET) {
|
||||
smd_read(ch, 0, sz);
|
||||
continue;
|
||||
}
|
||||
if (smd_read(ch, buf, sz) != sz) {
|
||||
printk(KERN_ERR "qmi: not enough data?!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* interface selector must be 1 */
|
||||
if (buf[0] != 0x01)
|
||||
continue;
|
||||
|
||||
qmi_process_qmux(ctxt, buf + 1, sz - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int qmi_request_wds_cid(struct qmi_ctxt *ctxt);
|
||||
|
||||
static void qmi_open_work(struct work_struct *ws)
|
||||
{
|
||||
struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, open_work);
|
||||
mutex_lock(&ctxt->lock);
|
||||
qmi_request_wds_cid(ctxt);
|
||||
mutex_unlock(&ctxt->lock);
|
||||
}
|
||||
|
||||
static void qmi_notify(void *priv, unsigned event)
|
||||
{
|
||||
struct qmi_ctxt *ctxt = priv;
|
||||
|
||||
switch (event) {
|
||||
case SMD_EVENT_DATA: {
|
||||
int sz;
|
||||
sz = smd_cur_packet_size(ctxt->ch);
|
||||
if ((sz > 0) && (sz <= smd_read_avail(ctxt->ch)))
|
||||
queue_work(qmi_wq, &ctxt->read_work);
|
||||
break;
|
||||
}
|
||||
case SMD_EVENT_OPEN:
|
||||
printk(KERN_INFO "qmi: smd opened\n");
|
||||
queue_work(qmi_wq, &ctxt->open_work);
|
||||
break;
|
||||
case SMD_EVENT_CLOSE:
|
||||
printk(KERN_INFO "qmi: smd closed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int qmi_request_wds_cid(struct qmi_ctxt *ctxt)
|
||||
{
|
||||
unsigned char data[64 + QMUX_OVERHEAD];
|
||||
struct qmi_msg msg;
|
||||
unsigned char n;
|
||||
|
||||
msg.service = QMI_CTL;
|
||||
msg.client_id = qmi_ctl_client_id;
|
||||
msg.txn_id = ctxt->ctl_txn_id;
|
||||
msg.type = 0x0022;
|
||||
msg.size = 0;
|
||||
msg.tlv = data + QMUX_HEADER;
|
||||
|
||||
ctxt->ctl_txn_id += 2;
|
||||
|
||||
n = QMI_WDS;
|
||||
qmi_add_tlv(&msg, 0x01, 0x01, &n);
|
||||
|
||||
return qmi_send(ctxt, &msg);
|
||||
}
|
||||
|
||||
static int qmi_network_get_profile(struct qmi_ctxt *ctxt)
|
||||
{
|
||||
unsigned char data[96 + QMUX_OVERHEAD];
|
||||
struct qmi_msg msg;
|
||||
|
||||
msg.service = QMI_WDS;
|
||||
msg.client_id = ctxt->wds_client_id;
|
||||
msg.txn_id = ctxt->wds_txn_id;
|
||||
msg.type = 0x002D;
|
||||
msg.size = 0;
|
||||
msg.tlv = data + QMUX_HEADER;
|
||||
|
||||
ctxt->wds_txn_id += 2;
|
||||
|
||||
return qmi_send(ctxt, &msg);
|
||||
}
|
||||
|
||||
static int qmi_network_up(struct qmi_ctxt *ctxt, char *apn)
|
||||
{
|
||||
unsigned char data[96 + QMUX_OVERHEAD];
|
||||
struct qmi_msg msg;
|
||||
char *auth_type;
|
||||
char *user;
|
||||
char *pass;
|
||||
|
||||
for (user = apn; *user; user++) {
|
||||
if (*user == ' ') {
|
||||
*user++ = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (pass = user; *pass; pass++) {
|
||||
if (*pass == ' ') {
|
||||
*pass++ = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auth_type = pass; *auth_type; auth_type++) {
|
||||
if (*auth_type == ' ') {
|
||||
*auth_type++ = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
msg.service = QMI_WDS;
|
||||
msg.client_id = ctxt->wds_client_id;
|
||||
msg.txn_id = ctxt->wds_txn_id;
|
||||
msg.type = 0x0020;
|
||||
msg.size = 0;
|
||||
msg.tlv = data + QMUX_HEADER;
|
||||
|
||||
ctxt->wds_txn_id += 2;
|
||||
|
||||
qmi_add_tlv(&msg, 0x14, strlen(apn), apn);
|
||||
if (*auth_type)
|
||||
qmi_add_tlv(&msg, 0x16, strlen(auth_type), auth_type);
|
||||
if (*user) {
|
||||
if (!*auth_type) {
|
||||
unsigned char x;
|
||||
x = 3;
|
||||
qmi_add_tlv(&msg, 0x16, 1, &x);
|
||||
}
|
||||
qmi_add_tlv(&msg, 0x17, strlen(user), user);
|
||||
if (*pass)
|
||||
qmi_add_tlv(&msg, 0x18, strlen(pass), pass);
|
||||
}
|
||||
return qmi_send(ctxt, &msg);
|
||||
}
|
||||
|
||||
static int qmi_network_down(struct qmi_ctxt *ctxt)
|
||||
{
|
||||
unsigned char data[16 + QMUX_OVERHEAD];
|
||||
struct qmi_msg msg;
|
||||
|
||||
msg.service = QMI_WDS;
|
||||
msg.client_id = ctxt->wds_client_id;
|
||||
msg.txn_id = ctxt->wds_txn_id;
|
||||
msg.type = 0x0021;
|
||||
msg.size = 0;
|
||||
msg.tlv = data + QMUX_HEADER;
|
||||
|
||||
ctxt->wds_txn_id += 2;
|
||||
|
||||
qmi_add_tlv(&msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle);
|
||||
|
||||
return qmi_send(ctxt, &msg);
|
||||
}
|
||||
|
||||
static int qmi_print_state(struct qmi_ctxt *ctxt, char *buf, int max)
|
||||
{
|
||||
int i;
|
||||
char *statename;
|
||||
|
||||
if (ctxt->state == STATE_ONLINE) {
|
||||
statename = "up";
|
||||
} else if (ctxt->state == STATE_OFFLINE) {
|
||||
statename = "down";
|
||||
} else {
|
||||
statename = "busy";
|
||||
}
|
||||
|
||||
i = scnprintf(buf, max, "STATE=%s\n", statename);
|
||||
i += scnprintf(buf + i, max - i, "CID=%d\n", ctxt->wds_client_id);
|
||||
|
||||
if (ctxt->state != STATE_ONLINE)
|
||||
return i;
|
||||
|
||||
i += scnprintf(buf + i, max - i, "ADDR=%d.%d.%d.%d\n",
|
||||
ctxt->addr[0], ctxt->addr[1], ctxt->addr[2], ctxt->addr[3]);
|
||||
i += scnprintf(buf + i, max - i, "MASK=%d.%d.%d.%d\n",
|
||||
ctxt->mask[0], ctxt->mask[1], ctxt->mask[2], ctxt->mask[3]);
|
||||
i += scnprintf(buf + i, max - i, "GATEWAY=%d.%d.%d.%d\n",
|
||||
ctxt->gateway[0], ctxt->gateway[1], ctxt->gateway[2],
|
||||
ctxt->gateway[3]);
|
||||
i += scnprintf(buf + i, max - i, "DNS1=%d.%d.%d.%d\n",
|
||||
ctxt->dns1[0], ctxt->dns1[1], ctxt->dns1[2], ctxt->dns1[3]);
|
||||
i += scnprintf(buf + i, max - i, "DNS2=%d.%d.%d.%d\n",
|
||||
ctxt->dns2[0], ctxt->dns2[1], ctxt->dns2[2], ctxt->dns2[3]);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static ssize_t qmi_read(struct file *fp, char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
struct qmi_ctxt *ctxt = fp->private_data;
|
||||
char msg[256];
|
||||
int len;
|
||||
int r;
|
||||
|
||||
mutex_lock(&ctxt->lock);
|
||||
for (;;) {
|
||||
if (ctxt->state_dirty) {
|
||||
ctxt->state_dirty = 0;
|
||||
len = qmi_print_state(ctxt, msg, 256);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&ctxt->lock);
|
||||
r = wait_event_interruptible(qmi_wait_queue, ctxt->state_dirty);
|
||||
if (r < 0)
|
||||
return r;
|
||||
mutex_lock(&ctxt->lock);
|
||||
}
|
||||
mutex_unlock(&ctxt->lock);
|
||||
|
||||
if (len > count)
|
||||
len = count;
|
||||
|
||||
if (copy_to_user(buf, msg, len))
|
||||
return -EFAULT;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t qmi_write(struct file *fp, const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
struct qmi_ctxt *ctxt = fp->private_data;
|
||||
unsigned char cmd[64];
|
||||
int len;
|
||||
int r;
|
||||
|
||||
if (count < 1)
|
||||
return 0;
|
||||
|
||||
len = count > 63 ? 63 : count;
|
||||
|
||||
if (copy_from_user(cmd, buf, len))
|
||||
return -EFAULT;
|
||||
|
||||
cmd[len] = 0;
|
||||
|
||||
/* lazy */
|
||||
if (cmd[len-1] == '\n') {
|
||||
cmd[len-1] = 0;
|
||||
len--;
|
||||
}
|
||||
|
||||
if (!strncmp(cmd, "verbose", 7)) {
|
||||
verbose = 1;
|
||||
} else if (!strncmp(cmd, "terse", 5)) {
|
||||
verbose = 0;
|
||||
} else if (!strncmp(cmd, "poll", 4)) {
|
||||
ctxt->state_dirty = 1;
|
||||
wake_up(&qmi_wait_queue);
|
||||
} else if (!strncmp(cmd, "down", 4)) {
|
||||
retry_down:
|
||||
mutex_lock(&ctxt->lock);
|
||||
if (ctxt->wds_busy) {
|
||||
mutex_unlock(&ctxt->lock);
|
||||
r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy);
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto retry_down;
|
||||
}
|
||||
ctxt->wds_busy = 1;
|
||||
qmi_network_down(ctxt);
|
||||
mutex_unlock(&ctxt->lock);
|
||||
} else if (!strncmp(cmd, "up:", 3)) {
|
||||
retry_up:
|
||||
mutex_lock(&ctxt->lock);
|
||||
if (ctxt->wds_busy) {
|
||||
mutex_unlock(&ctxt->lock);
|
||||
r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy);
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto retry_up;
|
||||
}
|
||||
ctxt->wds_busy = 1;
|
||||
qmi_network_up(ctxt, cmd+3);
|
||||
mutex_unlock(&ctxt->lock);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int qmi_open(struct inode *ip, struct file *fp)
|
||||
{
|
||||
struct qmi_ctxt *ctxt = qmi_minor_to_ctxt(MINOR(ip->i_rdev));
|
||||
int r = 0;
|
||||
|
||||
if (!ctxt) {
|
||||
printk(KERN_ERR "unknown qmi misc %d\n", MINOR(ip->i_rdev));
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
fp->private_data = ctxt;
|
||||
|
||||
mutex_lock(&ctxt->lock);
|
||||
if (ctxt->ch == 0)
|
||||
r = smd_open(ctxt->ch_name, &ctxt->ch, ctxt, qmi_notify);
|
||||
if (r == 0)
|
||||
wake_up(&qmi_wait_queue);
|
||||
mutex_unlock(&ctxt->lock);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int qmi_release(struct inode *ip, struct file *fp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct file_operations qmi_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = qmi_read,
|
||||
.write = qmi_write,
|
||||
.open = qmi_open,
|
||||
.release = qmi_release,
|
||||
};
|
||||
|
||||
static struct qmi_ctxt qmi_device0 = {
|
||||
.ch_name = "SMD_DATA5_CNTL",
|
||||
.misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "qmi0",
|
||||
.fops = &qmi_fops,
|
||||
}
|
||||
};
|
||||
static struct qmi_ctxt qmi_device1 = {
|
||||
.ch_name = "SMD_DATA6_CNTL",
|
||||
.misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "qmi1",
|
||||
.fops = &qmi_fops,
|
||||
}
|
||||
};
|
||||
static struct qmi_ctxt qmi_device2 = {
|
||||
.ch_name = "SMD_DATA7_CNTL",
|
||||
.misc = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "qmi2",
|
||||
.fops = &qmi_fops,
|
||||
}
|
||||
};
|
||||
|
||||
static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n)
|
||||
{
|
||||
if (n == qmi_device0.misc.minor)
|
||||
return &qmi_device0;
|
||||
if (n == qmi_device1.misc.minor)
|
||||
return &qmi_device1;
|
||||
if (n == qmi_device2.misc.minor)
|
||||
return &qmi_device2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init qmi_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
qmi_wq = create_singlethread_workqueue("qmi");
|
||||
if (qmi_wq == 0)
|
||||
return -ENOMEM;
|
||||
|
||||
qmi_ctxt_init(&qmi_device0, 0);
|
||||
qmi_ctxt_init(&qmi_device1, 1);
|
||||
qmi_ctxt_init(&qmi_device2, 2);
|
||||
|
||||
ret = misc_register(&qmi_device0.misc);
|
||||
if (ret == 0)
|
||||
ret = misc_register(&qmi_device1.misc);
|
||||
if (ret == 0)
|
||||
ret = misc_register(&qmi_device2.misc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
module_init(qmi_init);
|
File diff suppressed because it is too large
Load Diff
|
@ -1,193 +0,0 @@
|
|||
/** 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 <mach/msm_smd.h>
|
||||
#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;
|
||||
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
|
|
@ -1,377 +0,0 @@
|
|||
/* 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/poll.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/msm_rpcrouter.h>
|
||||
#include <linux/slab.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);
|
||||
}
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
/* 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/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/msm_rpcrouter.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <mach/msm_rpcrouter.h>
|
||||
#include "smd_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 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 (;;) {
|
||||
rc = wait_event_interruptible(endpoint->wait_q,
|
||||
!list_empty(&endpoint->read_q));
|
||||
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)
|
||||
{
|
||||
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");
|
|
@ -1,208 +0,0 @@
|
|||
/* arch/arm/mach-msm/smd_tty.c
|
||||
*
|
||||
* Copyright (C) 2007 Google, Inc.
|
||||
* Author: Brian Swetland <swetland@google.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/fs.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/tty_flip.h>
|
||||
|
||||
#include <mach/msm_smd.h>
|
||||
|
||||
#define MAX_SMD_TTYS 32
|
||||
|
||||
static DEFINE_MUTEX(smd_tty_lock);
|
||||
|
||||
struct smd_tty_info {
|
||||
smd_channel_t *ch;
|
||||
struct tty_struct *tty;
|
||||
int open_count;
|
||||
};
|
||||
|
||||
static struct smd_tty_info smd_tty[MAX_SMD_TTYS];
|
||||
|
||||
|
||||
static void smd_tty_notify(void *priv, unsigned event)
|
||||
{
|
||||
unsigned char *ptr;
|
||||
int avail;
|
||||
struct smd_tty_info *info = priv;
|
||||
struct tty_struct *tty = info->tty;
|
||||
|
||||
if (!tty)
|
||||
return;
|
||||
|
||||
if (event != SMD_EVENT_DATA)
|
||||
return;
|
||||
|
||||
for (;;) {
|
||||
if (test_bit(TTY_THROTTLED, &tty->flags)) break;
|
||||
avail = smd_read_avail(info->ch);
|
||||
if (avail == 0) break;
|
||||
|
||||
avail = tty_prepare_flip_string(tty, &ptr, avail);
|
||||
|
||||
if (smd_read(info->ch, ptr, avail) != avail) {
|
||||
/* shouldn't be possible since we're in interrupt
|
||||
** context here and nobody else could 'steal' our
|
||||
** characters.
|
||||
*/
|
||||
printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!");
|
||||
}
|
||||
|
||||
tty_flip_buffer_push(tty);
|
||||
}
|
||||
|
||||
/* XXX only when writable and necessary */
|
||||
tty_wakeup(tty);
|
||||
}
|
||||
|
||||
static int smd_tty_open(struct tty_struct *tty, struct file *f)
|
||||
{
|
||||
int res = 0;
|
||||
int n = tty->index;
|
||||
struct smd_tty_info *info;
|
||||
const char *name;
|
||||
|
||||
if (n == 0) {
|
||||
name = "SMD_DS";
|
||||
} else if (n == 27) {
|
||||
name = "SMD_GPSNMEA";
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
info = smd_tty + n;
|
||||
|
||||
mutex_lock(&smd_tty_lock);
|
||||
tty->driver_data = info;
|
||||
|
||||
if (info->open_count++ == 0) {
|
||||
info->tty = tty;
|
||||
if (info->ch) {
|
||||
smd_kick(info->ch);
|
||||
} else {
|
||||
res = smd_open(name, &info->ch, info, smd_tty_notify);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&smd_tty_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void smd_tty_close(struct tty_struct *tty, struct file *f)
|
||||
{
|
||||
struct smd_tty_info *info = tty->driver_data;
|
||||
|
||||
if (info == 0)
|
||||
return;
|
||||
|
||||
mutex_lock(&smd_tty_lock);
|
||||
if (--info->open_count == 0) {
|
||||
info->tty = 0;
|
||||
tty->driver_data = 0;
|
||||
if (info->ch) {
|
||||
smd_close(info->ch);
|
||||
info->ch = 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&smd_tty_lock);
|
||||
}
|
||||
|
||||
static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len)
|
||||
{
|
||||
struct smd_tty_info *info = tty->driver_data;
|
||||
int avail;
|
||||
|
||||
/* if we're writing to a packet channel we will
|
||||
** never be able to write more data than there
|
||||
** is currently space for
|
||||
*/
|
||||
avail = smd_write_avail(info->ch);
|
||||
if (len > avail)
|
||||
len = avail;
|
||||
|
||||
return smd_write(info->ch, buf, len);
|
||||
}
|
||||
|
||||
static int smd_tty_write_room(struct tty_struct *tty)
|
||||
{
|
||||
struct smd_tty_info *info = tty->driver_data;
|
||||
return smd_write_avail(info->ch);
|
||||
}
|
||||
|
||||
static int smd_tty_chars_in_buffer(struct tty_struct *tty)
|
||||
{
|
||||
struct smd_tty_info *info = tty->driver_data;
|
||||
return smd_read_avail(info->ch);
|
||||
}
|
||||
|
||||
static void smd_tty_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct smd_tty_info *info = tty->driver_data;
|
||||
smd_kick(info->ch);
|
||||
}
|
||||
|
||||
static struct tty_operations smd_tty_ops = {
|
||||
.open = smd_tty_open,
|
||||
.close = smd_tty_close,
|
||||
.write = smd_tty_write,
|
||||
.write_room = smd_tty_write_room,
|
||||
.chars_in_buffer = smd_tty_chars_in_buffer,
|
||||
.unthrottle = smd_tty_unthrottle,
|
||||
};
|
||||
|
||||
static struct tty_driver *smd_tty_driver;
|
||||
|
||||
static int __init smd_tty_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
|
||||
if (smd_tty_driver == 0)
|
||||
return -ENOMEM;
|
||||
|
||||
smd_tty_driver->owner = THIS_MODULE;
|
||||
smd_tty_driver->driver_name = "smd_tty_driver";
|
||||
smd_tty_driver->name = "smd";
|
||||
smd_tty_driver->major = 0;
|
||||
smd_tty_driver->minor_start = 0;
|
||||
smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
|
||||
smd_tty_driver->init_termios = tty_std_termios;
|
||||
smd_tty_driver->init_termios.c_iflag = 0;
|
||||
smd_tty_driver->init_termios.c_oflag = 0;
|
||||
smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
|
||||
smd_tty_driver->init_termios.c_lflag = 0;
|
||||
smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS |
|
||||
TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
||||
tty_set_operations(smd_tty_driver, &smd_tty_ops);
|
||||
|
||||
ret = tty_register_driver(smd_tty_driver);
|
||||
if (ret) return ret;
|
||||
|
||||
/* this should be dynamic */
|
||||
tty_register_device(smd_tty_driver, 0, 0);
|
||||
tty_register_device(smd_tty_driver, 27, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(smd_tty_init);
|
Loading…
Reference in New Issue