blob: 5470b8d6c8a456cfea61272d17bee477e2f48530 [file] [log] [blame]
/*
* Copyright (C) 2011 Samsung Electronics.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __MODEM_UTILS_H__
#define __MODEM_UTILS_H__
#include <linux/rbtree.h>
#include "modem_prj.h"
#include "link_device_memory.h"
#define MIF_TAG "mif"
#define IS_CONNECTED(iod, ld) ((iod)->link_types & LINKTYPE((ld)->link_type))
#define MAX_MIF_BUFF_SIZE 0x80000 /* 512kb */
#define MAX_MIF_SEPA_SIZE 32
#define MIF_SEPARATOR "IPC_LOGGER(VER1.1)"
#define MIF_SEPARATOR_DPRAM "DPRAM_LOGGER(VER1.1)"
#define MAX_IPC_SKB_SIZE 4096
#define MAX_LOG_SIZE 64
#define MIF_TAG "mif"
#define MAX_LOG_CNT (MAX_MIF_BUFF_SIZE / MAX_LOG_SIZE)
#define MIF_ID_SIZE sizeof(enum mif_log_id)
#define MAX_IPC_LOG_SIZE \
(MAX_LOG_SIZE - sizeof(enum mif_log_id) \
- sizeof(unsigned long long) - sizeof(size_t))
#define MAX_IRQ_LOG_SIZE \
(MAX_LOG_SIZE - sizeof(enum mif_log_id) \
- sizeof(unsigned long long) - sizeof(struct mif_irq_map))
#define MAX_COM_LOG_SIZE \
(MAX_LOG_SIZE - sizeof(enum mif_log_id) \
- sizeof(unsigned long long))
#define MAX_TIM_LOG_SIZE \
(MAX_LOG_SIZE - sizeof(enum mif_log_id) \
- sizeof(unsigned long long) - sizeof(struct timespec))
enum mif_log_id {
MIF_IPC_RL2AP = 1,
MIF_IPC_AP2CP,
MIF_IPC_CP2AP,
MIF_IPC_AP2RL,
MIF_IRQ,
MIF_COM,
MIF_TIME
};
struct mif_irq_map {
u16 magic;
u16 access;
u16 fmt_tx_in;
u16 fmt_tx_out;
u16 fmt_rx_in;
u16 fmt_rx_out;
u16 raw_tx_in;
u16 raw_tx_out;
u16 raw_rx_in;
u16 raw_rx_out;
u16 cp2ap;
};
struct mif_ipc_block {
enum mif_log_id id;
unsigned long long time;
size_t len;
char buff[MAX_IPC_LOG_SIZE];
};
struct mif_irq_block {
enum mif_log_id id;
unsigned long long time;
struct mif_irq_map map;
char buff[MAX_IRQ_LOG_SIZE];
};
struct mif_common_block {
enum mif_log_id id;
unsigned long long time;
char buff[MAX_COM_LOG_SIZE];
};
struct mif_time_block {
enum mif_log_id id;
unsigned long long time;
struct timespec epoch;
char buff[MAX_TIM_LOG_SIZE];
};
enum ipc_layer {
LINK,
IODEV,
APP,
MAX_SIPC_LAYER
};
static const char * const sipc_layer_string[] = {
[LINK] = "LNK",
[IODEV] = "IOD",
[APP] = "APP",
[MAX_SIPC_LAYER] = "INVALID"
};
static const inline char *layer_str(enum ipc_layer layer)
{
if (unlikely(layer >= MAX_SIPC_LAYER))
return "INVALID";
else
return sipc_layer_string[layer];
}
static const char * const dev_format_string[] = {
[IPC_FMT] = "FMT",
[IPC_RAW] = "RAW",
[IPC_RFS] = "RFS",
[IPC_MULTI_RAW] = "MULTI_RAW",
[IPC_BOOT] = "BOOT",
[IPC_DUMP] = "DUMP",
[IPC_CMD] = "CMD",
[IPC_DEBUG] = "DEBUG",
};
static const inline char *dev_str(enum dev_format dev)
{
if (unlikely(dev >= MAX_DEV_FORMAT))
return "INVALID";
else
return dev_format_string[dev];
}
static inline enum direction opposite(enum direction dir)
{
return (dir == TX) ? RX : TX;
}
static const char * const direction_string[] = {
[TX] = "TX",
[RX] = "RX"
};
static const inline char *dir_str(enum direction dir)
{
if (unlikely(dir >= MAX_DIR))
return "INVALID";
else
return direction_string[dir];
}
static const char * const udl_string[] = {
[UL] = "UL",
[DL] = "DL"
};
static const inline char *udl_str(enum direction dir)
{
if (unlikely(dir >= ULDL))
return "INVALID";
else
return udl_string[dir];
}
static const char * const q_direction_string[] = {
[TX] = "TXQ",
[RX] = "RXQ"
};
static const inline char *q_dir(enum direction dir)
{
if (unlikely(dir >= MAX_DIR))
return "INVALID";
else
return q_direction_string[dir];
}
static const char * const ipc_direction_string[] = {
[TX] = "AP->CP",
[RX] = "AP<-CP"
};
static const inline char *ipc_dir(enum direction dir)
{
if (unlikely(dir >= MAX_DIR))
return "INVALID";
else
return ipc_direction_string[dir];
}
static const char * const arrow_direction[] = {
[TX] = "->",
[RX] = "<-"
};
static const inline char *arrow(enum direction dir)
{
if (unlikely(dir >= MAX_DIR))
return "><";
else
return arrow_direction[dir];
}
static const char * const modem_state_string[] = {
[STATE_OFFLINE] = "OFFLINE",
[STATE_CRASH_RESET] = "CRASH_RESET",
[STATE_CRASH_EXIT] = "CRASH_EXIT",
[STATE_BOOTING] = "BOOTING",
[STATE_ONLINE] = "ONLINE",
[STATE_NV_REBUILDING] = "NV_REBUILDING",
[STATE_LOADER_DONE] = "LOADER_DONE",
[STATE_SIM_ATTACH] = "SIM_ATTACH",
[STATE_SIM_DETACH] = "SIM_DETACH",
[STATE_CRASH_WATCHDOG] = "WDT_RESET",
};
static const inline char *cp_state_str(enum modem_state state)
{
return modem_state_string[state];
}
static const inline char *mc_state(struct modem_ctl *mc)
{
return cp_state_str(mc->phone_state);
}
struct __packed utc_time {
u32 year:18,
mon:4,
day:5,
hour:5;
u32 min:6,
sec:6,
us:20;
};
/* {Hour, Minute, Second, U(micro)-second} format */
#define HMSU_FMT "[%02d:%02d:%02d.%06d]"
static inline unsigned long ns2us(unsigned long ns)
{
return (ns > 0) ? (ns / 1000) : 0;
}
static inline unsigned long ns2ms(unsigned long ns)
{
return (ns > 0) ? (ns / 1000000) : 0;
}
static inline unsigned long us2ms(unsigned long us)
{
return (us > 0) ? (us / 1000) : 0;
}
static inline unsigned long ms2us(unsigned long ms)
{
return ms * 1E3L;
}
static inline unsigned long ms2ns(unsigned long ms)
{
return ms * 1E6L;
}
void get_utc_time(struct utc_time *utc);
static inline unsigned int calc_offset(void *target, void *base)
{
return (unsigned long)target - (unsigned long)base;
}
int mif_dump_log(struct modem_shared *, struct io_device *);
#define mif_irq_log(msd, map, data, len) \
_mif_irq_log(MIF_IRQ, msd, map, data, len)
#define mif_com_log(msd, format, ...) \
_mif_com_log(MIF_COM, msd, pr_fmt(format), ##__VA_ARGS__)
#define mif_time_log(msd, epoch, data, len) \
_mif_time_log(MIF_TIME, msd, epoch, data, len)
void mif_ipc_log(enum mif_log_id,
struct modem_shared *, const char *, size_t);
void _mif_irq_log(enum mif_log_id,
struct modem_shared *, struct mif_irq_map, const char *, size_t);
void _mif_com_log(enum mif_log_id,
struct modem_shared *, const char *, ...);
void _mif_time_log(enum mif_log_id,
struct modem_shared *, struct timespec, const char *, size_t);
static inline struct link_device *find_linkdev(struct modem_shared *msd,
enum modem_link link_type)
{
struct link_device *ld;
list_for_each_entry(ld, &msd->link_dev_list, list) {
if (ld->link_type == link_type)
return ld;
}
return NULL;
}
static inline unsigned int count_bits(unsigned int n)
{
unsigned int i;
for (i = 0; n != 0; i++)
n &= (n - 1);
return i;
}
static inline bool count_flood(int cnt, int mask)
{
return (cnt > 0 && (cnt & mask) == 0) ? true : false;
}
void mif_pkt(u8 ch, const char *tag, struct sk_buff *skb);
/* print buffer as hex string */
int pr_buffer(const char *tag, const char *data, size_t data_len,
size_t max_len);
/* print a sk_buff as hex string */
#define pr_skb(tag, skb) \
pr_buffer(tag, (char *)((skb)->data), (size_t)((skb)->len), (size_t)16)
/* print a urb as hex string */
#define pr_urb(tag, urb) \
pr_buffer(tag, (char *)((urb)->transfer_buffer), \
(size_t)((urb)->actual_length), (size_t)16)
/* Stop/wake all TX queues in network interfaces */
void stop_net_iface(struct link_device *ld, unsigned int channel);
void resume_net_iface(struct link_device *ld, unsigned int channel);
void stop_net_ifaces(struct link_device *ld);
void resume_net_ifaces(struct link_device *ld);
/* flow control CMD from CP, it use in serial devices */
int link_rx_flowctl_cmd(struct link_device *ld, const char *data, size_t len);
/* Get an IO device */
struct io_device *get_iod_with_format(struct modem_shared *msd,
enum dev_format format);
static inline struct io_device *link_get_iod_with_format(
struct link_device *ld, enum dev_format format)
{
struct io_device *iod = get_iod_with_format(ld->msd, format);
return (iod && IS_CONNECTED(iod, ld)) ? iod : NULL;
}
static inline struct io_device *get_iod_with_channel(
struct modem_shared *msd, unsigned int channel)
{
return msd->ch2iod[channel];
}
static inline struct io_device *link_get_iod_with_channel(
struct link_device *ld, unsigned int channel)
{
struct io_device *iod = get_iod_with_channel(ld->msd, channel);
struct mem_link_device *mld = ld->mdm_data->mld;
if (!iod && atomic_read(&mld->cp_boot_done))
mif_err("No IOD matches channel (%d)\n", channel);
return (iod && IS_CONNECTED(iod, ld)) ? iod : NULL;
}
/* insert iod to tree functions */
struct io_device *insert_iod_with_format(struct modem_shared *msd,
enum dev_format format, struct io_device *iod);
void insert_iod_with_channel(struct modem_shared *msd, unsigned int channel,
struct io_device *iod);
/* iodev for each */
typedef void (*action_fn)(struct io_device *iod, void *args);
void iodevs_for_each(struct modem_shared *msd, action_fn action, void *args);
/* netif wake/stop queue of iod */
void iodev_netif_wake(struct io_device *iod, void *args);
void iodev_netif_stop(struct io_device *iod, void *args);
/* netif wake/stop queue of iod having activated ndev */
void netif_tx_flowctl(struct modem_shared *msd, bool tx_stop);
/* change tx_link of raw devices */
void rawdevs_set_tx_link(struct modem_shared *msd, enum modem_link link_type);
__be32 ipv4str_to_be32(const char *ipv4str, size_t count);
void mif_add_timer(struct timer_list *timer, unsigned long expire,
void (*function)(unsigned long), unsigned long data);
/* debug helper functions for sipc4, sipc5 */
void mif_print_data(const u8 *data, int len);
void mif_dump2format16(const u8 *data, int len, char *buff, char *tag);
void mif_dump2format4(const u8 *data, int len, char *buff, char *tag);
void mif_print_dump(const u8 *data, int len, int width);
/*---------------------------------------------------------------------------
IPv4 Header Format
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |C|D|M| Fragment Offset |
| |E|F|F| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
IHL - Header Length
Flags - Consist of 3 bits
The 1st bit is "Congestion" bit.
The 2nd bit is "Dont Fragment" bit.
The 3rd bit is "More Fragments" bit.
---------------------------------------------------------------------------*/
#define IPV4_HDR_SIZE 20
/*-------------------------------------------------------------------------
TCP Header Format
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |C|E|U|A|P|R|S|F| |
| Offset| Rsvd |W|C|R|C|S|S|Y|I| Window |
| | |R|E|G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-------------------------------------------------------------------------*/
#define TCP_HDR_SIZE 20
/*-------------------------------------------------------------------------
UDP Header Format
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-------------------------------------------------------------------------*/
#define UDP_HDR_SIZE 8
void print_ipv4_packet(const u8 *ip_pkt, enum direction dir);
bool is_dns_packet(const u8 *ip_pkt);
bool is_syn_packet(const u8 *ip_pkt);
void mif_init_irq(struct modem_irq *irq, unsigned int num, const char *name,
unsigned long flags);
int mif_request_irq(struct modem_irq *irq, irq_handler_t isr, void *data);
void mif_enable_irq(struct modem_irq *irq);
void mif_disable_irq(struct modem_irq *irq);
void mif_disable_irq_sync(struct modem_irq *irq);
struct file *mif_open_file(const char *path);
void mif_save_file(struct file *fp, const char *buff, size_t size);
void mif_close_file(struct file *fp);
int board_gpio_export(struct device *dev,
unsigned gpio, bool dir, const char *name);
void make_gpio_floating(unsigned int gpio, bool floating);
#ifdef CONFIG_ARGOS
/* kernel team needs to provide argos header file. !!!
* As of now, there's nothing to use. */
#ifdef CONFIG_SCHED_HMP
extern struct cpumask hmp_slow_cpu_mask;
extern struct cpumask hmp_fast_cpu_mask;
#endif
int argos_irq_affinity_setup_label(unsigned int irq, const char *label,
struct cpumask *affinity_cpu_mask,
struct cpumask *default_cpu_mask);
int argos_task_affinity_setup_label(struct task_struct *p, const char *label,
struct cpumask *affinity_cpu_mask,
struct cpumask *default_cpu_mask);
#endif
void mif_set_snapshot(bool enable);
void set_wakeup_packet_log(bool enable);
/* MIF buffer management */
/* Printout debuggin message for MIF buffer */
//#define MIF_BUFF_DEBUG
/*
IP packet : 2048
sizeof(struct skb_shared_info): 512
2048 + 512 = 2560 (0xA00)
*/
#define MIF_BUFF_DEFAULT_CELL_SIZE (2048 + 512)
#define MIF_BUFF_MAP_CELL_SIZE (sizeof(uint64_t))
#define MIF_BITS_FOR_BYTE (8)
#define MIF_BITS_FOR_MAP_CELL (MIF_BUFF_MAP_CELL_SIZE * MIF_BITS_FOR_BYTE)
#define MIF_64BIT_FIRST_BIT (0x8000000000000000ULL)
struct mif_buff_mng {
unsigned char *buffer_start;
unsigned char *buffer_end;
unsigned int buffer_size;
unsigned int cell_size;
unsigned int cell_count;
unsigned int used_cell_count;
unsigned int free_cell_count;
spinlock_t lock;
uint64_t *buffer_map;
unsigned int buffer_map_size;
int current_map_index;
};
struct mif_buff_mng *init_mif_buff_mng(unsigned char *buffer_start,
unsigned int buffer_size, unsigned int cell_size);
void exit_mif_buff_mng(struct mif_buff_mng *bm);
void *alloc_mif_buff(struct mif_buff_mng *bm);
int free_mif_buff(struct mif_buff_mng *bm, void *buffer);
static inline unsigned int get_mif_buff_free_count(struct mif_buff_mng *bm)
{
if (bm)
return bm->free_cell_count;
else
return 0;
}
static inline unsigned int get_mif_buff_used_count(struct mif_buff_mng *bm)
{
if (bm)
return bm->used_cell_count;
else
return 0;
}
extern struct mif_buff_mng *g_mif_buff_mng;
int security_request_cp_ram_logging(void);
void set_dflags(unsigned long flag);
#endif/*__MODEM_UTILS_H__*/