opnsense-src/sys/contrib/dev/mediatek/mt76/mcu.c
Bjoern A. Zeeb 6c92544d7c mt76: import mediatek/mt76 driver
Import ISC-licensed driver parts of mediatek/mt76
assumed to be based on Linux wireless-testing at
a02411a5b98612c12be99349836d99f07db12a77 (tag: wt-2022-11-23).

Complement the driver and LinuxKPI with our own (dummy)
implementations of missing parts (util.h and soc/mediatek/)
as well as changes to make compile on FreeBSD with changes
covered by #ifdef (__FreeBSD__) conditions.
Further select updates were applied since the initial import
in order to keep compiling along with other LinuxKPI based
drivers.

For the moment we only target the mt7915 and mt7921 PCI parts.
More may follow in the future.

Firmware is provided by port net/wifi-firmware-mt76-kmod.

Given the lack of full license texts on non-local files this is
imported under the draft policy for handling SPDX files (D29226). [1]

Approved by:	core (emaste, 2022-04-08) [1]
MFC after:	2 months
2023-04-23 21:29:49 +00:00

138 lines
3 KiB
C

// SPDX-License-Identifier: ISC
/*
* Copyright (C) 2019 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
#include "mt76.h"
struct sk_buff *
__mt76_mcu_msg_alloc(struct mt76_dev *dev, const void *data,
int len, int data_len, gfp_t gfp)
{
const struct mt76_mcu_ops *ops = dev->mcu_ops;
struct sk_buff *skb;
len = max_t(int, len, data_len);
len = ops->headroom + len + ops->tailroom;
skb = alloc_skb(len, gfp);
if (!skb)
return NULL;
memset(skb->head, 0, len);
skb_reserve(skb, ops->headroom);
if (data && data_len)
skb_put_data(skb, data, data_len);
return skb;
}
EXPORT_SYMBOL_GPL(__mt76_mcu_msg_alloc);
struct sk_buff *mt76_mcu_get_response(struct mt76_dev *dev,
unsigned long expires)
{
unsigned long timeout;
if (!time_is_after_jiffies(expires))
return NULL;
timeout = expires - jiffies;
wait_event_timeout(dev->mcu.wait,
(!skb_queue_empty(&dev->mcu.res_q) ||
test_bit(MT76_MCU_RESET, &dev->phy.state)),
timeout);
return skb_dequeue(&dev->mcu.res_q);
}
EXPORT_SYMBOL_GPL(mt76_mcu_get_response);
void mt76_mcu_rx_event(struct mt76_dev *dev, struct sk_buff *skb)
{
skb_queue_tail(&dev->mcu.res_q, skb);
wake_up(&dev->mcu.wait);
}
EXPORT_SYMBOL_GPL(mt76_mcu_rx_event);
int mt76_mcu_send_and_get_msg(struct mt76_dev *dev, int cmd, const void *data,
int len, bool wait_resp, struct sk_buff **ret_skb)
{
struct sk_buff *skb;
if (dev->mcu_ops->mcu_send_msg)
return dev->mcu_ops->mcu_send_msg(dev, cmd, data, len, wait_resp);
skb = mt76_mcu_msg_alloc(dev, data, len);
if (!skb)
return -ENOMEM;
return mt76_mcu_skb_send_and_get_msg(dev, skb, cmd, wait_resp, ret_skb);
}
EXPORT_SYMBOL_GPL(mt76_mcu_send_and_get_msg);
int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb,
int cmd, bool wait_resp,
struct sk_buff **ret_skb)
{
unsigned long expires;
int ret, seq;
if (ret_skb)
*ret_skb = NULL;
mutex_lock(&dev->mcu.mutex);
ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb, cmd, &seq);
if (ret < 0)
goto out;
if (!wait_resp) {
ret = 0;
goto out;
}
expires = jiffies + dev->mcu.timeout;
do {
skb = mt76_mcu_get_response(dev, expires);
ret = dev->mcu_ops->mcu_parse_response(dev, cmd, skb, seq);
if (!ret && ret_skb)
*ret_skb = skb;
else
dev_kfree_skb(skb);
} while (ret == -EAGAIN);
out:
mutex_unlock(&dev->mcu.mutex);
return ret;
}
EXPORT_SYMBOL_GPL(mt76_mcu_skb_send_and_get_msg);
#if defined(__linux__)
int __mt76_mcu_send_firmware(struct mt76_dev *dev, int cmd, const void *data,
#elif defined(__FreeBSD__)
int __mt76_mcu_send_firmware(struct mt76_dev *dev, int cmd, const u8 *data,
#endif
int len, int max_len)
{
int err, cur_len;
while (len > 0) {
cur_len = min_t(int, max_len, len);
err = mt76_mcu_send_msg(dev, cmd, data, cur_len, false);
if (err)
return err;
data += cur_len;
len -= cur_len;
if (dev->queue_ops->tx_cleanup)
dev->queue_ops->tx_cleanup(dev,
dev->q_mcu[MT_MCUQ_FWDL],
false);
}
return 0;
}
EXPORT_SYMBOL_GPL(__mt76_mcu_send_firmware);