185 lines
4.6 KiB
C
Raw Normal View History

2025-05-10 21:58:58 +08:00
/*
* Copyright (C) 2015 Spreadtrum Communications Inc.
*
* Authors :
* Keguang Zhang <keguang.zhang@spreadtrum.com>
* Jingxiang Li <Jingxiang.li@spreadtrum.com>
*
* 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