linux/drivers/net/wireless/mediatek/mt76/mcu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: ISC
   2/*
   3 * Copyright (C) 2019 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
   4 */
   5
   6#include "mt76.h"
   7
   8struct sk_buff *
   9mt76_mcu_msg_alloc(struct mt76_dev *dev, const void *data,
  10                   int data_len)
  11{
  12        const struct mt76_mcu_ops *ops = dev->mcu_ops;
  13        int length = ops->headroom + data_len + ops->tailroom;
  14        struct sk_buff *skb;
  15
  16        skb = alloc_skb(length, GFP_KERNEL);
  17        if (!skb)
  18                return NULL;
  19
  20        memset(skb->head, 0, length);
  21        skb_reserve(skb, ops->headroom);
  22
  23        if (data && data_len)
  24                skb_put_data(skb, data, data_len);
  25
  26        return skb;
  27}
  28EXPORT_SYMBOL_GPL(mt76_mcu_msg_alloc);
  29
  30struct sk_buff *mt76_mcu_get_response(struct mt76_dev *dev,
  31                                      unsigned long expires)
  32{
  33        unsigned long timeout;
  34
  35        if (!time_is_after_jiffies(expires))
  36                return NULL;
  37
  38        timeout = expires - jiffies;
  39        wait_event_timeout(dev->mcu.wait,
  40                           (!skb_queue_empty(&dev->mcu.res_q) ||
  41                            test_bit(MT76_MCU_RESET, &dev->phy.state)),
  42                           timeout);
  43        return skb_dequeue(&dev->mcu.res_q);
  44}
  45EXPORT_SYMBOL_GPL(mt76_mcu_get_response);
  46
  47void mt76_mcu_rx_event(struct mt76_dev *dev, struct sk_buff *skb)
  48{
  49        skb_queue_tail(&dev->mcu.res_q, skb);
  50        wake_up(&dev->mcu.wait);
  51}
  52EXPORT_SYMBOL_GPL(mt76_mcu_rx_event);
  53
  54int mt76_mcu_send_and_get_msg(struct mt76_dev *dev, int cmd, const void *data,
  55                              int len, bool wait_resp, struct sk_buff **ret_skb)
  56{
  57        struct sk_buff *skb;
  58
  59        if (dev->mcu_ops->mcu_send_msg)
  60                return dev->mcu_ops->mcu_send_msg(dev, cmd, data, len, wait_resp);
  61
  62        skb = mt76_mcu_msg_alloc(dev, data, len);
  63        if (!skb)
  64                return -ENOMEM;
  65
  66        return mt76_mcu_skb_send_and_get_msg(dev, skb, cmd, wait_resp, ret_skb);
  67}
  68EXPORT_SYMBOL_GPL(mt76_mcu_send_and_get_msg);
  69
  70int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb,
  71                                  int cmd, bool wait_resp,
  72                                  struct sk_buff **ret_skb)
  73{
  74        unsigned long expires;
  75        int ret, seq;
  76
  77        if (ret_skb)
  78                *ret_skb = NULL;
  79
  80        mutex_lock(&dev->mcu.mutex);
  81
  82        ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb, cmd, &seq);
  83        if (ret < 0)
  84                goto out;
  85
  86        if (!wait_resp) {
  87                ret = 0;
  88                goto out;
  89        }
  90
  91        expires = jiffies + dev->mcu.timeout;
  92
  93        do {
  94                skb = mt76_mcu_get_response(dev, expires);
  95                ret = dev->mcu_ops->mcu_parse_response(dev, cmd, skb, seq);
  96                if (!ret && ret_skb)
  97                        *ret_skb = skb;
  98                else
  99                        dev_kfree_skb(skb);
 100        } while (ret == -EAGAIN);
 101
 102out:
 103        mutex_unlock(&dev->mcu.mutex);
 104
 105        return ret;
 106}
 107EXPORT_SYMBOL_GPL(mt76_mcu_skb_send_and_get_msg);
 108
 109int mt76_mcu_send_firmware(struct mt76_dev *dev, int cmd, const void *data,
 110                           int len)
 111{
 112        int err, cur_len;
 113
 114        while (len > 0) {
 115                cur_len = min_t(int, 4096 - dev->mcu_ops->headroom, len);
 116
 117                err = mt76_mcu_send_msg(dev, cmd, data, cur_len, false);
 118                if (err)
 119                        return err;
 120
 121                data += cur_len;
 122                len -= cur_len;
 123
 124                if (dev->queue_ops->tx_cleanup)
 125                        dev->queue_ops->tx_cleanup(dev,
 126                                                   dev->q_mcu[MT_MCUQ_FWDL],
 127                                                   false);
 128        }
 129
 130        return 0;
 131}
 132EXPORT_SYMBOL_GPL(mt76_mcu_send_firmware);
 133