/* * Copyright (C) 2012 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. * */ #include #include #include #include #include #include #include #include #include #include #include "modem_prj.h" #include "modem_utils.h" int mif_dump_log(struct modem_shared *msd, struct io_device *iod) { struct sk_buff *skb; unsigned long read_len = 0; unsigned long int flags; spin_lock_irqsave(&msd->lock, flags); while (read_len < MAX_MIF_BUFF_SIZE) { skb = alloc_skb(MAX_IPC_SKB_SIZE, GFP_ATOMIC); if (!skb) { pr_err("[MIF] <%s> alloc skb failed\n", __func__); spin_unlock_irqrestore(&msd->lock, flags); return -ENOMEM; } memcpy(skb_put(skb, MAX_IPC_SKB_SIZE), msd->storage.addr + read_len, MAX_IPC_SKB_SIZE); skb_queue_tail(&iod->sk_rx_q, skb); read_len += MAX_IPC_SKB_SIZE; wake_up(&iod->wq); } spin_unlock_irqrestore(&msd->lock, flags); return 0; } static unsigned long long get_kernel_time(void) { int this_cpu; unsigned long flags; unsigned long long time; preempt_disable(); raw_local_irq_save(flags); this_cpu = smp_processor_id(); time = cpu_clock(this_cpu); preempt_enable(); raw_local_irq_restore(flags); return time; } void mif_ipc_log(enum mif_log_id id, struct modem_shared *msd, const char *data, size_t len) { struct mif_ipc_block *block; unsigned long int flags; spin_lock_irqsave(&msd->lock, flags); block = (struct mif_ipc_block *) (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt)); msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ? msd->storage.cnt + 1 : 0; spin_unlock_irqrestore(&msd->lock, flags); block->id = id; block->time = get_kernel_time(); block->len = (len > MAX_IPC_LOG_SIZE) ? MAX_IPC_LOG_SIZE : len; memcpy(block->buff, data, block->len); } void _mif_irq_log(enum mif_log_id id, struct modem_shared *msd, struct mif_irq_map map, const char *data, size_t len) { struct mif_irq_block *block; unsigned long int flags; spin_lock_irqsave(&msd->lock, flags); block = (struct mif_irq_block *) (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt)); msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ? msd->storage.cnt + 1 : 0; spin_unlock_irqrestore(&msd->lock, flags); block->id = id; block->time = get_kernel_time(); memcpy(&(block->map), &map, sizeof(struct mif_irq_map)); if (data) memcpy(block->buff, data, (len > MAX_IRQ_LOG_SIZE) ? MAX_IRQ_LOG_SIZE : len); } void _mif_com_log(enum mif_log_id id, struct modem_shared *msd, const char *format, ...) { struct mif_common_block *block; unsigned long int flags; va_list args; int ret; spin_lock_irqsave(&msd->lock, flags); block = (struct mif_common_block *) (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt)); msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ? msd->storage.cnt + 1 : 0; spin_unlock_irqrestore(&msd->lock, flags); block->id = id; block->time = get_kernel_time(); va_start(args, format); ret = vsnprintf(block->buff, MAX_COM_LOG_SIZE, format, args); va_end(args); } void _mif_time_log(enum mif_log_id id, struct modem_shared *msd, struct timespec epoch, const char *data, size_t len) { struct mif_time_block *block; unsigned long int flags; spin_lock_irqsave(&msd->lock, flags); block = (struct mif_time_block *) (msd->storage.addr + (MAX_LOG_SIZE * msd->storage.cnt)); msd->storage.cnt = ((msd->storage.cnt + 1) < MAX_LOG_CNT) ? msd->storage.cnt + 1 : 0; spin_unlock_irqrestore(&msd->lock, flags); block->id = id; block->time = get_kernel_time(); memcpy(&block->epoch, &epoch, sizeof(struct timespec)); if (data) memcpy(block->buff, data, (len > MAX_IRQ_LOG_SIZE) ? MAX_IRQ_LOG_SIZE : len); } /* dump2hex * dump data to hex as fast as possible. * the length of @buf must be greater than "@len * 3" * it need 3 bytes per one data byte to print. */ static inline int dump2hex(char *buf, const char *data, size_t len) { static const char *hex = "0123456789abcdef"; char *dest = buf; int i; for (i = 0; i < len; i++) { *dest++ = hex[(data[i] >> 4) & 0xf]; *dest++ = hex[data[i] & 0xf]; *dest++ = ' '; } if (likely(len > 0)) dest--; /* last space will be overwrited with null */ *dest = '\0'; return dest - buf; } /* print buffer as hex string */ int pr_buffer(const char *tag, const char *data, size_t data_len, size_t max_len) { size_t len = min(data_len, max_len); unsigned char hexstr[len ? len * 3 : 1]; /* 1 <= sizeof <= max_len*3 */ dump2hex(hexstr, data, len); /* don't change this printk to mif_debug for print this as level7 */ return printk(KERN_INFO "%s(%u): %s%s\n", tag, data_len, hexstr, len == data_len ? "" : " ..."); } struct io_device *get_iod_with_channel(struct modem_shared *msd, unsigned channel) { struct rb_node *n = msd->iodevs_tree_chan.rb_node; struct io_device *iodev; while (n) { iodev = rb_entry(n, struct io_device, node_chan); if (channel < iodev->id) n = n->rb_left; else if (channel > iodev->id) n = n->rb_right; else return iodev; } return NULL; } struct io_device *get_iod_with_format(struct modem_shared *msd, enum dev_format format) { struct rb_node *n = msd->iodevs_tree_fmt.rb_node; struct io_device *iodev; while (n) { iodev = rb_entry(n, struct io_device, node_fmt); if (format < iodev->format) n = n->rb_left; else if (format > iodev->format) n = n->rb_right; else return iodev; } return NULL; } struct io_device *insert_iod_with_channel(struct modem_shared *msd, unsigned channel, struct io_device *iod) { struct rb_node **p = &msd->iodevs_tree_chan.rb_node; struct rb_node *parent = NULL; struct io_device *iodev; while (*p) { parent = *p; iodev = rb_entry(parent, struct io_device, node_chan); if (channel < iodev->id) p = &(*p)->rb_left; else if (channel > iodev->id) p = &(*p)->rb_right; else return iodev; } rb_link_node(&iod->node_chan, parent, p); rb_insert_color(&iod->node_chan, &msd->iodevs_tree_chan); return NULL; } struct io_device *insert_iod_with_format(struct modem_shared *msd, enum dev_format format, struct io_device *iod) { struct rb_node **p = &msd->iodevs_tree_fmt.rb_node; struct rb_node *parent = NULL; struct io_device *iodev; while (*p) { parent = *p; iodev = rb_entry(parent, struct io_device, node_fmt); if (format < iodev->format) p = &(*p)->rb_left; else if (format > iodev->format) p = &(*p)->rb_right; else return iodev; } rb_link_node(&iod->node_fmt, parent, p); rb_insert_color(&iod->node_fmt, &msd->iodevs_tree_fmt); return NULL; } void iodevs_for_each(struct modem_shared *msd, action_fn action, void *args) { struct io_device *iod; struct rb_node *node = rb_first(&msd->iodevs_tree_chan); for (; node; node = rb_next(node)) { iod = rb_entry(node, struct io_device, node_chan); action(iod, args); } } void iodev_netif_wake(struct io_device *iod, void *args) { if (iod->io_typ == IODEV_NET && iod->ndev) { netif_wake_queue(iod->ndev); mif_info("%s\n", iod->name); } } void iodev_netif_stop(struct io_device *iod, void *args) { if (iod->io_typ == IODEV_NET && iod->ndev) { netif_stop_queue(iod->ndev); mif_info("%s\n", iod->name); } }