aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/samsung_modemctl
diff options
context:
space:
mode:
authorPawit Pornkitprasan <p.pawit@gmail.com>2011-12-24 09:34:37 +0700
committerPawit Pornkitprasan <p.pawit@gmail.com>2011-12-24 09:35:11 +0700
commitfdc19983538bec90010fa7f344a7417ff4731ba8 (patch)
tree8a84a1edd1c026b71fec4756caf27cbbb9e827d5 /drivers/misc/samsung_modemctl
parentb55e9ac4df4d240b39eda4cd9c0198453dd59061 (diff)
downloadkernel_samsung_aries-fdc19983538bec90010fa7f344a7417ff4731ba8.zip
kernel_samsung_aries-fdc19983538bec90010fa7f344a7417ff4731ba8.tar.gz
kernel_samsung_aries-fdc19983538bec90010fa7f344a7417ff4731ba8.tar.bz2
Added support for aries
Reverts "S5PC11X : FIMC apply v4l2 standard for asynchronous dequeue/queue" 5f4b037e6ebb18d65a3ac896032eb559d7fe2baf
Diffstat (limited to 'drivers/misc/samsung_modemctl')
-rw-r--r--drivers/misc/samsung_modemctl/Makefile6
-rwxr-xr-xdrivers/misc/samsung_modemctl/dpram/Makefile1
-rwxr-xr-xdrivers/misc/samsung_modemctl/dpram/dpram.c2765
-rw-r--r--drivers/misc/samsung_modemctl/dpram/dpram.h214
-rw-r--r--drivers/misc/samsung_modemctl/dpram/modemctl.h38
-rw-r--r--drivers/misc/samsung_modemctl/dpram/onedram.h52
-rw-r--r--[-rwxr-xr-x]drivers/misc/samsung_modemctl/modem_ctl.c17
-rwxr-xr-xdrivers/misc/samsung_modemctl/modemctl/Makefile1
-rw-r--r--drivers/misc/samsung_modemctl/modemctl/modemctl.c870
-rw-r--r--drivers/misc/samsung_modemctl/modemctl/modemctl.h39
-rwxr-xr-xdrivers/misc/samsung_modemctl/onedram/Makefile2
-rw-r--r--drivers/misc/samsung_modemctl/onedram/onedram.c949
-rw-r--r--drivers/misc/samsung_modemctl/onedram/onedram.h54
-rwxr-xr-xdrivers/misc/samsung_modemctl/svnet/Makefile6
-rw-r--r--drivers/misc/samsung_modemctl/svnet/main.c866
-rw-r--r--drivers/misc/samsung_modemctl/svnet/main.h7
-rw-r--r--drivers/misc/samsung_modemctl/svnet/pdp.c110
-rw-r--r--drivers/misc/samsung_modemctl/svnet/pdp.h34
-rw-r--r--drivers/misc/samsung_modemctl/svnet/sipc.h64
-rw-r--r--drivers/misc/samsung_modemctl/svnet/sipc4.c2069
-rw-r--r--drivers/misc/samsung_modemctl/svnet/sipc4.h252
21 files changed, 8415 insertions, 1 deletions
diff --git a/drivers/misc/samsung_modemctl/Makefile b/drivers/misc/samsung_modemctl/Makefile
index 31061af..a533fe9 100644
--- a/drivers/misc/samsung_modemctl/Makefile
+++ b/drivers/misc/samsung_modemctl/Makefile
@@ -1 +1,5 @@
-obj-y += modem_ctl.o modem_io.o modem_dbg.o
+obj-$(CONFIG_PHONE_ARIES) += modemctl/
+obj-$(CONFIG_PHONE_ARIES) += onedram/
+obj-$(CONFIG_PHONE_ARIES) += svnet/
+obj-$(CONFIG_PHONE_ARIES_CDMA) += dpram/
+obj-$(CONFIG_PHONE_CRESPO) += modem_ctl.o modem_io.o modem_dbg.o
diff --git a/drivers/misc/samsung_modemctl/dpram/Makefile b/drivers/misc/samsung_modemctl/dpram/Makefile
new file mode 100755
index 0000000..6fdbd20
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/dpram/Makefile
@@ -0,0 +1 @@
+obj-y += dpram.o
diff --git a/drivers/misc/samsung_modemctl/dpram/dpram.c b/drivers/misc/samsung_modemctl/dpram/dpram.c
new file mode 100755
index 0000000..2b8fba4
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/dpram/dpram.c
@@ -0,0 +1,2765 @@
+/****************************************************************************
+**
+** COPYRIGHT(C) : Samsung Electronics Co.Ltd, 2006-2010 ALL RIGHTS RESERVED
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License version 2 as
+** published by the Free Software Foundation.
+**
+** Onedram Device Driver
+**
+****************************************************************************/
+
+#define _DEBUG
+/* HSDPA DUN & Internal FTP Throughput Support. @LDK@ */
+#define _HSDPA_DPRAM
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/irq.h>
+#include <linux/poll.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <mach/regs-gpio.h>
+#include <plat/gpio-cfg.h>
+#include <mach/hardware.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif /* CONFIG_PROC_FS */
+
+/***************************************************************************/
+/* GPIO SETTING */
+/***************************************************************************/
+#include <mach/gpio.h>
+
+#define GPIO_LEVEL_LOW 0
+#define GPIO_LEVEL_HIGH 1
+
+#define GPIO_AP_RXD S5PV210_GPA1(2)
+#define GPIO_AP_RXD_AF 0x2 // UART_2_RXD
+
+#define GPIO_AP_TXD S5PV210_GPA1(3)
+#define GPIO_AP_TXD_AF 0x2 // UART_2_TXD
+
+#define GPIO_PHONE_ON S5PV210_GPJ1(0)
+#define GPIO_PHONE_ON_AF 0x1
+
+#define GPIO_PHONE_RST_N S5PV210_GPH3(7)
+#define GPIO_PHONE_RST_N_AF 0x1
+
+#define GPIO_PDA_ACTIVE S5PV210_GPH1(0)
+#define GPIO_PDA_ACTIVE_AF 0x1
+
+#if 0
+#define GPIO_UART_SEL S5PV210_MP05(7)
+#define GPIO_UART_SEL_AF 0x1
+#endif
+
+#define GPIO_PHONE_ACTIVE S5PV210_GPH1(7)
+#define GPIO_PHONE_ACTIVE_AF 0xff
+
+#define GPIO_ONEDRAM_INT_N S5PV210_GPH1(3)
+#define GPIO_ONEDRAM_INT_N_AF 0xff
+
+#define IRQ_ONEDRAM_INT_N IRQ_EINT11
+#define IRQ_PHONE_ACTIVE IRQ_EINT15
+
+
+/*****************************************************************************/
+/* MULTIPDP FEATURE */
+/*****************************************************************************/
+#include <linux/miscdevice.h>
+#include <linux/netdevice.h>
+/* Device node name for application interface */
+#define APP_DEVNAME "multipdp"
+/* number of PDP context */
+#define NUM_PDP_CONTEXT 4
+
+/* Device types */
+#define DEV_TYPE_NET 0 /* network device for IP data */
+#define DEV_TYPE_SERIAL 1 /* serial device for CSD */
+
+/* Device major & minor number */
+#define CSD_MAJOR_NUM 240
+#define CSD_MINOR_NUM 0
+
+/* Maximum number of PDP context */
+#define MAX_PDP_CONTEXT 10
+
+/* Maximum PDP data length */
+#define MAX_PDP_DATA_LEN 1500
+
+/* Device flags */
+#define DEV_FLAG_STICKY 0x1 /* Sticky */
+/* Maximum PDP packet length including header and start/stop bytes */
+#define MAX_PDP_PACKET_LEN (MAX_PDP_DATA_LEN + 4 + 2)
+
+
+/* Multiple PDP */
+typedef struct pdp_arg {
+ unsigned char id;
+ char ifname[16];
+} __attribute__ ((packed)) pdp_arg_t;
+
+/* PDP data packet header format */
+struct pdp_hdr {
+ u16 len; /* Data length */
+ u8 id; /* Channel ID */
+ u8 control; /* Control field */
+} __attribute__ ((packed));
+
+/* PDP information type */
+struct pdp_info {
+ /* PDP context ID */
+ u8 id;
+
+ /* Device type */
+ unsigned type;
+
+ /* Device flags */
+ unsigned flags;
+
+ /* Tx packet buffer */
+ u8 *tx_buf;
+
+ /* App device interface */
+ union {
+ /* Virtual serial interface */
+ struct {
+ struct tty_driver tty_driver[NUM_PDP_CONTEXT]; // CSD, CDMA, TRFB, CIQ
+ int refcount;
+ struct tty_struct *tty_table[1];
+ struct ktermios *termios[1];
+ struct ktermios *termios_locked[1];
+ char tty_name[16];
+ struct tty_struct *tty;
+ struct semaphore write_lock;
+ } vs_u;
+ } dev_u;
+#define vn_dev dev_u.vnet_u
+#define vs_dev dev_u.vs_u
+};
+
+
+
+static struct pdp_info *pdp_table[MAX_PDP_CONTEXT];
+static DEFINE_MUTEX(pdp_lock);
+
+static inline struct pdp_info * pdp_get_dev(u8 id);
+static inline void check_pdp_table(char*, int);
+
+/*****************************************************************************/
+
+#include "dpram.h"
+
+#define DRIVER_NAME "DPRAM"
+#define DRIVER_PROC_ENTRY "driver/dpram"
+#define DRIVER_MAJOR_NUM 255
+
+#ifdef _DEBUG
+#define _ENABLE_ERROR_DEVICE
+#if 0
+#define PRINT_WRITE
+#define PRINT_READ
+#define PRINT_WRITE_SHORT
+#define PRINT_READ_SHORT
+#define PRINT_SEND_IRQ
+#define PRINT_RECV_IRQ
+#define PRINT_HEAD_TAIL
+#endif
+#endif
+
+//#define ENABLE_GPIO_PHONE_RST
+
+#ifdef _DEBUG
+#define dprintk(s, args...) printk(KERN_ERR "[OneDRAM] %s:%d - " s, __func__, __LINE__, ##args)
+#else
+#define dprintk(s, args...)
+#endif /* _DEBUG */
+
+#define WRITE_TO_DPRAM(dest, src, size) \
+ _memcpy((void *)(DPRAM_VBASE + dest), src, size)
+
+#define READ_FROM_DPRAM(dest, src, size) \
+ _memcpy(dest, (void *)(DPRAM_VBASE + src), size)
+
+#ifdef _ENABLE_ERROR_DEVICE
+#define DPRAM_ERR_MSG_LEN 65
+#define DPRAM_ERR_DEVICE "dpramerr"
+#endif /* _ENABLE_ERROR_DEVICE */
+
+static int onedram_get_semaphore(const char*);
+static int return_onedram_semaphore(const char*);
+static void send_interrupt_to_phone_with_semaphore(u16 irq_mask);
+
+static void __iomem *dpram_base = 0;
+static unsigned int *onedram_sem;
+static unsigned int *onedram_mailboxBA; //send mail
+static unsigned int *onedram_mailboxAB; //received mail
+
+static atomic_t onedram_lock;
+static int onedram_lock_with_semaphore(const char*);
+static void onedram_release_lock(const char*);
+static void dpram_drop_data(dpram_device_t *device);
+
+static int requested_semaphore = 0;
+static int unreceived_semaphore = 0;
+static int phone_sync = 0;
+static int dump_on = 0;
+
+static int dpram_phone_getstatus(void);
+#define DPRAM_VBASE dpram_base
+static struct tty_driver *dpram_tty_driver;
+static dpram_tasklet_data_t dpram_tasklet_data[MAX_INDEX];
+static dpram_device_t dpram_table[MAX_INDEX] = {
+ {
+ .in_head_addr = DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS,
+ .in_tail_addr = DPRAM_PHONE2PDA_FORMATTED_TAIL_ADDRESS,
+ .in_buff_addr = DPRAM_PHONE2PDA_FORMATTED_BUFFER_ADDRESS,
+ .in_buff_size = DPRAM_PHONE2PDA_FORMATTED_BUFFER_SIZE,
+ .in_head_saved = 0,
+ .in_tail_saved = 0,
+
+ .out_head_addr = DPRAM_PDA2PHONE_FORMATTED_HEAD_ADDRESS,
+ .out_tail_addr = DPRAM_PDA2PHONE_FORMATTED_TAIL_ADDRESS,
+ .out_buff_addr = DPRAM_PDA2PHONE_FORMATTED_BUFFER_ADDRESS,
+ .out_buff_size = DPRAM_PDA2PHONE_FORMATTED_BUFFER_SIZE,
+ .out_head_saved = 0,
+ .out_tail_saved = 0,
+
+ .mask_req_ack = INT_MASK_REQ_ACK_F,
+ .mask_res_ack = INT_MASK_RES_ACK_F,
+ .mask_send = INT_MASK_SEND_F,
+ },
+ {
+ .in_head_addr = DPRAM_PHONE2PDA_RAW_HEAD_ADDRESS,
+ .in_tail_addr = DPRAM_PHONE2PDA_RAW_TAIL_ADDRESS,
+ .in_buff_addr = DPRAM_PHONE2PDA_RAW_BUFFER_ADDRESS,
+ .in_buff_size = DPRAM_PHONE2PDA_RAW_BUFFER_SIZE,
+ .in_head_saved = 0,
+ .in_tail_saved = 0,
+
+ .out_head_addr = DPRAM_PDA2PHONE_RAW_HEAD_ADDRESS,
+ .out_tail_addr = DPRAM_PDA2PHONE_RAW_TAIL_ADDRESS,
+ .out_buff_addr = DPRAM_PDA2PHONE_RAW_BUFFER_ADDRESS,
+ .out_buff_size = DPRAM_PDA2PHONE_RAW_BUFFER_SIZE,
+ .out_head_saved = 0,
+ .out_tail_saved = 0,
+
+ .mask_req_ack = INT_MASK_REQ_ACK_R,
+ .mask_res_ack = INT_MASK_RES_ACK_R,
+ .mask_send = INT_MASK_SEND_R,
+ },
+};
+
+static struct tty_struct *dpram_tty[MAX_INDEX];
+static struct ktermios *dpram_termios[MAX_INDEX];
+static struct ktermios *dpram_termios_locked[MAX_INDEX];
+
+static void res_ack_tasklet_handler(unsigned long data);
+static void fmt_rcv_tasklet_handler(unsigned long data);
+static void raw_rcv_tasklet_handler(unsigned long data);
+
+static DECLARE_TASKLET(fmt_send_tasklet, fmt_rcv_tasklet_handler, 0);
+static DECLARE_TASKLET(raw_send_tasklet, raw_rcv_tasklet_handler, 0);
+static DECLARE_TASKLET(fmt_res_ack_tasklet, res_ack_tasklet_handler,
+ (unsigned long)&dpram_table[FORMATTED_INDEX]);
+static DECLARE_TASKLET(raw_res_ack_tasklet, res_ack_tasklet_handler,
+ (unsigned long)&dpram_table[RAW_INDEX]);
+
+static void semaphore_control_handler(unsigned long data);
+static DECLARE_TASKLET(semaphore_control_tasklet, semaphore_control_handler, 0);
+
+#ifdef _ENABLE_ERROR_DEVICE
+static unsigned int dpram_err_len;
+static char dpram_err_buf[DPRAM_ERR_MSG_LEN];
+
+struct class *dpram_class;
+
+static DECLARE_WAIT_QUEUE_HEAD(dpram_err_wait_q);
+static struct fasync_struct *dpram_err_async_q;
+extern void usb_switch_mode(int);
+#endif /* _ENABLE_ERROR_DEVICE */
+
+// 2008.10.20.
+//static DECLARE_MUTEX(write_mutex);
+
+/* tty related functions. */
+static inline void byte_align(unsigned long dest, unsigned long src)
+{
+ u16 *p_src;
+ volatile u16 *p_dest;
+
+ if (!(dest % 2) && !(src % 2)) {
+ p_dest = (u16 *)dest;
+ p_src = (u16 *)src;
+
+ *p_dest = (*p_dest & 0xFF00) | (*p_src & 0x00FF);
+ }
+
+ else if ((dest % 2) && (src % 2)) {
+ p_dest = (u16 *)(dest - 1);
+ p_src = (u16 *)(src - 1);
+
+ *p_dest = (*p_dest & 0x00FF) | (*p_src & 0xFF00);
+ }
+
+ else if (!(dest % 2) && (src % 2)) {
+ p_dest = (u16 *)dest;
+ p_src = (u16 *)(src - 1);
+
+ *p_dest = (*p_dest & 0xFF00) | ((*p_src >> 8) & 0x00FF);
+ }
+
+ else if ((dest % 2) && !(src % 2)) {
+ p_dest = (u16 *)(dest - 1);
+ p_src = (u16 *)src;
+
+ *p_dest = (*p_dest & 0x00FF) | ((*p_src << 8) & 0xFF00);
+ }
+
+ else {
+ dprintk(KERN_ERR "oops.~\n");
+ }
+}
+
+static inline void _memcpy(void *p_dest, const void *p_src, int size)
+{
+ unsigned long dest = (unsigned long)p_dest;
+ unsigned long src = (unsigned long)p_src;
+
+ if (!(*onedram_sem)) {
+ printk(KERN_ERR "[OneDRAM] memory access without semaphore!: %d\n", *onedram_sem);
+ return;
+ }
+ if (size <= 0) {
+ return;
+ }
+
+ if (dest & 1) {
+ byte_align(dest, src);
+ dest++, src++;
+ size--;
+ }
+
+ if (size & 1) {
+ byte_align(dest + size - 1, src + size - 1);
+ size--;
+ }
+
+ if (src & 1) {
+ unsigned char *s = (unsigned char *)src;
+ volatile u16 *d = (unsigned short *)dest;
+
+ size >>= 1;
+
+ while (size--) {
+ *d++ = s[0] | (s[1] << 8);
+ s += 2;
+ }
+ }
+
+ else {
+ u16 *s = (u16 *)src;
+ volatile u16 *d = (unsigned short *)dest;
+
+ size >>= 1;
+
+ while (size--) { *d++ = *s++; }
+ }
+}
+
+static inline int _memcmp(u8 *dest, u8 *src, int size)
+{
+ int i = 0;
+ if (!(*onedram_sem)) {
+ printk(KERN_ERR "[OneDRAM] memory access without semaphore!: %d\n", *onedram_sem);
+ return 1;
+ }
+
+ while (i++ < size) {
+ if (*(dest + i) != *(src + i)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static inline int WRITE_TO_DPRAM_VERIFY(u32 dest, void *src, int size)
+{
+ int cnt = 3;
+
+ while (cnt--) {
+ _memcpy((void *)(DPRAM_VBASE + dest), (void *)src, size);
+
+ if (!_memcmp((u8 *)(DPRAM_VBASE + dest), (u8 *)src, size))
+ return 0;
+ }
+
+ return -1;
+}
+
+static inline int READ_FROM_DPRAM_VERIFY(void *dest, u32 src, int size)
+{
+ int cnt = 3;
+
+ while (cnt--) {
+ _memcpy((void *)dest, (void *)(DPRAM_VBASE + src), size);
+
+ if (!_memcmp((u8 *)dest, (u8 *)(DPRAM_VBASE + src), size))
+ return 0;
+ }
+
+ return -1;
+}
+
+#if 0
+static void send_interrupt_to_phone(u16 irq_mask)
+{
+ *onedram_mailboxBA = irq_mask;
+#ifdef PRINT_SEND_IRQ
+ printk(KERN_ERR "=====> send IRQ: %x for ack\n", irq_mask);
+#endif
+
+}
+#endif
+
+static int dpram_write(dpram_device_t *device,
+ const unsigned char *buf, int len)
+{
+ int retval = 0;
+ int size = 0;
+ u16 head, tail;
+ u16 irq_mask = 0;
+ unsigned long flags;
+
+// down_interruptible(&write_mutex);
+#ifdef PRINT_WRITE
+ int i;
+ printk(KERN_ERR "WRITE\n");
+ for (i = 0; i < len; i++)
+ printk(KERN_ERR "%02x ", *((unsigned char *)buf + i));
+ printk(KERN_ERR "\n");
+#endif
+#ifdef PRINT_WRITE_SHORT
+ printk(KERN_ERR "WRITE: len: %d\n", len);
+#endif
+
+ if(!onedram_get_semaphore(__func__)) {
+ return -EINTR;
+ }
+
+ if(onedram_lock_with_semaphore(__func__) < 0) {
+ return -EINTR;
+ }
+
+ READ_FROM_DPRAM_VERIFY(&head, device->out_head_addr, sizeof(head));
+ READ_FROM_DPRAM_VERIFY(&tail, device->out_tail_addr, sizeof(tail));
+
+//printk(KERN_ERR "%s, head: %d, tail: %d\n", __func__, head, tail);
+
+ // +++++++++ head ---------- tail ++++++++++ //
+ if (head < tail) {
+ size = tail - head - 1;
+ size = (len > size) ? size : len;
+
+ WRITE_TO_DPRAM(device->out_buff_addr + head, buf, size);
+ retval = size;
+ }
+
+ // tail +++++++++++++++ head --------------- //
+ else if (tail == 0) {
+ size = device->out_buff_size - head - 1;
+ size = (len > size) ? size : len;
+
+ WRITE_TO_DPRAM(device->out_buff_addr + head, buf, size);
+ retval = size;
+ }
+
+ // ------ tail +++++++++++ head ------------ //
+ else {
+ size = device->out_buff_size - head;
+ size = (len > size) ? size : len;
+
+ WRITE_TO_DPRAM(device->out_buff_addr + head, buf, size);
+ retval = size;
+
+ if (len > retval) {
+ size = (len - retval > tail - 1) ? tail - 1 : len - retval;
+
+ WRITE_TO_DPRAM(device->out_buff_addr, buf + retval, size);
+ retval += size;
+ }
+ }
+
+ /* @LDK@ calculate new head */
+ head = (u16)((head + retval) % device->out_buff_size);
+ WRITE_TO_DPRAM_VERIFY(device->out_head_addr, &head, sizeof(head));
+
+
+ device->out_head_saved = head;
+ device->out_tail_saved = tail;
+
+ /* @LDK@ send interrupt to the phone, if.. */
+ irq_mask = INT_MASK_VALID;
+
+ if (retval > 0)
+ irq_mask |= device->mask_send;
+
+ if (len > retval)
+ irq_mask |= device->mask_req_ack;
+
+ onedram_release_lock(__func__);
+// send_interrupt_to_phone(irq_mask);
+ send_interrupt_to_phone_with_semaphore(irq_mask);
+// up(&write_mutex);
+#ifdef PRINT_WRITE_SHORT
+ printk(KERN_ERR "WRITE: return: %d\n", retval);
+#endif
+ return retval;
+
+}
+
+static inline int dpram_tty_insert_data(dpram_device_t *device, const u8 *psrc, u16 size)
+{
+#define CLUSTER_SEGMENT 1500
+
+ u16 copied_size = 0;
+ int retval = 0;
+
+#ifdef PRINT_READ
+ int i;
+ printk(KERN_ERR "READ: %d\n", size);
+ for(i=0; i<size; i++)
+ printk(KERN_ERR "%02x ", *(psrc+ + i));
+ printk(KERN_ERR "\n");
+#endif
+#ifdef PRINT_READ_SHORT
+ printk(KERN_ERR "READ: size: %d\n", size);
+#endif
+
+ if (size > CLUSTER_SEGMENT && (device->serial.tty->index == 1)) {
+ while (size) {
+ copied_size = (size > CLUSTER_SEGMENT) ? CLUSTER_SEGMENT : size;
+ tty_insert_flip_string(device->serial.tty, psrc + retval, copied_size);
+
+ size -= copied_size;
+ retval += copied_size;
+ }
+
+ return retval;
+ }
+
+ return tty_insert_flip_string(device->serial.tty, psrc, size);
+}
+
+static int dpram_read_fmt(dpram_device_t *device, const u16 non_cmd)
+{
+ int retval = 0;
+ int retval_add = 0;
+ int size = 0;
+ u16 head, tail;
+
+ if(!*onedram_sem)
+ printk(KERN_ERR "!!!!! %s no sem\n", __func__);
+
+ if(onedram_lock_with_semaphore(__func__) < 0)
+ return -EINTR;
+
+ READ_FROM_DPRAM_VERIFY(&head, device->in_head_addr, sizeof(head));
+ READ_FROM_DPRAM_VERIFY(&tail, device->in_tail_addr, sizeof(tail));
+
+// printk(KERN_ERR "=====> %s, head: %d, tail: %d\n", __func__, head, tail);
+
+ if (head != tail) {
+ u16 up_tail = 0;
+
+ // ------- tail ++++++++++++ head -------- //
+ if (head > tail) {
+ size = head - tail;
+ retval = dpram_tty_insert_data(device, (u8 *)(DPRAM_VBASE + (device->in_buff_addr + tail)), size);
+ if(size!= retval)
+ printk(KERN_ERR "[OneDRAM: size: %d, retval: %d\n", size, retval);
+#ifdef PRINT_READ_SHORT
+ else
+ printk(KERN_ERR "READ -return: %d\n", retval);
+#endif
+ }
+
+ // +++++++ head ------------ tail ++++++++ //
+ else {
+ int tmp_size = 0;
+
+ // Total Size.
+ size = device->in_buff_size - tail + head;
+
+ // 1. tail -> buffer end.
+ tmp_size = device->in_buff_size - tail;
+ retval = dpram_tty_insert_data(device, (u8 *)(DPRAM_VBASE + (device->in_buff_addr + tail)), tmp_size);
+ if(tmp_size!= retval)
+ printk(KERN_ERR "[OneDRAM: size: %d, retval: %d\n", size, retval);
+#ifdef PRINT_READ_SHORT
+ else
+ printk(KERN_ERR "READ -return: %d\n", retval);
+#endif
+
+ // 2. buffer start -> head.
+ if (size > tmp_size) {
+ retval_add = dpram_tty_insert_data(device, (u8 *)(DPRAM_VBASE + device->in_buff_addr), size - tmp_size);
+ retval += retval_add;
+
+ if((size - tmp_size)!= retval_add)
+ printk(KERN_ERR "[OneDRAM: size - tmp_size: %d, retval_add: %d\n", size - tmp_size, retval_add);
+#ifdef PRINT_READ_SHORT
+ else
+ printk(KERN_ERR "READ -return_add: %d\n", retval_add);
+#endif
+
+
+ }
+ }
+
+ /* new tail */
+ up_tail = (u16)((tail + retval) % device->in_buff_size);
+ WRITE_TO_DPRAM_VERIFY(device->in_tail_addr, &up_tail, sizeof(up_tail));
+ }
+
+
+ device->in_head_saved = head;
+ device->in_tail_saved = tail;
+
+ onedram_release_lock(__func__);
+ if (non_cmd & device->mask_req_ack)
+ send_interrupt_to_phone_with_semaphore(INT_NON_COMMAND(device->mask_res_ack));
+
+ return retval;
+
+}
+
+static int dpram_read_raw(dpram_device_t *device, const u16 non_cmd)
+{
+ int retval = 0;
+ int retval_add = 0;
+ int size = 0;
+ u16 head, tail;
+ u16 up_tail = 0;
+
+ int ret;
+ size_t len;
+ struct pdp_info *dev = NULL;
+ struct pdp_hdr hdr;
+ u16 read_offset;
+ u8 len_high, len_low, id, control;
+ u16 pre_hdr_size, pre_data_size;
+ u8 ch;
+
+ int i;
+
+ if(!*onedram_sem)
+ printk(KERN_ERR "!!!!! %s no sem\n", __func__);
+
+ if(onedram_lock_with_semaphore(__func__) < 0)
+ return -EINTR;
+
+
+ READ_FROM_DPRAM_VERIFY(&head, device->in_head_addr, sizeof(head));
+ READ_FROM_DPRAM_VERIFY(&tail, device->in_tail_addr, sizeof(tail));
+
+// printk(KERN_ERR "=====> %s, head: %d, tail: %d\n", __func__, head, tail);
+
+ if(head != tail) {
+
+ up_tail = 0;
+
+ if (head > tail) {
+ size = head - tail; /* ----- (tail) 7f 00 00 7e (head) ----- */
+#if 0
+ printk(KERN_ERR "READ\n");
+ for(i=0; i<size; i++)
+ printk(KERN_ERR "%02x ", *((unsigned char *)(DPRAM_VBASE + (device->in_buff_addr + tail)) + i));
+ printk(KERN_ERR "\n");
+#endif
+ }
+ else
+ size = device->in_buff_size - tail + head; /* 00 7e (head) ----------- (tail) 7f 00 */
+
+ read_offset = 0;
+// printk(KERN_ERR "=====> %s, head: %d, tail: %d, size: %d\n", __func__, head, tail, size);
+
+ while(size){
+ READ_FROM_DPRAM(&ch, device->in_buff_addr +((u16)(tail + read_offset) % device->in_buff_size), sizeof(ch));
+
+ if(ch == 0x7f) {
+ read_offset ++;
+ }
+ else {
+ printk(KERN_ERR "[OneDram] %s failed.. First byte: %d, drop byte: %d\n", __func__, ch, size);
+ printk(KERN_ERR "buff addr: %x\n", (device->in_buff_addr));
+ printk(KERN_ERR "read addr: %x\n", (device->in_buff_addr + ((u16)(tail + read_offset) % device->in_buff_size)));
+
+ dpram_drop_data(device);
+ onedram_release_lock(__func__);
+ return -1;
+ }
+
+ len_high = len_low = id = control = 0;
+ READ_FROM_DPRAM(&len_low, device->in_buff_addr + ((u16)(tail + read_offset) % device->in_buff_size) ,sizeof(len_high));
+ read_offset ++;
+ READ_FROM_DPRAM(&len_high, device->in_buff_addr + ((u16)(tail + read_offset) % device->in_buff_size) ,sizeof(len_low));
+ read_offset ++;
+ READ_FROM_DPRAM(&id, device->in_buff_addr + ((u16)(tail + read_offset) % device->in_buff_size) ,sizeof(id));
+ read_offset ++;
+ READ_FROM_DPRAM(&control, device->in_buff_addr + ((u16)(tail + read_offset) % device->in_buff_size) ,sizeof(control));
+ read_offset ++;
+
+ hdr.len = len_high <<8 | len_low;
+ hdr.id = id;
+ hdr.control = control;
+
+ len = hdr.len - sizeof(struct pdp_hdr);
+ if(len <= 0) {
+ printk(KERN_ERR "[OneDram] %s uups..\n", __func__);
+ printk(KERN_ERR "%s, %d read_offset: %d, len: %d hdr.id: %d\n", __func__, __LINE__, read_offset, len, hdr.id);
+
+ dpram_drop_data(device);
+ onedram_release_lock(__func__);
+ return -1;
+
+
+ }
+ dev = pdp_get_dev(hdr.id);
+// printk(KERN_ERR "%s, %d read_offset: %d, len: %d hdr.id: %d\n", __func__, __LINE__, read_offset, len, hdr.id);
+
+ if(!dev) {
+ printk(KERN_ERR "[OneDram] %s failed.. NULL dev detected \n", __func__);
+ check_pdp_table(__func__, __LINE__);
+ dpram_drop_data(device);
+ onedram_release_lock(__func__);
+ return -1;
+ }
+
+
+ if (dev->vs_dev.tty != NULL && dev->vs_dev.refcount) {
+
+ if((u16)(tail + read_offset) % device->in_buff_size + len < device->in_buff_size) {
+ ret = tty_insert_flip_string(dev->vs_dev.tty, (u8 *)(DPRAM_VBASE + (device->in_buff_addr + (u16)(tail + read_offset) % device->in_buff_size)), len);
+ tty_flip_buffer_push(dev->vs_dev.tty);
+ }else {
+ pre_data_size = device->in_buff_size - (tail + read_offset);
+ ret = tty_insert_flip_string(dev->vs_dev.tty, (u8 *)(DPRAM_VBASE + (device->in_buff_addr + tail + read_offset)), pre_data_size);
+ ret += tty_insert_flip_string(dev->vs_dev.tty, (u8 *)(DPRAM_VBASE + (device->in_buff_addr)),len - pre_data_size);
+ tty_flip_buffer_push(dev->vs_dev.tty);
+// printk(KERN_ERR "=====> pre_data_size: %d, len-pre_data_size: %d, ret: %d\n", pre_data_size, len- pre_data_size, ret);
+ }
+ }
+ else {
+ printk(KERN_ERR "[%s]failed.. tty channel(id:%d) is not opened.\n", __func__, dev->id);
+ ret = len;
+ }
+
+ if(!ret) {
+ printk(KERN_ERR "[OneDram] %s failed.. (tty_insert_flip_string) drop byte: %d\n", __func__, size);
+ printk(KERN_ERR "buff addr: %x\n", (device->in_buff_addr));
+ printk(KERN_ERR "read addr: %x\n", (device->in_buff_addr + ((u16)(tail + read_offset) % device->in_buff_size)));
+ dpram_drop_data(device);
+ onedram_release_lock(__func__);
+ return -1;
+ }
+
+ read_offset += ret;
+// printk(KERN_ERR "%s,%d read_offset: %d ret= %d\n", __func__, __LINE__, read_offset, ret);
+
+ READ_FROM_DPRAM(&ch, (device->in_buff_addr + ((u16)(tail + read_offset) % device->in_buff_size)), sizeof(ch));
+ if(ch == 0x7e)
+ read_offset ++;
+ else {
+ printk(KERN_ERR "[OneDram] %s failed.. Last byte: %d, drop byte: %d\n", __func__, ch, size);
+ printk(KERN_ERR "buff addr: %x\n", (device->in_buff_addr));
+ printk(KERN_ERR "read addr: %x\n", (device->in_buff_addr + ((u16)(tail + read_offset) % device->in_buff_size)));
+ dpram_drop_data(device);
+ onedram_release_lock(__func__);
+ return -1;
+ }
+
+ size -= (ret + sizeof(struct pdp_hdr) + 2);
+ retval += (ret + sizeof(struct pdp_hdr) + 2);
+// printk(KERN_ERR "%s, %d retval= %d, read_offset: %d, size: %d\n", __func__, __LINE__, retval, read_offset, size);
+
+ if(size < 0) {
+ printk(KERN_ERR "something wrong....\n");
+ break;
+ }
+
+ }
+ up_tail = (u16)((tail + read_offset) % device->in_buff_size);
+ WRITE_TO_DPRAM_VERIFY(device->in_tail_addr, &up_tail, sizeof(up_tail));
+ }
+#if 0
+ /* new tail */
+ up_tail = (u16)((tail + retval) % device->in_buff_size);
+ WRITE_TO_DPRAM_VERIFY(device->in_tail_addr, &up_tail, sizeof(up_tail));
+#endif
+
+ device->in_head_saved = head;
+ device->in_tail_saved = tail;
+
+ onedram_release_lock(__func__);
+ if (non_cmd & device->mask_req_ack)
+ send_interrupt_to_phone_with_semaphore(INT_NON_COMMAND(device->mask_res_ack));
+
+ return retval;
+
+}
+#ifdef _ENABLE_ERROR_DEVICE
+void request_phone_reset()
+{
+ char buf[DPRAM_ERR_MSG_LEN];
+ unsigned long flags;
+
+ memset((void *)buf, 0, sizeof (buf));
+ buf[0] = '9';
+ buf[1] = ' ';
+
+ memcpy(buf+2, "$PHONE-RESET", sizeof("$PHONE-RESET"));
+ printk(KERN_ERR "[PHONE ERROR] ->> %s\n", buf);
+
+ local_irq_save(flags);
+ memcpy(dpram_err_buf, buf, DPRAM_ERR_MSG_LEN);
+ dpram_err_len = 64;
+ local_irq_restore(flags);
+
+ wake_up_interruptible(&dpram_err_wait_q);
+ kill_fasync(&dpram_err_async_q, SIGIO, POLL_IN);
+}
+#endif
+
+static int onedram_get_semaphore(const char *func)
+{
+ int i, req_try = 100;
+
+ const u16 cmd = INT_COMMAND(INT_MASK_CMD_SMP_REQ);
+
+ if(dump_on) return -1;
+
+ for(i = 0; i < req_try; i++) {
+ if(*onedram_sem) {
+ unreceived_semaphore = 0;
+ return 1;
+ }
+ if (i == 0)
+ *onedram_mailboxBA = cmd;
+ udelay(40);
+ }
+
+ unreceived_semaphore++;
+ printk(KERN_ERR "[OneDRAM](%s) Failed to get a Semaphore. sem:%d, PHONE_ACTIVE:%s, fail_cnt:%d\n",
+ func, *onedram_sem, gpio_get_value(GPIO_PHONE_ACTIVE)?"HIGH":"LOW ", unreceived_semaphore);
+
+#ifdef _ENABLE_ERROR_DEVICE
+ if(unreceived_semaphore > 10)
+ request_phone_reset();
+#endif
+
+ return 0;
+}
+
+
+static int onedram_get_semaphore_for_init(const char *func)
+{
+ int i, chk_try = 10;
+ int j, req_try = 3;
+
+ const u16 cmd = INT_COMMAND(INT_MASK_CMD_SMP_REQ);
+
+ if(dump_on) return -1;
+
+ for(j = 0; j < req_try; j++) {
+ for(i = 0; i < chk_try; i++) {
+ if(*onedram_sem) {
+ unreceived_semaphore = 0;
+ return 1;
+ }
+ mdelay(1);
+ }
+ *onedram_mailboxBA = cmd;
+ printk(KERN_ERR "=====> send IRQ: %x\n", cmd);
+ }
+
+ unreceived_semaphore++;
+ printk(KERN_ERR "[OneDRAM](%s) Failed to get a Semaphore. sem:%d, PHONE_ACTIVE:%s, fail_cnt:%d\n",
+ func, *onedram_sem, gpio_get_value(GPIO_PHONE_ACTIVE)?"HIGH":"LOW ", unreceived_semaphore);
+
+#ifdef _ENABLE_ERROR_DEVICE
+ if(unreceived_semaphore > 10)
+ request_phone_reset();
+#endif
+
+ return 0;
+}
+
+static void send_interrupt_to_phone_with_semaphore(u16 irq_mask)
+{
+ if(dump_on) return;
+
+ if(!atomic_read(&onedram_lock))
+ {
+ if(*onedram_sem) {
+ *onedram_sem = 0x0;
+ *onedram_mailboxBA = irq_mask;
+#ifdef PRINT_SEND_IRQ
+ printk(KERN_ERR "=====> send IRQ: %x with sem\n", irq_mask);
+#endif
+ requested_semaphore = 0;
+ }else {
+ *onedram_mailboxBA = irq_mask;
+#ifdef PRINT_SEND_IRQ
+ printk(KERN_ERR "=====> send IRQ: %x\n", irq_mask);
+#endif
+ }
+ }else {
+ printk(KERN_ERR "[OneDRAM] (%s) lock set. can't return semaphore.\n", __func__);
+ }
+
+
+}
+static int return_onedram_semaphore(const char* func)
+{
+
+ if(!atomic_read(&onedram_lock))
+ {
+ if(*onedram_sem) { *onedram_sem = 0x0;
+ return 1;
+ }
+ }else {
+ requested_semaphore++;
+ printk(KERN_ERR "[OneDRAM] (%s) PDA is accessing onedram. %d\n", __func__, requested_semaphore);
+ }
+
+ return 0;
+
+}
+
+static int onedram_lock_with_semaphore(const char* func)
+{
+ int lock_value;
+
+ if(!(lock_value = atomic_inc_return(&onedram_lock)))
+ printk(KERN_ERR "[OneDRAM] (%s, lock) fail to locking onedram access. %d\n", func, lock_value);
+
+ if(lock_value != 1)
+ printk(KERN_ERR "[OneDRAM] (%s, lock) lock_value: %d\n", func, lock_value);
+
+ if(*onedram_sem) {
+ return 0;
+ }
+ else{
+ printk(KERN_ERR "[OneDRAM] (%s, lock) failed.. no sem\n", func);
+ if((lock_value = atomic_dec_return(&onedram_lock)) < 0)
+ printk(KERN_ERR "[OneDRAM] (%s, lock) fail to unlocking onedram access. %d\n", func, lock_value);
+
+ if(lock_value != 0)
+ printk(KERN_ERR "[OneDRAM] (%s, lock) lock_value: %d\n", func, lock_value);
+ return -1;
+ }
+}
+
+static void onedram_release_lock(const char* func)
+{
+ int lock_value;
+
+ if((lock_value = atomic_dec_return(&onedram_lock)) < 0)
+ printk(KERN_ERR "[OneDRAM] (%s, release) fail to unlocking onedram access. %d\n", func, lock_value);
+
+ if(requested_semaphore) {
+ if(!atomic_read(&onedram_lock)) {
+ if(*onedram_sem) {
+ printk(KERN_ERR "[OneDRAM] (%s, release) requested semaphore(%d) return to Phone.\n", func, requested_semaphore);
+ *onedram_sem = 0x0;
+ requested_semaphore = 0;
+ }
+ }
+ }
+
+ if(lock_value != 0)
+ printk(KERN_ERR "[OneDRAM] (%s, release) lock_value: %d\n", func, lock_value);
+
+}
+
+static int dpram_shared_bank_remap(void)
+{
+ dpram_base = ioremap_nocache(DPRAM_START_ADDRESS_PHYS + DPRAM_SHARED_BANK, DPRAM_SHARED_BANK_SIZE);
+ if (dpram_base == NULL) {
+ printk(KERN_ERR "failed ioremap\n");
+ return -ENOENT;
+ }
+
+ onedram_sem = DPRAM_VBASE + DPRAM_SMP;
+ onedram_mailboxBA = DPRAM_VBASE + DPRAM_MBX_BA;
+ onedram_mailboxAB = DPRAM_VBASE + DPRAM_MBX_AB;
+ atomic_set(&onedram_lock, 0);
+
+ return 0;
+}
+
+static void dpram_clear(void)
+{
+ long i = 0;
+ unsigned long flags;
+
+ u16 value = 0;
+
+ /* @LDK@ clear DPRAM except interrupt area */
+ local_irq_save(flags);
+
+ for (i = DPRAM_PDA2PHONE_FORMATTED_HEAD_ADDRESS;
+ i < DPRAM_SIZE - (DPRAM_INTERRUPT_PORT_SIZE * 2);
+ i += 2)
+ {
+ *((u16 *)(DPRAM_VBASE + i)) = 0;
+ }
+
+ local_irq_restore(flags);
+
+ value = *onedram_mailboxAB;
+}
+
+static int dpram_init_and_report(void)
+{
+ const u16 magic_code = 0x00aa;
+ const u16 init_start = INT_COMMAND(INT_MASK_CMD_INIT_START);
+ const u16 init_end = INT_COMMAND(INT_MASK_CMD_INIT_END);
+ u16 ac_code = 0;
+
+ if (!(*onedram_sem)) {
+ printk(KERN_ERR "[OneDRAM] %s, sem: %d\n", __func__, *onedram_sem);
+ if(!onedram_get_semaphore_for_init(__func__)) {
+ printk(KERN_ERR "[OneDRAM] %s failed to onedram init!!! semaphore: %d\n", __func__, *onedram_sem);
+ return -EINTR;
+ }
+ }
+
+ if(onedram_lock_with_semaphore(__func__) < 0)
+ return -EINTR;
+#if 0
+ /* @LDK@ send init start code to phone */
+ *onedram_mailboxBA = init_start;
+ printk(KERN_ERR "[OneDRAM] Send to MailboxBA 0x%x (onedram init start).\n", init_start);
+#endif
+
+ /* @LDK@ write DPRAM disable code */
+ WRITE_TO_DPRAM(DPRAM_ACCESS_ENABLE_ADDRESS, &ac_code, sizeof(ac_code));
+
+ /* @LDK@ dpram clear */
+ dpram_clear();
+
+ /* @LDK@ write magic code */
+ WRITE_TO_DPRAM(DPRAM_MAGIC_CODE_ADDRESS, &magic_code, sizeof(magic_code));
+
+ /* @LDK@ write DPRAM enable code */
+ ac_code = 0x0001;
+ WRITE_TO_DPRAM(DPRAM_ACCESS_ENABLE_ADDRESS, &ac_code, sizeof(ac_code));
+
+ /* @LDK@ send init end code to phone */
+ onedram_release_lock(__func__);
+ send_interrupt_to_phone_with_semaphore(init_end);
+ printk(KERN_ERR "[OneDRAM] Send 0x%x to MailboxBA (onedram init finish).\n", init_end);
+
+ phone_sync = 1;
+ return 0;
+}
+
+static inline int dpram_get_read_available(dpram_device_t *device)
+{
+ u16 head, tail;
+
+ if(*onedram_sem) {
+
+ READ_FROM_DPRAM_VERIFY(&head, device->in_head_addr, sizeof(head));
+ READ_FROM_DPRAM_VERIFY(&tail, device->in_tail_addr, sizeof(tail));
+// printk(KERN_ERR "H: %d, T: %d, H-T: %d\n",head, tail, head-tail);
+
+ return head - tail;
+ }
+ else {
+// printk(KERN_ERR "[OneDRAM] (%s) semaphore: %d\n", __func__, *onedram_sem);
+ return 0;
+ }
+}
+
+static void dpram_drop_data(dpram_device_t *device)
+{
+ u16 head, tail;
+
+ if(*onedram_sem) {
+ READ_FROM_DPRAM_VERIFY(&head, device->in_head_addr, sizeof(head));
+ WRITE_TO_DPRAM_VERIFY(device->in_tail_addr, &head, sizeof(head));
+
+ READ_FROM_DPRAM_VERIFY(&tail, device->in_tail_addr, sizeof(tail));
+ printk(KERN_ERR "[OneDram] %s, head: %d, tail: %d\n", __func__, head, tail);
+
+ }
+}
+
+static void dpram_phone_power_on(void)
+{
+
+ printk(KERN_ERR "[OneDRAM] Phone Power on! sem: %d lock: %d\n", *onedram_sem, atomic_read(&onedram_lock));
+ *onedram_sem = 0x00;
+ printk(KERN_ERR "[OneDRAM] set semaphore: %d\n", *onedram_sem);
+
+ printk(KERN_ERR "[OneDRAM] power control (with GPIO_PHONE_RST_N)\n");
+ gpio_set_value(GPIO_PHONE_ON, GPIO_LEVEL_HIGH);
+ mdelay(50);
+ gpio_set_value(GPIO_PHONE_RST_N, GPIO_LEVEL_LOW);
+ mdelay(100);
+ gpio_set_value(GPIO_PHONE_RST_N, GPIO_LEVEL_HIGH);
+ mdelay(500);
+ gpio_set_value(GPIO_PHONE_ON, GPIO_LEVEL_LOW);
+}
+
+static void dpram_phone_power_off(void)
+{
+ printk(KERN_ERR "[OneDRAM] Phone power Off. - do nothing\n");
+}
+
+static int dpram_phone_getstatus(void)
+{
+ return gpio_get_value(GPIO_PHONE_ACTIVE);
+}
+
+static void dpram_phone_reset(void)
+{
+ printk(KERN_ERR "[OneDRAM] Phone Reset! sem: %d lock: %d\n", *onedram_sem, atomic_read(&onedram_lock));
+ if(*onedram_sem) {
+ *onedram_sem = 0x00;
+ printk(KERN_ERR "[OneDRAM] set semaphore: %d\n", *onedram_sem);
+ }
+
+ gpio_set_value(GPIO_PHONE_RST_N, GPIO_LEVEL_LOW);
+ mdelay(100);
+ gpio_set_value(GPIO_PHONE_RST_N, GPIO_LEVEL_HIGH);
+}
+
+static int dpram_extra_mem_rw(struct _param_em *param)
+{
+
+ if(param->offset + param->size > 0xFFF800) {
+ printk(KERN_ERR "[OneDRAM] %s failed.. wrong rage of external memory access\n", __func__);
+ return -1;
+ }
+
+ if(!onedram_get_semaphore(__func__))
+ return -EINTR;
+
+ if(onedram_lock_with_semaphore(__func__) < 0)
+ return -EINTR;
+
+ if (param->rw) { //write
+ WRITE_TO_DPRAM(param->offset, param->addr, param->size);
+ }
+ else { //read
+ READ_FROM_DPRAM(param->addr, param->offset, param->size);
+ }
+
+ onedram_release_lock(__func__);
+ return 0;
+}
+
+#if 0
+static void dpram_mem_rw(struct _mem_param *param)
+{
+ /* @LDK@ write */
+ if (param->dir) {
+ WRITE_TO_DPRAM(param->addr, (void *)&param->data, sizeof(param->data));
+ }
+
+ /* @LDK@ read */
+ else {
+ READ_FROM_DPRAM((void *)&param->data, param->addr, sizeof(param->data));
+ }
+}
+#endif
+
+
+static int dpram_phone_ramdump_on(void)
+{
+ const u16 rdump_flag1 = 0x554C;
+ const u16 rdump_flag2 = 0x454D;
+ const u16 temp1, temp2;
+
+ printk(KERN_ERR "[OneDRAM] Ramdump ON.\n");
+
+ if(!onedram_get_semaphore(__func__))
+ return -EINTR;
+
+ if(onedram_lock_with_semaphore(__func__) < 0)
+ return -EINTR;
+
+ WRITE_TO_DPRAM(DPRAM_MAGIC_CODE_ADDRESS, &rdump_flag1, sizeof(rdump_flag1));
+ WRITE_TO_DPRAM(DPRAM_ACCESS_ENABLE_ADDRESS, &rdump_flag2, sizeof(rdump_flag2));
+
+ READ_FROM_DPRAM((void *)&temp1, DPRAM_MAGIC_CODE_ADDRESS, sizeof(temp1));
+ READ_FROM_DPRAM((void *)&temp2, DPRAM_ACCESS_ENABLE_ADDRESS, sizeof(temp2));
+ printk(KERN_ERR "[OneDRAM] flag1: %x flag2: %x\n", temp1, temp2);
+
+ /* @LDK@ send init end code to phone */
+ onedram_release_lock(__func__);
+
+ dump_on = 1;
+
+ return_onedram_semaphore(__func__);
+ if(*onedram_sem) {
+ printk(KERN_ERR "[OneDRAM] Failed to return semaphore. try again\n");
+ *onedram_sem = 0x00;
+ }
+
+ return 0;
+
+}
+
+static int dpram_phone_ramdump_off(void)
+{
+#if 1 //XXXXX
+
+ dump_on = 0;
+ phone_sync = 0;
+
+#else
+
+ const u16 rdump_flag1 = 0x00aa;
+ const u16 rdump_flag2 = 0x0001;
+// const u16 temp1, temp2;
+
+ printk(KERN_ERR "[OneDRAM] Ramdump OFF.\n");
+
+ dump_on = 0;
+
+ if(!onedram_get_semaphore(__func__))
+ return -EINTR;
+
+ if(onedram_lock_with_semaphore(__func__) < 0)
+ return -EINTR;
+
+ WRITE_TO_DPRAM(DPRAM_MAGIC_CODE_ADDRESS, &rdump_flag1, sizeof(rdump_flag1));
+ WRITE_TO_DPRAM(DPRAM_ACCESS_ENABLE_ADDRESS, &rdump_flag2, sizeof(rdump_flag2));
+#if 0
+ READ_FROM_DPRAM((void *)&temp1, DPRAM_MAGIC_CODE_ADDRESS, sizeof(temp1));
+ READ_FROM_DPRAM((void *)&temp2, DPRAM_ACCESS_ENABLE_ADDRESS, sizeof(temp2));
+ printk(KERN_ERR "[OneDRAM] flag1: %x flag2: %x\n", temp1, temp2);
+#endif
+ /* @LDK@ send init end code to phone */
+ onedram_release_lock(__func__);
+
+ usb_switch_mode(1);
+
+ phone_sync = 0;
+
+// *onedram_sem = 0x00;
+ return_onedram_semaphore(__func__);
+ if(*onedram_sem) {
+ printk(KERN_ERR "[OneDRAM] Failed to return semaphore. try again\n");
+ *onedram_sem = 0x00;
+ }
+ dpram_phone_reset();
+#endif
+ return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+static int dpram_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *p = page;
+ int len;
+
+ u16 magic, enable;
+ u16 fmt_in_head, fmt_in_tail, fmt_out_head, fmt_out_tail;
+ u16 raw_in_head, raw_in_tail, raw_out_head, raw_out_tail;
+ u16 in_interrupt = 0, out_interrupt = 0;
+
+ int fih, fit, foh, fot;
+ int rih, rit, roh, rot;
+ int sem;
+
+#ifdef _ENABLE_ERROR_DEVICE
+ char buf[DPRAM_ERR_MSG_LEN];
+ unsigned long flags;
+#endif /* _ENABLE_ERROR_DEVICE */
+
+ if(*onedram_sem) {
+
+ READ_FROM_DPRAM((void *)&magic, DPRAM_MAGIC_CODE_ADDRESS, sizeof(magic));
+ READ_FROM_DPRAM((void *)&enable, DPRAM_ACCESS_ENABLE_ADDRESS, sizeof(enable));
+ READ_FROM_DPRAM((void *)&fmt_in_head, DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS, sizeof(fmt_in_head));
+ READ_FROM_DPRAM((void *)&fmt_in_tail, DPRAM_PHONE2PDA_FORMATTED_TAIL_ADDRESS, sizeof(fmt_in_tail));
+ READ_FROM_DPRAM((void *)&fmt_out_head, DPRAM_PDA2PHONE_FORMATTED_HEAD_ADDRESS, sizeof(fmt_out_head));
+ READ_FROM_DPRAM((void *)&fmt_out_tail, DPRAM_PDA2PHONE_FORMATTED_TAIL_ADDRESS, sizeof(fmt_out_tail));
+ READ_FROM_DPRAM((void *)&raw_in_head, DPRAM_PHONE2PDA_RAW_HEAD_ADDRESS, sizeof(raw_in_head));
+ READ_FROM_DPRAM((void *)&raw_in_tail, DPRAM_PHONE2PDA_RAW_TAIL_ADDRESS, sizeof(raw_in_tail));
+ READ_FROM_DPRAM((void *)&raw_out_head, DPRAM_PDA2PHONE_RAW_HEAD_ADDRESS, sizeof(raw_out_head));
+ READ_FROM_DPRAM((void *)&raw_out_tail, DPRAM_PDA2PHONE_RAW_TAIL_ADDRESS, sizeof(raw_out_tail));
+ }
+ else {
+ magic = enable = 0;
+ fmt_in_head = fmt_in_tail = fmt_out_head = fmt_out_tail = 0;
+ raw_in_head = raw_in_tail = raw_out_head = raw_out_tail = 0;
+ }
+
+ fih = dpram_table[FORMATTED_INDEX].in_head_saved;
+ fit = dpram_table[FORMATTED_INDEX].in_tail_saved;
+ foh = dpram_table[FORMATTED_INDEX].out_head_saved;
+ fot = dpram_table[FORMATTED_INDEX].out_tail_saved;
+ rih = dpram_table[RAW_INDEX].in_head_saved;
+ rit = dpram_table[RAW_INDEX].in_tail_saved;
+ roh = dpram_table[RAW_INDEX].out_head_saved;
+ rot = dpram_table[RAW_INDEX].out_tail_saved;
+
+ sem = *onedram_sem;
+
+ in_interrupt = *onedram_mailboxAB;
+ out_interrupt = *onedram_mailboxBA;
+
+#ifdef _ENABLE_ERROR_DEVICE
+ memset((void *)buf, '\0', DPRAM_ERR_MSG_LEN);
+ local_irq_save(flags);
+ memcpy(buf, dpram_err_buf, DPRAM_ERR_MSG_LEN - 1);
+ local_irq_restore(flags);
+#endif /* _ENABLE_ERROR_DEVICE */
+
+ p += sprintf(p,
+ "-------------------------------------\n"
+ "| NAME\t\t\t| VALUE\n"
+ "-------------------------------------\n"
+ "|R MAGIC CODE\t\t| 0x%04x\n"
+ "|R ENABLE CODE\t\t| 0x%04x\n"
+ "|R PHONE->PDA FMT HEAD\t| %u\n"
+ "|R PHONE->PDA FMT TAIL\t| %u\n"
+ "|R PDA->PHONE FMT HEAD\t| %u\n"
+ "|R PDA->PHONE FMT TAIL\t| %u\n"
+ "|R PHONE->PDA RAW HEAD\t| %u\n"
+ "|R RPHONE->PDA RAW TAIL\t| %u\n"
+ "|R PDA->PHONE RAW HEAD\t| %u\n"
+ "|R PDA->PHONE RAW TAIL\t| %u\n"
+ "-------------------------------------\n"
+ "| Onedram Semaphore\t| %d\n"
+ "| requested Semaphore\t| %d\n"
+ "| unreceived Semaphore\t| %d\n"
+ "-------------------------------------\n"
+ "| FMT PHONE->PDA HEAD\t| %d\n"
+ "| FMT PHONE->PDA TAIL\t| %d\n"
+ "| FMT PDA->PHONE HEAD\t| %d\n"
+ "| FMT PDA->PHONE TAIL\t| %d\n"
+ "-------------------------------------\n"
+ "| RAW PHONE->PDA HEAD\t| %d\n"
+ "| RAW PHONE->PDA TAIL\t| %d\n"
+ "| RAW PDA->PHONE HEAD\t| %d\n"
+ "| RAW PDA->PHONE TAIL\t| %d\n"
+ "-------------------------------------\n"
+ "| PHONE->PDA MAILBOX\t| 0x%04x\n"
+ "| PDA->PHONE MAILBOX\t| 0x%04x\n"
+ "-------------------------------------\n"
+#ifdef _ENABLE_ERROR_DEVICE
+ "| LAST PHONE ERR MSG\t| %s\n"
+#endif /* _ENABLE_ERROR_DEVICE */
+ "| PHONE ACTIVE\t\t| %s\n"
+ "| DPRAM INT Level\t| %d\n"
+ "-------------------------------------\n",
+ magic, enable,
+ fmt_in_head, fmt_in_tail, fmt_out_head, fmt_out_tail,
+ raw_in_head, raw_in_tail, raw_out_head, raw_out_tail,
+ sem,
+ requested_semaphore,
+ unreceived_semaphore,
+ fih, fit, foh, fot,
+ rih, rit, roh, rot,
+ in_interrupt, out_interrupt,
+
+#ifdef _ENABLE_ERROR_DEVICE
+ (buf[0] != '\0' ? buf : "NONE"),
+#endif /* _ENABLE_ERROR_DEVICE */
+
+ (dpram_phone_getstatus() ? "ACTIVE" : "INACTIVE"),
+ gpio_get_value(IRQ_PHONE_ACTIVE)
+ );
+
+ len = (p - page) - off;
+ if (len < 0) {
+ len = 0;
+ }
+
+ *eof = (len <= count) ? 1 : 0;
+ *start = page + off;
+
+ return len;
+}
+#endif /* CONFIG_PROC_FS */
+
+/* dpram tty file operations. */
+static int dpram_tty_open(struct tty_struct *tty, struct file *file)
+{
+ dpram_device_t *device = &dpram_table[tty->index];
+
+ device->serial.tty = tty;
+ device->serial.open_count++;
+
+ if (device->serial.open_count > 1) {
+ device->serial.open_count--;
+ return -EBUSY;
+ }
+
+ tty->driver_data = (void *)device;
+ tty->low_latency = 1;
+ return 0;
+}
+
+static void dpram_tty_close(struct tty_struct *tty, struct file *file)
+{
+ dpram_device_t *device = (dpram_device_t *)tty->driver_data;
+
+ if (device && (device == &dpram_table[tty->index])) {
+ down(&device->serial.sem);
+ device->serial.open_count--;
+ device->serial.tty = NULL;
+ up(&device->serial.sem);
+ }
+}
+
+static int dpram_tty_write(struct tty_struct *tty,
+ const unsigned char *buffer, int count)
+{
+ dpram_device_t *device = (dpram_device_t *)tty->driver_data;
+
+ if (!device) {
+ return 0;
+ }
+
+ return dpram_write(device, buffer, count);
+}
+
+static int dpram_tty_write_room(struct tty_struct *tty)
+{
+ int avail;
+ u16 head, tail;
+
+ dpram_device_t *device = (dpram_device_t *)tty->driver_data;
+
+ if (device != NULL) {
+#if 0
+ onedram_lock_with_semaphore();
+
+ READ_FROM_DPRAM_VERIFY(&head, device->out_head_addr, sizeof(head));
+ READ_FROM_DPRAM_VERIFY(&tail, device->out_tail_addr, sizeof(tail));
+
+ onedram_release_lock();
+#else
+ head = device->out_head_saved;
+ tail = device->out_tail_saved;
+#endif
+ avail = (head < tail) ? tail - head - 1 :
+ device->out_buff_size + tail - head - 1;
+
+ return avail;
+ }
+
+ return 0;
+}
+
+
+static int dpram_tty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int val;
+
+ switch (cmd) {
+ case DPRAM_PHONE_ON:
+ phone_sync = 0;
+ dump_on = 0;
+ requested_semaphore = 0;
+ unreceived_semaphore = 0;
+ dpram_phone_power_on();
+#if 0
+ if (!(*onedram_sem)) {
+ printk(KERN_ERR "[OneDRAM] (%s) semaphore: %d\n", __func__, *onedram_sem);
+ onedram_get_semaphore(__func__);
+ }
+#endif
+ return 0;
+
+ case DPRAM_PHONE_GETSTATUS:
+ val = dpram_phone_getstatus();
+ return copy_to_user((unsigned int *)arg, &val, sizeof(val));
+
+ case DPRAM_PHONE_RESET:
+ phone_sync = 0;
+ requested_semaphore = 0;
+ unreceived_semaphore = 0;
+ dpram_phone_reset();
+ return 0;
+
+ case DPRAM_PHONE_OFF:
+ dpram_phone_power_off();
+ return 0;
+
+ case DPRAM_PHONE_RAMDUMP_ON:
+ dpram_phone_ramdump_on();
+ return 0;
+
+ case DPRAM_PHONE_RAMDUMP_OFF:
+ dpram_phone_ramdump_off();
+ return 0;
+
+ case DPRAM_EXTRA_MEM_RW:
+ {
+ struct _param_em param;
+
+ val = copy_from_user((void *)&param, (void *)arg, sizeof(param));
+ if (dpram_extra_mem_rw(&param) < 0) {
+ printk(KERN_ERR "[OneDRAM] external memory access fail..\n");
+ return -1;
+ }
+ if (!param.rw) { //read
+ return copy_to_user((unsigned long *)arg, &param, sizeof(param));
+ }
+
+ return 0;
+ }
+
+#if 0
+ case DPRAM_MEM_RW:
+ {
+ struct _mem_param param;
+
+ val = copy_from_user((void *)&param, (void *)arg, sizeof(param));
+ dpram_mem_rw(&param);
+
+ if (!param.dir) {
+ return copy_to_user((unsigned long *)arg, &param, sizeof(param));
+ }
+
+ return 0;
+ }
+#endif
+ default:
+ break;
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static int dpram_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ int data;
+ u16 head, tail;
+
+ dpram_device_t *device = (dpram_device_t *)tty->driver_data;
+
+ if (device != NULL) {
+#if 0
+ onedram_lock_with_semaphore();
+
+ READ_FROM_DPRAM_VERIFY(&head, device->out_head_addr, sizeof(head));
+ READ_FROM_DPRAM_VERIFY(&tail, device->out_tail_addr, sizeof(tail));
+
+ onedram_release_lock();
+#else
+ head = device->out_head_saved;
+ tail = device->out_tail_saved;
+#endif
+ data = (head > tail) ? head - tail - 1 :
+ device->out_buff_size - tail + head;
+
+ return data;
+ }
+
+ return 0;
+}
+
+#ifdef _ENABLE_ERROR_DEVICE
+static int dpram_err_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ unsigned long flags;
+ ssize_t ret;
+ size_t ncopy;
+
+ add_wait_queue(&dpram_err_wait_q, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (1) {
+ local_irq_save(flags);
+
+ if (dpram_err_len) {
+ ncopy = min(count, dpram_err_len);
+
+ if (copy_to_user(buf, dpram_err_buf, ncopy)) {
+ ret = -EFAULT;
+ }
+
+ else {
+ ret = ncopy;
+ }
+
+ dpram_err_len = 0;
+
+ local_irq_restore(flags);
+ break;
+ }
+
+ local_irq_restore(flags);
+
+ if (filp->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ schedule();
+ }
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dpram_err_wait_q, &wait);
+
+ return ret;
+}
+
+static int dpram_err_fasync(int fd, struct file *filp, int mode)
+{
+ return fasync_helper(fd, filp, mode, &dpram_err_async_q);
+}
+
+static unsigned int dpram_err_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ poll_wait(filp, &dpram_err_wait_q, wait);
+ return ((dpram_err_len) ? (POLLIN | POLLRDNORM) : 0);
+}
+#endif /* _ENABLE_ERROR_DEVICE */
+
+/* handlers. */
+static void res_ack_tasklet_handler(unsigned long data)
+{
+ dpram_device_t *device = (dpram_device_t *)data;
+
+ if (device && device->serial.tty) {
+ struct tty_struct *tty = device->serial.tty;
+
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc->ops->write_wakeup) {
+ (tty->ldisc->ops->write_wakeup)(tty);
+ } // nandu
+
+ wake_up_interruptible(&tty->write_wait);
+ }
+}
+
+static void fmt_rcv_tasklet_handler(unsigned long data)
+{
+ dpram_tasklet_data_t *tasklet_data = (dpram_tasklet_data_t *)data;
+
+ dpram_device_t *device = tasklet_data->device;
+ u16 non_cmd = tasklet_data->non_cmd;
+
+ int ret = 0;
+ int cnt = 0;
+
+ if (device && device->serial.tty) {
+ struct tty_struct *tty = device->serial.tty;
+
+ while (dpram_get_read_available(device)) {
+ ret = dpram_read_fmt(device, non_cmd);
+
+ if (!ret) cnt++;
+
+ if (cnt > 10) {
+ dpram_drop_data(device);
+ break;
+ }
+ if (ret < 0) {
+ printk(KERN_ERR "%s, dpram_read_fmt failed\n", __func__);
+ /* TODO: ... wrong.. */
+ }
+ tty_flip_buffer_push(tty);
+ }
+ }
+
+ else {
+ dpram_drop_data(device);
+ }
+}
+
+static void raw_rcv_tasklet_handler(unsigned long data)
+{
+ dpram_tasklet_data_t *tasklet_data = (dpram_tasklet_data_t *)data;
+
+ dpram_device_t *device = tasklet_data->device;
+ u16 non_cmd = tasklet_data->non_cmd;
+
+ int ret = 0;
+
+ while (dpram_get_read_available(device)) {
+ ret = dpram_read_raw(device, non_cmd);
+ if (ret < 0) {
+ printk(KERN_ERR "%s, dpram_read failed\n", __func__);
+ /* TODO: ... wrong.. */
+ }
+ }
+}
+
+static void cmd_req_active_handler(void)
+{
+#if 0
+ send_interrupt_to_phone(INT_COMMAND(INT_MASK_CMD_RES_ACTIVE));
+#else
+ send_interrupt_to_phone_with_semaphore(INT_COMMAND(INT_MASK_CMD_RES_ACTIVE));
+#endif
+}
+
+static void cmd_error_display_handler(void)
+{
+
+#ifdef _ENABLE_ERROR_DEVICE
+ char buf[DPRAM_ERR_MSG_LEN];
+ unsigned long flags;
+
+ memset((void *)buf, 0, sizeof (buf));
+
+ if (!dpram_phone_getstatus()) {
+ memcpy((void *)buf, "8 $PHONE-OFF", sizeof("8 $PHONE-OFF"));
+ }
+ else {
+ buf[0] = '1';
+ buf[1] = ' ';
+
+ if(*onedram_sem == 0x1) {
+ READ_FROM_DPRAM((buf + 2), DPRAM_PHONE2PDA_FORMATTED_BUFFER_ADDRESS,
+ sizeof (buf) - 3);
+ }
+ }
+
+ printk(KERN_ERR "[PHONE ERROR] ->> %s\n", buf);
+
+ local_irq_save(flags);
+ memcpy(dpram_err_buf, buf, DPRAM_ERR_MSG_LEN);
+ dpram_err_len = 64;
+ local_irq_restore(flags);
+
+ wake_up_interruptible(&dpram_err_wait_q);
+ kill_fasync(&dpram_err_async_q, SIGIO, POLL_IN);
+
+#endif /* _ENABLE_ERROR_DEVICE */
+
+}
+
+static void cmd_phone_start_handler(void)
+{
+
+
+ printk(KERN_ERR "[OneDRAM] Received 0xc8 from MailboxAB (Phone Boot OK).\n");
+ if(!phone_sync) {
+ dpram_init_and_report();
+ }
+}
+
+static void cmd_req_time_sync_handler(void)
+{
+ /* TODO: add your codes here.. */
+}
+
+static void cmd_phone_deep_sleep_handler(void)
+{
+ /* TODO: add your codes here.. */
+}
+
+static void cmd_nv_rebuilding_handler(void)
+{
+ /* TODO: add your codes here.. */
+}
+
+static void cmd_emer_down_handler(void)
+{
+ /* TODO: add your codes here.. */
+}
+
+#if 0
+static void cmd_smp_req_handler(void)
+{
+ const u16 cmd = INT_COMMAND(INT_MASK_CMD_SMP_REP);
+
+
+ if(return_onedram_semaphore(__func__)) {
+ *onedram_mailboxBA = cmd;
+#ifdef PRINT_SEND_IRQ
+ printk(KERN_ERR "=====> send IRQ: %x\n", cmd);
+#endif
+ }
+}
+#endif
+
+static void cmd_smp_rep_handler(void)
+{
+ /* TODO: add your codes here.. */
+ unreceived_semaphore = 0;
+}
+
+static void semaphore_control_handler(unsigned long data)
+{
+ const u16 cmd = INT_COMMAND(INT_MASK_CMD_SMP_REP);
+
+
+ if(return_onedram_semaphore(__func__)) {
+ *onedram_mailboxBA = cmd;
+#ifdef PRINT_SEND_IRQ
+ printk(KERN_ERR "=====> send IRQ: %x\n", cmd);
+#endif
+ }
+}
+
+
+static void command_handler(u16 cmd)
+{
+ switch (cmd) {
+ case INT_MASK_CMD_REQ_ACTIVE:
+ cmd_req_active_handler();
+ break;
+
+ case INT_MASK_CMD_ERR_DISPLAY:
+ cmd_error_display_handler();
+ break;
+
+ case INT_MASK_CMD_PHONE_START:
+ cmd_phone_start_handler();
+ break;
+
+ case INT_MASK_CMD_REQ_TIME_SYNC:
+ cmd_req_time_sync_handler();
+ break;
+
+ case INT_MASK_CMD_PHONE_DEEP_SLEEP:
+ cmd_phone_deep_sleep_handler();
+ break;
+
+ case INT_MASK_CMD_NV_REBUILDING:
+ cmd_nv_rebuilding_handler();
+ break;
+
+ case INT_MASK_CMD_EMER_DOWN:
+ cmd_emer_down_handler();
+ break;
+
+ case INT_MASK_CMD_SMP_REQ:
+ tasklet_schedule(&semaphore_control_tasklet);
+// cmd_smp_req_handler();
+ break;
+
+ case INT_MASK_CMD_SMP_REP:
+ cmd_smp_rep_handler();
+ break;
+
+ default:
+ dprintk(KERN_ERR "Unknown command.. %x\n", cmd);
+ }
+}
+
+static void non_command_handler(u16 non_cmd)
+{
+ u16 head, tail;
+
+ /* @LDK@ formatted check. */
+
+
+ if(!(*onedram_sem)) {
+// printk(KERN_ERR "[OneDRAM] %s failed! no sem. cmd: %x\n", __func__, non_cmd);
+ return;
+ }
+
+ READ_FROM_DPRAM_VERIFY(&head, DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS, sizeof(head));
+ READ_FROM_DPRAM_VERIFY(&tail, DPRAM_PHONE2PDA_FORMATTED_TAIL_ADDRESS, sizeof(tail));
+
+ if (head != tail) {
+ non_cmd |= INT_MASK_SEND_F;
+ }else {
+ if(non_cmd & INT_MASK_REQ_ACK_F)
+ printk(KERN_ERR "=====> FMT DATA EMPTY & REQ_ACK_F\n");
+ }
+
+ /* @LDK@ raw check. */
+ READ_FROM_DPRAM_VERIFY(&head, DPRAM_PHONE2PDA_RAW_HEAD_ADDRESS, sizeof(head));
+ READ_FROM_DPRAM_VERIFY(&tail, DPRAM_PHONE2PDA_RAW_TAIL_ADDRESS, sizeof(tail));
+
+ if (head != tail) {
+ non_cmd |= INT_MASK_SEND_R;
+ }else {
+ if(non_cmd & INT_MASK_REQ_ACK_R)
+ printk(KERN_ERR "=====> RAW DATA EMPTY & REQ_ACK_R\n");
+ }
+
+ /* @LDK@ +++ scheduling.. +++ */
+ if (non_cmd & INT_MASK_SEND_F) {
+ dpram_tasklet_data[FORMATTED_INDEX].device = &dpram_table[FORMATTED_INDEX];
+ dpram_tasklet_data[FORMATTED_INDEX].non_cmd = non_cmd;
+ fmt_send_tasklet.data = (unsigned long)&dpram_tasklet_data[FORMATTED_INDEX];
+ tasklet_schedule(&fmt_send_tasklet);
+ }
+ if (non_cmd & INT_MASK_SEND_R) {
+ dpram_tasklet_data[RAW_INDEX].device = &dpram_table[RAW_INDEX];
+ dpram_tasklet_data[RAW_INDEX].non_cmd = non_cmd;
+ raw_send_tasklet.data = (unsigned long)&dpram_tasklet_data[RAW_INDEX];
+ /* @LDK@ raw buffer op. -> soft irq level. */
+ tasklet_hi_schedule(&raw_send_tasklet);
+ }
+
+ if (non_cmd & INT_MASK_RES_ACK_F) {
+ tasklet_schedule(&fmt_res_ack_tasklet);
+ }
+
+ if (non_cmd & INT_MASK_RES_ACK_R) {
+ tasklet_hi_schedule(&raw_res_ack_tasklet);
+ }
+
+}
+
+static inline
+void check_int_pin_level(void)
+{
+ u16 mask = 0, cnt = 0;
+
+ while (cnt++ < 3) {
+ mask = *onedram_mailboxAB;
+ if (gpio_get_value(GPIO_ONEDRAM_INT_N))
+ break;
+ }
+}
+
+/* @LDK@ interrupt handlers. */
+static irqreturn_t dpram_irq_handler(int irq, void *dev_id)
+{
+ u16 irq_mask = 0;
+#ifdef PRINT_HEAD_TAIL
+ u16 fih, fit, foh, fot;
+ u16 rih, rit, roh, rot;
+#endif
+
+ irq_mask = *onedram_mailboxAB;
+// check_int_pin_level();
+
+#ifdef PRINT_RECV_IRQ
+ printk(KERN_ERR "=====> received IRQ: %x\n", irq_mask);
+#endif
+
+#ifdef PRINT_HEAD_TAIL
+ if(*onedram_sem) {
+ READ_FROM_DPRAM_VERIFY(&fih, DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS, sizeof(fih));
+ READ_FROM_DPRAM_VERIFY(&fit, DPRAM_PHONE2PDA_FORMATTED_TAIL_ADDRESS, sizeof(fit));
+ READ_FROM_DPRAM_VERIFY(&foh, DPRAM_PDA2PHONE_FORMATTED_HEAD_ADDRESS, sizeof(foh));
+ READ_FROM_DPRAM_VERIFY(&fot, DPRAM_PDA2PHONE_FORMATTED_TAIL_ADDRESS, sizeof(fot));
+ READ_FROM_DPRAM_VERIFY(&rih, DPRAM_PHONE2PDA_RAW_HEAD_ADDRESS, sizeof(rih));
+ READ_FROM_DPRAM_VERIFY(&rit, DPRAM_PHONE2PDA_RAW_TAIL_ADDRESS, sizeof(rit));
+ READ_FROM_DPRAM_VERIFY(&roh, DPRAM_PDA2PHONE_RAW_HEAD_ADDRESS, sizeof(roh));
+ READ_FROM_DPRAM_VERIFY(&rot, DPRAM_PDA2PHONE_RAW_TAIL_ADDRESS, sizeof(rot));
+
+ printk(KERN_ERR "\n fmt_in H:%4d, T:%4d, M:%4d\n fmt_out H:%4d, T:%4d, M:%4d\n raw_in H:%4d, T:%4d, M:%4d\n raw out H:%4d, T:%4d, M:%4d\n", fih, fit, DPRAM_PHONE2PDA_FORMATTED_BUFFER_SIZE, foh, fot,DPRAM_PDA2PHONE_FORMATTED_BUFFER_SIZE, rih, rit, DPRAM_PHONE2PDA_RAW_BUFFER_SIZE, roh, rot, DPRAM_PDA2PHONE_RAW_BUFFER_SIZE);
+ }
+#endif
+
+ /* valid bit verification. @LDK@ */
+ if (!(irq_mask & INT_MASK_VALID)) {
+ printk(KERN_ERR "Invalid interrupt mask: 0x%04x\n", irq_mask);
+ return IRQ_NONE;
+ }
+
+ /* command or non-command? @LDK@ */
+ if (irq_mask & INT_MASK_COMMAND) {
+ irq_mask &= ~(INT_MASK_VALID | INT_MASK_COMMAND);
+ command_handler(irq_mask);
+ }
+ else {
+ irq_mask &= ~INT_MASK_VALID;
+ non_command_handler(irq_mask);
+ }
+
+ return IRQ_HANDLED;
+}
+
+#define DPRAM_USES_DELAYED_PHONE_ACTIVE_IRQ
+
+#ifdef DPRAM_USES_DELAYED_PHONE_ACTIVE_IRQ
+static void phone_active_delayed_work_handler(struct work_struct *ignored);
+
+static DECLARE_DELAYED_WORK(phone_active_delayed_work, phone_active_delayed_work_handler);
+
+static void phone_active_delayed_work_handler(struct work_struct *ignored)
+{
+ /* ignore momentary drop in the phone_active gpio */
+ if(gpio_get_value(GPIO_PHONE_ACTIVE) != 0) {
+ printk("[%s] Phone active is now high! Ignored...", __func__);
+ return;
+ }
+
+ printk("Phone active is still low!!!" );
+
+ if(phone_sync)
+ request_phone_reset();
+}
+#endif /* DPRAM_USES_DELAYED_PHONE_ACTIVE_IRQ */
+
+static irqreturn_t phone_active_irq_handler(int irq, void *dev_id)
+{
+ printk(KERN_ERR "[OneDRAM] PHONE_ACTIVE level: %s, sem: %d, phone_sync: %d\n",
+ gpio_get_value(GPIO_PHONE_ACTIVE)?"HIGH":"LOW ", *onedram_sem, phone_sync);
+
+#ifdef DPRAM_USES_DELAYED_PHONE_ACTIVE_IRQ
+ if(gpio_get_value(GPIO_PHONE_ACTIVE) == 0) {
+ schedule_delayed_work(&phone_active_delayed_work, 100);
+ }
+#endif
+
+#ifdef _ENABLE_ERROR_DEVICE
+ if((phone_sync) && (!gpio_get_value(GPIO_PHONE_ACTIVE)))
+ request_phone_reset();
+#endif
+
+ return IRQ_HANDLED;
+}
+
+/* basic functions. */
+#ifdef _ENABLE_ERROR_DEVICE
+static struct file_operations dpram_err_ops = {
+ .owner = THIS_MODULE,
+ .read = dpram_err_read,
+ .fasync = dpram_err_fasync,
+ .poll = dpram_err_poll,
+ .llseek = no_llseek,
+
+ /* TODO: add more operations */
+};
+#endif /* _ENABLE_ERROR_DEVICE */
+
+static struct tty_operations dpram_tty_ops = {
+ .open = dpram_tty_open,
+ .close = dpram_tty_close,
+ .write = dpram_tty_write,
+ .write_room = dpram_tty_write_room,
+ .ioctl = dpram_tty_ioctl,
+ .chars_in_buffer = dpram_tty_chars_in_buffer,
+
+ /* TODO: add more operations */
+};
+
+#ifdef _ENABLE_ERROR_DEVICE
+
+static void unregister_dpram_err_device(void)
+{
+ unregister_chrdev(DRIVER_MAJOR_NUM, DPRAM_ERR_DEVICE);
+ class_destroy(dpram_class);
+}
+
+static int register_dpram_err_device(void)
+{
+ /* @LDK@ 1 = formatted, 2 = raw, so error device is '0' */
+ struct device *dpram_err_dev_t;
+ int ret = register_chrdev(DRIVER_MAJOR_NUM, DPRAM_ERR_DEVICE, &dpram_err_ops);
+
+ if ( ret < 0 )
+ {
+ return ret;
+ }
+
+ dpram_class = class_create(THIS_MODULE, "err");
+
+ if (IS_ERR(dpram_class))
+ {
+ unregister_dpram_err_device();
+ return -EFAULT;
+ }
+
+ dpram_err_dev_t = device_create(dpram_class, NULL,
+ MKDEV(DRIVER_MAJOR_NUM, 0), NULL, DPRAM_ERR_DEVICE);
+
+ if (IS_ERR(dpram_err_dev_t))
+ {
+ unregister_dpram_err_device();
+ return -EFAULT;
+ }
+
+ return 0;
+}
+#endif /* _ENABLE_ERROR_DEVICE */
+
+static int register_dpram_driver(void)
+{
+ int retval = 0;
+
+ /* @LDK@ allocate tty driver */
+ dpram_tty_driver = alloc_tty_driver(MAX_INDEX);
+
+ if (!dpram_tty_driver) {
+ return -ENOMEM;
+ }
+
+ /* @LDK@ initialize tty driver */
+ dpram_tty_driver->owner = THIS_MODULE;
+ dpram_tty_driver->magic = TTY_DRIVER_MAGIC;
+ dpram_tty_driver->driver_name = DRIVER_NAME;
+ dpram_tty_driver->name = "dpram";
+ dpram_tty_driver->major = DRIVER_MAJOR_NUM;
+ dpram_tty_driver->minor_start = 1;
+ dpram_tty_driver->num = 2;
+ dpram_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ dpram_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ dpram_tty_driver->flags = TTY_DRIVER_REAL_RAW;
+ dpram_tty_driver->init_termios = tty_std_termios;
+ dpram_tty_driver->init_termios.c_cflag =
+ (B115200 | CS8 | CREAD | CLOCAL | HUPCL);
+
+ tty_set_operations(dpram_tty_driver, &dpram_tty_ops);
+
+ dpram_tty_driver->ttys = dpram_tty;
+ dpram_tty_driver->termios = dpram_termios;
+ dpram_tty_driver->termios_locked = dpram_termios_locked;
+
+ /* @LDK@ register tty driver */
+ retval = tty_register_driver(dpram_tty_driver);
+
+ if (retval) {
+ dprintk(KERN_ERR "tty_register_driver error\n");
+ put_tty_driver(dpram_tty_driver);
+ return retval;
+ }
+
+ return 0;
+}
+
+static void unregister_dpram_driver(void)
+{
+ tty_unregister_driver(dpram_tty_driver);
+}
+
+static int multipdp_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return -EINVAL;
+}
+
+static struct file_operations multipdp_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = multipdp_ioctl,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice multipdp_dev = {
+ .minor = 132, //MISC_DYNAMIC_MINOR,
+ .name = APP_DEVNAME,
+ .fops = &multipdp_fops,
+};
+
+static inline struct pdp_info * pdp_get_serdev(const char *name)
+{
+ int slot;
+ struct pdp_info *dev;
+
+ for (slot = 0; slot < MAX_PDP_CONTEXT; slot++) {
+ dev = pdp_table[slot];
+ if (dev && dev->type == DEV_TYPE_SERIAL &&
+ strcmp(name, dev->vs_dev.tty_name) == 0) {
+ return dev;
+ }
+ }
+ return NULL;
+}
+
+
+static inline struct pdp_info * pdp_remove_dev(u8 id)
+{
+ int slot;
+ struct pdp_info *dev;
+
+ for (slot = 0; slot < MAX_PDP_CONTEXT; slot++) {
+ if (pdp_table[slot] && pdp_table[slot]->id == id) {
+ dev = pdp_table[slot];
+ pdp_table[slot] = NULL;
+ return dev;
+ }
+ }
+ return NULL;
+}
+
+
+static int vs_open(struct tty_struct *tty, struct file *filp)
+{
+ struct pdp_info *dev;
+
+ dev = pdp_get_serdev(tty->driver->name); // 2.6 kernel porting
+
+ if (dev == NULL) {
+ return -ENODEV;
+ }
+
+ tty->driver_data = (void *)dev;
+ tty->low_latency = 1;
+ dev->vs_dev.tty = tty;
+ dev->vs_dev.refcount++;
+ printk(KERN_ERR "[%s] %s, refcount: %d \n", __func__, tty->driver->name, dev->vs_dev.refcount);
+
+ return 0;
+}
+
+static void vs_close(struct tty_struct *tty, struct file *filp)
+{
+ struct pdp_info *dev;
+
+ dev = pdp_get_serdev(tty->driver->name);
+
+ if (!dev )
+ return;
+ dev->vs_dev.refcount--;
+ printk(KERN_ERR "[%s] %s, refcount: %d \n", __func__, tty->driver->name, dev->vs_dev.refcount);
+
+ // TODO..
+
+ return;
+}
+
+static int pdp_mux(struct pdp_info *dev, const void *data, size_t len )
+{
+ int ret;
+ size_t nbytes;
+ u8 *tx_buf;
+ struct pdp_hdr *hdr;
+ const u8 *buf;
+
+ tx_buf = dev->tx_buf;
+ hdr = (struct pdp_hdr *)(tx_buf + 1);
+ buf = data;
+
+ hdr->id = dev->id;
+ hdr->control = 0;
+
+ while (len) {
+ if (len > MAX_PDP_DATA_LEN) {
+ nbytes = MAX_PDP_DATA_LEN;
+ } else {
+ nbytes = len;
+ }
+ hdr->len = nbytes + sizeof(struct pdp_hdr);
+
+ tx_buf[0] = 0x7f;
+
+ memcpy(tx_buf + 1 + sizeof(struct pdp_hdr), buf, nbytes);
+
+ tx_buf[1 + hdr->len] = 0x7e;
+
+// printk(KERN_ERR "hdr->id: %d, hdr->len: %d\n", hdr->id, hdr->len);
+
+ ret = dpram_write(&dpram_table[RAW_INDEX], tx_buf, hdr->len + 2);
+
+ if (ret < 0) {
+ printk(KERN_ERR "write_to_dpram() failed: %d\n", ret);
+ return ret;
+ }
+ buf += nbytes;
+ len -= nbytes;
+ }
+ return 0;
+}
+
+
+static int vs_write(struct tty_struct *tty,
+ const unsigned char *buf, int count)
+{
+ int ret;
+ struct pdp_info *dev = (struct pdp_info *)tty->driver_data;
+
+ ret = pdp_mux(dev, buf, count);
+
+ if (ret == 0) {
+ ret = count;
+ }
+
+ return ret;
+}
+
+static int vs_write_room(struct tty_struct *tty)
+{
+// return TTY_FLIPBUF_SIZE;
+ return 8192*2;
+}
+
+static int vs_chars_in_buffer(struct tty_struct *tty)
+{
+ return 0;
+}
+
+static int vs_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+
+static struct tty_operations multipdp_tty_ops = {
+ .open = vs_open,
+ .close = vs_close,
+ .write = vs_write,
+ .write_room = vs_write_room,
+ .ioctl = vs_ioctl,
+ .chars_in_buffer = vs_chars_in_buffer,
+
+ /* TODO: add more operations */
+};
+
+static struct tty_driver* get_tty_driver_by_id(struct pdp_info *dev)
+{
+ int index = 0;
+
+ switch (dev->id) {
+ case 1: index = 0; break;
+ case 7: index = 1; break;
+ case 9: index = 2; break;
+ case 27: index = 3; break;
+ default: index = 0;
+ }
+
+ return &dev->vs_dev.tty_driver[index];
+}
+
+static int get_minor_start_index(int id)
+{
+ int start = 0;
+
+ switch (id) {
+ case 1: start = 0; break;
+ case 7: start = 1; break;
+ case 9: start = 2; break;
+ case 27: start = 3; break;
+ default: start = 0;
+ }
+
+ return start;
+}
+
+
+static int vs_add_dev(struct pdp_info *dev)
+{
+ struct tty_driver *tty_driver;
+
+ tty_driver = get_tty_driver_by_id(dev);
+
+ if (!tty_driver) {
+ printk(KERN_ERR "tty driver is NULL!\n");
+ return -1;
+ }
+
+ kref_init(&tty_driver->kref);
+
+ tty_driver->magic = TTY_DRIVER_MAGIC;
+ tty_driver->driver_name = "multipdp";
+ tty_driver->name = dev->vs_dev.tty_name;
+ tty_driver->major = CSD_MAJOR_NUM;
+ tty_driver->minor_start = get_minor_start_index(dev->id);
+ tty_driver->num = 1;
+ tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ tty_driver->flags = TTY_DRIVER_REAL_RAW;
+// kref_set(&tty_driver->kref, dev->vs_dev.refcount);
+ tty_driver->ttys = dev->vs_dev.tty_table; // 2.6 kernel porting
+ tty_driver->termios = dev->vs_dev.termios;
+ tty_driver->termios_locked = dev->vs_dev.termios_locked;
+
+ tty_set_operations(tty_driver, &multipdp_tty_ops);
+ return tty_register_driver(tty_driver);
+}
+
+static void vs_del_dev(struct pdp_info *dev)
+{
+ struct tty_driver *tty_driver = NULL;
+
+ tty_driver = get_tty_driver_by_id(dev);
+ tty_unregister_driver(tty_driver);
+}
+
+static inline void check_pdp_table(char * func, int line)
+{
+ int slot;
+ for (slot = 0; slot < MAX_PDP_CONTEXT; slot++) {
+ if(pdp_table[slot])
+ printk(KERN_ERR "----->[%s,%d] addr: %x slot: %d id: %d, name: %s\n", func, line, pdp_table[slot], slot, pdp_table[slot]->id, pdp_table[slot]->vs_dev.tty_name);
+ }
+
+
+}
+
+static inline struct pdp_info * pdp_get_dev(u8 id)
+{
+ int slot;
+
+
+ for (slot = 0; slot < MAX_PDP_CONTEXT; slot++) {
+ if (pdp_table[slot] && pdp_table[slot]->id == id) {
+ return pdp_table[slot];
+ }
+ }
+ return NULL;
+}
+
+static inline int pdp_add_dev(struct pdp_info *dev)
+{
+ int slot;
+
+ if (pdp_get_dev(dev->id)) {
+ return -EBUSY;
+ }
+
+ for (slot = 0; slot < MAX_PDP_CONTEXT; slot++) {
+ if (pdp_table[slot] == NULL) {
+ pdp_table[slot] = dev;
+ return slot;
+ }
+ }
+ return -ENOSPC;
+}
+
+
+static int pdp_activate(pdp_arg_t *pdp_arg, unsigned type, unsigned flags)
+{
+ int ret;
+ struct pdp_info *dev;
+
+ printk(KERN_ERR "%s, id: %d\n", __func__, pdp_arg->id);
+
+ dev = kmalloc(sizeof(struct pdp_info) + MAX_PDP_PACKET_LEN, GFP_KERNEL);
+ if (dev == NULL) {
+ printk(KERN_ERR "out of memory\n");
+ return -ENOMEM;
+ }
+ memset(dev, 0, sizeof(struct pdp_info));
+
+ dev->id = pdp_arg->id;
+
+ dev->type = type;
+ dev->flags = flags;
+ dev->tx_buf = (u8 *)(dev + 1);
+
+ if (type == DEV_TYPE_SERIAL) {
+ init_MUTEX(&dev->vs_dev.write_lock);
+ strcpy(dev->vs_dev.tty_name, pdp_arg->ifname);
+
+ ret = vs_add_dev(dev);
+ if (ret < 0) {
+ kfree(dev);
+ return ret;
+ }
+
+ mutex_lock(&pdp_lock);
+ ret = pdp_add_dev(dev);
+ if (ret < 0) {
+ printk(KERN_ERR "pdp_add_dev() failed\n");
+ mutex_unlock(&pdp_lock);
+ vs_del_dev(dev);
+ kfree(dev);
+ return ret;
+ }
+ mutex_unlock(&pdp_lock);
+
+ {
+ struct tty_driver * tty_driver = get_tty_driver_by_id(dev);
+
+ printk(KERN_ERR "%s(id: %u) serial device is created.\n",
+ tty_driver->name, dev->id);
+ }
+ }
+
+ return 0;
+}
+
+static int multipdp_init(void)
+{
+ int i;
+
+ pdp_arg_t pdp_args[NUM_PDP_CONTEXT] = {
+ { .id = 1, .ifname = "ttyCSD" },
+ { .id = 7, .ifname = "ttyCDMA" },
+ { .id = 9, .ifname = "ttyTRFB" },
+ { .id = 27, .ifname = "ttyCIQ" },
+ };
+
+
+ /* create serial device for Circuit Switched Data */
+ for (i = 0; i < NUM_PDP_CONTEXT; i++) {
+ if (pdp_activate(&pdp_args[i], DEV_TYPE_SERIAL, DEV_FLAG_STICKY) < 0) {
+ printk(KERN_ERR "failed to create a serial device for %s\n", pdp_args[i].ifname);
+ }
+ }
+
+ return 0;
+}
+
+
+static void init_devices(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_INDEX; i++) {
+ init_MUTEX(&dpram_table[i].serial.sem);
+
+ dpram_table[i].serial.open_count = 0;
+ dpram_table[i].serial.tty = NULL;
+ }
+}
+
+static void init_hw_setting(void)
+{
+// u32 mask;
+
+ /* initial pin settings - dpram driver control */
+ s3c_gpio_cfgpin(GPIO_PHONE_ACTIVE, S3C_GPIO_SFN(GPIO_PHONE_ACTIVE_AF));
+ s3c_gpio_setpull(GPIO_PHONE_ACTIVE, S3C_GPIO_PULL_NONE);
+ set_irq_type(IRQ_PHONE_ACTIVE, IRQ_TYPE_EDGE_BOTH);
+
+ s3c_gpio_cfgpin(GPIO_ONEDRAM_INT_N, S3C_GPIO_SFN(GPIO_ONEDRAM_INT_N_AF));
+ s3c_gpio_setpull(GPIO_ONEDRAM_INT_N, S3C_GPIO_PULL_NONE);
+ set_irq_type(IRQ_ONEDRAM_INT_N, IRQ_TYPE_EDGE_FALLING);
+
+ if (gpio_is_valid(GPIO_PHONE_ON)) {
+ if (gpio_request(GPIO_PHONE_ON, "dpram/GPIO_PHONE_ON"))
+ printk(KERN_ERR "Filed to request GPIO_PHONE_ON!\n");
+ gpio_direction_output(GPIO_PHONE_ON, GPIO_LEVEL_LOW);
+ }
+ s3c_gpio_setpull(GPIO_PHONE_ON, S3C_GPIO_PULL_NONE);
+ gpio_set_value(GPIO_PHONE_ON, GPIO_LEVEL_LOW);
+
+ if (gpio_is_valid(GPIO_PHONE_RST_N)) {
+ if (gpio_request(GPIO_PHONE_RST_N, "dpram/GPIO_PHONE_RST_N"))
+ printk(KERN_ERR "Filed to request GPIO_PHONE_RST_N!\n");
+ gpio_direction_output(GPIO_PHONE_RST_N, GPIO_LEVEL_HIGH);
+ }
+ s3c_gpio_setpull(GPIO_PHONE_RST_N, S3C_GPIO_PULL_NONE);
+
+ if (gpio_is_valid(GPIO_PDA_ACTIVE)) {
+ if (gpio_request(GPIO_PDA_ACTIVE, "dpram/GPIO_PDA_ACTIVE"))
+ printk(KERN_ERR "Filed to request GPIO_PDA_ACTIVE!\n");
+ gpio_direction_output(GPIO_PDA_ACTIVE, GPIO_LEVEL_HIGH);
+ }
+ s3c_gpio_setpull(GPIO_PDA_ACTIVE, S3C_GPIO_PULL_NONE);
+
+}
+
+static void kill_tasklets(void)
+{
+ tasklet_kill(&fmt_res_ack_tasklet);
+ tasklet_kill(&raw_res_ack_tasklet);
+
+ tasklet_kill(&fmt_send_tasklet);
+ tasklet_kill(&raw_send_tasklet);
+}
+
+static int register_interrupt_handler(void)
+{
+
+ unsigned int dpram_irq, phone_active_irq;
+ int retval = 0;
+
+ dpram_irq = IRQ_ONEDRAM_INT_N;
+ phone_active_irq = IRQ_PHONE_ACTIVE;
+
+ /* @LDK@ interrupt area read - pin level will be driven high. */
+// dpram_clear();
+
+ /* @LDK@ dpram interrupt */
+ retval = request_irq(dpram_irq, dpram_irq_handler, IRQF_DISABLED, "dpram irq", NULL);
+
+ if (retval) {
+ dprintk(KERN_ERR "DPRAM interrupt handler failed.\n");
+ unregister_dpram_driver();
+ return -1;
+ }
+
+ /* @LDK@ phone active interrupt */
+ retval = request_irq(phone_active_irq, phone_active_irq_handler, IRQF_DISABLED, "Phone Active", NULL);
+
+ if (retval) {
+ dprintk(KERN_ERR "Phone active interrupt handler failed.\n");
+ free_irq(phone_active_irq, NULL);
+ unregister_dpram_driver();
+ return -1;
+ }
+
+ enable_irq_wake(dpram_irq);
+ enable_irq_wake(phone_active_irq);
+
+ return 0;
+}
+
+static void check_miss_interrupt(void)
+{
+ unsigned long flags;
+
+ if (gpio_get_value(GPIO_PHONE_ACTIVE) &&
+ (!gpio_get_value(GPIO_ONEDRAM_INT_N))) {
+ dprintk(KERN_ERR "there is a missed interrupt. try to read it!\n");
+
+ if (!(*onedram_sem)) {
+ printk(KERN_ERR "[OneDRAM] (%s) semaphore: %d\n", __func__, *onedram_sem);
+ onedram_get_semaphore(__func__);
+ }
+
+ local_irq_save(flags);
+ dpram_irq_handler(IRQ_ONEDRAM_INT_N, NULL);
+ local_irq_restore(flags);
+ }
+}
+
+static int dpram_suspend(struct platform_device *dev, pm_message_t state)
+{
+ gpio_set_value(GPIO_PDA_ACTIVE, GPIO_LEVEL_LOW);
+ if(requested_semaphore)
+ printk(KERN_ERR "=====> %s requested semaphore: %d\n", __func__, requested_semaphore);
+ return 0;
+}
+
+static int dpram_resume(struct platform_device *dev)
+{
+ gpio_set_value(GPIO_PDA_ACTIVE, GPIO_LEVEL_HIGH);
+ if(requested_semaphore)
+ printk(KERN_ERR "=====> %s requested semaphore: %d\n", __func__, requested_semaphore);
+ check_miss_interrupt();
+ return 0;
+}
+
+static int dpram_shutdown(struct platform_Device *dev)
+{
+ int ret = 0;
+ printk("\ndpram_shutdown !!!!!!!!!!!!!!!!!!!!!\n");
+ //ret = del_timer(&request_semaphore_timer);
+ printk("\ndpram_shutdown ret : %d\n", ret);
+
+ unregister_dpram_driver();
+ unregister_dpram_err_device();
+
+ free_irq(IRQ_ONEDRAM_INT_N, NULL);
+ free_irq(IRQ_PHONE_ACTIVE, NULL);
+
+ kill_tasklets();
+ return 0;
+}
+
+static int __devinit dpram_probe(struct platform_device *dev)
+{
+ int retval;
+
+ /* @LDK@ register dpram (tty) driver */
+ retval = register_dpram_driver();
+ if (retval) {
+ dprintk(KERN_ERR "Failed to register dpram (tty) driver.\n");
+ return -1;
+ }
+
+#ifdef _ENABLE_ERROR_DEVICE
+ /* @LDK@ register dpram error device */
+ retval = register_dpram_err_device();
+ if (retval) {
+ dprintk(KERN_ERR "Failed to register dpram error device.\n");
+
+ unregister_dpram_driver();
+ return -1;
+ }
+
+ memset((void *)dpram_err_buf, '\0', sizeof dpram_err_buf);
+#endif /* _ENABLE_ERROR_DEVICE */
+
+ /* create app. interface device */
+ retval = misc_register(&multipdp_dev);
+ if (retval < 0) {
+ printk(KERN_ERR "misc_register() failed\n");
+ return -1;
+ }
+ multipdp_init();
+
+ /* @LDK@ H/W setting */
+ init_hw_setting();
+
+ dpram_shared_bank_remap();
+
+ /* @LDK@ initialize device table */
+ init_devices();
+
+ /* @LDK@ register interrupt handler */
+ if ((retval = register_interrupt_handler()) < 0) {
+ return -1;
+ }
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry(DRIVER_PROC_ENTRY, 0, 0, dpram_read_proc, NULL);
+#endif /* CONFIG_PROC_FS */
+
+ /* @LDK@ check out missing interrupt from the phone */
+ //check_miss_interrupt();
+
+ return 0;
+}
+
+static int __devexit dpram_remove(struct platform_device *dev)
+{
+ /* @LDK@ unregister dpram (tty) driver */
+ unregister_dpram_driver();
+
+ /* @LDK@ unregister dpram error device */
+#ifdef _ENABLE_ERROR_DEVICE
+ unregister_dpram_err_device();
+#endif
+
+ /* remove app. interface device */
+ misc_deregister(&multipdp_dev);
+
+ /* @LDK@ unregister irq handler */
+ free_irq(IRQ_ONEDRAM_INT_N, NULL);
+ free_irq(IRQ_PHONE_ACTIVE, NULL);
+
+ kill_tasklets();
+
+ return 0;
+}
+
+static struct platform_driver platform_dpram_driver = {
+ .probe = dpram_probe,
+ .remove = __devexit_p(dpram_remove),
+ .suspend = dpram_suspend,
+ .resume = dpram_resume,
+ .shutdown = dpram_shutdown,
+ .driver = {
+ .name = "dpram-device",
+ },
+};
+
+/* init & cleanup. */
+static int __init dpram_init(void)
+{
+ return platform_driver_register(&platform_dpram_driver);
+}
+
+static void __exit dpram_exit(void)
+{
+ platform_driver_unregister(&platform_dpram_driver);
+}
+
+module_init(dpram_init);
+module_exit(dpram_exit);
+
+MODULE_AUTHOR("SAMSUNG ELECTRONICS CO., LTD");
+MODULE_DESCRIPTION("Onedram Device Driver.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/samsung_modemctl/dpram/dpram.h b/drivers/misc/samsung_modemctl/dpram/dpram.h
new file mode 100644
index 0000000..1987c8f
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/dpram/dpram.h
@@ -0,0 +1,214 @@
+/****************************************************************************
+
+**
+
+** COPYRIGHT(C) : Samsung Electronics Co.Ltd, 2006-2010 ALL RIGHTS RESERVED
+
+**
+
+** AUTHOR : Kim, Geun-Young <geunyoung.kim@samsung.com> @LDK@
+
+** @LDK@
+
+****************************************************************************/
+
+#ifndef __DPRAM_H__
+#define __DPRAM_H__
+
+/* 32KB Size */
+#define DPRAM_SIZE 0x8000
+
+/* Memory Address */
+#define DPRAM_START_ADDRESS 0x0000
+#define DPRAM_MAGIC_CODE_ADDRESS (DPRAM_START_ADDRESS)
+#define DPRAM_ACCESS_ENABLE_ADDRESS (DPRAM_START_ADDRESS + 0x0002)
+
+#define DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS (DPRAM_START_ADDRESS + 0x0004)
+#define DPRAM_PDA2PHONE_FORMATTED_HEAD_ADDRESS (DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS)
+#define DPRAM_PDA2PHONE_FORMATTED_TAIL_ADDRESS (DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS + 0x0002)
+#define DPRAM_PDA2PHONE_FORMATTED_BUFFER_ADDRESS (DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS + 0x0004)
+//#define DPRAM_PDA2PHONE_FORMATTED_BUFFER_SIZE 8186 // 0x1ffc
+#define DPRAM_PDA2PHONE_FORMATTED_BUFFER_SIZE 4092
+
+#define DPRAM_PDA2PHONE_RAW_START_ADDRESS (DPRAM_PDA2PHONE_FORMATTED_START_ADDRESS + (DPRAM_PDA2PHONE_FORMATTED_BUFFER_SIZE+4))
+#define DPRAM_PDA2PHONE_RAW_HEAD_ADDRESS (DPRAM_PDA2PHONE_RAW_START_ADDRESS)
+#define DPRAM_PDA2PHONE_RAW_TAIL_ADDRESS (DPRAM_PDA2PHONE_RAW_START_ADDRESS + 2)
+#define DPRAM_PDA2PHONE_RAW_BUFFER_ADDRESS (DPRAM_PDA2PHONE_RAW_START_ADDRESS + 4)
+//#define DPRAM_PDA2PHONE_RAW_BUFFER_SIZE 8186 // 0x1ff4
+#define DPRAM_PDA2PHONE_RAW_BUFFER_SIZE 12272
+
+#define DPRAM_PHONE2PDA_FORMATTED_START_ADDRESS (DPRAM_PDA2PHONE_RAW_START_ADDRESS + (DPRAM_PDA2PHONE_RAW_BUFFER_SIZE+4))
+#define DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS (DPRAM_PHONE2PDA_FORMATTED_START_ADDRESS)
+#define DPRAM_PHONE2PDA_FORMATTED_TAIL_ADDRESS (DPRAM_PHONE2PDA_FORMATTED_START_ADDRESS + 0x0002)
+#define DPRAM_PHONE2PDA_FORMATTED_BUFFER_ADDRESS (DPRAM_PHONE2PDA_FORMATTED_START_ADDRESS + 0x0004)
+//#define DPRAM_PHONE2PDA_FORMATTED_BUFFER_SIZE 8186 // 0x1ffc
+#define DPRAM_PHONE2PDA_FORMATTED_BUFFER_SIZE 4092
+
+
+#define DPRAM_PHONE2PDA_RAW_START_ADDRESS (DPRAM_PHONE2PDA_FORMATTED_START_ADDRESS + (DPRAM_PHONE2PDA_FORMATTED_BUFFER_SIZE+4))
+#define DPRAM_PHONE2PDA_RAW_HEAD_ADDRESS (DPRAM_PHONE2PDA_RAW_START_ADDRESS)
+#define DPRAM_PHONE2PDA_RAW_TAIL_ADDRESS (DPRAM_PHONE2PDA_RAW_START_ADDRESS + 0x0002)
+#define DPRAM_PHONE2PDA_RAW_BUFFER_ADDRESS (DPRAM_PHONE2PDA_RAW_START_ADDRESS + 0x0004)
+//#define DPRAM_PHONE2PDA_RAW_BUFFER_SIZE 8186 // 0x1ff4
+#define DPRAM_PHONE2PDA_RAW_BUFFER_SIZE 12272
+
+#if 0
+/* indicator area*/
+#define DPRAM_PDA2PHONE_INTERRUPT_ADDRESS (DPRAM_START_ADDRESS + 0x7FFC)
+#define DPRAM_PHONE2PDA_INTERRUPT_ADDRESS (DPRAM_START_ADDRESS + 0x7FFE)
+#endif
+
+#define DPRAM_INTERRUPT_PORT_SIZE 2
+#define DPRAM_START_ADDRESS_PHYS 0x30000000
+#define DPRAM_SHARED_BANK 0x5000000
+
+#define DPRAM_SHARED_BANK_SIZE 0x1000000
+#define MAX_MODEM_IMG_SIZE 0x1000000 //16 * 1024 * 1024
+#define MAX_DBL_IMG_SIZE 0x5000 //20 * 1024
+
+#define DPRAM_SFR 0xFFF800
+#define DPRAM_SMP DPRAM_SFR //semaphore
+
+
+#define DPRAM_MBX_AB DPRAM_SFR + 0x20 //mailbox a -> b
+#define DPRAM_MBX_BA DPRAM_SFR + 0x40 //mailbox b -> a
+//#define DPRAM_CHECK_AB DPRAM_SFR + 0xA0 //check mailbox a -> b read
+#define DPRAM_CHECK_BA DPRAM_SFR + 0xC0 //check mailbox b -> a read
+
+
+#define DPRAM_PDA2PHONE_INTERRUPT_ADDRESS DPRAM_MBX_BA
+#define DPRAM_PHONE2PDA_INTERRUPT_ADDRESS DPRAM_MBX_AB
+
+#define PARTITION_ID_MODEM_IMG 0x08
+//#define PARTITION_ID_MODEM_IMG 0x05
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * interrupt masks.
+ */
+#define INT_MASK_VALID 0x0080
+#define INT_MASK_COMMAND 0x0040
+#define INT_MASK_REQ_ACK_F 0x0020
+#define INT_MASK_REQ_ACK_R 0x0010
+#define INT_MASK_RES_ACK_F 0x0008
+#define INT_MASK_RES_ACK_R 0x0004
+#define INT_MASK_SEND_F 0x0002
+#define INT_MASK_SEND_R 0x0001
+
+#define INT_MASK_CMD_INIT_START 0x0001
+#define INT_MASK_CMD_INIT_END 0x0002
+#define INT_MASK_CMD_REQ_ACTIVE 0x0003
+#define INT_MASK_CMD_RES_ACTIVE 0x0004
+#define INT_MASK_CMD_REQ_TIME_SYNC 0x0005
+#define INT_MASK_CMD_PHONE_START 0x0008
+#define INT_MASK_CMD_ERR_DISPLAY 0x0009
+#define INT_MASK_CMD_PHONE_DEEP_SLEEP 0x000A
+#define INT_MASK_CMD_NV_REBUILDING 0x000B
+#define INT_MASK_CMD_EMER_DOWN 0x000C
+#define INT_MASK_CMD_SMP_REQ 0x000D
+#define INT_MASK_CMD_SMP_REP 0x000E
+
+#define INT_COMMAND(x) (INT_MASK_VALID | INT_MASK_COMMAND | x)
+#define INT_NON_COMMAND(x) (INT_MASK_VALID | x)
+
+#define FORMATTED_INDEX 0
+#define RAW_INDEX 1
+#define MAX_INDEX 2
+
+/* ioctl command definitions. */
+#define IOC_MZ_MAGIC ('o')
+#define DPRAM_PHONE_POWON _IO(IOC_MZ_MAGIC, 0xd0)
+#define DPRAM_PHONEIMG_LOAD _IO(IOC_MZ_MAGIC, 0xd1)
+#define DPRAM_NVDATA_LOAD _IO(IOC_MZ_MAGIC, 0xd2)
+#define DPRAM_PHONE_BOOTSTART _IO(IOC_MZ_MAGIC, 0xd3)
+
+struct _param_nv {
+ unsigned char *addr;
+ unsigned int size;
+};
+
+struct _param_em {
+ unsigned int offset;
+ unsigned char *addr;
+ unsigned int size;
+ int rw;
+};
+
+
+#if 1
+#define IOC_SEC_MAGIC (0xf0)
+#define DPRAM_PHONE_ON _IO(IOC_SEC_MAGIC, 0xc0)
+#define DPRAM_PHONE_OFF _IO(IOC_SEC_MAGIC, 0xc1)
+#define DPRAM_PHONE_GETSTATUS _IOR(IOC_SEC_MAGIC, 0xc2, unsigned int)
+//#define DPRAM_PHONE_MDUMP _IO(IOC_SEC_MAGIC, 0xc3)
+//#define DPRAM_PHONE_BATTERY _IO(IOC_SEC_MAGIC, 0xc4)
+#define DPRAM_PHONE_RESET _IO(IOC_SEC_MAGIC, 0xc5)
+#define DPRAM_PHONE_RAMDUMP_ON _IO(IOC_SEC_MAGIC, 0xc6)
+#define DPRAM_PHONE_RAMDUMP_OFF _IO(IOC_SEC_MAGIC, 0xc7)
+#define DPRAM_EXTRA_MEM_RW _IOWR(IOC_SEC_MAGIC, 0xc8, unsigned long)
+
+#else
+#define IOC_SEC_MAGIC (0xf0)
+#define DPRAM_PHONE_ON _IO(IOC_SEC_MAGIC, 0xc0)
+#define DPRAM_PHONE_GETSTATUS _IOR(IOC_MZ_MAGIC, 0xc1, unsigned int)
+//#define DPRAM_PHONE_OFF _IO(IOC_MZ_MAGIC, 0xd3)
+//#define DPRAM_PHONE_ON _IO(IOC_MZ_MAGIC, 0xd3)
+//#define DPRAM_PHONE_RESET _IO(IOC_MZ_MAGIC, 0xd5)
+#define DPRAM_MEM_RW _IOWR(IOC_MZ_MAGIC, 0xd6, unsigned long)
+#endif
+
+/*
+ * structure definitions.
+ */
+typedef struct dpram_serial {
+ /* pointer to the tty for this device */
+ struct tty_struct *tty;
+
+ /* number of times this port has been opened */
+ int open_count;
+
+ /* locks this structure */
+ struct semaphore sem;
+} dpram_serial_t;
+
+typedef struct dpram_device {
+ /* DPRAM memory addresses */
+ unsigned long in_head_addr;
+ unsigned long in_tail_addr;
+ unsigned long in_buff_addr;
+ unsigned long in_buff_size;
+
+ unsigned long out_head_addr;
+ unsigned long out_tail_addr;
+ unsigned long out_buff_addr;
+ unsigned long out_buff_size;
+
+ unsigned int in_head_saved;
+ unsigned int in_tail_saved;
+ unsigned int out_head_saved;
+ unsigned int out_tail_saved;
+
+ u_int16_t mask_req_ack;
+ u_int16_t mask_res_ack;
+ u_int16_t mask_send;
+
+ dpram_serial_t serial;
+} dpram_device_t;
+
+typedef struct dpram_tasklet_data {
+ dpram_device_t *device;
+ u_int16_t non_cmd;
+} dpram_tasklet_data_t;
+
+struct _mem_param {
+ unsigned short addr;
+ unsigned long data;
+ int dir;
+};
+
+
+/* TODO: add more definitions */
+
+#endif /* __DPRAM_H__ */
+
diff --git a/drivers/misc/samsung_modemctl/dpram/modemctl.h b/drivers/misc/samsung_modemctl/dpram/modemctl.h
new file mode 100644
index 0000000..ee59364
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/dpram/modemctl.h
@@ -0,0 +1,38 @@
+/**
+ * header for modem control
+ *
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __MODEM_CONTROL_H__
+#define __MODEM_CONTROL_H__
+
+struct modemctl_platform_data {
+ const char *name;
+
+ unsigned gpio_phone_on;
+ unsigned gpio_phone_active;
+ unsigned gpio_pda_active;
+ unsigned gpio_cp_reset;
+ unsigned gpio_usim_boot;
+ unsigned gpio_flm_sel;
+ unsigned gpio_sim_ndetect;
+
+ void (*cfg_gpio)(void);
+};
+
+#endif /* __MODEM_CONTROL_H__ */
diff --git a/drivers/misc/samsung_modemctl/dpram/onedram.h b/drivers/misc/samsung_modemctl/dpram/onedram.h
new file mode 100644
index 0000000..64214aa
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/dpram/onedram.h
@@ -0,0 +1,52 @@
+/**
+ * header for onedram driver
+ *
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __ONEDRAM_H__
+#define __ONEDRAM_H__
+
+#include <linux/ioport.h>
+#include <linux/types.h>
+
+struct onedram_platform_data {
+ void (*cfg_gpio)(void);
+};
+
+extern int onedram_register_handler(void (*handler)(u32, void *), void *data);
+extern int onedram_unregister_handler(void (*handler)(u32, void *));
+
+extern struct resource* onedram_request_region(resource_size_t start,
+ resource_size_t size, const char *name);
+extern void onedram_release_region(resource_size_t start,
+ resource_size_t size);
+
+extern int onedram_read_mailbox(u32 *);
+extern int onedram_write_mailbox(u32);
+
+extern int onedram_get_auth(u32 cmd);
+extern int onedram_put_auth(int release);
+
+extern int onedram_rel_sem(void);
+extern int onedram_read_sem(void);
+
+#define ONEDRAM_GET_AUTH _IOW('o', 0x20, u32)
+#define ONEDRAM_PUT_AUTH _IO('o', 0x21)
+#define ONEDRAM_REL_SEM _IO('o', 0x22)
+
+#endif /* __ONEDRAM_H__ */
diff --git a/drivers/misc/samsung_modemctl/modem_ctl.c b/drivers/misc/samsung_modemctl/modem_ctl.c
index e7504fd..0bee8cd 100755..100644
--- a/drivers/misc/samsung_modemctl/modem_ctl.c
+++ b/drivers/misc/samsung_modemctl/modem_ctl.c
@@ -2,6 +2,7 @@
*
* Copyright (C) 2010 Google, Inc.
* Copyright (C) 2010 Samsung Electronics.
+ * Copyright (C) 2010 Kolja Dummann (k.dummann@gmail.com)
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -724,11 +725,24 @@ static int modem_start(struct modemctl *mc, int ramdump)
return -EINVAL;
}
+#ifdef CONFIG_MODEM_HAS_CRAPPY_BOOTLOADER
+
+ /* we do this as the BP bootloader from the SGS is a little bit
+ crapy it does not send the magic data MODEM_MSG_SBL_DONE when
+ it has finished loading. so we wait some amount of time */
+
+ pr_info("[MODEM] we have a crappy bootloader an wait for it");
+
+ //waiting 1500 ms should be enough, maybe we can decrease this but unsure
+ msleep(1500);
+
+#else
if (!mc->is_cdma_modem &&
readl(mc->mmio + OFF_MBOX_BP) != MODEM_MSG_SBL_DONE) {
pr_err("[MODEM] bootloader not ready\n");
return -EIO;
}
+#endif
writel(0, mc->mmio + OFF_SEM);
if (ramdump) {
@@ -762,6 +776,9 @@ static int modem_reset(struct modemctl *mc)
{
pr_info("[MODEM] modem_reset()\n");
+ /* ensure phone active pin irq type */
+ set_irq_type(mc->gpio_phone_active, IRQ_TYPE_EDGE_BOTH);
+
/* ensure pda active pin set to low */
gpio_set_value(mc->gpio_pda_active, 0);
diff --git a/drivers/misc/samsung_modemctl/modemctl/Makefile b/drivers/misc/samsung_modemctl/modemctl/Makefile
new file mode 100755
index 0000000..f1bc187
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/modemctl/Makefile
@@ -0,0 +1 @@
+obj-y += modemctl.o \ No newline at end of file
diff --git a/drivers/misc/samsung_modemctl/modemctl/modemctl.c b/drivers/misc/samsung_modemctl/modemctl/modemctl.c
new file mode 100644
index 0000000..ab20c78
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/modemctl/modemctl.c
@@ -0,0 +1,870 @@
+/**
+ * header for modem control
+ *
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+//#define DEBUG
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+
+#include "modemctl.h"
+
+#ifdef CONFIG_HAS_WAKELOCK
+#include <linux/wakelock.h>
+#define MODEM_CTL_DEFAULT_WAKLOCK_HZ (2*HZ)
+#endif
+
+
+#define DRVNAME "modemctl"
+
+#define SIM_DEBOUNCE_TIME_HZ (HZ)
+
+struct modemctl;
+
+struct modemctl_ops {
+ void (*modem_on)(struct modemctl *);
+ void (*modem_off)(struct modemctl *);
+ void (*modem_reset)(struct modemctl *);
+ void (*modem_boot_on)(struct modemctl *);
+ void (*modem_boot_off)(struct modemctl *);
+};
+
+struct modemctl_info {
+ const char *name;
+ struct modemctl_ops ops;
+};
+
+struct modemctl {
+ int irq_phone_active;
+ int irq_sim_ndetect;
+
+ unsigned gpio_phone_on;
+ unsigned gpio_phone_active;
+ unsigned gpio_pda_active;
+ unsigned gpio_cp_reset;
+ unsigned gpio_reset_req_n;
+ unsigned gpio_usim_boot;
+ unsigned gpio_flm_sel;
+
+ unsigned gpio_sim_ndetect;
+ unsigned sim_reference_level;
+ unsigned sim_change_reset;
+ struct timer_list sim_irq_debounce_timer;
+
+#ifdef CONFIG_HAS_WAKELOCK
+ struct wake_lock mc_wlock;
+ long waketime;
+#endif
+
+ struct modemctl_ops *ops;
+
+ struct class *class;
+ struct device *dev;
+ const struct attribute_group *group;
+
+ struct work_struct work;
+};
+
+enum {
+ SIM_LEVEL_NONE = -1,
+ SIM_LEVEL_STABLE,
+ SIM_LEVEL_CHANGED
+};
+
+#ifdef CONFIG_HAS_WAKELOCK
+static inline void _wake_lock_init(struct modemctl *mc)
+{
+ wake_lock_init(&mc->mc_wlock, WAKE_LOCK_SUSPEND, "modemctl");
+ mc->waketime = MODEM_CTL_DEFAULT_WAKLOCK_HZ;
+}
+
+static inline void _wake_lock_destroy(struct modemctl *mc)
+{
+ wake_lock_destroy(&mc->mc_wlock);
+}
+
+static inline void _wake_lock_timeout(struct modemctl *mc)
+{
+ wake_lock_timeout(&mc->mc_wlock, mc->waketime);
+}
+
+static inline void _wake_lock_settime(struct modemctl *mc, long time)
+{
+ if (mc)
+ mc->waketime = time;
+}
+
+static inline long _wake_lock_gettime(struct modemctl *mc)
+{
+ return mc?mc->waketime:MODEM_CTL_DEFAULT_WAKLOCK_HZ;
+}
+#else
+# define _wake_lock_init(mc) do { } while(0)
+# define _wake_lock_destroy(mc) do { } while(0)
+# define _wake_lock_timeout(mc) do { } while(0)
+# define _wake_lock_settime(mc, time) do { } while(0)
+# define _wake_lock_gettime(mc) (0)
+#endif
+
+static int sim_check_status(struct modemctl *);
+static int sim_get_reference_status(struct modemctl *);
+static void sim_irq_debounce_timer_func(unsigned);
+
+static void xmm_on(struct modemctl *);
+static void xmm_off(struct modemctl *);
+static void xmm_reset(struct modemctl *);
+static void xmm_boot(struct modemctl *);
+
+static struct modemctl_info mdmctl_info[] = {
+ {
+ .name = "xmm",
+ .ops = {
+ .modem_on = xmm_on,
+ .modem_off = xmm_off,
+ .modem_reset = xmm_reset,
+ .modem_boot_on = xmm_boot,
+ },
+ },
+};
+
+static ssize_t show_control(struct device *d,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_control(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t show_status(struct device *d,
+ struct device_attribute *attr, char *buf);
+static ssize_t show_debug(struct device *d,
+ struct device_attribute *attr, char *buf);
+static ssize_t show_sim(struct device *d,
+ struct device_attribute *attr, char *buf);
+static ssize_t show_phoneactive(struct device *d,
+ struct device_attribute *attr, char *buf);
+
+static DEVICE_ATTR(control, 0664, show_control, store_control);
+static DEVICE_ATTR(status, 0664, show_status, NULL);
+static DEVICE_ATTR(debug, 0664, show_debug, NULL);
+static DEVICE_ATTR(sim, 0664, show_sim, NULL);
+static DEVICE_ATTR(phoneactive, 0664, show_phoneactive, NULL);
+
+static struct attribute *modemctl_attributes[] = {
+ &dev_attr_control.attr,
+ &dev_attr_status.attr,
+ &dev_attr_debug.attr,
+ &dev_attr_sim.attr,
+ &dev_attr_phoneactive.attr,
+ NULL
+};
+
+static const struct attribute_group modemctl_group = {
+ .attrs = modemctl_attributes,
+};
+
+static void xmm_on(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->gpio_cp_reset)
+ return;
+
+ /* ensure pda active pin set to low */
+ gpio_set_value(mc->gpio_pda_active, 0);
+ /* ensure cp_reset pin set to low */
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ if(mc->gpio_reset_req_n)
+ gpio_direction_output(mc->gpio_reset_req_n, 0);
+
+ msleep(100);
+
+ //gpio_set_value(mc->gpio_cp_reset, 1);
+ if(mc->gpio_phone_on)
+ gpio_set_value(mc->gpio_phone_on, 1);
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ msleep(100); /* no spec, confirm later exactly how much time
+ needed to initialize CP with RESET_PMU_N */
+
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ /* Follow RESET timming delay not Power-On timming,
+ because CP_RST & PHONE_ON have been set high already. */
+ // msleep(30); /* > 26.6 + 2 msec */
+ //msleep(40); /* > 37.2 + 2 msec */
+ msleep(100); /*wait modem stable */
+
+ gpio_set_value(mc->gpio_pda_active, 1);
+ if(mc->gpio_reset_req_n)
+ gpio_direction_input(mc->gpio_reset_req_n);
+}
+
+static void xmm_off(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->gpio_cp_reset)
+ return;
+
+ if(mc->gpio_phone_on)
+ gpio_set_value(mc->gpio_phone_on, 0);
+ gpio_set_value(mc->gpio_cp_reset, 0);
+}
+
+static void xmm_reset(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->gpio_cp_reset)
+ return;
+
+ /* To Do :
+ * hard_reset(RESET_PMU_N) and soft_reset(RESET_REQ_N)
+ * should be divided later.
+ * soft_reset is used for CORE_DUMP
+ */
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ msleep(100); /* no spec, confirm later exactly how much time
+ needed to initialize CP with RESET_PMU_N */
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ //msleep(40); /* > 37.2 + 2 msec */
+ msleep(100); /*wait modem stable */
+
+ /* leave it as reset state */
+// gpio_set_value(mc->gpio_phone_on, 0);
+// gpio_set_value(mc->gpio_cp_reset, 0);
+}
+
+static void xmm_boot(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+
+ if(mc->gpio_usim_boot)
+ gpio_set_value(mc->gpio_usim_boot, 1);
+
+ if(mc->gpio_flm_sel)
+ gpio_set_value(mc->gpio_flm_sel, 0);
+}
+
+
+static int modem_on(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->ops || !mc->ops->modem_on) {
+ //
+ return -ENXIO;
+ }
+
+ mc->ops->modem_on(mc);
+
+ return 0;
+}
+
+static int modem_off(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->ops || !mc->ops->modem_off) {
+ //
+ return -ENXIO;
+ }
+
+ mc->ops->modem_off(mc);
+
+ return 0;
+}
+
+static int modem_reset(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->ops || !mc->ops->modem_reset) {
+ //
+ return -ENXIO;
+ }
+
+ mc->ops->modem_reset(mc);
+
+ return 0;
+}
+
+static int modem_boot_on(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->ops || !mc->ops->modem_boot_on) {
+ //
+ return -ENXIO;
+ }
+
+ mc->ops->modem_boot_on(mc);
+
+ return 0;
+}
+
+static int modem_boot_off(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->ops || !mc->ops->modem_boot_off) {
+ //
+ return -ENXIO;
+ }
+
+ mc->ops->modem_boot_off(mc);
+
+ return 0;
+}
+
+static int pda_on(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->gpio_pda_active) {
+
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_pda_active, 1);
+
+ return 0;
+}
+
+static int pda_off(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->gpio_pda_active) {
+
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_pda_active, 0);
+
+ return 0;
+}
+
+static int modem_get_active(struct modemctl *mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->gpio_phone_active || !mc->gpio_cp_reset)
+ return -ENXIO;
+
+ dev_dbg(mc->dev, "cp %d phone %d\n",
+ gpio_get_value(mc->gpio_cp_reset),
+ gpio_get_value(mc->gpio_phone_active));
+
+ if(gpio_get_value(mc->gpio_cp_reset))
+ return !!gpio_get_value(mc->gpio_phone_active);
+
+ return 0;
+}
+
+static ssize_t show_control(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ struct modemctl *mc = dev_get_drvdata(d);
+ struct modemctl_ops *ops = mc->ops;
+
+ if(ops) {
+ if(ops->modem_on)
+ p += sprintf(p, "on ");
+ if(ops->modem_off)
+ p += sprintf(p, "off ");
+ if(ops->modem_reset)
+ p += sprintf(p, "reset ");
+ if(ops->modem_boot_on)
+ p += sprintf(p, "boot_on ");
+
+ if(ops->modem_boot_off)
+ p += sprintf(p, "boot_off ");
+ } else {
+ p += sprintf(p, "(No ops)");
+ }
+
+ p += sprintf(p, "\n");
+ return p - buf;
+}
+
+static ssize_t store_control(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct modemctl *mc = dev_get_drvdata(d);
+
+ if(!strncmp(buf, "on", 2)) {
+ modem_on(mc);
+ return count;
+ }
+
+ if(!strncmp(buf, "off", 3)) {
+ modem_off(mc);
+ return count;
+ }
+
+ if(!strncmp(buf, "reset", 5)) {
+ modem_reset(mc);
+ return count;
+ }
+
+ if(!strncmp(buf, "boot_on", 7)) {
+ modem_boot_on(mc);
+ return count;
+ }
+
+ if(!strncmp(buf, "boot_off", 8)) {
+ modem_boot_off(mc);
+ return count;
+ }
+ // for compatibility
+ if(!strncmp(buf, "boot", 4)) {
+ modem_boot_on(mc);
+ return count;
+ }
+
+ return count;
+}
+
+static ssize_t show_status(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ struct modemctl *mc = dev_get_drvdata(d);
+
+ p += sprintf(p, "%d\n", modem_get_active(mc));
+
+ return p - buf;
+}
+
+static ssize_t show_sim(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ int level = 3;
+ struct modemctl *mc = dev_get_drvdata(d);
+
+ if (mc->gpio_sim_ndetect) {
+ level = gpio_get_value(mc->gpio_sim_ndetect);
+ }
+
+ p += sprintf(p, "%d\n", level);
+ return p - buf;
+}
+
+static ssize_t show_phoneactive(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ int level = 3;
+ struct modemctl *mc = dev_get_drvdata(d);
+
+ if (mc->gpio_phone_active) {
+ level = gpio_get_value(mc->gpio_phone_active);
+ }
+
+ p += sprintf(p, "%d\n", level);
+ return p - buf;
+}
+
+static ssize_t show_debug(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ /*
+ char *p = buf;
+ int i;
+ struct modemctl *mc = dev_get_drvdata(d);
+
+ if(mc->irq_phone_active)
+ p += sprintf(p, "Irq Phone Active: %d\n", mc->irq_phone_active);
+ if(mc->irq_sim_ndetect)
+ p += sprintf(p, "Irq Sim nDetect: %d\n", mc->irq_sim_ndetect);
+
+ p += sprintf(p, "GPIO ---- \n");
+
+ if(mc->gpio_phone_on)
+ p += sprintf(p, "\t%3d %d : phone on\n", mc->gpio_phone_on,
+ gpio_get_value(mc->gpio_phone_on));
+ if(mc->gpio_phone_active)
+ p += sprintf(p, "\t%3d %d : phone active\n", mc->gpio_phone_active,
+ gpio_get_value(mc->gpio_phone_active));
+ if(mc->gpio_pda_active)
+ p += sprintf(p, "\t%3d %d : pda active\n", mc->gpio_pda_active,
+ gpio_get_value(mc->gpio_pda_active));
+ if(mc->gpio_cp_reset)
+ p += sprintf(p, "\t%3d %d : CP reset\n", mc->gpio_cp_reset,
+ gpio_get_value(mc->gpio_cp_reset));
+ if(mc->gpio_usim_boot)
+ p += sprintf(p, "\t%3d %d : USIM boot\n", mc->gpio_usim_boot,
+ gpio_get_value(mc->gpio_usim_boot));
+ if(mc->gpio_flm_sel)
+ p += sprintf(p, "\t%3d %d : FLM sel\n", mc->gpio_flm_sel,
+ gpio_get_value(mc->gpio_flm_sel));
+ if(mc->gpio_sim_ndetect)
+ p += sprintf(p, "\t%3d %d : Sim n Detect\n", mc->gpio_sim_ndetect,
+ gpio_get_value(mc->gpio_sim_ndetect));
+
+ p += sprintf(p, "Support types --- \n");
+ for(i=0;i<ARRAY_SIZE(mdmctl_info);i++) {
+ if(mc->ops == &mdmctl_info[i].ops) {
+ p += sprintf(p, "\t * ");
+ } else {
+ p += sprintf(p, "\t ");
+ }
+ p += sprintf(p, "%s\n", mdmctl_info[i].name);
+ }
+*/
+ return 0;
+}
+
+static void mc_work(struct work_struct *work)
+{
+ struct modemctl *mc = container_of(work, struct modemctl, work);
+ int r;
+
+ r = modem_get_active(mc);
+ if (r < 0) {
+ dev_err(mc->dev, "Not initialized\n");
+ return;
+ }
+
+ dev_info(mc->dev, "PHONE ACTIVE: %d\n", r);
+
+ if (r) {
+ if (mc->sim_change_reset == SIM_LEVEL_CHANGED) {
+ kobject_uevent(&mc->dev->kobj, KOBJ_CHANGE);
+ } else {
+ if (mc->sim_reference_level == SIM_LEVEL_NONE) {
+ sim_get_reference_status(mc);
+ }
+ kobject_uevent(&mc->dev->kobj, KOBJ_ONLINE);
+ }
+ }
+ else
+ kobject_uevent(&mc->dev->kobj, KOBJ_OFFLINE);
+}
+
+static irqreturn_t modemctl_irq_handler(int irq, void *dev_id)
+{
+ struct modemctl *mc = (struct modemctl *)dev_id;
+
+ if (!work_pending(&mc->work))
+ schedule_work(&mc->work);
+
+ return IRQ_HANDLED;
+}
+
+static int sim_get_reference_status(struct modemctl* mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->gpio_sim_ndetect)
+ return -ENXIO;
+
+ mc->sim_reference_level = gpio_get_value(mc->gpio_sim_ndetect);
+
+ return 0;
+}
+
+static int sim_check_status(struct modemctl* mc)
+{
+ dev_dbg(mc->dev, "%s\n", __func__);
+ if(!mc->gpio_sim_ndetect || mc->sim_reference_level == SIM_LEVEL_NONE) {
+ return -ENXIO;
+ }
+
+ if (mc->sim_reference_level != gpio_get_value(mc->gpio_sim_ndetect)) {
+ mc->sim_change_reset = SIM_LEVEL_CHANGED;
+ }
+ else
+ {
+ mc->sim_change_reset = SIM_LEVEL_STABLE;
+ }
+
+ return 0;
+}
+
+static void sim_irq_debounce_timer_func(unsigned aulong)
+{
+ struct modemctl *mc = (struct modemctl *)aulong;
+ int r;
+
+ r = sim_check_status(mc);
+ if (r < 0) {
+ dev_err(mc->dev, "Not initialized\n");
+ return;
+ }
+
+ if (mc->sim_change_reset == SIM_LEVEL_CHANGED) {
+ if (!work_pending(&mc->work))
+ schedule_work(&mc->work);
+
+ _wake_lock_timeout(mc);
+ }
+}
+
+static irqreturn_t simctl_irq_handler(int irq, void *dev_id)
+{
+ struct modemctl *mc = (struct modemctl *)dev_id;
+ int r;
+
+ if ( mc->sim_reference_level == SIM_LEVEL_NONE) {
+ return IRQ_HANDLED;
+ }
+
+ r = sim_check_status(mc);
+ if (r < 0) {
+ dev_err(mc->dev, "Not initialized\n");
+ return IRQ_HANDLED;
+ }
+
+ if (mc->sim_change_reset == SIM_LEVEL_CHANGED) {
+ mod_timer(&mc->sim_irq_debounce_timer, jiffies + SIM_DEBOUNCE_TIME_HZ);
+ _wake_lock_timeout(mc);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct modemctl_ops* _find_ops(const char *name)
+{
+ int i;
+ struct modemctl_ops *ops = NULL;
+
+ for(i=0;i<ARRAY_SIZE(mdmctl_info);i++) {
+ if(mdmctl_info[i].name && !strcmp(name, mdmctl_info[i].name))
+ ops = &mdmctl_info[i].ops;
+ }
+
+ return ops;
+}
+
+static void _free_all(struct modemctl *mc)
+{
+ if(mc) {
+ if(mc->ops)
+ mc->ops = NULL;
+
+ if(mc->group)
+ sysfs_remove_group(&mc->dev->kobj, mc->group);
+
+ if(mc->irq_phone_active)
+ free_irq(mc->irq_phone_active, mc);
+
+ if(mc->irq_sim_ndetect)
+ free_irq(mc->irq_sim_ndetect, mc);
+
+ if(mc->dev)
+ device_destroy(mc->class, mc->dev->devt);
+
+ if(mc->class)
+ class_destroy(mc->class);
+
+ _wake_lock_destroy(mc);
+
+ kfree(mc);
+ }
+}
+
+static int __devinit modemctl_probe(struct platform_device *pdev)
+{
+ struct modemctl *mc = NULL;
+ struct modemctl_platform_data *pdata;
+ struct resource *res;
+ int r = 0;
+ int irq_phone_active, irq_sim_ndetect;
+
+ printk("[%s]\n",__func__);
+
+ pdata = pdev->dev.platform_data;
+ if(!pdata || !pdata->cfg_gpio) {
+ dev_err(&pdev->dev, "No platform data\n");
+ r = -EINVAL;
+ goto err;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if(!res) {
+ dev_err(&pdev->dev, "failed to get irq number\n");
+ r = -EINVAL;
+ goto err;
+ }
+ irq_phone_active = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if(!res) {
+ dev_err(&pdev->dev, "failed to get irq number\n");
+ r = -EINVAL;
+ goto err;
+ }
+ irq_sim_ndetect = res->start;
+
+ mc = kzalloc(sizeof(struct modemctl), GFP_KERNEL);
+ if(!mc) {
+ dev_err(&pdev->dev, "failed to allocate device\n");
+ r = -ENOMEM;
+ goto err;
+ }
+
+ mc->gpio_phone_on = pdata->gpio_phone_on;
+ mc->gpio_phone_active = pdata->gpio_phone_active;
+ mc->gpio_pda_active = pdata->gpio_pda_active;
+ mc->gpio_cp_reset = pdata->gpio_cp_reset;
+ mc->gpio_reset_req_n = pdata->gpio_reset_req_n;
+ mc->gpio_usim_boot = pdata->gpio_usim_boot;
+ mc->gpio_flm_sel = pdata->gpio_flm_sel;
+ mc->gpio_sim_ndetect = pdata->gpio_sim_ndetect;
+ mc->sim_change_reset = SIM_LEVEL_NONE;
+ mc->sim_reference_level = SIM_LEVEL_NONE;
+
+ mc->ops = _find_ops(pdata->name);
+ if(!mc->ops) {
+ dev_err(&pdev->dev, "can't find operations: %s\n", pdata->name);
+ goto err;
+ }
+
+ mc->class = class_create(THIS_MODULE, "modemctl");
+ if(IS_ERR(mc->class)) {
+ dev_err(&pdev->dev, "failed to create sysfs class\n");
+ r = PTR_ERR(mc->class);
+ mc->class = NULL;
+ goto err;
+ }
+
+ pdata->cfg_gpio();
+
+ mc->dev = device_create(mc->class, &pdev->dev, MKDEV(0, 0), NULL, "%s", pdata->name);
+ if(IS_ERR(mc->dev)) {
+ dev_err(&pdev->dev, "failed to create device\n");
+ r = PTR_ERR(mc->dev);
+ goto err;
+ }
+ dev_set_drvdata(mc->dev, mc);
+
+ r = sysfs_create_group(&mc->dev->kobj, &modemctl_group);
+ if(r) {
+ dev_err(&pdev->dev, "failed to create sysfs files\n");
+ goto err;
+ }
+ mc->group = &modemctl_group;
+
+ INIT_WORK(&mc->work, mc_work);
+
+ r = request_irq(irq_phone_active, modemctl_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "phone_active", mc);
+ if(r) {
+ dev_err(&pdev->dev, "failed to allocate an interrupt(%d)\n",
+ irq_phone_active);
+ goto err;
+ }
+ r = enable_irq_wake(irq_phone_active);
+ if(r) {
+ dev_err(&pdev->dev, "failed to set wakeup source(%d)\n",
+ irq_phone_active);
+ goto err;
+ }
+
+ mc->irq_phone_active = irq_phone_active;
+
+ setup_timer(&mc->sim_irq_debounce_timer, (void*)sim_irq_debounce_timer_func,(unsigned long)mc);
+
+ r = request_irq(irq_sim_ndetect, simctl_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "sim_ndetect", mc);
+ if(r) {
+ dev_err(&pdev->dev, "failed to allocate an interrupt(%d)\n",
+ irq_sim_ndetect);
+ goto err;
+ }
+
+ r = enable_irq_wake(irq_sim_ndetect);
+ if(r) {
+ dev_err(&pdev->dev, "failed to set wakeup source(%d)\n",
+ irq_sim_ndetect);
+ goto err;
+ }
+
+ mc->irq_sim_ndetect= irq_sim_ndetect;
+
+ _wake_lock_init(mc);
+
+ platform_set_drvdata(pdev, mc);
+
+ return 0;
+
+err:
+ _free_all(mc);
+ return r;
+}
+
+static int __devexit modemctl_remove(struct platform_device *pdev)
+{
+ struct modemctl *mc = platform_get_drvdata(pdev);
+
+ flush_work(&mc->work);
+ platform_set_drvdata(pdev, NULL);
+ _free_all(mc);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int modemctl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct modemctl *mc = platform_get_drvdata(pdev);
+
+ pda_off(mc);
+
+ return 0;
+}
+
+static int modemctl_resume(struct platform_device *pdev)
+{
+ struct modemctl *mc = platform_get_drvdata(pdev);
+
+ pda_on(mc);
+
+ return 0;
+}
+#else
+# define modemctl_suspend NULL
+# define modemctl_resume NULL
+#endif
+
+static struct platform_driver modemctl_driver = {
+ .probe = modemctl_probe,
+ .remove = __devexit_p(modemctl_remove),
+ .suspend = modemctl_suspend,
+ .resume = modemctl_resume,
+ .driver = {
+ .name = DRVNAME,
+ },
+};
+
+static int __init modemctl_init(void)
+{
+ printk("[%s]\n",__func__);
+ return platform_driver_register(&modemctl_driver);
+}
+
+static void __exit modemctl_exit(void)
+{
+ platform_driver_unregister(&modemctl_driver);
+}
+
+module_init(modemctl_init);
+module_exit(modemctl_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Suchang Woo <suchang.woo@samsung.com>");
+MODULE_DESCRIPTION("Modem control");
diff --git a/drivers/misc/samsung_modemctl/modemctl/modemctl.h b/drivers/misc/samsung_modemctl/modemctl/modemctl.h
new file mode 100644
index 0000000..65bf46f
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/modemctl/modemctl.h
@@ -0,0 +1,39 @@
+/**
+ * header for modem control
+ *
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __MODEM_CONTROL_H__
+#define __MODEM_CONTROL_H__
+
+struct modemctl_platform_data {
+ const char *name;
+
+ unsigned gpio_phone_on;
+ unsigned gpio_phone_active;
+ unsigned gpio_pda_active;
+ unsigned gpio_cp_reset;
+ unsigned gpio_reset_req_n;
+ unsigned gpio_usim_boot;
+ unsigned gpio_flm_sel;
+ unsigned gpio_sim_ndetect;
+
+ void (*cfg_gpio)(void);
+};
+
+#endif /* __MODEM_CONTROL_H__ */
diff --git a/drivers/misc/samsung_modemctl/onedram/Makefile b/drivers/misc/samsung_modemctl/onedram/Makefile
new file mode 100755
index 0000000..3f53c4a
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/onedram/Makefile
@@ -0,0 +1,2 @@
+obj-y += onedram.o
+
diff --git a/drivers/misc/samsung_modemctl/onedram/onedram.c b/drivers/misc/samsung_modemctl/onedram/onedram.c
new file mode 100644
index 0000000..15fe9b4
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/onedram/onedram.c
@@ -0,0 +1,949 @@
+/**
+ * Samsung Virtual Network driver using OneDram device
+ *
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+//#define DEBUG
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/spinlock.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <linux/wait.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include "onedram.h"
+
+#define DRVNAME "onedram"
+
+#define ONEDRAM_REG_OFFSET 0xFFF800
+#define ONEDRAM_REG_SIZE 0x800
+
+static DEFINE_MUTEX(onedram_mutex);
+
+struct onedram_reg_mapped {
+ u32 sem;
+ u32 reserved1[7];
+ u32 mailbox_AB; // CP write, AP read
+ u32 reserved2[7];
+ u32 mailbox_BA; // AP write, CP read
+ u32 reserved3[23];
+ u32 check_AB; // can't read
+ u32 reserved4[7];
+ u32 check_BA; // 0: CP read, 1: CP don't read
+};
+
+struct onedram_handler {
+ struct list_head list;
+ void *data;
+ void (*handler)(u32, void *);
+};
+
+struct onedram_handler_head {
+ struct list_head list;
+ u32 len;
+ spinlock_t lock;
+};
+static struct onedram_handler_head h_list;
+
+static struct resource onedram_resource = {
+ .name = DRVNAME,
+ .start = 0,
+ .end = -1,
+ .flags = IORESOURCE_MEM,
+};
+
+struct onedram {
+ struct class *class;
+ struct device *dev;
+ struct cdev cdev;
+ dev_t devid;
+
+ wait_queue_head_t waitq;
+ struct fasync_struct *async_queue;
+ u32 mailbox;
+
+ unsigned long base;
+ unsigned long size;
+ void __iomem *mmio;
+
+ int irq;
+
+ struct completion comp;
+ atomic_t ref_sem;
+ unsigned long flags;
+
+ const struct attribute_group *group;
+
+ struct onedram_reg_mapped *reg;
+};
+struct onedram *onedram;
+
+static DEFINE_SPINLOCK(onedram_lock);
+
+static unsigned long hw_tmp; /* for hardware */
+static inline int _read_sem(struct onedram *od);
+static inline void _write_sem(struct onedram *od, int v);
+
+static unsigned long recv_cnt;
+static unsigned long send_cnt;
+static ssize_t show_debug(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ struct onedram *od = dev_get_drvdata(d);
+
+ if (!od)
+ return 0;
+
+ p += sprintf(p, "Semaphore: %d (%d)\n", _read_sem(od), (char)hw_tmp);
+ p += sprintf(p, "Mailbox: %x\n", od->reg->mailbox_AB);
+ p += sprintf(p, "Reference count: %d\n", atomic_read(&od->ref_sem));
+ p += sprintf(p, "Mailbox send: %lu\n", send_cnt);
+ p += sprintf(p, "Mailbox recv: %lu\n", recv_cnt);
+
+ return p - buf;
+}
+
+static DEVICE_ATTR(debug, 0664, show_debug, NULL);
+
+static struct attribute *onedram_attributes[] = {
+ &dev_attr_debug.attr,
+ NULL
+};
+
+static const struct attribute_group onedram_group = {
+ .attrs = onedram_attributes,
+};
+
+static inline void _write_sem(struct onedram *od, int v)
+{
+ od->reg->sem = v;
+ hw_tmp = od->reg->sem; /* for hardware */
+}
+
+static inline int _read_sem(struct onedram *od)
+{
+ return od->reg->sem;
+}
+
+static inline int _send_cmd(struct onedram *od, u32 cmd)
+{
+ if (!od) {
+ printk(KERN_ERR "[%s]onedram: Dev is NULL, but try to access\n",__func__);
+ return -EFAULT;
+ }
+
+ if (!od->reg) {
+ dev_err(od->dev, "Failed to send cmd, not initialized\n");
+ return -EFAULT;
+ }
+
+ dev_dbg(od->dev, "send %x\n", cmd);
+ send_cnt++;
+ od->reg->mailbox_BA = cmd;
+ return 0;
+}
+
+static inline int _recv_cmd(struct onedram *od, u32 *cmd)
+{
+ if (!cmd)
+ return -EINVAL;
+
+ if (!od) {
+ printk(KERN_ERR "[%s]onedram: Dev is NULL, but try to access\n",__func__);
+ return -EFAULT;
+ }
+
+ if (!od->reg) {
+ dev_err(od->dev, "Failed to read cmd, not initialized\n");
+ return -EFAULT;
+ }
+
+ recv_cnt++;
+ *cmd = od->reg->mailbox_AB;
+ return 0;
+}
+
+static inline int _get_auth(struct onedram *od, u32 cmd)
+{
+ unsigned long timeleft;
+ int retry = 0;
+
+ /* send cmd every 20m seconds */
+ while (1) {
+ _send_cmd(od, cmd);
+
+ timeleft = wait_for_completion_timeout(&od->comp, HZ/50);
+#if 0
+ if (timeleft)
+ break;
+#endif
+ if (_read_sem(od))
+ break;
+
+ retry++;
+ if (retry > 50 ) { /* time out after 1 seconds */
+ dev_err(od->dev, "get authority time out\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int get_auth(struct onedram *od, u32 cmd)
+{
+ int r;
+
+ if (!od) {
+ printk(KERN_ERR "[%s]onedram: Dev is NULL, but try to access\n",__func__);
+ return -EFAULT;
+ }
+
+ if (!od->reg) {
+ dev_err(od->dev, "Failed to get authority\n");
+ return -EFAULT;
+ }
+
+ atomic_inc(&od->ref_sem);
+
+ if (_read_sem(od))
+ return 0;
+
+ if (cmd)
+ r = _get_auth(od, cmd);
+ else
+ r = -EACCES;
+
+ if (r < 0)
+ atomic_dec(&od->ref_sem);
+
+ return r;
+}
+
+static int put_auth(struct onedram *od, int release)
+{
+ if (!od) {
+ printk(KERN_ERR "[%s]onedram: Dev is NULL, but try to access\n",__func__);
+ return -EFAULT;
+ }
+
+ if (!od->reg) {
+ dev_err(od->dev, "Failed to put authority\n");
+ return -EFAULT;
+ }
+
+ if (release)
+ set_bit(0, &od->flags);
+
+ if (atomic_dec_and_test(&od->ref_sem)
+ && test_and_clear_bit(0, &od->flags)) {
+ INIT_COMPLETION(od->comp);
+ _write_sem(od, 0);
+ dev_dbg(od->dev, "rel_sem: %d\n", _read_sem(od));
+ }
+
+ return 0;
+}
+
+static int rel_sem(struct onedram *od)
+{
+ if (!od) {
+ printk(KERN_ERR "[%s]onedram: Dev is NULL, but try to access\n",__func__);
+ return -EFAULT;
+ }
+
+ if (!od->reg) {
+ dev_err(od->dev, "Failed to put authority\n");
+ return -EFAULT;
+ }
+
+ if (atomic_read(&od->ref_sem))
+ return -EBUSY;
+
+ INIT_COMPLETION(od->comp);
+ clear_bit(0, &od->flags);
+ _write_sem(od, 0);
+ dev_dbg(od->dev, "rel_sem: %d\n", _read_sem(od));
+
+ return 0;
+}
+
+int onedram_read_mailbox(u32 *mb)
+{
+ return _recv_cmd(onedram, mb);
+}
+EXPORT_SYMBOL(onedram_read_mailbox);
+
+int onedram_write_mailbox(u32 mb)
+{
+ return _send_cmd(onedram, mb);
+}
+EXPORT_SYMBOL(onedram_write_mailbox);
+
+void onedram_init_mailbox(void)
+{
+ int r = 0;
+ /* flush mailbox before registering onedram irq */
+ r = onedram->reg->mailbox_AB;
+
+ /* Set nINT_ONEDRAM_CP to low */
+ onedram->reg->mailbox_BA=0x0;
+}
+EXPORT_SYMBOL(onedram_init_mailbox);
+
+int onedram_get_auth(u32 cmd)
+{
+ return get_auth(onedram, cmd);
+}
+EXPORT_SYMBOL(onedram_get_auth);
+
+int onedram_put_auth(int release)
+{
+ return put_auth(onedram, release);
+}
+EXPORT_SYMBOL(onedram_put_auth);
+
+int onedram_rel_sem(void)
+{
+ return rel_sem(onedram);
+}
+EXPORT_SYMBOL(onedram_rel_sem);
+
+int onedram_read_sem(void)
+{
+ return _read_sem(onedram);
+}
+EXPORT_SYMBOL(onedram_read_sem);
+
+void onedram_get_vbase(void** vbase)
+{
+ *vbase = (void*)onedram->mmio;
+}
+EXPORT_SYMBOL(onedram_get_vbase);
+
+static unsigned long long old_clock;
+static u32 old_mailbox;
+
+static irqreturn_t onedram_irq_handler(int irq, void *data)
+{
+ struct onedram *od = (struct onedram *)data;
+ struct list_head *l;
+ unsigned long flags;
+ int r;
+ u32 mailbox;
+
+ r = onedram_read_mailbox(&mailbox);
+ if (r)
+ return IRQ_HANDLED;
+
+// if (old_mailbox == mailbox &&
+// old_clock + 100000 > cpu_clock(smp_processor_id()))
+// return IRQ_HANDLED;
+
+ dev_dbg(od->dev, "[%d] recv %x\n", _read_sem(od), mailbox);
+ hw_tmp = _read_sem(od); /* for hardware */
+
+ if (h_list.len) {
+ spin_lock_irqsave(&h_list.lock, flags);
+ list_for_each(l, &h_list.list) {
+ struct onedram_handler *h =
+ list_entry(l, struct onedram_handler, list);
+
+ if (h->handler)
+ h->handler(mailbox, h->data);
+ }
+ spin_unlock_irqrestore(&h_list.lock, flags);
+
+ spin_lock(&onedram_lock);
+ od->mailbox = mailbox;
+ spin_unlock(&onedram_lock);
+ } else {
+ od->mailbox = mailbox;
+ }
+
+ if (_read_sem(od))
+ complete_all(&od->comp);
+
+ wake_up_interruptible(&od->waitq);
+ kill_fasync(&od->async_queue, SIGIO, POLL_IN);
+
+// old_clock = cpu_clock(smp_processor_id());
+// old_mailbox = mailbox;
+
+ return IRQ_HANDLED;
+}
+
+static void onedram_vm_close(struct vm_area_struct *vma)
+{
+ struct onedram *od = vma->vm_private_data;
+ unsigned long offset;
+ unsigned long size;
+
+ put_auth(od, 0);
+
+ offset = (vma->vm_pgoff << PAGE_SHIFT) - od->base;
+ size = vma->vm_end - vma->vm_start;
+ dev_dbg(od->dev, "Rel region: 0x%08lx 0x%08lx\n", offset, size);
+ onedram_release_region(offset, size);
+}
+
+static struct vm_operations_struct onedram_vm_ops = {
+ .close = onedram_vm_close,
+};
+
+static int onedram_open(struct inode *inode, struct file *filp)
+{
+ struct cdev *cdev = inode->i_cdev;
+ struct onedram *od = container_of(cdev, struct onedram, cdev);
+
+ filp->private_data = od;
+ return 0;
+}
+
+static int onedram_release(struct inode *inode, struct file *filp)
+{
+ filp->private_data = NULL;
+ return 0;
+}
+
+static unsigned int onedram_poll(struct file *filp, poll_table *wait)
+{
+ struct onedram *od;
+ u32 data;
+
+ od = filp->private_data;
+
+ poll_wait(filp, &od->waitq, wait);
+
+ spin_lock_irq(&onedram_lock);
+ data = od->mailbox;
+ spin_unlock_irq(&onedram_lock);
+
+ if (data)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static ssize_t onedram_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ u32 data;
+ ssize_t retval;
+ struct onedram *od;
+
+ od = filp->private_data;
+
+ if (count < sizeof(u32))
+ return -EINVAL;
+
+ add_wait_queue(&od->waitq, &wait);
+
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irq(&onedram_lock);
+ data = od->mailbox;
+ od->mailbox = 0;
+ spin_unlock_irq(&onedram_lock);
+
+ if (data)
+ break;
+ else if (filp->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ goto out;
+ } else if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ goto out;
+ }
+ schedule();
+ }
+
+ retval = put_user(data, (u32 __user *)buf);
+ if (!retval)
+ retval = sizeof(u32);
+out:
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&od->waitq, &wait);
+
+ return retval;
+}
+
+static ssize_t onedram_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct onedram *od;
+
+ od = filp->private_data;
+
+ if (count) {
+ u32 data;
+
+ if (get_user(data, (u32 __user *)buf))
+ return -EFAULT;
+
+ _send_cmd(od, data);
+ }
+
+ return count;
+}
+
+static int onedram_fasync(int fd, struct file *filp, int on)
+{
+ struct onedram *od;
+
+ od = filp->private_data;
+
+ return fasync_helper(fd, filp, on, &od->async_queue);
+}
+
+static int onedram_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ int r;
+ struct onedram *od;
+ unsigned long size;
+ unsigned long pfn;
+ unsigned long offset;
+ struct resource *res;
+
+ od = filp->private_data;
+ if (!od || !vma)
+ return -EFAULT;
+
+ atomic_inc(&od->ref_sem);
+ if (!_read_sem(od)) {
+ atomic_dec(&od->ref_sem);
+ return -EPERM;
+ }
+
+ size = vma->vm_end - vma->vm_start;
+ offset = vma->vm_pgoff << PAGE_SHIFT;
+ if (size > od->size - PAGE_ALIGN(ONEDRAM_REG_SIZE) - offset)
+ return -EINVAL;
+
+ dev_dbg(od->dev, "Req region: 0x%08lx 0x%08lx\n", offset, size);
+ res = onedram_request_region(offset, size, "mmap");
+ if (!res)
+ return -EBUSY;
+
+ pfn = __phys_to_pfn(od->base + offset);
+ r = remap_pfn_range(vma, vma->vm_start, pfn,
+ size,
+ vma->vm_page_prot);
+ if (r)
+ return -EAGAIN;
+
+ vma->vm_ops = &onedram_vm_ops;
+ vma->vm_private_data = od;
+ return 0;
+}
+
+static long onedram_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct cdev *cdev = filp->f_dentry->d_inode->i_cdev;
+ struct onedram *od = container_of(cdev, struct onedram, cdev);
+ int r;
+
+ mutex_lock(&onedram_mutex);
+
+ switch (cmd) {
+ case ONEDRAM_GET_AUTH:
+ r = get_auth(od, arg);
+ break;
+ case ONEDRAM_PUT_AUTH:
+ r = put_auth(od, 0);
+ break;
+ case ONEDRAM_REL_SEM:
+ r = rel_sem(od);
+ break;
+ default:
+ r = -ENOIOCTLCMD;
+ break;
+ }
+
+ mutex_unlock(&onedram_mutex);
+
+ return r;
+}
+
+static const struct file_operations onedram_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = onedram_read,
+ .write = onedram_write,
+ .poll = onedram_poll,
+ .fasync = onedram_fasync,
+ .open = onedram_open,
+ .release = onedram_release,
+ .mmap = onedram_mmap,
+ .unlocked_ioctl = onedram_ioctl,
+};
+
+static int _register_chrdev(struct onedram *od)
+{
+ int r;
+ dev_t devid;
+
+ od->class = class_create(THIS_MODULE, DRVNAME);
+ if (IS_ERR(od->class)) {
+ r = PTR_ERR(od->class);
+ od->class = NULL;
+ return r;
+ }
+
+ r = alloc_chrdev_region(&devid, 0, 1, DRVNAME);
+ if (r)
+ return r;
+
+ cdev_init(&od->cdev, &onedram_fops);
+
+ r = cdev_add(&od->cdev, devid, 1);
+ if (r) {
+ unregister_chrdev_region(devid, 1);
+ return r;
+ }
+ od->devid = devid;
+
+ od->dev = device_create(od->class, NULL, od->devid, od, DRVNAME);
+ if (IS_ERR(od->dev)) {
+ r = PTR_ERR(od->dev);
+ od->dev = NULL;
+ return r;
+ }
+ dev_set_drvdata(od->dev, od);
+
+ return 0;
+}
+
+static inline int _request_mem(struct onedram *od, struct platform_device *pdev)
+{
+ struct resource *reso;
+
+ reso = request_mem_region(od->base, od->size, DRVNAME);
+ if (!reso) {
+ dev_err(&pdev->dev, "Failed to request the mem region:"
+ " 0x%08lx (%lu)\n", od->base, od->size);
+ return -EBUSY;
+ }
+
+ od->mmio = ioremap_nocache(od->base, od->size);
+ if (!od->mmio) {
+ release_mem_region(od->base, od->size);
+ dev_err(&pdev->dev, "Failed to ioremap: 0x%08lx (%lu)\n",
+ od->base, od->size);
+ return -EBUSY;
+ }
+
+ od->reg = (struct onedram_reg_mapped *)(
+ (char *)od->mmio + ONEDRAM_REG_OFFSET);
+ dev_dbg(&pdev->dev, "Onedram semaphore: %d\n", _read_sem(od));
+
+ onedram_resource.start = (resource_size_t)od->mmio;
+ onedram_resource.end = (resource_size_t)od->mmio + od->size - 1;
+
+ return 0;
+}
+
+static void _release(struct onedram *od)
+{
+ if (!od)
+ return;
+
+ if (od->irq)
+ free_irq(od->irq, od);
+
+ if (od->group)
+ sysfs_remove_group(&od->dev->kobj, od->group);
+
+ if (od->dev)
+ device_destroy(od->class, od->devid);
+
+ if (od->devid) {
+ cdev_del(&od->cdev);
+ unregister_chrdev_region(od->devid, 1);
+ }
+
+ if (od->mmio) {
+ od->reg = NULL;
+ iounmap(od->mmio);
+ release_mem_region(od->base, od->size);
+ onedram_resource.start = 0;
+ onedram_resource.end = -1;
+ }
+
+ if (od->class)
+ class_destroy(od->class);
+
+ kfree(od);
+}
+
+struct resource* onedram_request_region(resource_size_t start,
+ resource_size_t n, const char *name)
+{
+ struct resource *res;
+
+ start += onedram_resource.start;
+ res = __request_region(&onedram_resource, start, n, name, 0);
+ if (!res)
+ return NULL;
+
+ return res;
+}
+EXPORT_SYMBOL(onedram_request_region);
+
+void onedram_release_region(resource_size_t start, resource_size_t n)
+{
+ start += onedram_resource.start;
+ __release_region(&onedram_resource, start, n);
+}
+EXPORT_SYMBOL(onedram_release_region);
+
+int onedram_register_handler(void (*handler)(u32, void *), void *data)
+{
+ unsigned long flags;
+ struct onedram_handler *hd;
+
+ if (!handler)
+ return -EINVAL;
+
+ hd = kzalloc(sizeof(struct onedram_handler), GFP_KERNEL);
+ if (!hd)
+ return -ENOMEM;
+
+ hd->data = data;
+ hd->handler = handler;
+
+ spin_lock_irqsave(&h_list.lock, flags);
+ list_add_tail(&hd->list, &h_list.list);
+ h_list.len++;
+ spin_unlock_irqrestore(&h_list.lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(onedram_register_handler);
+
+int onedram_unregister_handler(void (*handler)(u32, void *))
+{
+ unsigned long flags;
+ struct list_head *l, *tmp;
+
+ if (!handler)
+ return -EINVAL;
+
+ spin_lock_irqsave(&h_list.lock, flags);
+ list_for_each_safe(l, tmp, &h_list.list) {
+ struct onedram_handler *hd =
+ list_entry(l, struct onedram_handler, list);
+
+ if (hd->handler == handler) {
+ list_del(&hd->list);
+ h_list.len--;
+ kfree(hd);
+ }
+ }
+ spin_unlock_irqrestore(&h_list.lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(onedram_unregister_handler);
+
+static void _unregister_all_handlers(void)
+{
+ unsigned long flags;
+ struct list_head *l, *tmp;
+
+ spin_lock_irqsave(&h_list.lock, flags);
+ list_for_each_safe(l, tmp, &h_list.list) {
+ struct onedram_handler *hd =
+ list_entry(l, struct onedram_handler, list);
+
+ list_del(&hd->list);
+ h_list.len--;
+ kfree(hd);
+ }
+ spin_unlock_irqrestore(&h_list.lock, flags);
+}
+
+static void _init_data(struct onedram *od)
+{
+ init_completion(&od->comp);
+ atomic_set(&od->ref_sem, 0);
+ INIT_LIST_HEAD(&h_list.list);
+ spin_lock_init(&h_list.lock);
+ h_list.len = 0;
+ init_waitqueue_head(&od->waitq);
+}
+
+static int __devinit onedram_probe(struct platform_device *pdev)
+{
+ int r;
+ int irq;
+ struct onedram *od = NULL;
+ struct onedram_platform_data *pdata;
+ struct resource *res;
+
+ printk("[%s]\n",__func__);
+ pdata = pdev->dev.platform_data;
+ if (!pdata || !pdata->cfg_gpio) {
+ dev_err(&pdev->dev, "No platform data\n");
+ r = -EINVAL;
+ goto err;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get irq number\n");
+ r = -EINVAL;
+ goto err;
+ }
+ irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get mem region\n");
+ r = -EINVAL;
+ goto err;
+ }
+
+ od = kzalloc(sizeof(struct onedram), GFP_KERNEL);
+ if (!od) {
+ dev_err(&pdev->dev, "failed to allocate device\n");
+ r = -ENOMEM;
+ goto err;
+ }
+ onedram = od;
+
+ dev_dbg(&pdev->dev, "Onedram dev: %p\n", od);
+
+ od->base = res->start;
+ od->size = resource_size(res);
+ r = _request_mem(od, pdev);
+ if (r)
+ goto err;
+
+ /* init mailbox state before registering irq handler */
+ onedram_init_mailbox();
+
+ _init_data(od);
+
+ pdata->cfg_gpio();
+
+ r = request_irq(irq, onedram_irq_handler,
+ IRQF_TRIGGER_LOW, "onedram", od);
+ if (r) {
+ dev_err(&pdev->dev, "Failed to allocate an interrupt: %d\n",
+ irq);
+ goto err;
+ }
+ od->irq = irq;
+ enable_irq_wake(od->irq);
+
+ r = _register_chrdev(od);
+ if (r) {
+ dev_err(&pdev->dev, "Failed to register chrdev\n");
+ goto err;
+ }
+
+ r = sysfs_create_group(&od->dev->kobj, &onedram_group);
+ if (r) {
+ dev_err(&pdev->dev, "Failed to create sysfs files\n");
+ goto err;
+ }
+ od->group = &onedram_group;
+
+ platform_set_drvdata(pdev, od);
+
+ return 0;
+
+err:
+ _release(od);
+ return r;
+}
+
+static int __devexit onedram_remove(struct platform_device *pdev)
+{
+ struct onedram *od = platform_get_drvdata(pdev);
+
+ /* TODO: need onedram_resource clean? */
+ _unregister_all_handlers();
+ platform_set_drvdata(pdev, NULL);
+ onedram = NULL;
+ _release(od);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int onedram_suspend(struct platform_device *pdev, pm_message_t state)
+{
+// struct onedram *od = platform_get_drvdata(pdev);
+
+ return 0;
+}
+
+static int onedram_resume(struct platform_device *pdev)
+{
+// struct onedram *od = platform_get_drvdata(pdev);
+
+ return 0;
+}
+#else
+# define onedram_suspend NULL
+# define onedram_resume NULL
+#endif
+
+
+static struct platform_driver onedram_driver = {
+ .probe = onedram_probe,
+ .remove = __devexit_p(onedram_remove),
+ .suspend = onedram_suspend,
+ .resume = onedram_resume,
+ .driver = {
+ .name = DRVNAME,
+ },
+};
+
+static int __init onedram_init(void)
+{
+ printk("[%s]\n",__func__);
+ return platform_driver_register(&onedram_driver);
+}
+
+static void __exit onedram_exit(void)
+{
+ platform_driver_unregister(&onedram_driver);
+}
+
+module_init(onedram_init);
+module_exit(onedram_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Suchang Woo <suchang.woo@samsung.com>");
+MODULE_DESCRIPTION("Onedram driver");
diff --git a/drivers/misc/samsung_modemctl/onedram/onedram.h b/drivers/misc/samsung_modemctl/onedram/onedram.h
new file mode 100644
index 0000000..5fb50a9
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/onedram/onedram.h
@@ -0,0 +1,54 @@
+/**
+ * header for onedram driver
+ *
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __ONEDRAM_H__
+#define __ONEDRAM_H__
+
+#include <linux/ioport.h>
+#include <linux/types.h>
+
+struct onedram_platform_data {
+ void (*cfg_gpio)(void);
+};
+
+extern int onedram_register_handler(void (*handler)(u32, void *), void *data);
+extern int onedram_unregister_handler(void (*handler)(u32, void *));
+
+extern struct resource* onedram_request_region(resource_size_t start,
+ resource_size_t size, const char *name);
+extern void onedram_release_region(resource_size_t start,
+ resource_size_t size);
+
+extern int onedram_read_mailbox(u32 *);
+extern int onedram_write_mailbox(u32);
+
+extern int onedram_get_auth(u32 cmd);
+extern int onedram_put_auth(int release);
+
+extern int onedram_rel_sem(void);
+extern int onedram_read_sem(void);
+
+extern void onedram_get_vbase(void **);
+
+#define ONEDRAM_GET_AUTH _IOW('o', 0x20, u32)
+#define ONEDRAM_PUT_AUTH _IO('o', 0x21)
+#define ONEDRAM_REL_SEM _IO('o', 0x22)
+
+#endif /* __ONEDRAM_H__ */
diff --git a/drivers/misc/samsung_modemctl/svnet/Makefile b/drivers/misc/samsung_modemctl/svnet/Makefile
new file mode 100755
index 0000000..868232b
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/svnet/Makefile
@@ -0,0 +1,6 @@
+svnet-y := main.o pdp.o
+
+svnet-y += sipc4.o
+
+obj-y += svnet.o
+
diff --git a/drivers/misc/samsung_modemctl/svnet/main.c b/drivers/misc/samsung_modemctl/svnet/main.c
new file mode 100644
index 0000000..16829ad
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/svnet/main.c
@@ -0,0 +1,866 @@
+/**
+ * Samsung Virtual Network driver using OneDram device
+ *
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+//#define DEBUG
+
+#if defined(DEBUG)
+# define NOISY_DEBUG
+#endif
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/list.h>
+#include <linux/jiffies.h>
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+
+#include <linux/if_phonet.h>
+#include <linux/phonet.h>
+#include <net/phonet/phonet.h>
+
+#ifdef CONFIG_HAS_WAKELOCK
+#include <linux/wakelock.h>
+
+#define DEFAULT_RAW_WAKE_TIME (6*HZ)
+#define DEFAULT_FMT_WAKE_TIME (HZ/2)
+#endif
+
+#if defined(NOISY_DEBUG)
+# define _dbg(dev, format, arg...) dev_dbg(dev, format, ## arg)
+#else
+# define _dbg(dev, format, arg...) do { } while (0)
+#endif
+
+#include "sipc.h"
+#include "pdp.h"
+
+#define SVNET_DEV_ADDR 0xa0
+
+enum {
+ SVNET_NORMAL = 0,
+ SVNET_RESET,
+ SVNET_EXIT,
+ SVNET_MAX,
+};
+
+struct svnet_stat {
+ unsigned int st_wq_state;
+ unsigned long st_recv_evt;
+ unsigned long st_recv_pkt_ph;
+ unsigned long st_recv_pkt_pdp;
+ unsigned long st_do_write;
+ unsigned long st_do_read;
+ unsigned long st_do_rx;
+};
+static struct svnet_stat stat;
+
+struct svnet_evt {
+ struct list_head list;
+ u32 event;
+};
+
+struct svnet_evt_head {
+ struct list_head list;
+ u32 len;
+ spinlock_t lock;
+};
+
+struct svnet {
+ struct net_device *ndev;
+ const struct attribute_group *group;
+
+ struct workqueue_struct *wq;
+ struct work_struct work_read;
+ struct delayed_work work_write;
+ struct delayed_work work_rx;
+
+ struct work_struct work_exit;
+ int exit_flag;
+
+ struct sk_buff_head txq;
+ struct svnet_evt_head rxq;
+
+ struct sipc *si;
+#ifdef CONFIG_HAS_WAKELOCK
+ struct wake_lock wlock;
+ long wake_time; /* jiffies */ /* wake time for not fmt packet */
+ long wake_process_time; /* jiffies */ /* processing wake time */
+#endif
+};
+
+static struct svnet *svnet_dev;
+
+#ifdef CONFIG_HAS_WAKELOCK
+static inline void _wake_lock_init(struct svnet *sn)
+{
+ wake_lock_init(&sn->wlock, WAKE_LOCK_SUSPEND, "svnet");
+ sn->wake_time = DEFAULT_RAW_WAKE_TIME;
+ sn->wake_process_time = DEFAULT_FMT_WAKE_TIME;
+ sn->wlock.expires = jiffies;
+}
+
+static inline void _wake_lock_destroy(struct svnet *sn)
+{
+ wake_lock_destroy(&sn->wlock);
+}
+
+static inline void _wake_lock_timeout(struct svnet *sn)
+{
+ long exp_cnt = sn->wlock.expires - jiffies;
+ if (exp_cnt < sn->wake_time)
+ wake_lock_timeout(&sn->wlock, sn->wake_time);
+}
+
+void _non_fmt_wakelock_timeout() {
+ if (svnet_dev)
+ _wake_lock_timeout(svnet_dev);
+}
+
+static inline void _wake_process_lock_timeout(struct svnet *sn)
+{
+ long exp_cnt = sn->wlock.expires - jiffies;
+ if (exp_cnt < sn->wake_process_time)
+ wake_lock_timeout(&sn->wlock, sn->wake_process_time);
+}
+
+void _fmt_wakelock_timeout() {
+ if (svnet_dev)
+ _wake_process_lock_timeout(svnet_dev);
+}
+
+static inline void _wake_lock_settime(struct svnet *sn, long time)
+{
+ if (sn)
+ sn->wake_time = time;
+}
+
+static inline long _wake_lock_gettime(struct svnet *sn)
+{
+ return sn?sn->wake_time:DEFAULT_RAW_WAKE_TIME;
+}
+#else
+#define _wake_lock_init(sn) do { } while(0)
+#define _wake_lock_destroy(sn) do { } while(0)
+#define _wake_lock_timeout(sn) do { } while(0)
+#define _wake_process_lock_timeout(sn) do { } while(0)
+#define _non_fmt_wakelock_timeout() do { } while(0)
+#define _fmt_wakelock_timeout() do { } while(0)
+#define _wake_lock_settime(sn, time) do { } while(0)
+#define _wake_lock_gettime(sn) (0)
+#endif
+
+static unsigned long long tmp_itor;
+static unsigned long long tmp_xtow;
+static unsigned long long time_max_itor;
+static unsigned long long time_max_xtow;
+static unsigned long long time_max_read;
+static unsigned long long time_max_write;
+
+extern unsigned long long time_max_semlat;
+
+static int _queue_evt(struct svnet_evt_head *h, u32 event);
+
+static ssize_t show_version(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Samsung IPC version %s\n", sipc_version);
+}
+
+static ssize_t show_waketime(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+ unsigned int msec;
+ unsigned long j;
+
+ if (!svnet_dev)
+ return 0;
+
+ j = _wake_lock_gettime(svnet_dev);
+ msec = jiffies_to_msecs(j);
+ p += sprintf(p, "%u\n", msec);
+
+ return p - buf;
+}
+
+static ssize_t store_waketime(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long msec;
+ unsigned long j;
+ int r;
+
+ if (!svnet_dev)
+ return count;
+
+ r = strict_strtoul(buf, 10, &msec);
+ if (r)
+ return count;
+
+ j = msecs_to_jiffies(msec);
+ _wake_lock_settime(svnet_dev, j);
+
+ return count;
+}
+
+static inline int _show_stat(char *buf)
+{
+ char *p = buf;
+
+ p += sprintf(p, "Stat -------- \n");
+ p += sprintf(p, "\twork state: %d\n", stat.st_wq_state);
+ p += sprintf(p, "\trecv mailbox: %lu\n", stat.st_recv_evt);
+ p += sprintf(p, "\trecv phonet: %lu\n", stat.st_recv_pkt_ph);
+ p += sprintf(p, "\trecv packet: %lu\n", stat.st_recv_pkt_pdp);
+ p += sprintf(p, "\twrite count: %lu\n", stat.st_do_write);
+ p += sprintf(p, "\tread count: %lu\n", stat.st_do_read);
+ p += sprintf(p, "\trx count: %lu\n", stat.st_do_rx);
+ p += sprintf(p, "\n");
+
+ return p - buf;
+}
+
+static ssize_t show_latency(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+
+ p += sprintf(p, "Max read latency: %12llu ns\n", time_max_itor);
+ p += sprintf(p, "Max read time: %12llu ns\n", time_max_read);
+ p += sprintf(p, "Max write latency: %12llu ns\n", time_max_xtow);
+ p += sprintf(p, "Max write time: %12llu ns\n", time_max_write);
+ p += sprintf(p, "Max sem. latency: %12llu ns\n", time_max_semlat);
+
+ return p - buf;
+}
+
+static ssize_t show_debug(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ char *p = buf;
+
+ if (!svnet_dev)
+ return 0;
+
+ p += _show_stat(p);
+
+ p += sprintf(p, "Event queue ----- \n");
+ p += sprintf(p, "\tTX queue\t%u\n", skb_queue_len(&svnet_dev->txq));
+ p += sprintf(p, "\tRX queue\t%u\n", svnet_dev->rxq.len);
+
+ p += sipc_debug_show(svnet_dev->si, p);
+
+ return p - buf;
+}
+
+static ssize_t store_debug(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ if (!svnet_dev)
+ return count;
+
+ switch (buf[0]) {
+ case 'R':
+ sipc_debug(svnet_dev->si, buf);
+ break;
+ default:
+
+ break;
+ }
+
+ return count;
+}
+
+static ssize_t store_whitelist(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ if (!svnet_dev)
+ return count;
+
+ switch (buf[0]) {
+ case 0x7F:
+ return sipc_whitelist(svnet_dev->si, buf, count);
+ break;
+ default:
+
+ break;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(version, 0664, show_version, NULL);
+static DEVICE_ATTR(latency, 0664, show_latency, NULL);
+static DEVICE_ATTR(waketime, 0664, show_waketime, store_waketime);
+static DEVICE_ATTR(debug, 0664, show_debug, store_debug);
+static DEVICE_ATTR(whitelist, 0664, NULL, store_whitelist);
+
+static struct attribute *svnet_attributes[] = {
+ &dev_attr_version.attr,
+ &dev_attr_waketime.attr,
+ &dev_attr_debug.attr,
+ &dev_attr_latency.attr,
+ &dev_attr_whitelist.attr,
+ NULL
+};
+
+static const struct attribute_group svnet_group = {
+ .attrs = svnet_attributes,
+};
+
+
+int vnet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct svnet *sn;
+ struct pdp_priv *priv;
+
+ dev_dbg(&ndev->dev, "recv inet packet %p: %d bytes\n", skb, skb->len);
+ stat.st_recv_pkt_pdp++;
+
+ priv = netdev_priv(ndev);
+ if (!priv)
+ goto drop;
+
+ sn = netdev_priv(priv->parent);
+ if (!sn)
+ goto drop;
+
+ if (!tmp_xtow)
+ tmp_xtow = cpu_clock(smp_processor_id());
+
+ skb_queue_tail(&sn->txq, skb);
+
+ _wake_process_lock_timeout(sn);
+ queue_delayed_work(sn->wq, &sn->work_write, 0);
+
+ return NETDEV_TX_OK;
+
+drop:
+ ndev->stats.tx_dropped++;
+
+ return NETDEV_TX_OK;
+}
+
+static int svnet_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct svnet *sn;
+
+ if (skb->protocol != __constant_htons(ETH_P_PHONET)) {
+ dev_err(&ndev->dev, "recv not a phonet message\n");
+ goto drop;
+ }
+
+ stat.st_recv_pkt_ph++;
+ dev_dbg(&ndev->dev, "recv packet %p: %d bytes\n", skb, skb->len);
+
+ sn = netdev_priv(ndev);
+
+ if (sipc_check_skb(sn->si, skb)) {
+ sipc_do_cmd(sn->si, skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (!tmp_xtow)
+ tmp_xtow = cpu_clock(smp_processor_id());
+
+ skb_queue_tail(&sn->txq, skb);
+
+ _wake_process_lock_timeout(sn);
+ queue_delayed_work(sn->wq, &sn->work_write, 0);
+
+ return NETDEV_TX_OK;
+
+drop:
+ dev_kfree_skb(skb);
+ ndev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+}
+
+static int _queue_evt(struct svnet_evt_head *h, u32 event)
+{
+ unsigned long flags;
+ struct svnet_evt *e;
+
+ e = kmalloc(sizeof(struct svnet_evt), GFP_ATOMIC);
+ if (!e)
+ return -ENOMEM;
+
+ e->event = event;
+
+ spin_lock_irqsave(&h->lock, flags);
+ list_add_tail(&e->list, &h->list);
+ h->len++;
+ spin_unlock_irqrestore(&h->lock, flags);
+
+ return 0;
+}
+
+static void _queue_purge(struct svnet_evt_head *h)
+{
+ unsigned long flags;
+ struct svnet_evt *e, *next;
+
+ spin_lock_irqsave(&h->lock, flags);
+ list_for_each_entry_safe(e, next, &h->list, list) {
+ list_del(&e->list);
+ h->len--;
+ kfree(e);
+ }
+ spin_unlock_irqrestore(&h->lock, flags);
+}
+
+static u32 _dequeue_evt(struct svnet_evt_head *h)
+{
+ unsigned long flags;
+ struct list_head *p;
+ struct svnet_evt *e;
+ u32 event;
+
+ spin_lock_irqsave(&h->lock, flags);
+ p = h->list.next;
+ if (p == &h->list) {
+ e = NULL;
+ event = 0;
+ } else {
+ e = list_entry(p, struct svnet_evt, list);
+ list_del(&e->list);
+ h->len--;
+ event = e->event;
+ }
+ spin_unlock_irqrestore(&h->lock, flags);
+
+ if (e)
+ kfree(e);
+
+ return event;
+}
+
+static int _proc_private_event(struct svnet *sn, u32 evt)
+{
+ switch(evt) {
+ case SIPC_EXIT_MB:
+ dev_err(&sn->ndev->dev, "Modem crash message received\n");
+ sn->exit_flag = SVNET_EXIT;
+ break;
+ case SIPC_RESET_MB:
+ dev_err(&sn->ndev->dev, "Modem reset message received\n");
+ sn->exit_flag = SVNET_RESET;
+ break;
+ default:
+ return 0;
+ }
+
+ queue_work(sn->wq, &sn->work_exit);
+
+ return 1;
+}
+
+static void svnet_queue_event(u32 evt, void *data)
+{
+ struct net_device *ndev = (struct net_device *)data;
+ struct svnet *sn;
+ int r;
+
+ if (!tmp_itor)
+ tmp_itor = cpu_clock(smp_processor_id());
+
+ stat.st_recv_evt++;
+
+ if (!ndev)
+ return;
+
+ sn = netdev_priv(ndev);
+ if (!sn)
+ return;
+
+ r = _proc_private_event(sn, evt);
+ if (r)
+ return;
+
+ r = _queue_evt(&sn->rxq, evt);
+ if (r) {
+ dev_err(&sn->ndev->dev, "Not enough memory: event skipped\n");
+ return;
+ }
+
+ _wake_process_lock_timeout(sn);
+ queue_work(sn->wq, &sn->work_read);
+}
+
+static int svnet_open(struct net_device *ndev)
+{
+ struct svnet *sn = netdev_priv(ndev);
+
+ dev_dbg(&ndev->dev, "%s\n", __func__);
+
+ /* TODO: check modem state */
+
+ if (!sn->si) {
+ sn->si = sipc_open(svnet_queue_event, ndev);
+ if (IS_ERR(sn->si)) {
+ dev_err(&ndev->dev, "IPC init error\n");
+ return PTR_ERR(sn->si);
+ }
+ sn->exit_flag = SVNET_NORMAL;
+ }
+
+ netif_wake_queue(ndev);
+ return 0;
+}
+
+static int svnet_close(struct net_device *ndev)
+{
+ struct svnet *sn = netdev_priv(ndev);
+
+ dev_dbg(&ndev->dev, "%s\n", __func__);
+
+ if (sn->wq)
+ flush_workqueue(sn->wq);
+ skb_queue_purge(&sn->txq);
+
+ if (sn->si)
+ sipc_close(&sn->si);
+
+ netif_stop_queue(ndev);
+
+ if (sn->wq)
+ flush_workqueue(sn->wq);
+ skb_queue_purge(&sn->txq);
+
+ return 0;
+}
+
+static int svnet_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
+{
+ struct if_phonet_req *req = (struct if_phonet_req *)ifr;
+
+ switch (cmd) {
+ case SIOCPNGAUTOCONF:
+ req->ifr_phonet_autoconf.device = SVNET_DEV_ADDR;
+ return 0;
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static const struct net_device_ops svnet_ops = {
+ .ndo_open = svnet_open,
+ .ndo_stop = svnet_close,
+ .ndo_start_xmit = svnet_xmit,
+ .ndo_do_ioctl = svnet_ioctl,
+};
+
+static void svnet_setup(struct net_device *ndev)
+{
+ ndev->features = 0;
+ ndev->netdev_ops = &svnet_ops;
+ ndev->header_ops = &phonet_header_ops;
+ ndev->type = ARPHRD_PHONET;
+ ndev->flags = IFF_POINTOPOINT | IFF_NOARP;
+ ndev->mtu = PHONET_MAX_MTU;
+ ndev->hard_header_len = 1;
+ ndev->dev_addr[0] = SVNET_DEV_ADDR;
+ ndev->addr_len = 1;
+ ndev->tx_queue_len = 1000;
+
+// ndev->destructor = free_netdev;
+}
+
+static void svnet_read_wq(struct work_struct *work)
+{
+ struct svnet *sn = container_of(work,
+ struct svnet, work_read);
+ u32 event;
+ int r = 0;
+ int contd = 0;
+ unsigned long long t, d;
+
+ t = cpu_clock(smp_processor_id());
+ if (tmp_itor) {
+ d = t - tmp_itor;
+ _dbg(&sn->ndev->dev, "int_to_read %llu ns\n", d);
+ tmp_itor = 0;
+ if (time_max_itor < d)
+ time_max_itor = d;
+ }
+
+ dev_dbg(&sn->ndev->dev, "%s\n", __func__);
+ stat.st_do_read++;
+
+ stat.st_wq_state = 1;
+ event = _dequeue_evt(&sn->rxq);
+ while (event) {
+ // isn't it possible that merge the events?
+ dev_dbg(&sn->ndev->dev, "event %x\n", event);
+
+ if (sn->si) {
+ r = sipc_read(sn->si, event, &contd);
+ if (r < 0) {
+ dev_err(&sn->ndev->dev, "ret %d -> queue %x\n",
+ r, event);
+ _queue_evt(&sn->rxq, event);
+ break;
+ }
+ } else {
+ dev_err(&sn->ndev->dev,
+ "IPC not work, skip event %x\n", event);
+ }
+ event = _dequeue_evt(&sn->rxq);
+ }
+
+ if (contd > 0)
+ queue_delayed_work(sn->wq, &sn->work_rx, 0);
+
+ switch (r) {
+ case -EINVAL:
+ dev_err(&sn->ndev->dev, "Invalid argument\n");
+ break;
+ case -EBADMSG:
+ dev_err(&sn->ndev->dev, "Bad message, purge the buffer\n");
+ break;
+ case -ETIMEDOUT:
+ dev_err(&sn->ndev->dev, "Timed out\n");
+ break;
+ default:
+
+ break;
+ }
+
+ stat.st_wq_state = 2;
+
+ d = cpu_clock(smp_processor_id()) - t;
+ _dbg(&sn->ndev->dev, "read_time %llu ns\n", d);
+ if (d > time_max_read)
+ time_max_read = d;
+}
+
+static void svnet_write_wq(struct work_struct *work)
+{
+ struct svnet *sn = container_of(work,
+ struct svnet, work_write.work);
+ int r;
+ unsigned long long t, d;
+
+ t = cpu_clock(smp_processor_id());
+ if (tmp_xtow) {
+ d = t - tmp_xtow;
+ _dbg(&sn->ndev->dev, "xmit_to_write %llu ns\n", d);
+ tmp_xtow = 0;
+ if (d > time_max_xtow)
+ time_max_xtow = d;
+ }
+
+ dev_dbg(&sn->ndev->dev, "%s\n", __func__);
+ stat.st_do_write++;
+
+ stat.st_wq_state = 3;
+ if (sn->si)
+ r = sipc_write(sn->si, &sn->txq);
+ else {
+ skb_queue_purge(&sn->txq);
+ dev_err(&sn->ndev->dev, "IPC not work, drop packet\n");
+ r = 0;
+ }
+
+ switch (r) {
+ case -ENOSPC:
+ dev_err(&sn->ndev->dev, "buffer is full, wait...\n");
+ queue_delayed_work(sn->wq, &sn->work_write, HZ/10);
+ break;
+ case -EINVAL:
+ dev_err(&sn->ndev->dev, "Invalid arugment\n");
+ break;
+ case -ENXIO:
+ dev_err(&sn->ndev->dev, "IPC not work, purge the queue\n");
+ break;
+ case -ETIMEDOUT:
+ dev_err(&sn->ndev->dev, "Timed out\n");
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ stat.st_wq_state = 4;
+ d = cpu_clock(smp_processor_id()) - t;
+ _dbg(&sn->ndev->dev, "write_time %llu ns\n", d);
+ if (d > time_max_write)
+ time_max_write = d;
+}
+
+static void svnet_rx_wq(struct work_struct *work)
+{
+ struct svnet *sn = container_of(work,
+ struct svnet, work_rx.work);
+ int r = 0;
+
+ dev_dbg(&sn->ndev->dev, "%s\n", __func__);
+ stat.st_do_rx++;
+
+ stat.st_wq_state = 5;
+ if (sn->si)
+ r = sipc_rx(sn->si);
+
+ if (r > 0)
+ queue_delayed_work(sn->wq, &sn->work_rx, HZ/10);
+
+ stat.st_wq_state = 6;
+}
+
+static char *uevent_envs[SVNET_MAX] = {
+ "",
+ "MAILBOX=cp_reset", /* reset */
+ "MAILBOX=cp_exit", /* exit */
+};
+static void svnet_exit_wq(struct work_struct *work)
+{
+ struct svnet *sn = container_of(work,
+ struct svnet, work_exit);
+ char *envs[2] = { NULL, NULL };
+
+ dev_dbg(&sn->ndev->dev, "%s: %d\n", __func__, sn->exit_flag);
+
+ if (sn->exit_flag == SVNET_NORMAL || sn->exit_flag >= SVNET_MAX)
+ return;
+
+ envs[0] = uevent_envs[sn->exit_flag];
+ kobject_uevent_env(&sn->ndev->dev.kobj, KOBJ_OFFLINE, envs);
+
+ _queue_purge(&sn->rxq);
+ skb_queue_purge(&sn->txq);
+
+ if (sn->exit_flag == SVNET_EXIT)
+ sipc_ramdump(sn->si);
+
+#if 0
+ rtnl_lock();
+ if (netif_running(sn->ndev))
+ dev_close(sn->ndev);
+ rtnl_unlock();
+#endif
+}
+
+static inline void _init_data(struct svnet *sn)
+{
+ INIT_WORK(&sn->work_read, svnet_read_wq);
+ INIT_DELAYED_WORK(&sn->work_write, svnet_write_wq);
+ INIT_DELAYED_WORK(&sn->work_rx, svnet_rx_wq);
+ INIT_WORK(&sn->work_exit, svnet_exit_wq);
+
+ INIT_LIST_HEAD(&sn->rxq.list);
+ spin_lock_init(&sn->rxq.lock);
+ sn->rxq.len = 0;
+ skb_queue_head_init(&sn->txq);
+}
+
+static void _free(struct svnet *sn)
+{
+ if (!sn)
+ return;
+
+ _wake_lock_destroy(sn);
+
+ if (sn->group)
+ sysfs_remove_group(&sn->ndev->dev.kobj, &svnet_group);
+
+ if (sn->wq) {
+ flush_workqueue(sn->wq);
+ destroy_workqueue(sn->wq);
+ }
+
+ if (sn->si) {
+ sipc_close(&sn->si);
+ sipc_exit();
+ }
+
+ if (sn->ndev)
+ unregister_netdev(sn->ndev);
+
+ // sn is ndev's priv
+ free_netdev(sn->ndev);
+}
+
+static int __init svnet_init(void)
+{
+ int r;
+ struct svnet *sn = NULL;
+ struct net_device *ndev;
+
+ printk("[%s]\n",__func__);
+ ndev = alloc_netdev(sizeof(struct svnet), "svnet%d", svnet_setup);
+ if (!ndev) {
+ r = -ENOMEM;
+ goto err;
+ }
+ netif_stop_queue(ndev);
+ sn = netdev_priv(ndev);
+
+ _wake_lock_init(sn);
+
+ r = register_netdev(ndev);
+ if (r) {
+ dev_err(&ndev->dev, "failed to register netdev\n");
+ goto err;
+ }
+ sn->ndev = ndev;
+
+ _init_data(sn);
+
+ sn->wq = create_workqueue("svnetd");
+ if (!sn->wq) {
+ dev_err(&ndev->dev, "failed to create a workqueue\n");
+ goto err;
+ }
+
+ r = sysfs_create_group(&sn->ndev->dev.kobj, &svnet_group);
+ if (r) {
+ dev_err(&ndev->dev, "failed to create sysfs group\n");
+ goto err;
+ }
+ sn->group = &svnet_group;
+
+ dev_dbg(&ndev->dev, "Svnet dev: %p\n", sn);
+ svnet_dev = sn;
+
+ return 0;
+
+err:
+ _free(sn);
+ return r;
+}
+
+static void __exit svnet_exit(void)
+{
+
+ _free(svnet_dev);
+ svnet_dev = NULL;
+}
+
+module_init(svnet_init);
+module_exit(svnet_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Suchang Woo <suchang.woo@samsung.com>");
+MODULE_DESCRIPTION("Samsung Virtual network interface");
diff --git a/drivers/misc/samsung_modemctl/svnet/main.h b/drivers/misc/samsung_modemctl/svnet/main.h
new file mode 100644
index 0000000..171115f
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/svnet/main.h
@@ -0,0 +1,7 @@
+/*add interface for sipc files*/
+
+void _non_fmt_wakelock_timeout(void);
+
+void _fmt_wakelock_timeout(void);
+
+
diff --git a/drivers/misc/samsung_modemctl/svnet/pdp.c b/drivers/misc/samsung_modemctl/svnet/pdp.c
new file mode 100644
index 0000000..3065871
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/svnet/pdp.c
@@ -0,0 +1,110 @@
+/**
+ * Samsung Virtual Network driver using OneDram device
+ *
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+//#define DEBUG
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+
+#include "pdp.h"
+
+extern int vnet_start_xmit(struct sk_buff *skb, struct net_device *ndev);
+
+static int vnet_open(struct net_device *ndev)
+{
+ netif_start_queue(ndev);
+ return 0;
+}
+
+static int vnet_stop(struct net_device *ndev)
+{
+ netif_stop_queue(ndev);
+ return 0;
+}
+
+static void vnet_tx_timeout(struct net_device *ndev)
+{
+ ndev->trans_start = jiffies;
+ ndev->stats.tx_errors++;
+ netif_wake_queue(ndev);
+}
+
+static struct net_device_ops vnet_ops = {
+ .ndo_open = vnet_open,
+ .ndo_stop = vnet_stop,
+// .ndo_tx_timeout = vnet_tx_timeout,
+// .ndo_start_xmit = vnet_start_xmit,
+};
+
+static void vnet_setup(struct net_device *ndev)
+{
+ vnet_ops.ndo_start_xmit = vnet_start_xmit;
+
+ ndev->netdev_ops = &vnet_ops;
+ ndev->type = ARPHRD_PPP;
+ ndev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ ndev->hard_header_len = 0;
+ ndev->addr_len = 0;
+ ndev->tx_queue_len = 1000;
+ ndev->mtu = ETH_DATA_LEN;
+ ndev->watchdog_timeo = 5 * HZ;
+}
+
+struct net_device* create_pdp(int channel, struct net_device *parent)
+{
+ int r;
+ struct pdp_priv *priv;
+ struct net_device *ndev;
+ char devname[IFNAMSIZ];
+
+ if (!parent)
+ return ERR_PTR(-EINVAL);
+
+ sprintf(devname, "pdp%d", channel - 1);
+ ndev = alloc_netdev(sizeof(struct pdp_priv), devname, vnet_setup);
+ if (!ndev)
+ return ERR_PTR(-ENOMEM);
+
+ priv = netdev_priv(ndev);
+ priv->channel = channel;
+ priv->parent = parent;
+
+ r = register_netdev(ndev);
+ if (r) {
+ free_netdev(ndev);
+ return ERR_PTR(r);
+ }
+
+ return ndev;
+}
+
+void destroy_pdp(struct net_device **ndev)
+{
+ if (!ndev || !*ndev)
+ return;
+
+ unregister_netdev(*ndev);
+ free_netdev(*ndev);
+ *ndev = NULL;
+}
+
diff --git a/drivers/misc/samsung_modemctl/svnet/pdp.h b/drivers/misc/samsung_modemctl/svnet/pdp.h
new file mode 100644
index 0000000..77ec683
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/svnet/pdp.h
@@ -0,0 +1,34 @@
+/**
+ * SAMSUNG MODEM IPC header
+ *
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __PACKET_DATA_PROTOCOL_H__
+#define __PACKET_DATA_PROTOCOL_H__
+
+#include <linux/netdevice.h>
+
+struct pdp_priv {
+ int channel;
+ struct net_device *parent;
+};
+
+extern struct net_device* create_pdp(int channel, struct net_device *parent);
+extern void destroy_pdp(struct net_device **);
+
+#endif /* __PACKET_DATA_PROTOCOL_H__ */
diff --git a/drivers/misc/samsung_modemctl/svnet/sipc.h b/drivers/misc/samsung_modemctl/svnet/sipc.h
new file mode 100644
index 0000000..16e74c8
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/svnet/sipc.h
@@ -0,0 +1,64 @@
+/**
+ * SAMSUNG MODEM IPC header
+ *
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __SAMSUNG_IPC_H__
+#define __SAMSUNG_IPC_H__
+
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+extern const char *sipc_version;
+
+/*
+#if 1
+# include "sipc4.h"
+#else
+# error "Unknown version"
+#endif
+*/
+
+#define SIPC_RESET_MB 0xFFFFFF7E /* -2 & ~(INT_VALID) */
+#define SIPC_EXIT_MB 0xFFFFFF7F /* -1 & ~(INT_VALID) */
+
+struct sipc;
+
+extern struct sipc* sipc_open(void (*queue)(u32 mailbox, void *data),
+ struct net_device *ndev);
+extern void sipc_close(struct sipc **);
+
+extern void sipc_exit(void);
+
+extern int sipc_write(struct sipc *, struct sk_buff_head *);
+extern int sipc_read(struct sipc *, u32 mailbox, int *cond);
+extern int sipc_rx(struct sipc *);
+
+
+/* TODO: use PN_CMD ?? */
+extern int sipc_check_skb(struct sipc *, struct sk_buff *skb);
+extern int sipc_do_cmd(struct sipc *, struct sk_buff *skb);
+
+extern ssize_t sipc_debug_show(struct sipc *, char *);
+extern int sipc_debug(struct sipc *, const char *);
+extern int sipc_whitelist(struct sipc *si, const char *buf, size_t count);
+
+extern void sipc_ramdump(struct sipc *);
+
+#endif /* __SAMSUNG_IPC_H__ */
diff --git a/drivers/misc/samsung_modemctl/svnet/sipc4.c b/drivers/misc/samsung_modemctl/svnet/sipc4.c
new file mode 100644
index 0000000..becf032
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/svnet/sipc4.c
@@ -0,0 +1,2069 @@
+/**
+ * SAMSUNG MODEM IPC version 4
+ *
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+//#define DEBUG
+
+#if defined(DEBUG)
+//# define NOISY_DEBUG
+#endif
+
+#include "pdp.h"
+#include "sipc.h"
+#include "sipc4.h"
+#include "main.h"
+
+#include <linux/circ_buf.h>
+#include <linux/workqueue.h>
+#include <asm/errno.h>
+
+#include <net/sock.h>
+#include <linux/if_ether.h>
+#include <linux/phonet.h>
+#include <net/phonet/phonet.h>
+
+#include "../onedram/onedram.h"
+
+#if defined(CONFIG_KERNEL_DEBUG_SEC)
+#include <linux/kernel_sec_common.h>
+#define ERRMSG "Unknown CP Crash"
+static char cp_errmsg[65];
+static void _go_dump(struct sipc *si);
+#else
+#define _go_dump(si) do { } while(0)
+#endif
+
+#if defined(NOISY_DEBUG)
+static struct device *_dev;
+# define _dbg(format, arg...) \
+ dev_dbg(_dev, format, ## arg)
+#else
+# define _dbg(format, arg...) \
+ do { } while (0)
+#endif
+
+const char *sipc_version = "4.1";
+
+static const char hdlc_start[1] = { HDLC_START };
+static const char hdlc_end[1] = { HDLC_END };
+
+struct mailbox_data {
+ u16 mask_send;
+ u16 mask_req_ack;
+ u16 mask_res_ack;
+};
+
+static struct mailbox_data mb_data[IPCIDX_MAX] = {
+ {
+ .mask_send = MBD_SEND_FMT,
+ .mask_req_ack = MBD_REQ_ACK_FMT,
+ .mask_res_ack = MBD_RES_ACK_FMT,
+ },
+ {
+ .mask_send = MBD_SEND_RAW,
+ .mask_req_ack = MBD_REQ_ACK_RAW,
+ .mask_res_ack = MBD_RES_ACK_RAW,
+ },
+ {
+ .mask_send = MBD_SEND_RFS,
+ .mask_req_ack = MBD_REQ_ACK_RFS,
+ .mask_res_ack = MBD_RES_ACK_RFS,
+ },
+};
+
+/* semaphore latency */
+unsigned long long time_max_semlat;
+//static volatile unsigned long *TCNT = (unsigned long *)0xF520000C;
+
+struct sipc;
+struct ringbuf;
+
+struct ringbuf_info {
+ unsigned int out_off;
+ unsigned int in_off;
+ unsigned int size;
+ int (*read)(struct sipc *si, int inbuf, struct ringbuf *rb);
+};
+
+struct ringbuf {
+ unsigned char *out_base;
+ unsigned char *in_base;
+ struct ringbuf_cont *cont;
+ struct ringbuf_info *info;
+};
+#define rb_size info->size
+#define rb_read info->read
+#define rb_out_head cont->out_head
+#define rb_out_tail cont->out_tail
+#define rb_in_head cont->in_head
+#define rb_in_tail cont->in_tail
+
+
+static int _read_fmt(struct sipc *si, int inbuf, struct ringbuf *rb);
+static int _read_raw(struct sipc *si, int inbuf, struct ringbuf *rb);
+static int _read_rfs(struct sipc *si, int inbuf, struct ringbuf *rb);
+
+static struct ringbuf_info rb_info[IPCIDX_MAX] = {
+ {
+ .out_off = FMT_OUT,
+ .in_off = FMT_IN,
+ .size = FMT_SZ,
+ .read = _read_fmt,
+ },
+ {
+ .out_off = RAW_OUT,
+ .in_off = RAW_IN,
+ .size = RAW_SZ,
+ .read = _read_raw,
+ },
+ {
+ .out_off = RFS_OUT,
+ .in_off = RFS_IN,
+ .size = RFS_SZ,
+ .read = _read_rfs,
+ },
+};
+
+#define FRAG_BLOCK_MAX (PAGE_SIZE - sizeof(struct list_head) \
+ - sizeof(u32) - sizeof(char *))
+struct frag_block {
+ struct list_head list;
+ u32 len;
+ char *ptr;
+ char buf[FRAG_BLOCK_MAX];
+};
+
+struct frag_list {
+ struct list_head list;
+ u8 msg_id;
+ u32 len;
+ // timeout??
+ struct list_head block_head;
+};
+
+struct frag_head {
+ struct list_head head;
+ unsigned long bitmap[FMT_ID_SIZE/BITS_PER_LONG];
+};
+
+struct frag_info {
+ struct sk_buff *skb;
+ unsigned int offset;
+ u8 msg_id;
+};
+
+struct sipc {
+ struct sipc_mapped *map;
+ struct ringbuf rb[IPCIDX_MAX];
+
+ struct resource *res;
+
+ void (*queue)(u32, void *);
+ void *queue_data;
+
+ /* for fragmentation */
+ u8 msg_id;
+ char *frag_buf;
+ struct frag_info frag;
+
+ /* for merging */
+ struct frag_head frag_map;
+
+ int od_rel; /* onedram authority release */
+
+ struct net_device *svndev;
+
+ const struct attribute_group *group;
+
+ struct sk_buff_head rfs_rx;
+};
+
+/* sizeof(struct phonethdr) + NET_SKB_PAD > SMP_CACHE_BYTES */
+//#define RFS_MTU (PAGE_SIZE - sizeof(struct phonethdr) - NET_SKB_PAD)
+/* SMP_CACHE_BYTES > sizeof(struct phonethdr) + NET_SKB_PAD */
+#define RFS_MTU (PAGE_SIZE - SMP_CACHE_BYTES)
+#define RFS_TX_RATE 4
+
+/* set at storage device */
+unsigned int factory_test_force_sleep = 0;
+EXPORT_SYMBOL(factory_test_force_sleep);
+
+/* from mach-XX.c */
+extern unsigned int HWREV;
+
+/* TODO: move PDP related codes to other source file */
+static DEFINE_MUTEX(pdp_mutex);
+static struct net_device *pdp_devs[PDP_MAX];
+static int pdp_cnt;
+unsigned long pdp_bitmap[PDP_MAX/BITS_PER_LONG];
+
+static void clear_pdp_wq(struct work_struct *work);
+static DECLARE_WORK(pdp_work, clear_pdp_wq);
+
+static ssize_t show_act(struct device *d,
+ struct device_attribute *attr, char *buf);
+static ssize_t show_deact(struct device *d,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_act(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t store_deact(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t show_suspend(struct device *d,
+ struct device_attribute *attr, char *buf);
+static ssize_t store_suspend(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t store_resume(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static DEVICE_ATTR(activate, 0664, show_act, store_act);
+static DEVICE_ATTR(deactivate, 0664, show_deact, store_deact);
+static DEVICE_ATTR(suspend, 0664, show_suspend, store_suspend);
+static DEVICE_ATTR(resume, 0664, NULL, store_resume);
+
+static struct attribute *pdp_attributes[] = {
+ &dev_attr_activate.attr,
+ &dev_attr_deactivate.attr,
+ &dev_attr_suspend.attr,
+ &dev_attr_resume.attr,
+ NULL
+};
+
+static const struct attribute_group pdp_group = {
+ .name = "pdp",
+ .attrs = pdp_attributes,
+};
+
+
+#if defined(NOISY_DEBUG)
+#define DUMP_LIMIT 32
+static char dump_buf[64];
+void _dbg_dump(u8 *buf, int size)
+{
+ int i;
+ int len = 0;
+
+ if (!buf)
+ return;
+
+ if (size > DUMP_LIMIT)
+ size = DUMP_LIMIT;
+
+ for (i=0;i<32 && i<size;i++) {
+ len += sprintf(&dump_buf[len], "%02x ", buf[i]);
+ if ((i & 0xf) == 0xf) {
+ dump_buf[len] = '\0';
+ _dbg("dump %04x [ %s]\n", (i>>4), dump_buf);
+ len = 0;
+ }
+ }
+ if (len) {
+ dump_buf[len] = '\0';
+ _dbg("dump %04x [ %s]\n", i, dump_buf);
+ }
+}
+#else
+# define _dbg_dump(buf, size) do { } while(0)
+#endif
+
+static u8 _get_msg_id(struct sipc *si)
+{
+ if (!si)
+ return 0;
+
+ si->msg_id = (si->msg_id + 1) & FMT_ID_MASK;
+
+ return si->msg_id;
+}
+
+static int _get_auth(void)
+{
+ int r;
+ unsigned long long t, d;
+
+ t = cpu_clock(smp_processor_id());
+
+ r = onedram_get_auth(MB_CMD(MBC_REQ_SEM)); // wait for completion
+
+ d = cpu_clock(smp_processor_id()) - t;
+ if (d > time_max_semlat)
+ time_max_semlat = d;
+
+ return r;
+}
+
+static void _put_auth(struct sipc *si)
+{
+ if (!si)
+ return;
+
+ onedram_put_auth(0);
+
+ if (si->od_rel && !onedram_rel_sem()) {
+ onedram_write_mailbox(MB_CMD(MBC_RES_SEM));
+ si->od_rel = 0;
+ }
+}
+
+static inline void _req_rel_auth(struct sipc *si)
+{
+ si->od_rel = 1;
+}
+
+static int _get_auth_try(void)
+{
+ return onedram_get_auth(0);
+}
+
+static void _check_buffer(struct sipc *si)
+{
+ int i;
+ u32 mailbox;
+
+#if 0
+ i = onedram_read_sem();
+ if (i != 0x1)
+ return;
+#endif
+ i = _get_auth_try();
+ if (i)
+ return;
+
+ mailbox = 0;
+
+ for (i=0;i<IPCIDX_MAX;i++) {
+ int inbuf;
+ struct ringbuf *rb;
+
+ rb = &si->rb[i];
+ inbuf = CIRC_CNT(rb->rb_in_head, rb->rb_in_tail, rb->rb_size);
+ if (!inbuf)
+ continue;
+
+ mailbox |= mb_data[i].mask_send;
+ }
+ _put_auth(si);
+
+ if (mailbox)
+ si->queue(MB_DATA(mailbox), si->queue_data);
+}
+
+static void _do_command(struct sipc *si, u32 mailbox)
+{
+ int r;
+ u32 cmd = (mailbox & MBC_MASK) & ~(MB_CMD(0));
+
+// dev_dbg(&si->svndev->dev, "Command: %x\n", cmd);
+
+ switch(cmd) {
+ case MBC_REQ_SEM:
+ r = onedram_rel_sem();
+ if (r) {
+ dev_dbg(&si->svndev->dev, "onedram in use, "
+ "defer releasing semaphore\n");
+ _req_rel_auth(si);
+ }
+ else
+ onedram_write_mailbox(MB_CMD(MBC_RES_SEM));
+ break;
+ case MBC_RES_SEM:
+ /* do nothing */
+ break;
+ case MBC_PHONE_START:
+ onedram_write_mailbox(MB_CMD(MBC_INIT_END) | CP_BOOT_AIRPLANE
+ | AP_OS_ANDROID);
+ break;
+ case MBC_RESET:
+ printk("svnet reset mailbox msg : 0x%08x\n", mailbox);
+ si->queue(SIPC_RESET_MB, si->queue_data);
+ break;
+ case MBC_ERR_DISPLAY:
+ printk("svnet error display mailbox msg : 0x%08x\n", mailbox);
+ si->queue(SIPC_EXIT_MB, si->queue_data);
+ break;
+ /* TODO : impletment other commands... */
+ default:
+ /* do nothing */
+
+ break;
+ }
+}
+
+void sipc_handler(u32 mailbox, void *data)
+{
+ struct sipc *si = (struct sipc *)data;
+
+ if (!si || !si->queue)
+ return;
+
+ dev_dbg(&si->svndev->dev, "recv mailbox %x\n", mailbox);
+
+#if defined(CONFIG_KERNEL_DEBUG_SEC)
+ if (mailbox == KERNEL_SEC_DUMP_AP_DEAD_ACK) {
+ // deal ack for ap crash that indicated to cp
+ kernel_sec_set_cp_ack();
+ }
+#endif
+
+ if ((mailbox & MB_VALID) == 0) {
+ dev_err(&si->svndev->dev, "Invalid mailbox message: %x\n", mailbox);
+ return;
+ }
+
+ if (mailbox & MB_COMMAND) {
+ _check_buffer(si); // check buffer for missing interrupt
+ _do_command(si, mailbox);
+ return;
+ }
+
+ si->queue(mailbox, si->queue_data);
+}
+
+static inline void _init_data(struct sipc *si, unsigned char *base)
+{
+ int i;
+
+ si->map = (struct sipc_mapped *)base;
+ si->map->magic = 0x0;
+ si->map->access = 0x0;
+ si->map->hwrev = HWREV;
+
+ for (i=0;i<IPCIDX_MAX;i++) {
+ struct ringbuf *r = &si->rb[i];
+ struct ringbuf_info *info = &rb_info[i];
+ struct ringbuf_cont *cont = &si->map->rbcont[i];
+
+ r->out_base = base + info->out_off;
+ r->in_base = base + info->in_off;
+ r->info = info;
+ r->cont = cont;
+
+ cont->out_head = 0;
+ cont->out_tail = 0;
+ cont->in_head = 0;
+ cont->in_tail = 0;
+ }
+}
+
+static void _init_proc(struct sipc *si)
+{
+ u32 mailbox;
+ int r;
+
+ r = onedram_read_mailbox(&mailbox);
+ if (r)
+ return;
+
+ sipc_handler(mailbox, si);
+}
+
+struct sipc* sipc_open(void (*queue)(u32, void*), struct net_device *ndev)
+{
+ struct sipc *si;
+ struct resource *res;
+ int r;
+ void * onedram_vbase;
+
+ if (!queue || !ndev)
+ return ERR_PTR(-EINVAL);
+
+ si = kzalloc(sizeof(struct sipc), GFP_KERNEL);
+ if (!si)
+ return ERR_PTR(-ENOMEM);
+
+ /* If FMT_SZ grown up, MUST be changed!! */
+ si->frag_buf = kmalloc(FMT_SZ, GFP_KERNEL);
+ if (!si->frag_buf) {
+ sipc_close(&si);
+ return ERR_PTR(-ENOMEM);
+ }
+ INIT_LIST_HEAD(&si->frag_map.head);
+
+ res = onedram_request_region(0, SIPC_MAP_SIZE, SIPC_NAME);
+ if (!res) {
+ sipc_close(&si);
+ return ERR_PTR(-EBUSY);
+ }
+ si->res = res;
+
+ r = onedram_register_handler(sipc_handler, si);
+ if (r) {
+ sipc_close(&si);
+ return ERR_PTR(r);
+ }
+ si->queue = queue;
+ si->queue_data = ndev;
+ si->svndev = ndev;
+
+ /* TODO: need?? */
+ if (work_pending(&pdp_work))
+ flush_work(&pdp_work);
+
+ r = sysfs_create_group(&si->svndev->dev.kobj, &pdp_group);
+ if (r) {
+ sipc_close(&si);
+ return ERR_PTR(r);
+ }
+ si->group = &pdp_group;
+
+#if defined(NOISY_DEBUG)
+ _dev = &si->svndev->dev;
+#endif
+
+ onedram_get_vbase(&onedram_vbase);
+
+ // use io remapped address rather than request_region
+ if (onedram_vbase) {
+ _init_data(si, (unsigned char *)onedram_vbase);
+ }else {
+ _init_data(si, (unsigned char *)res->start);
+ }
+
+ skb_queue_head_init(&si->rfs_rx);
+
+ /* process init message */
+ _init_proc(si);
+
+ return si;
+}
+
+static void clear_pdp_wq(struct work_struct *work)
+{
+ int i;
+
+ mutex_lock(&pdp_mutex);
+
+ for (i=0;i<sizeof(pdp_devs)/sizeof(pdp_devs[0]);i++) {
+ if (pdp_devs[i]) {
+ destroy_pdp(&pdp_devs[i]);
+ clear_bit(i, pdp_bitmap);
+ }
+ }
+ pdp_cnt = 0;
+
+ mutex_unlock(&pdp_mutex);
+}
+
+void sipc_exit(void)
+{
+ if (work_pending(&pdp_work))
+ flush_work(&pdp_work);
+ else
+ clear_pdp_wq(NULL);
+}
+
+void sipc_close(struct sipc **psi)
+{
+ struct sipc *si;
+
+ if (!psi || !*psi)
+ return;
+
+ si = *psi;
+
+ if (si->group && si->svndev) {
+ int i;
+ sysfs_remove_group(&si->svndev->dev.kobj, si->group);
+
+ mutex_lock(&pdp_mutex);
+ for (i=0;i<sizeof(pdp_devs)/sizeof(pdp_devs[0]);i++) {
+ if (pdp_devs[i])
+ netif_stop_queue(pdp_devs[i]);
+ }
+ mutex_unlock(&pdp_mutex);
+ schedule_work(&pdp_work);
+ }
+
+ if (si->frag_buf)
+ kfree(si->frag_buf);
+
+ if (si->queue)
+ onedram_unregister_handler(sipc_handler);
+
+ if (si->res)
+ onedram_release_region(0, SIPC_MAP_SIZE);
+
+ kfree(si);
+ *psi = NULL;
+}
+
+static inline void _wake_queue(int idx)
+{
+ mutex_lock(&pdp_mutex);
+
+ if (pdp_devs[idx] && !test_bit(idx, pdp_bitmap))
+ netif_wake_queue(pdp_devs[idx]);
+
+ mutex_unlock(&pdp_mutex);
+}
+
+static int __write(struct ringbuf *rb, u8 *buf, unsigned int size)
+{
+ int c;
+ int len = 0;
+
+ // no check space
+
+ _dbg("%s b: size %u head %u tail %u\n", __func__,
+ size, rb->rb_out_head, rb->rb_out_tail);
+ _dbg_dump(buf, size);
+
+ while(1) {
+ c = CIRC_SPACE_TO_END(rb->rb_out_head, rb->rb_out_tail, rb->rb_size);
+ if(size < c)
+ c = size;
+ if(c <= 0)
+ break;
+ memcpy(rb->out_base + rb->rb_out_head, buf, c);
+ rb->rb_out_head = (rb->rb_out_head + c) & (rb->rb_size - 1);
+ buf += c;
+ size -= c;
+ len += c;
+ }
+
+ _dbg("%s a: size %u head %u tail %u\n", __func__,
+ len, rb->rb_out_head, rb->rb_out_tail);
+
+ return len;
+}
+
+static inline void _set_raw_hdr(struct raw_hdr *h, int res,
+ unsigned int len, int control)
+{
+ h->len = len;
+ h->channel = CHID(res);
+ h->control = 0;
+}
+
+static int _write_raw_buf(struct ringbuf *rb, int res, struct sk_buff *skb)
+{
+ int len;
+ struct raw_hdr h;
+
+ _dbg("%s: packet %p res 0x%02x\n", __func__, skb, res);
+
+ len = skb->len + sizeof(h);
+
+ _set_raw_hdr(&h, res, len, 0);
+
+ len = __write(rb, (u8 *)hdlc_start, sizeof(hdlc_start));
+ len += __write(rb, (u8 *)&h, sizeof(h));
+ len += __write(rb, skb->data, skb->len);
+ len += __write(rb, (u8 *)hdlc_end, sizeof(hdlc_end));
+
+ return len;
+}
+
+static int _write_raw_skb(struct ringbuf *rb, int res, struct sk_buff *skb)
+{
+ char *b;
+
+ _dbg("%s: packet %p res 0x%02x\n", __func__, skb, res);
+
+ b = skb_put(skb, sizeof(hdlc_end));
+ memcpy(b, hdlc_end, sizeof(hdlc_end));
+
+ b = skb_push(skb, sizeof(struct raw_hdr) + sizeof(hdlc_start));
+ memcpy(b, hdlc_start, sizeof(hdlc_start));
+
+ b += sizeof(hdlc_start);
+
+ _set_raw_hdr((struct raw_hdr *)b, res,
+ skb->len - sizeof(hdlc_start) - sizeof(hdlc_end), 0);
+
+ return __write(rb, skb->data, skb->len);
+}
+
+static int _write_raw(struct ringbuf *rb, struct sk_buff *skb, int res)
+{
+ int len;
+ int space;
+
+ space = CIRC_SPACE(rb->rb_out_head, rb->rb_out_tail, rb->rb_size);
+ if(space < skb->len + sizeof(struct raw_hdr)
+ + sizeof(hdlc_start) + sizeof(hdlc_end))
+ return -ENOSPC;
+
+ if(skb_headroom(skb) > (sizeof(struct raw_hdr) + sizeof(hdlc_start))
+ && skb_tailroom(skb) > sizeof(hdlc_end)) {
+ len = _write_raw_skb(rb, res, skb);
+ } else {
+ len = _write_raw_buf(rb, res, skb);
+ }
+
+ if (res >= PN_PDP_START && res <= PN_PDP_END)
+ _wake_queue(PDP_ID(res));
+ else
+ netif_wake_queue(skb->dev);
+ return len;
+}
+
+static int _write_rfs_buf(struct ringbuf *rb, struct sk_buff *skb)
+{
+ int len;
+
+ _dbg("%s: packet %p\n", __func__, skb);
+ len = __write(rb, (u8 *)hdlc_start, sizeof(hdlc_start));
+ len += __write(rb, skb->data, skb->len);
+ len += __write(rb, (u8 *)hdlc_end, sizeof(hdlc_end));
+
+ return len;
+}
+
+static int _write_rfs_skb(struct ringbuf *rb, struct sk_buff *skb)
+{
+ char *b;
+
+ _dbg("%s: packet %p\n", __func__, skb);
+ b = skb_put(skb, sizeof(hdlc_end));
+ memcpy(b, hdlc_end, sizeof(hdlc_end));
+
+ b = skb_push(skb, sizeof(hdlc_start));
+ memcpy(b, hdlc_start, sizeof(hdlc_start));
+
+ return __write(rb, skb->data, skb->len);
+}
+
+static int _write_rfs(struct ringbuf *rb, struct sk_buff *skb)
+{
+ int len;
+ int space;
+
+ space = CIRC_SPACE(rb->rb_out_head, rb->rb_out_tail, rb->rb_size);
+ if(space < skb->len + sizeof(hdlc_start) + sizeof(hdlc_end))
+ return -ENOSPC;
+
+ if(skb_headroom(skb) > sizeof(hdlc_start)
+ && skb_tailroom(skb) > sizeof(hdlc_end)) {
+ len = _write_rfs_skb(rb, skb);
+ } else {
+ len = _write_rfs_buf(rb, skb);
+ }
+
+ netif_wake_queue(skb->dev);
+ return len;
+}
+
+static int _write_fmt_buf(char *frag_buf, struct ringbuf *rb,
+ struct sk_buff *skb, struct frag_info *fi, int wlen,
+ u8 control)
+{
+ char *buf = frag_buf;
+ struct fmt_hdr *h;
+
+ memcpy(buf, hdlc_start, sizeof(hdlc_start));
+ buf += sizeof(hdlc_start);
+
+ h = (struct fmt_hdr *)buf;
+ h->len = sizeof(struct fmt_hdr) + wlen;
+ h->control = control;
+ buf += sizeof(struct fmt_hdr);
+
+ memcpy(buf, skb->data + fi->offset, wlen);
+ buf += wlen;
+
+ memcpy(buf, hdlc_end, sizeof(hdlc_end));
+ buf += sizeof(hdlc_end);
+
+ return __write(rb, frag_buf, buf - frag_buf);
+}
+
+static int _write_fmt(struct sipc *si, struct ringbuf *rb, struct sk_buff *skb)
+{
+ int len;
+ int space;
+ int remain;
+ struct frag_info *fi = &si->frag;
+
+ if (skb != fi->skb) {
+ /* new packet */
+ fi->skb = skb;
+// fi->msg_id = _get_msg_id(si);
+ fi->offset = 0;
+ }
+
+ len = 0;
+ remain = skb->len - fi->offset;
+
+ _dbg("%s: packet %p length %d sent %d\n", __func__, skb, skb->len, fi->offset);
+
+ while (remain > 0) {
+ int wlen;
+ u8 control;
+
+ space = CIRC_SPACE(rb->rb_out_head, rb->rb_out_tail, rb->rb_size);
+ space -= sizeof(struct fmt_hdr)
+ + sizeof(hdlc_start) + sizeof(hdlc_end);
+ if (space < FMT_TX_MIN)
+ return -ENOSPC;
+
+ if (remain > space) {
+ /* multiple frame */
+ wlen = space;
+ control = 0x1 | FMT_MB_MASK;
+ } else {
+ wlen = remain;
+ if (fi->offset == 0) {
+ /* single frame */
+ control = 0x0;
+ } else {
+ /* last frmae */
+ control = 0x1;
+ }
+ }
+
+ wlen = _write_fmt_buf(si->frag_buf, rb, skb, fi, wlen, control);
+ if (wlen < 0)
+ return wlen;
+
+ len += wlen;
+
+ wlen -= sizeof(hdlc_start) + sizeof(struct fmt_hdr)
+ + sizeof(hdlc_end);
+
+ fi->offset += wlen;
+ remain -= wlen;
+ }
+
+ if (len > 0) {
+ fi->skb = NULL;
+ fi->offset = 0;
+ }
+
+ netif_wake_queue(skb->dev);
+ return len; /* total write bytes */
+}
+
+static int _write(struct sipc *si, int res, struct sk_buff *skb, u32 *mailbox)
+{
+ int r;
+ int rid;
+
+ rid = res_to_ridx(res);
+ if(rid < 0 || rid >= IPCIDX_MAX)
+ return -EINVAL;
+
+ switch(rid) {
+ case IPCIDX_FMT:
+ r = _write_fmt(si, &si->rb[rid], skb);
+ break;
+ case IPCIDX_RAW:
+ r = _write_raw(&si->rb[rid], skb, res);
+ break;
+ case IPCIDX_RFS:
+ r = _write_rfs(&si->rb[rid], skb);
+ break;
+ default:
+ /* do nothing */
+ r = 0;
+ break;
+ }
+
+ if(r > 0)
+ *mailbox |= mb_data[rid].mask_send;
+
+ _dbg("%s: return %d\n", __func__, r);
+ return r;
+}
+
+static inline void _update_stat(struct net_device *ndev, unsigned int len)
+{
+ if(!ndev)
+ return;
+
+ ndev->stats.tx_bytes += len;
+ ndev->stats.tx_packets++;
+}
+
+static inline int _write_pn(struct sipc *si, struct sk_buff *skb, u32 *mb)
+{
+ int r;
+ struct phonethdr *ph;
+
+ ph = pn_hdr(skb);
+ skb_pull(skb, sizeof(struct phonethdr) + 1); // 1 is addr len
+
+ r = _write(si, ph->pn_res, skb, mb);
+ if (r < 0)
+ skb_push(skb, sizeof(struct phonethdr) + 1);
+
+ return r;
+}
+
+int sipc_write(struct sipc *si, struct sk_buff_head *sbh)
+{
+ int r;
+ u32 mailbox;
+ struct sk_buff *skb;
+
+ if (!sbh)
+ return -EINVAL;
+
+ if (!si) {
+ skb_queue_purge(sbh);
+ return -ENXIO;
+ }
+
+ r = _get_auth();
+ if (r) {
+ if (factory_test_force_sleep){
+ printk("tx ignored for factory force sleep\n");
+ skb_queue_purge(sbh);
+ return 0;
+ } else {
+ return r;
+ }
+ }
+
+ r = mailbox = 0;
+ skb = skb_dequeue(sbh);
+ while (skb) {
+ struct net_device *ndev = skb->dev;
+ int len = skb->len;
+
+ dev_dbg(&si->svndev->dev, "write packet %p\n", skb);
+
+ if (skb->protocol != __constant_htons(ETH_P_PHONET)) {
+ struct pdp_priv *priv;
+ priv = netdev_priv(ndev);
+ r = _write(si, PN_PDP(priv->channel), skb, &mailbox);
+ } else
+ r = _write_pn(si, skb, &mailbox);
+
+ if (r < 0)
+ break;
+
+ _update_stat(ndev, len);
+ dev_kfree_skb_any(skb);
+
+ skb = skb_dequeue(sbh);
+ }
+
+ _req_rel_auth(si);
+ _put_auth(si);
+
+ if(mailbox)
+ onedram_write_mailbox(MB_DATA(mailbox));
+
+ if (r < 0) {
+ if (r == -ENOSPC) {
+ dev_err(&si->svndev->dev,
+ "write nospc queue %p\n", skb);
+ skb_queue_head(sbh, skb);
+ netif_stop_queue(skb->dev);
+ } else {
+ dev_err(&si->svndev->dev,
+ "write err %d, drop %p\n", r, skb);
+ dev_kfree_skb_any(skb);
+ }
+ }
+
+ return r;
+}
+
+extern int __read(struct ringbuf *rb, unsigned char *buf, unsigned int size)
+{
+ int c;
+ int len = 0;
+ unsigned char *p = buf;
+
+ _dbg("%s b: size %u head %u tail %u\n", __func__,
+ size, rb->rb_in_head, rb->rb_in_tail);
+
+ while(1) {
+ c = CIRC_CNT_TO_END(rb->rb_in_head, rb->rb_in_tail, rb->rb_size);
+ if(size < c)
+ c = size;
+ if(c <= 0)
+ break;
+ if (p) {
+ memcpy(p, rb->in_base + rb->rb_in_tail, c);
+ p += c;
+ }
+ rb->rb_in_tail = (rb->rb_in_tail + c) & (rb->rb_size - 1);
+ size -= c;
+ len += c;
+ }
+
+ _dbg("%s a: size %u head %u tail %u\n", __func__,
+ len, rb->rb_in_head, rb->rb_in_tail);
+ _dbg_dump(buf, len);
+
+ return len;
+}
+
+static inline void _get_raw_hdr(struct raw_hdr *h, int *res,
+ unsigned int *len, int *control)
+{
+ if(res)
+ *res = PN_RAW(h->channel);
+ if(len)
+ *len = h->len;
+ if(control)
+ *control = h->control;
+}
+
+static inline void _phonet_rx(struct net_device *ndev,
+ struct sk_buff *skb, int res)
+{
+ int r;
+ struct phonethdr *ph;
+
+ skb->protocol = __constant_htons(ETH_P_PHONET);
+
+ ph = (struct phonethdr *)skb_push(skb, sizeof(struct phonethdr));
+ ph->pn_rdev = ndev->dev_addr[0];
+ ph->pn_sdev = 0;
+ ph->pn_res = res;
+ ph->pn_length = __cpu_to_be16(skb->len + 2 - sizeof(*ph));
+ ph->pn_robj = 0;
+ ph->pn_sobj = 0;
+
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += skb->len;
+
+ skb_reset_mac_header(skb);
+
+ r = netif_rx_ni(skb);
+ if (r != NET_RX_SUCCESS)
+ dev_err(&ndev->dev, "phonet rx error: %d\n", r);
+
+ _dbg("%s: res 0x%02x packet %p len %d\n", __func__, res, skb, skb->len);
+}
+
+static int _read_pn(struct net_device *ndev, struct ringbuf *rb, int len,
+ int res)
+{
+ int r;
+ struct sk_buff *skb;
+ char *p;
+ int read_len = len + sizeof(hdlc_end);
+
+ _dbg("%s: res 0x%02x data %d\n", __func__, res, len);
+
+ skb = netdev_alloc_skb(ndev, read_len + sizeof(struct phonethdr));
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ skb_reserve(skb, sizeof(struct phonethdr));
+
+ p = skb_put(skb, len);
+ r = __read(rb, p, read_len);
+ if (r != read_len) {
+ kfree_skb(skb);
+ return -EBADMSG;
+ }
+
+ _phonet_rx(ndev, skb, res);
+
+ return r;
+}
+
+static inline struct sk_buff* _alloc_phskb(struct net_device *ndev, int len)
+{
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(ndev, len + sizeof(struct phonethdr));
+ if (likely(skb))
+ skb_reserve(skb, sizeof(struct phonethdr));
+
+ return skb;
+}
+
+static inline int _alloc_rfs(struct net_device *ndev,
+ struct sk_buff_head *list, int len)
+{
+ int r = 0;
+ struct sk_buff *skb;
+
+ __skb_queue_head_init(list);
+
+ while (len > 0) {
+ skb = _alloc_phskb(ndev, RFS_MTU);
+ if (unlikely(!skb)) {
+ r = -ENOMEM;
+ break;
+ }
+ __skb_queue_tail(list, skb);
+ len -= RFS_MTU;
+ }
+
+ return r;
+}
+static void _free_rfs(struct sk_buff_head *list)
+{
+ struct sk_buff *skb;
+
+ skb = __skb_dequeue(list);
+ while (skb) {
+ __kfree_skb(skb);
+ skb = __skb_dequeue(list);
+ }
+}
+
+static inline int _read_rfs_rb(struct ringbuf *rb, int len,
+ struct sk_buff_head *list)
+{
+ int r;
+ int read_len;
+ struct sk_buff *skb;
+ char *p;
+
+ read_len = 0;
+ skb = list->next;
+ while (skb != (struct sk_buff *)list) {
+ int rd = RFS_MTU;
+
+ if (skb == list->next) /* first sk has header */
+ rd -= sizeof(struct rfs_hdr);
+
+ if (len < rd)
+ rd = len;
+
+ p = skb_put(skb, rd);
+ r = __read(rb, p, rd);
+ if (r != rd)
+ return -EBADMSG;
+
+ len -= r;
+ read_len += r;
+ skb = skb->next;
+ }
+
+ return read_len;
+}
+
+static int _read_rfs_data(struct sipc *si, struct ringbuf *rb, int len,
+ struct rfs_hdr *h)
+{
+ int r;
+ struct sk_buff_head list;
+ struct sk_buff *skb;
+ char *p;
+ int read_len;
+ struct net_device *ndev = si->svndev;
+
+ _dbg("%s: %d bytes\n", __func__, len);
+
+ /* alloc sk_buffs */
+ r = _alloc_rfs(ndev, &list, len + sizeof(struct rfs_hdr));
+ if (r)
+ goto free_skb;
+
+ skb = list.next;
+ p = skb_put(skb, sizeof(struct rfs_hdr));
+ memcpy(p, h, sizeof(struct rfs_hdr));
+
+ /* read data all */
+ r = _read_rfs_rb(rb, len, &list);
+ if (r < 0)
+ goto free_skb;
+
+ read_len = r;
+
+ /* move to rfs_rx queue */
+ skb = __skb_dequeue(&list);
+ while (skb) {
+// _phonet_rx(ndev, skb, PN_RFS);
+ skb_queue_tail(&si->rfs_rx, skb);
+ skb = __skb_dequeue(&list);
+ }
+
+ /* remove hdlc_end */
+ read_len += __read(rb, NULL, sizeof(hdlc_end));
+
+ return read_len;
+
+free_skb:
+ _free_rfs(&list);
+ return r;
+}
+
+static int _read_pdp(struct ringbuf *rb, int len,
+ int res)
+{
+ int r;
+ struct sk_buff *skb;
+ char *p;
+ int read_len = len + sizeof(hdlc_end);
+ struct net_device *ndev;
+
+ _dbg("%s: res 0x%02x data %d\n", __func__, res, len);
+
+ mutex_lock(&pdp_mutex);
+
+ ndev = pdp_devs[PDP_ID(res)];
+ if (!ndev) {
+ // drop data
+ r = __read(rb, NULL, read_len);
+ mutex_unlock(&pdp_mutex);
+ return r;
+ }
+
+ skb = netdev_alloc_skb(ndev, read_len);
+ if (unlikely(!skb)) {
+ mutex_unlock(&pdp_mutex);
+ return -ENOMEM;
+ }
+
+ p = skb_put(skb, len);
+ r = __read(rb, p, read_len);
+ if (r != read_len) {
+ mutex_unlock(&pdp_mutex);
+ kfree_skb(skb);
+ return -EBADMSG;
+ }
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += skb->len;
+
+ mutex_unlock(&pdp_mutex);
+
+ read_len = r;
+
+ skb->protocol = __constant_htons(ETH_P_IP);
+
+ skb_reset_mac_header(skb);
+
+ _dbg("%s: pdp packet %p len %d\n", __func__, skb, skb->len);
+
+ r = netif_rx_ni(skb);
+ if (r != NET_RX_SUCCESS)
+ dev_err(&ndev->dev, "pdp rx error: %d\n", r);
+
+ return read_len;
+}
+
+static int _read_raw(struct sipc *si, int inbuf, struct ringbuf *rb)
+{
+ int r;
+ char buf[sizeof(struct raw_hdr) + sizeof(hdlc_start)];
+ int res, data_len;
+ u32 tail;
+
+ while (inbuf > 0) {
+ tail = rb->rb_in_tail;
+
+ r = __read(rb, buf, sizeof(buf));
+ if (r < sizeof(buf) ||
+ strncmp(buf, hdlc_start, sizeof(hdlc_start))) {
+ dev_err(&si->svndev->dev, "Bad message: %c %d\n", buf[0], r);
+ return -EBADMSG;
+ }
+ inbuf -= r;
+
+ _get_raw_hdr((struct raw_hdr *)&buf[sizeof(hdlc_start)],
+ &res, &data_len, NULL);
+
+ data_len -= sizeof(struct raw_hdr);
+
+ if (res >= PN_PDP_START && res <= PN_PDP_END) {
+ r = _read_pdp(rb, data_len, res);
+ } else {
+ r = _read_pn(si->svndev, rb, data_len, res);
+ }
+
+ if (r < 0) {
+ if (r == -ENOMEM)
+ rb->rb_in_tail = tail;
+
+ return r;
+ }
+
+ inbuf -= r;
+ }
+
+ return 0;
+}
+
+static int _read_rfs(struct sipc *si, int inbuf, struct ringbuf *rb)
+{
+ int r;
+ char buf[sizeof(struct rfs_hdr) + sizeof(hdlc_start)];
+ int data_len;
+ u32 tail;
+ struct rfs_hdr *h;
+
+ h = (struct rfs_hdr *)&buf[sizeof(hdlc_start)];
+ while (inbuf > 0) {
+ tail = rb->rb_in_tail;
+
+ r = __read(rb, buf, sizeof(buf));
+ if (r < sizeof(buf) ||
+ strncmp(buf, hdlc_start, sizeof(hdlc_start))) {
+ dev_err(&si->svndev->dev, "Bad message: %c %d\n", buf[0], r);
+ return -EBADMSG;
+ }
+ inbuf -= r;
+
+ data_len = h->len - sizeof(struct rfs_hdr);
+
+ r = _read_rfs_data(si, rb, data_len, h);
+ if (r < 0) {
+ if (r == -ENOMEM)
+ rb->rb_in_tail = tail;
+
+ return r;
+ }
+
+ inbuf -= r;
+ }
+
+ return 0;
+}
+
+
+static struct frag_list* _find_frag_list(u8 control, struct frag_head *fh)
+{
+ struct frag_list *fl;
+ u8 msg_id = control & FMT_ID_MASK;
+
+ if (!test_bit(msg_id, fh->bitmap))
+ return NULL;
+
+ list_for_each_entry(fl, &fh->head, list) {
+ if (fl->msg_id == msg_id)
+ break;
+ }
+
+ return fl;
+}
+
+static int _fill_skb(struct sk_buff *skb, struct frag_list *fl)
+{
+ struct frag_block *fb, *n;
+ int offset = 0;
+ char *p;
+
+ list_for_each_entry_safe(fb, n, &fl->block_head, list) {
+ p = skb_put(skb, fb->len);
+ memcpy(p, fb->buf, fb->len);
+ offset += fb->len;
+ list_del(&fb->list);
+ kfree(fb);
+ }
+
+ return offset;
+}
+
+static void _destroy_frag_list(struct frag_list *fl, struct frag_head *fh)
+{
+ struct frag_block *fb, *n;
+
+ if (!fl || !fh)
+ return;
+
+ list_for_each_entry_safe(fb, n, &fl->block_head, list) {
+ kfree(fb);
+ }
+
+ clear_bit(fl->msg_id, fh->bitmap);
+ list_del(&fl->list);
+ kfree(fl);
+}
+
+static struct frag_list* _create_frag_list(u8 control, struct frag_head *fh)
+{
+ struct frag_list *fl;
+ u8 msg_id = control & FMT_ID_MASK;
+
+ if (test_bit(msg_id, fh->bitmap)) {
+ fl = _find_frag_list(control, fh);
+ _destroy_frag_list(fl, fh);
+ }
+
+ fl = kmalloc(sizeof(struct frag_list), GFP_KERNEL);
+ if (!fl)
+ return NULL;
+
+ INIT_LIST_HEAD(&fl->block_head);
+ fl->msg_id = msg_id;
+ fl->len = 0;
+ list_add(&fl->list, &fh->head);
+ set_bit(msg_id, fh->bitmap);
+
+ return fl;
+}
+
+static inline struct frag_block* _create_frag_block(struct frag_list *fl)
+{
+ struct frag_block *fb;
+
+ fb = kmalloc(sizeof(struct frag_block), GFP_KERNEL);
+ if (!fb)
+ return NULL;
+
+ fb->len = 0;
+ fb->ptr = fb->buf;
+ list_add_tail(&fb->list, &fl->block_head);
+
+ return fb;
+}
+
+static struct frag_block* _prepare_frag_block(struct frag_list *fl, int size)
+{
+ struct frag_block *fb;
+
+ if (size > FRAG_BLOCK_MAX)
+ BUG();
+
+ if (list_empty(&fl->block_head)) {
+ fb = _create_frag_block(fl);
+ } else {
+ fb = list_entry(fl->block_head.prev, struct frag_block, list);
+ if (size > FRAG_BLOCK_MAX - fb->len)
+ fb = _create_frag_block(fl);
+ }
+
+ return fb;
+}
+
+static int _read_fmt_frag(struct frag_head *fh, struct fmt_hdr *h,
+ struct ringbuf *rb)
+{
+ int r;
+ int data_len;
+ int read_len;
+ struct frag_list *fl;
+ struct frag_block *fb;
+
+ data_len = h->len - sizeof(struct fmt_hdr);
+ read_len = data_len + sizeof(hdlc_end);
+
+ _dbg("%s: data %d\n", __func__, data_len);
+
+ fl = _find_frag_list(h->control, fh);
+ if (!fl)
+ fl = _create_frag_list(h->control, fh);
+
+ if (!fl)
+ return -ENOMEM;
+
+ fb = _prepare_frag_block(fl, read_len);
+ if (!fb) {
+ if (fl->len == 0)
+ _destroy_frag_list(fl, fh);
+
+ return -ENOMEM;
+ }
+
+ r = __read(rb, fb->ptr, read_len);
+ if (r != read_len) {
+ _destroy_frag_list(fl, fh);
+ return -EBADMSG;
+ }
+
+ fb->ptr += data_len;
+ fb->len += data_len;
+ fl->len += data_len;
+
+ _dbg("%s: fl %p len %d fb %p ptr %p len %d\n", __func__,
+ fl, fl->len, fb, fb->ptr, fb->len);
+
+ return r;
+}
+
+static int _read_fmt_last(struct frag_head *fh, struct fmt_hdr *h,
+ struct ringbuf *rb, struct net_device *ndev)
+{
+ int r;
+ int data_len;
+ int read_len;
+ int total_len;
+ struct sk_buff *skb;
+ struct frag_list *fl;
+ char *p;
+
+ total_len = data_len = h->len - sizeof(struct fmt_hdr);
+ read_len = data_len + sizeof(hdlc_end);
+
+ fl = _find_frag_list(h->control & FMT_ID_MASK, fh);
+ if (fl)
+ total_len += fl->len;
+
+ _dbg("%s: total %d data %d\n", __func__, total_len, data_len);
+
+ skb = netdev_alloc_skb(ndev, total_len
+ + sizeof(struct phonethdr) + sizeof(hdlc_end));
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ skb_reserve(skb, sizeof(struct phonethdr));
+
+ if (fl)
+ _fill_skb(skb, fl);
+
+ _destroy_frag_list(fl, fh);
+
+ p = skb_put(skb, data_len);
+ r = __read(rb, p, read_len);
+ if (r != read_len) {
+ kfree_skb(skb);
+ return -EBADMSG;
+ }
+
+ _phonet_rx(ndev, skb, PN_FMT);
+
+ return r;
+}
+
+static int _read_fmt(struct sipc *si, int inbuf, struct ringbuf *rb)
+{
+ int r;
+ char buf[sizeof(struct fmt_hdr) + sizeof(hdlc_start)];
+ struct fmt_hdr *h;
+ u32 tail;
+ struct net_device *ndev = si->svndev;
+
+ h = (struct fmt_hdr *)&buf[sizeof(hdlc_start)];
+ while (inbuf > 0) {
+ tail = rb->rb_in_tail;
+
+ r = __read(rb, buf, sizeof(buf));
+ if (r < sizeof(buf) ||
+ strncmp(buf, hdlc_start, sizeof(hdlc_start))) {
+ dev_err(&ndev->dev, "Bad message: %c %d\n", buf[0], r);
+ return -EBADMSG;
+ }
+ inbuf -= r;
+
+ if (is_fmt_last(h->control))
+ r = _read_fmt_last(&si->frag_map, h, rb, ndev);
+ else
+ r = _read_fmt_frag(&si->frag_map, h, rb);
+
+ if (r < 0) {
+ if (r == -ENOMEM)
+ rb->rb_in_tail = tail;
+
+ return r;
+ }
+
+ inbuf -= r;
+ }
+
+ return 0;
+}
+
+static inline int check_mailbox(u32 mailbox, int idx)
+{
+ return mailbox & mb_data[idx].mask_send;
+}
+
+static inline void purge_buffer(struct ringbuf *rb)
+{
+ rb->rb_in_tail = rb->rb_in_head;
+}
+
+int sipc_read(struct sipc *si, u32 mailbox, int *cond)
+{
+ int r = 0;
+ int i;
+ u32 res = 0;
+
+ if (!si)
+ return -EINVAL;
+
+ r = _get_auth();
+ if (r)
+ return r;
+
+ for (i=0;i<IPCIDX_MAX;i++) {
+ int inbuf;
+ struct ringbuf *rb;
+
+// if (!check_mailbox(mailbox, i))
+// continue;
+
+ rb = &si->rb[i];
+ inbuf = CIRC_CNT(rb->rb_in_head, rb->rb_in_tail, rb->rb_size);
+ if (!inbuf)
+ continue;
+
+ if (i == IPCIDX_FMT)
+ _fmt_wakelock_timeout();
+ else
+ _non_fmt_wakelock_timeout();
+
+ _dbg("%s: %d bytes in %d\n", __func__, inbuf, i);
+
+ r = rb->rb_read(si, inbuf, rb);
+ if (r < 0) {
+ if (r == -EBADMSG)
+ purge_buffer(rb);
+
+ dev_err(&si->svndev->dev, "read err %d\n", r);
+ break;
+ }
+
+ if (mailbox & mb_data[i].mask_req_ack)
+ res = mb_data[i].mask_res_ack;
+ }
+
+#if !defined(CONFIG_ARIES_NTT)
+ _req_rel_auth(si);
+#endif
+
+ _put_auth(si);
+
+ if (res)
+ onedram_write_mailbox(MB_DATA(res));
+
+ *cond = skb_queue_len(&si->rfs_rx);
+
+ return r;
+}
+
+int sipc_rx(struct sipc *si)
+{
+ int tx_cnt;
+ struct sk_buff *skb;
+
+ if (!si)
+ return -EINVAL;
+
+ if (skb_queue_len(&si->rfs_rx) == 0)
+ return 0;
+
+ tx_cnt = 0;
+ skb = skb_dequeue(&si->rfs_rx);
+ while (skb) {
+ _phonet_rx(si->svndev, skb, PN_RFS);
+ tx_cnt++;
+ if (tx_cnt > RFS_TX_RATE)
+ break;
+ skb = skb_dequeue(&si->rfs_rx);
+ }
+
+ return skb_queue_len(&si->rfs_rx);
+}
+
+static inline ssize_t _debug_show_buf(struct sipc *si, char *buf)
+{
+ int i;
+ int r;
+ int inbuf, outbuf;
+ char *p = buf;
+
+ r = _get_auth();
+ if (r) {
+ p += sprintf(p, "\nGet authority: timed out!\n");
+ return p - buf;
+ }
+
+ p += sprintf(p, "\nHeader info ---------\n");
+
+ for (i=0;i<IPCIDX_MAX;i++) {
+ struct ringbuf *rb = &si->rb[i];
+ inbuf = CIRC_CNT(rb->rb_in_head, rb->rb_in_tail, rb->rb_size);
+ outbuf = CIRC_CNT(rb->rb_out_head, rb->rb_out_tail, rb->rb_size);
+ p += sprintf(p, "%d\tSize\t%8u\n\tIn\t%8u\t%8u\t%8u\n\tOut\t%8u\t%8u\t%8u\n",
+ i, rb->rb_size,
+ rb->rb_in_head, rb->rb_in_tail, inbuf,
+ rb->rb_out_head, rb->rb_out_tail, outbuf);
+ }
+ _put_auth(si);
+
+ return p - buf;
+}
+
+static inline ssize_t _debug_show_pdp(struct sipc *si, char *buf)
+{
+ int i;
+ char *p = buf;
+
+ p += sprintf(p, "\nPDP count: %d\n", pdp_cnt);
+
+ mutex_lock(&pdp_mutex);
+ for (i=0;i<sizeof(pdp_devs)/sizeof(pdp_devs[0]);i++) {
+ if (pdp_devs[i])
+ p += sprintf(p, "pdp%d: %d", i,
+ netif_queue_stopped(pdp_devs[i]));
+ }
+ mutex_unlock(&pdp_mutex);
+
+ return p - buf;
+}
+
+ssize_t sipc_debug_show(struct sipc *si, char *buf)
+{
+ char *p = buf;
+
+ if (!si || !buf)
+ return 0;
+
+ p += _debug_show_buf(si, p);
+
+ p += _debug_show_pdp(si, p);
+
+ p += sprintf(p, "\nDebug command -----------\n");
+ p += sprintf(p, "R0\tcopy FMT out to in\n");
+ p += sprintf(p, "R1\tcopy RAW out to in\n");
+ p += sprintf(p, "R2\tcopy RFS out to in\n");
+
+ return p - buf;
+}
+
+static void test_copy_buf(struct sipc *si, int idx)
+{
+ struct ringbuf *rb;
+
+ if(idx >= IPCIDX_MAX)
+ return;
+
+ rb = (struct ringbuf *)&si->rb[idx];
+
+ memcpy(rb->in_base, rb->out_base, rb->info->size);
+ rb->rb_in_head = rb->rb_out_head;
+ rb->rb_in_tail = rb->rb_out_tail;
+
+ rb->rb_out_tail = rb->rb_out_head;
+
+ if (si->queue)
+ si->queue(MB_DATA(mb_data[idx].mask_send), si->queue_data);
+}
+
+int sipc_debug(struct sipc *si, const char *buf)
+{
+ int r;
+
+ if (!si || !buf)
+ return -EINVAL;
+
+ r = _get_auth();
+ if (r)
+ return r;
+
+ switch (buf[0]) {
+ case 'R':
+ test_copy_buf(si, buf[1]-'0');
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ _put_auth(si);
+
+ return 0;
+}
+
+int sipc_whitelist(struct sipc *si, const char *buf, size_t count)
+{
+ int r;
+ struct ringbuf *rb;
+
+ printk("[%s]\n",__func__);
+
+ if (factory_test_force_sleep) {
+ printk("[%s]factory test\n",__func__);
+ return count;
+ }
+
+ if (!si || !buf)
+ return -EINVAL;
+
+ r = _get_auth();
+ if (r)
+ return r;
+
+ rb = (struct ringbuf *)&si->rb[IPCIDX_FMT];
+
+ //write direct full-established-packet to buf
+ r = __write(rb,(u8 *) buf, (unsigned int )count);
+
+ _req_rel_auth(si);
+ _put_auth(si);
+
+ onedram_write_mailbox(MB_DATA(mb_data[IPCIDX_FMT].mask_send));
+ return r;
+}
+
+int sipc_check_skb(struct sipc *si, struct sk_buff *skb)
+{
+ struct phonethdr *ph;
+
+ ph = pn_hdr(skb);
+
+ if (ph->pn_res == PN_CMD)
+ return 1;
+
+ return 0;
+}
+
+int sipc_do_cmd(struct sipc *si, struct sk_buff *skb)
+{
+ if (!si)
+ return -EINVAL;
+
+ skb_pull(skb, sizeof(struct phonethdr) + 1);
+
+ // TODO:
+ if (!strncmp("PHONE_ON", skb->data, sizeof("PHONE_ON"))) {
+
+ return 0;
+ }
+
+ return 0;
+}
+
+static int pdp_activate(struct net_device *svndev, int channel)
+{
+ int idx;
+ struct net_device *ndev;
+
+ if (!svndev || channel < 1 || channel > PDP_MAX)
+ return -EINVAL;
+
+ idx = channel - 1; /* start from 0 */
+
+ mutex_lock(&pdp_mutex);
+
+ if (pdp_devs[idx]) {
+ mutex_unlock(&pdp_mutex);
+ return -EBUSY;
+ }
+
+ ndev = create_pdp(channel, svndev);
+ if (IS_ERR(ndev)) {
+ mutex_unlock(&pdp_mutex);
+ return PTR_ERR(ndev);
+ }
+
+ pdp_devs[idx] = ndev;
+ pdp_cnt++;
+
+ mutex_unlock(&pdp_mutex);
+
+ return 0;
+}
+
+static int pdp_deactivate(int channel)
+{
+ int idx;
+
+ if (channel < 1 || channel > PDP_MAX)
+ return -EINVAL;
+
+ idx = channel - 1; /* start from 0 */
+
+ mutex_lock(&pdp_mutex);
+
+ if (!pdp_devs[idx]) {
+ mutex_unlock(&pdp_mutex);
+ return -EBUSY;
+ }
+
+ destroy_pdp(&pdp_devs[idx]);
+ clear_bit(idx, pdp_bitmap);
+ pdp_cnt--;
+
+ mutex_unlock(&pdp_mutex);
+
+ return 0;
+}
+
+static ssize_t show_act(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ int i;
+ char *p = buf;
+
+ mutex_lock(&pdp_mutex);
+
+ for (i=0;i<sizeof(pdp_devs)/sizeof(pdp_devs[0]);i++) {
+ if (pdp_devs[i])
+ p += sprintf(p, "%d\n", (i+1));
+ }
+
+ mutex_unlock(&pdp_mutex);
+
+ return p - buf;
+}
+
+static ssize_t show_deact(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ int i;
+ char *p = buf;
+
+ mutex_lock(&pdp_mutex);
+
+ for(i=0;i<sizeof(pdp_devs)/sizeof(pdp_devs[0]);i++) {
+ if (!pdp_devs[i])
+ p += sprintf(p, "%d\n", (i+1));
+ }
+
+ mutex_unlock(&pdp_mutex);
+
+ return p - buf;
+}
+
+static ssize_t store_act(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int r;
+ unsigned long chan;
+ struct net_device *ndev = to_net_dev(d);
+
+ if (!ndev)
+ return count;
+
+ r = strict_strtoul(buf, 10, &chan);
+ if (!r)
+ r = pdp_activate(ndev, chan);
+
+ if (r)
+ dev_err(&ndev->dev, "Failed to activate pdp "
+ " channel %lu: %d\n", chan, r);
+
+ return count;
+}
+
+static ssize_t store_deact(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int r;
+ unsigned long chan;
+ struct net_device *ndev = to_net_dev(d);
+
+ if (!ndev)
+ return count;
+
+ r = strict_strtoul(buf, 10, &chan);
+ if (!r)
+ r = pdp_deactivate(chan);
+
+ if (r)
+ dev_err(&ndev->dev, "Failed to deactivate pdp"
+ " channel %lu: %d\n", chan, r);
+
+ return count;
+}
+
+static ssize_t show_suspend(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ int i;
+ char *p = buf;
+
+ mutex_lock(&pdp_mutex);
+
+ for (i=0;i<sizeof(pdp_devs)/sizeof(pdp_devs[0]);i++) {
+ if (test_bit(i, pdp_bitmap))
+ p += sprintf(p, "%d\n", (i+1));
+ }
+
+ mutex_unlock(&pdp_mutex);
+
+ return p - buf;
+}
+
+static ssize_t store_suspend(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int r;
+ unsigned long chan;
+ int id;
+
+ r = strict_strtoul(buf, 10, &chan);
+ if (r)
+ return count;
+
+ if (chan < 1 || chan > PDP_MAX)
+ return count;
+
+ id = chan - 1;
+
+ mutex_lock(&pdp_mutex);
+
+ set_bit(id, pdp_bitmap);
+
+ if (pdp_devs[id])
+ netif_stop_queue(pdp_devs[id]);
+
+ mutex_unlock(&pdp_mutex);
+
+ return count;
+}
+
+static ssize_t store_resume(struct device *d,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int r;
+ unsigned long chan;
+ int id;
+
+ r = strict_strtoul(buf, 10, &chan);
+ if (r)
+ return count;
+
+ if (chan < 1 || chan > PDP_MAX)
+ return count;
+
+ id = chan - 1;
+
+ mutex_lock(&pdp_mutex);
+
+ clear_bit(id, pdp_bitmap);
+
+ if (pdp_devs[id])
+ netif_wake_queue(pdp_devs[id]);
+
+ mutex_unlock(&pdp_mutex);
+
+ return count;
+}
+
+void sipc_ramdump(struct sipc *si)
+{
+#if defined(CONFIG_KERNEL_DEBUG_SEC)
+ /* silent reset at debug level low */
+ if ( kernel_sec_get_debug_level() == KERNEL_SEC_DEBUG_LEVEL_LOW )
+ return;
+#endif
+ _go_dump(si);
+}
+
+#if defined(CONFIG_KERNEL_DEBUG_SEC)
+static void _go_dump(struct sipc *si)
+{
+ int r;
+ t_kernel_sec_mmu_info mmu_info;
+
+ memset(cp_errmsg, 0, sizeof(cp_errmsg));
+
+ r = _get_auth();
+ if (r)
+ strcpy(cp_errmsg, ERRMSG);
+ else {
+ char *p;
+ p = (char *)si->map + FATAL_DISP;
+ memcpy(cp_errmsg, p, sizeof(cp_errmsg));
+ }
+
+ printk("CP Dump Cause - %s\n", cp_errmsg);
+
+// kernel_sec_set_cause_strptr(cp_errmsg, sizeof(cp_errmsg));
+ kernel_sec_set_upload_magic_number();
+ kernel_sec_get_mmu_reg_dump(&mmu_info);
+ kernel_sec_set_upload_cause(UPLOAD_CAUSE_CP_ERROR_FATAL);
+ kernel_sec_hw_reset(false);
+
+ // Never Return!!!
+}
+#endif
diff --git a/drivers/misc/samsung_modemctl/svnet/sipc4.h b/drivers/misc/samsung_modemctl/svnet/sipc4.h
new file mode 100644
index 0000000..c156a4f
--- /dev/null
+++ b/drivers/misc/samsung_modemctl/svnet/sipc4.h
@@ -0,0 +1,252 @@
+/**
+ * SAMSUNG MODEM IPC header version 4
+ *
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __SAMSUNG_IPC_V4_H__
+#define __SAMSUNG_IPC_V4_H__
+
+/* IPC4.1 NEW PARTITION MAP
+ * This map is seen by AP side
+
+ 0x00_0000 ===========================================
+ MAGIC(4)| ACCESS(4) | RESERVED(8)
+ 0x00_0010 -------------------------------------------
+ FMT_OUT_PTR | FMT_IN_PTR
+ HEAD(4) | TAIL(4) | HEAD(4) | TAIL(4)
+ 0x00_0020 -------------------------------------------
+ RAW_OUT_PTR | RAW_IN_PTR
+ HEAD(4) | TAIL(4) | HEAD(4) | TAIL(4)
+ 0x00_0030 -------------------------------------------
+ RFS_OUT_PTR | RFS_IN_PTR
+ HEAD(4) | TAIL(4) | HEAD(4) | TAIL(4)
+ 0x00_0040 -------------------------------------------
+ RESERVED (4KB - 64B)
+ 0x00_1000 -------------------------------------------
+ CP Fatal Display (160B)
+ 0x00_10A0 -------------------------------------------
+ RESERVED (1MB - 4kb-4kb-4kb - 160B)
+ 0x0F_E000 -------------------------------------------
+ Formatted Out (4KB)
+ 0x0F_F000 -------------------------------------------
+ Formatted In (4KB)
+ 0x10_0000 ===========================================
+ Raw Out (1MB)
+ 0x20_0000 ===========================================
+ Raw In (1MB)
+ 0x30_0000 ===========================================
+ RemoteFS Out (1MB)
+ 0x40_0000 ===========================================
+ RemoteFS In (1MB)
+ 0x50_0000 ===========================================
+
+ 0xFF_FFFF ===========================================
+*/
+
+#define FMT_OUT 0x0FE000
+#define FMT_IN 0x0FF000
+#define FMT_SZ 0x1000 /* 4096 bytes */
+
+#define RAW_OUT 0x100000
+#define RAW_IN 0x200000
+#define RAW_SZ 0x100000 /* 1 MB */
+
+#define RFS_OUT 0x300000
+#define RFS_IN 0x400000
+#define RFS_SZ 0x100000 /* 1 MB */
+
+#define FATAL_DISP 0x001000
+#define FATAL_DISP_SZ 0xA0 /* 160 bytes */
+
+#define SIPC_MAP_SIZE (RFS_IN + RFS_SZ)
+#define SIPC_NAME "IPCv4.1"
+
+enum {
+ IPCIDX_FMT = 0,
+ IPCIDX_RAW,
+ IPCIDX_RFS,
+ IPCIDX_MAX
+};
+
+struct ringbuf_cont {
+ u32 out_head;
+ u32 out_tail;
+ u32 in_head;
+ u32 in_tail;
+};
+
+struct sipc_mapped { /* map to the onedram start addr */
+ u32 magic;
+ u32 access;
+ u32 hwrev;
+ u32 reserved;
+
+ struct ringbuf_cont rbcont[IPCIDX_MAX];
+};
+
+
+#define PN_CMD 0x00
+#define PN_FMT 0x01
+#define PN_RFS 0x41
+#define PN_RAW(chid) (0x20 | (chid))
+#define CHID(x) ((x) & 0x1F)
+
+#define res_to_ridx(x) ((x) >> 5)
+
+/*
+ * IPC Frame Format
+ */
+#define HDLC_START 0x7F
+#define HDLC_END 0x7E
+
+/* Formatted IPC Frame */
+struct fmt_hdr {
+ u16 len;
+ u8 control;
+} __attribute__ ((packed));
+
+#define FMT_ID_MASK 0x7F /* Information ID mask */
+#define FMT_ID_SIZE 0x80 /* = 128 ( 0 ~ 127 ) */
+#define FMT_MB_MASK 0x80 /* More bit mask */
+
+#define FMT_TX_MIN 5 /* ??? */
+
+#define is_fmt_last(x) (!((x) & FMT_MB_MASK))
+
+/* RAW IPC Frame */
+struct raw_hdr {
+ u32 len;
+ u8 channel;
+ u8 control;
+} __attribute__ ((packed));
+
+
+/* RFS IPC Frame */
+struct rfs_hdr {
+ u32 len;
+ u8 cmd;
+ u8 id;
+} __attribute__ ((packed));
+
+/*
+ * RAW frame channel ID
+ */
+enum {
+ CHID_0 = 0,
+ CHID_CSD_VT_DATA,
+ CHID_PDS_PVT_CONTROL,
+ CHID_PDS_VT_AUDIO,
+ CHID_PDS_VT_VIDEO,
+ CHID_5, /* 5 */
+ CHID_6,
+ CHID_CDMA_DATA,
+ CHID_PCM_DATA,
+ CHID_TRANSFER_SCREEN,
+ CHID_PSD_DATA1, /* 10 */
+ CHID_PSD_DATA2,
+ CHID_PSD_DATA3,
+ CHID_PSD_DATA4,
+ CHID_PSD_DATA5,
+ CHID_PSD_DATA6, /* 15 */
+ CHID_PSD_DATA7,
+ CHID_PSD_DATA8,
+ CHID_PSD_DATA9,
+ CHID_PSD_DATA10,
+ CHID_PSD_DATA11, /* 20 */
+ CHID_PSD_DATA12,
+ CHID_PSD_DATA13,
+ CHID_PSD_DATA14,
+ CHID_PSD_DATA15,
+ CHID_BT_DUN, /* 25 */
+ CHID_CIQ_BRIDGE_DATA,
+ CHID_27,
+ CHID_CP_LOG1,
+ CHID_CP_LOG2,
+ CHID_30, /* 30 */
+ CHID_31,
+ CHID_MAX
+};
+
+#define PDP_MAX 15
+#define PN_PDP_START PN_RAW(CHID_PSD_DATA1)
+#define PN_PDP_END PN_RAW(CHID_PSD_DATA15)
+
+#define PN_PDP(chid) (0x20 | ((chid) + CHID_PSD_DATA1 - 1))
+#define PDP_ID(res) ((res) - PN_PDP_START)
+
+
+/*
+ * IPC 4.0 Mailbox message definition
+ */
+#define MB_VALID 0x0080
+#define MB_COMMAND 0x0040
+
+#define MB_CMD(x) (MB_VALID | MB_COMMAND | x)
+#define MB_DATA(x) (MB_VALID | x)
+
+/*
+ * If not command
+ */
+#define MBD_SEND_FMT 0x0002
+#define MBD_SEND_RAW 0x0001
+#define MBD_SEND_RFS 0x0100
+#define MBD_REQ_ACK_FMT 0x0020
+#define MBD_REQ_ACK_RAW 0x0010
+#define MBD_REQ_ACK_RFS 0x0400
+#define MBD_RES_ACK_FMT 0x0008
+#define MBD_RES_ACK_RAW 0x0004
+#define MBD_RES_ACK_RFS 0x0200
+
+/*
+ * If command
+ */
+enum {
+ MBC_NONE = 0,
+ MBC_INIT_START, // 0x0001
+ MBC_INIT_END, // 0x0002
+ MBC_REQ_ACTIVE, // 0x0003
+ MBC_RES_ACTIVE, // 0x0004
+ MBC_TIME_SYNC, // 0x0005
+ MBC_POWER_OFF, // 0x0006
+ MBC_RESET, // 0x0007
+ MBC_PHONE_START, // 0x0008
+ MBC_ERR_DISPLAY, // 0x0009
+ MBC_POWER_SAVE, // 0x000A
+ MBC_NV_REBUILD, // 0x000B
+ MBC_EMER_DOWN, // 0x000C
+ MBC_REQ_SEM, // 0x000D
+ MBC_RES_SEM, // 0x000E
+ MBC_MAX // 0x000F
+};
+#define MBC_MASK 0xFF
+
+/* CMD_INIT_END extended bit */
+#define CP_BOOT_ONLINE 0x0000
+#define CP_BOOT_AIRPLANE 0x1000
+#define AP_OS_ANDROID 0x0100
+#define AP_OS_WINMOBILE 0x0200
+#define AP_OS_LINUX 0x0300
+#define AP_OS_SYMBIAN 0x0400
+
+/* CMD_PHONE_START extended bit */
+#define CP_QUALCOMM 0x0100
+#define CP_INFINEON 0x0200
+#define CP_BROADCOM 0x0300
+
+#endif /* __SAMSUNG_IPC_V4_H__ */
+