Add Qorvo UWB driver for Qorvo UWB chipset QM35725s Change-Id: I77124d7d242863fd8c8a602db7d4cb59aa96ef57 Signed-off-by: zhuangnk1 <zhuangnk1@lenovo.com> Reviewed-on: https://gerrit.mot.com/2687911 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Tao Sun <suntao2@motorola.com> Submit-Approved: Jira Key
234 lines
5.1 KiB
C
234 lines
5.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
* This file is part of the QM35 UCI stack for linux.
|
|
*
|
|
* Copyright (c) 2021 Qorvo US, Inc.
|
|
*
|
|
* This software is provided under the GNU General Public License, version 2
|
|
* (GPLv2), as well as under a Qorvo commercial license.
|
|
*
|
|
* You may choose to use this software under the terms of the GPLv2 License,
|
|
* version 2 ("GPLv2"), as published by the Free Software Foundation.
|
|
* You should have received a copy of the GPLv2 along with this program. If
|
|
* not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* This program is distributed under the GPLv2 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 GPLv2 for more
|
|
* details.
|
|
*
|
|
* If you cannot meet the requirements of the GPLv2, you may not use this
|
|
* software for any purpose without first obtaining a commercial license from
|
|
* Qorvo.
|
|
* Please contact Qorvo to inquire about licensing terms.
|
|
*
|
|
* QM35 UCI layer HSSPI Protocol
|
|
*/
|
|
|
|
#include "qm35.h"
|
|
#include "hsspi_uci.h"
|
|
|
|
struct uci_packet *uci_packet_alloc(u16 length)
|
|
{
|
|
struct uci_packet *p;
|
|
|
|
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
|
if (!p)
|
|
return NULL;
|
|
|
|
if (hsspi_init_block(&p->blk, length)) {
|
|
kfree(p);
|
|
return NULL;
|
|
}
|
|
|
|
p->data = p->blk.data;
|
|
p->length = p->blk.length;
|
|
return p;
|
|
}
|
|
|
|
void uci_packet_free(struct uci_packet *p)
|
|
{
|
|
hsspi_deinit_block(&p->blk);
|
|
kfree(p);
|
|
}
|
|
|
|
static int uci_registered(struct hsspi_layer *layer)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void clear_rx_list(struct uci_layer *uci)
|
|
{
|
|
struct uci_packet *p;
|
|
|
|
mutex_lock(&uci->lock);
|
|
|
|
while (!list_empty(&uci->rx_list)) {
|
|
p = list_first_entry(&uci->rx_list, struct uci_packet, list);
|
|
|
|
list_del(&p->list);
|
|
|
|
uci_packet_free(p);
|
|
}
|
|
|
|
mutex_unlock(&uci->lock);
|
|
|
|
wake_up_interruptible(&uci->wq);
|
|
}
|
|
|
|
static void uci_unregistered(struct hsspi_layer *hlayer)
|
|
{
|
|
struct uci_layer *uci = container_of(hlayer, struct uci_layer, hlayer);
|
|
|
|
clear_rx_list(uci);
|
|
}
|
|
|
|
static struct hsspi_block *uci_get(struct hsspi_layer *hlayer, u16 length)
|
|
{
|
|
struct uci_packet *p;
|
|
|
|
p = uci_packet_alloc(length);
|
|
if (!p)
|
|
return NULL;
|
|
|
|
return &p->blk;
|
|
}
|
|
|
|
static void uci_sent(struct hsspi_layer *hlayer, struct hsspi_block *blk,
|
|
int status)
|
|
{
|
|
struct uci_packet *p = container_of(blk, struct uci_packet, blk);
|
|
|
|
p->status = status;
|
|
complete(p->write_done);
|
|
}
|
|
|
|
#define UCI_CONTROL_PACKET_PAYLOAD_SIZE_LOCATION (3)
|
|
|
|
static size_t get_payload_size_from_header(const u8 *header)
|
|
{
|
|
bool is_data_packet = ((header[0] >> 5) & 0x07) == 0;
|
|
bool is_control_packet = !is_data_packet && ((header[0] >> 7) == 0);
|
|
|
|
if (is_control_packet)
|
|
return header[UCI_CONTROL_PACKET_PAYLOAD_SIZE_LOCATION];
|
|
|
|
return (header[3] << 8) | header[2];
|
|
}
|
|
|
|
#define UCI_PACKET_HEADER_SIZE (4)
|
|
|
|
static void uci_received(struct hsspi_layer *hlayer, struct hsspi_block *blk,
|
|
int status)
|
|
{
|
|
struct uci_layer *uci = container_of(hlayer, struct uci_layer, hlayer);
|
|
struct uci_packet *p = container_of(blk, struct uci_packet, blk);
|
|
|
|
if (status)
|
|
uci_packet_free(p);
|
|
else {
|
|
struct uci_packet *next;
|
|
size_t readn = 0;
|
|
size_t payload_size;
|
|
|
|
while (1) {
|
|
if (blk->length - readn < UCI_PACKET_HEADER_SIZE)
|
|
// Incomplete UCI header
|
|
break;
|
|
|
|
payload_size = get_payload_size_from_header(
|
|
(u8 *)blk->data + readn);
|
|
|
|
if (blk->length - readn <=
|
|
UCI_PACKET_HEADER_SIZE + payload_size)
|
|
// blk contains no additional packet
|
|
break;
|
|
|
|
next = kzalloc(sizeof(*next), GFP_KERNEL);
|
|
if (!next)
|
|
break;
|
|
|
|
next->data = p->blk.data + readn;
|
|
next->length = UCI_PACKET_HEADER_SIZE + payload_size;
|
|
|
|
readn += next->length;
|
|
|
|
mutex_lock(&uci->lock);
|
|
list_add_tail(&next->list, &uci->rx_list);
|
|
mutex_unlock(&uci->lock);
|
|
}
|
|
|
|
p->data = p->blk.data + readn;
|
|
p->length = p->blk.length - readn;
|
|
|
|
mutex_lock(&uci->lock);
|
|
list_add_tail(&p->list, &uci->rx_list);
|
|
mutex_unlock(&uci->lock);
|
|
|
|
wake_up_interruptible(&uci->wq);
|
|
}
|
|
}
|
|
|
|
static const struct hsspi_layer_ops uci_ops = {
|
|
.registered = uci_registered,
|
|
.unregistered = uci_unregistered,
|
|
.get = uci_get,
|
|
.received = uci_received,
|
|
.sent = uci_sent,
|
|
};
|
|
|
|
int uci_layer_init(struct uci_layer *uci)
|
|
{
|
|
uci->hlayer.name = "UCI";
|
|
uci->hlayer.id = UL_UCI_APP;
|
|
uci->hlayer.ops = &uci_ops;
|
|
|
|
INIT_LIST_HEAD(&uci->rx_list);
|
|
mutex_init(&uci->lock);
|
|
init_waitqueue_head(&uci->wq);
|
|
return 0;
|
|
}
|
|
|
|
void uci_layer_deinit(struct uci_layer *uci)
|
|
{
|
|
clear_rx_list(uci);
|
|
}
|
|
|
|
bool uci_layer_has_data_available(struct uci_layer *uci)
|
|
{
|
|
bool ret;
|
|
|
|
mutex_lock(&uci->lock);
|
|
ret = !list_empty(&uci->rx_list);
|
|
mutex_unlock(&uci->lock);
|
|
return ret;
|
|
}
|
|
|
|
struct uci_packet *uci_layer_read(struct uci_layer *uci, size_t max_size,
|
|
bool non_blocking)
|
|
{
|
|
struct uci_packet *p;
|
|
int ret;
|
|
|
|
if (!non_blocking) {
|
|
ret = wait_event_interruptible(
|
|
uci->wq, uci_layer_has_data_available(uci));
|
|
if (ret)
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
mutex_lock(&uci->lock);
|
|
p = list_first_entry_or_null(&uci->rx_list, struct uci_packet, list);
|
|
if (p) {
|
|
if (p->length > max_size)
|
|
p = ERR_PTR(-EMSGSIZE);
|
|
else
|
|
list_del(&p->list);
|
|
} else
|
|
p = ERR_PTR(-EAGAIN);
|
|
|
|
mutex_unlock(&uci->lock);
|
|
return p;
|
|
}
|