/* * Copyright (C) 2015 Spreadtrum Communications Inc. * * Authors : * Keguang Zhang * Jingxiang Li * * 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 "sprdwl.h" #include "msg.h" /* static struct sprdwl_msg_list msg_list */ int sprdwl_msg_init(int num, struct sprdwl_msg_list *list) { int i; struct sprdwl_msg_buf *msg_buf; struct sprdwl_msg_buf *pos; if (!list) return -EPERM; INIT_LIST_HEAD(&list->freelist); INIT_LIST_HEAD(&list->busylist); INIT_LIST_HEAD(&list->cmd_to_free); list->maxnum = num; spin_lock_init(&list->freelock); spin_lock_init(&list->busylock); spin_lock_init(&list->complock); atomic_set(&list->ref, 0); atomic_set(&list->flow, 0); for (i = 0; i < num; i++) { msg_buf = kzalloc(sizeof(*msg_buf), GFP_KERNEL); if (msg_buf) { INIT_LIST_HEAD(&msg_buf->list); list_add_tail(&msg_buf->list, &list->freelist); } else { wl_err("%s failed to alloc msg_buf!\n", __func__); goto err_alloc_buf; } } return 0; err_alloc_buf: list_for_each_entry_safe(msg_buf, pos, &list->freelist, list) { list_del(&msg_buf->list); kfree(msg_buf); } return -ENOMEM; } #define SPRDWL_MSG_EXIT_VAL 0x8000 void sprdwl_msg_deinit(struct sprdwl_msg_list *list) { struct sprdwl_msg_buf *msg_buf; struct sprdwl_msg_buf *pos; struct timespec txmsgftime1, txmsgftime2; atomic_add(SPRDWL_MSG_EXIT_VAL, &list->ref); if (atomic_read(&list->ref) > SPRDWL_MSG_EXIT_VAL) wl_err("%s ref not ok! wait for pop!\n", __func__); getnstimeofday(&txmsgftime1); while (atomic_read(&list->ref) > SPRDWL_MSG_EXIT_VAL) { getnstimeofday(&txmsgftime2); if (((unsigned long)(timespec_to_ns(&txmsgftime2) - timespec_to_ns(&txmsgftime1))/1000000) > 3000) break; usleep_range(2000, 2500); } wl_info("%s list->ref ok!\n", __func__); if (!list_empty(&list->busylist)) WARN_ON(1); list_for_each_entry_safe(msg_buf, pos, &list->freelist, list) { list_del(&msg_buf->list); kfree(msg_buf); } } struct sprdwl_msg_buf *sprdwl_alloc_msg_buf(struct sprdwl_msg_list *list) { struct sprdwl_msg_buf *msg_buf = NULL; if (atomic_inc_return(&list->ref) >= SPRDWL_MSG_EXIT_VAL) { atomic_dec(&list->ref); return NULL; } spin_lock_bh(&list->freelock); if (!list_empty(&list->freelist)) { msg_buf = list_first_entry(&list->freelist, struct sprdwl_msg_buf, list); list_del(&msg_buf->list); } spin_unlock_bh(&list->freelock); if (!msg_buf) atomic_dec(&list->ref); return msg_buf; } void sprdwl_free_msg_buf(struct sprdwl_msg_buf *msg_buf, struct sprdwl_msg_list *list) { spin_lock_bh(&list->freelock); list_add_tail(&msg_buf->list, &list->freelist); atomic_dec(&list->ref); spin_unlock_bh(&list->freelock); } void sprdwl_queue_msg_buf(struct sprdwl_msg_buf *msg_buf, struct sprdwl_msg_list *list) { spin_lock_bh(&list->busylock); list_add_tail(&msg_buf->list, &list->busylist); spin_unlock_bh(&list->busylock); } struct sprdwl_msg_buf *sprdwl_peek_msg_buf(struct sprdwl_msg_list *list) { struct sprdwl_msg_buf *msg_buf = NULL; spin_lock_bh(&list->busylock); if (!list_empty(&list->busylist)) msg_buf = list_first_entry(&list->busylist, struct sprdwl_msg_buf, list); spin_unlock_bh(&list->busylock); return msg_buf; } void sprdwl_dequeue_msg_buf(struct sprdwl_msg_buf *msg_buf, struct sprdwl_msg_list *list) { spin_lock_bh(&list->busylock); list_del(&msg_buf->list); spin_unlock_bh(&list->busylock); sprdwl_free_msg_buf(msg_buf, list); } struct sprdwl_msg_buf *sprdwl_get_msgbuf_by_data(void *data, struct sprdwl_msg_list *list) { int find = 0; struct sprdwl_msg_buf *pos; struct sprdwl_msg_buf *msg_buf; spin_lock_bh(&list->busylock); list_for_each_entry_safe(msg_buf, pos, &list->busylist, list) { if (data == msg_buf->tran_data) { list_del(&msg_buf->list); find = 1; break; } } spin_unlock_bh(&list->busylock); return find ? msg_buf : NULL; } #if defined(UWE5621_FTR) struct sprdwl_msg_buf *sprdwl_get_tail_msg_buf(struct sprdwl_msg_list *list) { struct sprdwl_msg_buf *msg_buf = NULL; spin_lock_bh(&list->busylock); if (!list_empty(&list->busylist)) msg_buf = list_last_entry(&list->busylist, struct sprdwl_msg_buf, list); spin_unlock_bh(&list->busylock); return msg_buf; } #endif