/* * Copyright (C) 2015 Spreadtrum Communications Inc. * 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 "bufring.h" #include "loopcheck.h" #include "wcn_glb.h" #include "wcn_procfs.h" int mdbg_log_read(int channel, struct mbuf_t *head, struct mbuf_t *tail, int num); #ifdef CONFIG_WCN_PCIE int mdbg_log_push(int chn, struct mbuf_t **head, struct mbuf_t **tail, int *num); #endif static struct ring_device *ring_dev; static unsigned long long rx_count; static unsigned long long rx_count_last; #ifdef CONFIG_WCN_PCIE static struct mchn_ops_t mdbg_ringc_ops = { .channel = WCN_RING_RX, .inout = WCNBUS_RX, .hif_type = 1, .buf_size = 1056, .pool_size = 6, .cb_in_irq = 0, .pop_link = mdbg_log_read, .push_link = mdbg_log_push, }; #elif defined CONFIG_WCN_USB static struct mchn_ops_t mdbg_ringc_ops = { .channel = WCN_RING_RX, .inout = WCNBUS_RX, .pool_size = 10, .pop_link = mdbg_log_read, .hif_type = HW_TYPE_USB, }; #else static struct mchn_ops_t mdbg_ringc_ops = { .channel = WCN_RING_RX, .inout = WCNBUS_RX, .pool_size = 1, .pop_link = mdbg_log_read, }; #endif #ifdef CONFIG_WCN_PCIE int mdbg_log_push(int chn, struct mbuf_t **head, struct mbuf_t **tail, int *num) { WCN_INFO("%s enter num=%d,mbuf used done", __func__, *num); return 0; } #endif bool mdbg_rx_count_change(void) { rx_count = sprdwcn_bus_get_rx_total_cnt(); WCN_INFO("rx_count:0x%llx rx_count_last:0x%llx\n", rx_count, rx_count_last); if ((rx_count == 0) && (rx_count_last == 0)) { return true; } else if (rx_count != rx_count_last) { rx_count_last = rx_count; return true; } else { return false; } } int mdbg_read_release(unsigned int fifo_id) { return 0; } long mdbg_content_len(void) { if (unlikely(!ring_dev)) return 0; return mdbg_ring_readable_len(ring_dev->ring); } static long int mdbg_comm_write(char *buf, long int len, unsigned int subtype) { unsigned char *send_buf = NULL; char *str = NULL; struct mbuf_t *head, *tail; int num = 1; if (unlikely(marlin_get_module_status() != true)) { WCN_ERR("WCN module have not open\n"); return -EIO; } send_buf = kzalloc(len + PUB_HEAD_RSV + 1, GFP_KERNEL); if (!send_buf) return -ENOMEM; memcpy(send_buf + PUB_HEAD_RSV, buf, len); str = strstr(send_buf + PUB_HEAD_RSV, SMP_HEAD_STR); if (!str) str = strstr(send_buf + PUB_HEAD_RSV + ARMLOG_HEAD, SMP_HEAD_STR); if (str) { int ret; /* for arm log to pc */ WCN_INFO("smp len:%ld,str:%s\n", len, str); str[sizeof(SMP_HEAD_STR)] = 0; ret = kstrtol(&str[sizeof(SMP_HEAD_STR) - 1], 10, &ring_dev->flag_smp); WCN_INFO("smp ret:%d, flag_smp:%ld\n", ret, ring_dev->flag_smp); kfree(send_buf); } else { if (!sprdwcn_bus_list_alloc( mdbg_proc_ops[MDBG_AT_TX_OPS].channel, &head, &tail, &num)) { head->buf = send_buf; head->len = len; head->next = NULL; sprdwcn_bus_push_list( mdbg_proc_ops[MDBG_AT_TX_OPS].channel, head, tail, num); } } return len; } static void mdbg_ring_rx_task(struct work_struct *work) { struct ring_rx_data *rx = NULL; struct mdbg_ring_t *ring = NULL; struct mbuf_t *mbuf_node; int i; #ifdef CONFIG_WCN_SDIO struct bus_puh_t *puh = NULL; #endif if (unlikely(!ring_dev)) { WCN_ERR("ring_dev is NULL\n"); return; } spin_lock_bh(&ring_dev->rw_lock); rx = list_first_entry_or_null(&ring_dev->rx_head, struct ring_rx_data, entry); if (rx) { list_del(&rx->entry); } else { WCN_ERR("tasklet something err\n"); spin_unlock_bh(&ring_dev->rw_lock); return; } if (!list_empty(&ring_dev->rx_head)) schedule_work(&ring_dev->rx_task); ring = ring_dev->ring; spin_unlock_bh(&ring_dev->rw_lock); for (i = 0, mbuf_node = rx->head; i < rx->num; i++, mbuf_node = mbuf_node->next) { #ifdef CONFIG_WCN_SDIO rx->addr = mbuf_node->buf + PUB_HEAD_RSV; puh = (struct bus_puh_t *)mbuf_node->buf; #ifdef CONFIG_WCND mdbg_ring_write(ring, rx->addr, puh->len); #else log_rx_callback(rx->addr, puh->len); #endif #else log_rx_callback(mbuf_node->buf, mbuf_node->len); #endif } sprdwcn_bus_push_list(mdbg_ringc_ops.channel, rx->head, rx->tail, rx->num); wake_up_log_wait(); kfree(rx); } int mdbg_log_read(int channel, struct mbuf_t *head, struct mbuf_t *tail, int num) { struct ring_rx_data *rx; if (ring_dev) { mutex_lock(&ring_dev->mdbg_read_mutex); rx = kmalloc(sizeof(*rx), GFP_KERNEL); if (!rx) { WCN_ERR("mdbg ring low memory\n"); mutex_unlock(&ring_dev->mdbg_read_mutex); sprdwcn_bus_push_list(channel, head, tail, num); return 0; } mutex_unlock(&ring_dev->mdbg_read_mutex); spin_lock_bh(&ring_dev->rw_lock); rx->channel = channel; rx->head = head; rx->tail = tail; rx->num = num; list_add_tail(&rx->entry, &ring_dev->rx_head); spin_unlock_bh(&ring_dev->rw_lock); schedule_work(&ring_dev->rx_task); } return 0; } long int mdbg_send(char *buf, long int len, unsigned int subtype) { long int sent_size = 0; WCN_DEBUG("BYTE MODE"); __pm_stay_awake(ring_dev->rw_wake_lock); sent_size = mdbg_comm_write(buf, len, subtype); __pm_relax(ring_dev->rw_wake_lock); return sent_size; } EXPORT_SYMBOL_GPL(mdbg_send); long int mdbg_receive(void *buf, long int len) { return mdbg_ring_read(ring_dev->ring, buf, len); } int mdbg_tx_cb(int channel, struct mbuf_t *head, struct mbuf_t *tail, int num) { #ifndef CONFIG_WCN_PCIE struct mbuf_t *mbuf_node; int i; mbuf_node = head; for (i = 0; i < num; i++, mbuf_node = mbuf_node->next) { kfree(mbuf_node->buf); mbuf_node->buf = NULL; } #endif /* PCIe buf is witebuf[], not kmalloc, no need to free */ sprdwcn_bus_list_free(channel, head, tail, num); return 0; } int mdbg_tx_power_notify(int chn, int flag) { if (flag) { WCN_DEBUG("%s resume\n", __func__); #ifdef CONFIG_WCN_LOOPCHECK start_loopcheck(); #endif } else { WCN_DEBUG("%s suspend\n", __func__); #ifdef CONFIG_WCN_LOOPCHECK stop_loopcheck(); #endif } return 0; } static void mdbg_pt_ring_reg(void) { sprdwcn_bus_chn_init(&mdbg_ringc_ops); #ifdef CONFIG_WCN_PCIE prepare_free_buf(15, 1056, 6); #endif } static void mdbg_pt_ring_unreg(void) { sprdwcn_bus_chn_deinit(&mdbg_ringc_ops); } int mdbg_ring_init(void) { int err = 0; ring_dev = kmalloc(sizeof(struct ring_device), GFP_KERNEL); if (!ring_dev) return -ENOMEM; ring_dev->ring = mdbg_ring_alloc(MDBG_RX_RING_SIZE); if (!(ring_dev->ring)) { WCN_ERR("Ring malloc error."); return -MDBG_ERR_MALLOC_FAIL; } /*wakeup_source pointer*/ ring_dev->rw_wake_lock = wakeup_source_create("mdbg_wake_lock"); wakeup_source_add(ring_dev->rw_wake_lock); spin_lock_init(&ring_dev->rw_lock); mutex_init(&ring_dev->mdbg_read_mutex); INIT_LIST_HEAD(&ring_dev->rx_head); INIT_WORK(&ring_dev->rx_task, mdbg_ring_rx_task); ring_dev->flag_smp = 0; mdbg_pt_ring_reg(); WCN_DEBUG("mdbg_ring_init success!"); mdbg_dev->ring_dev = ring_dev; return err; } void mdbg_ring_remove(void) { struct ring_rx_data *pos, *next; MDBG_FUNC_ENTERY; mdbg_pt_ring_unreg(); cancel_work_sync(&ring_dev->rx_task); list_for_each_entry_safe(pos, next, &ring_dev->rx_head, entry) { list_del(&pos->entry); kfree(pos); } mutex_destroy(&ring_dev->mdbg_read_mutex); /*wakeup_source pointer*/ wakeup_source_remove(ring_dev->rw_wake_lock); wakeup_source_destroy(ring_dev->rw_wake_lock); mdbg_ring_destroy(ring_dev->ring); mdbg_dev->ring_dev = NULL; kfree(ring_dev); ring_dev = NULL; }