diff options
author | Pawit Pornkitprasan <p.pawit@gmail.com> | 2011-12-24 09:34:37 +0700 |
---|---|---|
committer | Pawit Pornkitprasan <p.pawit@gmail.com> | 2011-12-24 09:35:11 +0700 |
commit | fdc19983538bec90010fa7f344a7417ff4731ba8 (patch) | |
tree | 8a84a1edd1c026b71fec4756caf27cbbb9e827d5 /drivers/misc/samsung_modemctl | |
parent | b55e9ac4df4d240b39eda4cd9c0198453dd59061 (diff) | |
download | kernel_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')
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 *)¶m->data, sizeof(param->data)); + } + + /* @LDK@ read */ + else { + READ_FROM_DPRAM((void *)¶m->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 *)¶m, (void *)arg, sizeof(param)); + if (dpram_extra_mem_rw(¶m) < 0) { + printk(KERN_ERR "[OneDRAM] external memory access fail..\n"); + return -1; + } + if (!param.rw) { //read + return copy_to_user((unsigned long *)arg, ¶m, sizeof(param)); + } + + return 0; + } + +#if 0 + case DPRAM_MEM_RW: + { + struct _mem_param param; + + val = copy_from_user((void *)¶m, (void *)arg, sizeof(param)); + dpram_mem_rw(¶m); + + if (!param.dir) { + return copy_to_user((unsigned long *)arg, ¶m, 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__ */ + |