Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  usb.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright(c) 2025  Realtek Corporation
 */


#include <linux/usb.h>
#include "debug.h"
#include "mac.h"
#include "reg.h"
#include "txrx.h"
#include "usb.h"

static void rtw89_usb_read_port_complete(struct urb *urb);

static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr,
    void *data, u16 len, u8 reqtype)
{
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 struct usb_device *udev = rtwusb->udev;
 unsigned int pipe;
 u16 value, index;
 int attempt, ret;

 if (test_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags))
  return;

 value = u32_get_bits(addr, GENMASK(15, 0));
 index = u32_get_bits(addr, GENMASK(23, 16));

 for (attempt = 0; attempt < 10; attempt++) {
  *rtwusb->vendor_req_buf = 0;

  if (reqtype == RTW89_USB_VENQT_READ) {
   pipe = usb_rcvctrlpipe(udev, 0);
  } else { /* RTW89_USB_VENQT_WRITE */
   pipe = usb_sndctrlpipe(udev, 0);

   memcpy(rtwusb->vendor_req_buf, data, len);
  }

  ret = usb_control_msg(udev, pipe, RTW89_USB_VENQT, reqtype,
          value, index, rtwusb->vendor_req_buf,
          len, 500);

  if (ret == len) { /* Success */
   atomic_set(&rtwusb->continual_io_error, 0);

   if (reqtype == RTW89_USB_VENQT_READ)
    memcpy(data, rtwusb->vendor_req_buf, len);

   break;
  }

  if (ret == -ESHUTDOWN || ret == -ENODEV)
   set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags);
  else if (ret < 0)
   rtw89_warn(rtwdev,
       "usb %s%u 0x%x fail ret=%d value=0x%x attempt=%d\n",
       reqtype == RTW89_USB_VENQT_READ ? "read" : "write",
       len * 8, addr, ret,
       le32_to_cpup(rtwusb->vendor_req_buf),
       attempt);
  else if (ret > 0 && reqtype == RTW89_USB_VENQT_READ)
   memcpy(data, rtwusb->vendor_req_buf, len);

  if (atomic_inc_return(&rtwusb->continual_io_error) > 4) {
   set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags);
   break;
  }
 }
}

static u32 rtw89_usb_read_cmac(struct rtw89_dev *rtwdev, u32 addr)
{
 u32 addr32, val32, shift;
 __le32 data = 0;
 int count;

 addr32 = addr & ~0x3;
 shift = (addr & 0x3) * 8;

 for (count = 0; ; count++) {
  rtw89_usb_vendorreq(rtwdev, addr32, &data, 4,
        RTW89_USB_VENQT_READ);

  val32 = le32_to_cpu(data);
  if (val32 != RTW89_R32_DEAD)
   break;

  if (count >= MAC_REG_POOL_COUNT) {
   rtw89_warn(rtwdev, "%s: addr %#x = %#x\n",
       __func__, addr32, val32);
   val32 = RTW89_R32_DEAD;
   break;
  }

  rtw89_write32(rtwdev, R_AX_CK_EN, B_AX_CMAC_ALLCKEN);
 }

 return val32 >> shift;
}

static u8 rtw89_usb_ops_read8(struct rtw89_dev *rtwdev, u32 addr)
{
 u8 data = 0;

 if (ACCESS_CMAC(addr))
  return rtw89_usb_read_cmac(rtwdev, addr);

 rtw89_usb_vendorreq(rtwdev, addr, &data, 1, RTW89_USB_VENQT_READ);

 return data;
}

static u16 rtw89_usb_ops_read16(struct rtw89_dev *rtwdev, u32 addr)
{
 __le16 data = 0;

 if (ACCESS_CMAC(addr))
  return rtw89_usb_read_cmac(rtwdev, addr);

 rtw89_usb_vendorreq(rtwdev, addr, &data, 2, RTW89_USB_VENQT_READ);

 return le16_to_cpu(data);
}

static u32 rtw89_usb_ops_read32(struct rtw89_dev *rtwdev, u32 addr)
{
 __le32 data = 0;

 if (ACCESS_CMAC(addr))
  return rtw89_usb_read_cmac(rtwdev, addr);

 rtw89_usb_vendorreq(rtwdev, addr, &data, 4,
       RTW89_USB_VENQT_READ);

 return le32_to_cpu(data);
}

static void rtw89_usb_ops_write8(struct rtw89_dev *rtwdev, u32 addr, u8 val)
{
 u8 data = val;

 rtw89_usb_vendorreq(rtwdev, addr, &data, 1, RTW89_USB_VENQT_WRITE);
}

static void rtw89_usb_ops_write16(struct rtw89_dev *rtwdev, u32 addr, u16 val)
{
 __le16 data = cpu_to_le16(val);

 rtw89_usb_vendorreq(rtwdev, addr, &data, 2, RTW89_USB_VENQT_WRITE);
}

static void rtw89_usb_ops_write32(struct rtw89_dev *rtwdev, u32 addr, u32 val)
{
 __le32 data = cpu_to_le32(val);

 rtw89_usb_vendorreq(rtwdev, addr, &data, 4, RTW89_USB_VENQT_WRITE);
}

static u32
rtw89_usb_ops_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev,
         u8 txch)
{
 if (txch == RTW89_TXCH_CH12)
  return 1;

 return 42; /* TODO some kind of calculation? */
}

static u8 rtw89_usb_get_bulkout_id(u8 ch_dma)
{
 switch (ch_dma) {
 case RTW89_DMA_ACH0:
  return 3;
 case RTW89_DMA_ACH1:
  return 4;
 case RTW89_DMA_ACH2:
  return 5;
 case RTW89_DMA_ACH3:
  return 6;
 default:
 case RTW89_DMA_B0MG:
  return 0;
 case RTW89_DMA_B0HI:
  return 1;
 case RTW89_DMA_H2C:
  return 2;
 }
}

static void rtw89_usb_write_port_complete(struct urb *urb)
{
 struct rtw89_usb_tx_ctrl_block *txcb = urb->context;
 struct rtw89_dev *rtwdev = txcb->rtwdev;
 struct ieee80211_tx_info *info;
 struct rtw89_txwd_body *txdesc;
 struct sk_buff *skb;
 u32 txdesc_size;

 while (true) {
  skb = skb_dequeue(&txcb->tx_ack_queue);
  if (!skb)
   break;

  if (txcb->txch == RTW89_TXCH_CH12) {
   dev_kfree_skb_any(skb);
   continue;
  }

  txdesc = (struct rtw89_txwd_body *)skb->data;

  txdesc_size = rtwdev->chip->txwd_body_size;
  if (le32_get_bits(txdesc->dword0, RTW89_TXWD_BODY0_WD_INFO_EN))
   txdesc_size += rtwdev->chip->txwd_info_size;

  skb_pull(skb, txdesc_size);

  info = IEEE80211_SKB_CB(skb);
  ieee80211_tx_info_clear_status(info);

  if (urb->status == 0) {
   if (info->flags & IEEE80211_TX_CTL_NO_ACK)
    info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
   else
    info->flags |= IEEE80211_TX_STAT_ACK;
  }

  ieee80211_tx_status_irqsafe(rtwdev->hw, skb);
 }

 switch (urb->status) {
 case 0:
 case -EPIPE:
 case -EPROTO:
 case -EINPROGRESS:
 case -ENOENT:
 case -ECONNRESET:
  break;
 default:
  set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags);
  break;
 }

 kfree(txcb);
 usb_free_urb(urb);
}

static int rtw89_usb_write_port(struct rtw89_dev *rtwdev, u8 ch_dma,
    void *data, int len, void *context)
{
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 struct usb_device *usbd = rtwusb->udev;
 struct urb *urb;
 u8 bulkout_id = rtw89_usb_get_bulkout_id(ch_dma);
 unsigned int pipe;
 int ret;

 if (test_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags))
  return 0;

 urb = usb_alloc_urb(0, GFP_ATOMIC);
 if (!urb)
  return -ENOMEM;

 pipe = usb_sndbulkpipe(usbd, rtwusb->out_pipe[bulkout_id]);

 usb_fill_bulk_urb(urb, usbd, pipe, data, len,
     rtw89_usb_write_port_complete, context);
 urb->transfer_flags |= URB_ZERO_PACKET;
 ret = usb_submit_urb(urb, GFP_ATOMIC);

 if (ret)
  usb_free_urb(urb);

 if (ret == -ENODEV)
  set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags);

 return ret;
}

static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch)
{
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 struct rtw89_usb_tx_ctrl_block *txcb;
 struct sk_buff *skb;
 int ret;

 while (true) {
  skb = skb_dequeue(&rtwusb->tx_queue[txch]);
  if (!skb)
   break;

  txcb = kmalloc(sizeof(*txcb), GFP_ATOMIC);
  if (!txcb) {
   dev_kfree_skb_any(skb);
   continue;
  }

  txcb->rtwdev = rtwdev;
  txcb->txch = txch;
  skb_queue_head_init(&txcb->tx_ack_queue);

  skb_queue_tail(&txcb->tx_ack_queue, skb);

  ret = rtw89_usb_write_port(rtwdev, txch, skb->data, skb->len,
        txcb);
  if (ret) {
   rtw89_err(rtwdev, "write port txch %d failed: %d\n",
      txch, ret);

   skb_dequeue(&txcb->tx_ack_queue);
   kfree(txcb);
   dev_kfree_skb_any(skb);
  }
 }
}

static int rtw89_usb_tx_write_fwcmd(struct rtw89_dev *rtwdev,
        struct rtw89_core_tx_request *tx_req)
{
 struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 struct sk_buff *skb = tx_req->skb;
 struct sk_buff *skb512;
 u32 txdesc_size = rtwdev->chip->h2c_desc_size;
 void *txdesc;

 if (((desc_info->pkt_size + txdesc_size) % 512) == 0) {
  rtw89_debug(rtwdev, RTW89_DBG_HCI, "avoiding multiple of 512\n");

  skb512 = dev_alloc_skb(txdesc_size + desc_info->pkt_size +
           RTW89_USB_MOD512_PADDING);
  if (!skb512) {
   rtw89_err(rtwdev, "%s: failed to allocate skb\n",
      __func__);

   return -ENOMEM;
  }

  skb_pull(skb512, txdesc_size);
  skb_put_data(skb512, skb->data, skb->len);
  skb_put_zero(skb512, RTW89_USB_MOD512_PADDING);

  dev_kfree_skb_any(skb);
  skb = skb512;
  tx_req->skb = skb512;

  desc_info->pkt_size += RTW89_USB_MOD512_PADDING;
 }

 txdesc = skb_push(skb, txdesc_size);
 memset(txdesc, 0, txdesc_size);
 rtw89_chip_fill_txdesc_fwcmd(rtwdev, desc_info, txdesc);

 skb_queue_tail(&rtwusb->tx_queue[desc_info->ch_dma], skb);

 return 0;
}

static int rtw89_usb_ops_tx_write(struct rtw89_dev *rtwdev,
      struct rtw89_core_tx_request *tx_req)
{
 struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 struct sk_buff *skb = tx_req->skb;
 struct rtw89_txwd_body *txdesc;
 u32 txdesc_size;

 if ((desc_info->ch_dma == RTW89_TXCH_CH12 ||
      tx_req->tx_type == RTW89_CORE_TX_TYPE_FWCMD) &&
     (desc_info->ch_dma != RTW89_TXCH_CH12 ||
      tx_req->tx_type != RTW89_CORE_TX_TYPE_FWCMD)) {
  rtw89_err(rtwdev, "dma channel %d/TX type %d mismatch\n",
     desc_info->ch_dma, tx_req->tx_type);
  return -EINVAL;
 }

 if (desc_info->ch_dma == RTW89_TXCH_CH12)
  return rtw89_usb_tx_write_fwcmd(rtwdev, tx_req);

 txdesc_size = rtwdev->chip->txwd_body_size;
 if (desc_info->en_wd_info)
  txdesc_size += rtwdev->chip->txwd_info_size;

 txdesc = skb_push(skb, txdesc_size);
 memset(txdesc, 0, txdesc_size);
 rtw89_chip_fill_txdesc(rtwdev, desc_info, txdesc);

 le32p_replace_bits(&txdesc->dword0, 1, RTW89_TXWD_BODY0_STF_MODE);

 skb_queue_tail(&rtwusb->tx_queue[desc_info->ch_dma], skb);

 return 0;
}

static void rtw89_usb_rx_handler(struct work_struct *work)
{
 struct rtw89_usb *rtwusb = container_of(work, struct rtw89_usb, rx_work);
 struct rtw89_dev *rtwdev = rtwusb->rtwdev;
 struct rtw89_rx_desc_info desc_info;
 struct sk_buff *rx_skb;
 struct sk_buff *skb;
 u32 pkt_offset;
 int limit;

 for (limit = 0; limit < 200; limit++) {
  rx_skb = skb_dequeue(&rtwusb->rx_queue);
  if (!rx_skb)
   break;

  if (skb_queue_len(&rtwusb->rx_queue) >= RTW89_USB_MAX_RXQ_LEN) {
   rtw89_warn(rtwdev, "rx_queue overflow\n");
   dev_kfree_skb_any(rx_skb);
   continue;
  }

  memset(&desc_info, 0, sizeof(desc_info));
  rtw89_chip_query_rxdesc(rtwdev, &desc_info, rx_skb->data, 0);

  skb = rtw89_alloc_skb_for_rx(rtwdev, desc_info.pkt_size);
  if (!skb) {
   rtw89_debug(rtwdev, RTW89_DBG_HCI,
        "failed to allocate RX skb of size %u\n",
        desc_info.pkt_size);
   continue;
  }

  pkt_offset = desc_info.offset + desc_info.rxd_len;

  skb_put_data(skb, rx_skb->data + pkt_offset,
        desc_info.pkt_size);

  rtw89_core_rx(rtwdev, &desc_info, skb);

  if (skb_queue_len(&rtwusb->rx_free_queue) >= RTW89_USB_RX_SKB_NUM)
   dev_kfree_skb_any(rx_skb);
  else
   skb_queue_tail(&rtwusb->rx_free_queue, rx_skb);
 }

 if (limit == 200) {
  rtw89_debug(rtwdev, RTW89_DBG_HCI,
       "left %d rx skbs in the queue for later\n",
       skb_queue_len(&rtwusb->rx_queue));
  queue_work(rtwusb->rxwq, &rtwusb->rx_work);
 }
}

static void rtw89_usb_rx_resubmit(struct rtw89_usb *rtwusb,
      struct rtw89_usb_rx_ctrl_block *rxcb,
      gfp_t gfp)
{
 struct rtw89_dev *rtwdev = rtwusb->rtwdev;
 struct sk_buff *rx_skb;
 int ret;

 rx_skb = skb_dequeue(&rtwusb->rx_free_queue);
 if (!rx_skb)
  rx_skb = alloc_skb(RTW89_USB_RECVBUF_SZ, gfp);

 if (!rx_skb)
  goto try_later;

 skb_reset_tail_pointer(rx_skb);
 rx_skb->len = 0;

 rxcb->rx_skb = rx_skb;

 usb_fill_bulk_urb(rxcb->rx_urb, rtwusb->udev,
     usb_rcvbulkpipe(rtwusb->udev, rtwusb->in_pipe),
     rxcb->rx_skb->data, RTW89_USB_RECVBUF_SZ,
     rtw89_usb_read_port_complete, rxcb);

 ret = usb_submit_urb(rxcb->rx_urb, gfp);
 if (ret) {
  skb_queue_tail(&rtwusb->rx_free_queue, rxcb->rx_skb);

  if (ret == -ENODEV)
   set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags);
  else
   rtw89_err(rtwdev, "Err sending rx data urb %d\n", ret);

  if (ret == -ENOMEM)
   goto try_later;
 }

 return;

try_later:
 rxcb->rx_skb = NULL;
 queue_work(rtwusb->rxwq, &rtwusb->rx_urb_work);
}

static void rtw89_usb_rx_resubmit_work(struct work_struct *work)
{
 struct rtw89_usb *rtwusb = container_of(work, struct rtw89_usb, rx_urb_work);
 struct rtw89_usb_rx_ctrl_block *rxcb;
 int i;

 for (i = 0; i < RTW89_USB_RXCB_NUM; i++) {
  rxcb = &rtwusb->rx_cb[i];

  if (!rxcb->rx_skb)
   rtw89_usb_rx_resubmit(rtwusb, rxcb, GFP_ATOMIC);
 }
}

static void rtw89_usb_read_port_complete(struct urb *urb)
{
 struct rtw89_usb_rx_ctrl_block *rxcb = urb->context;
 struct rtw89_dev *rtwdev = rxcb->rtwdev;
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 struct sk_buff *skb = rxcb->rx_skb;

 if (urb->status == 0) {
  if (urb->actual_length > urb->transfer_buffer_length ||
      urb->actual_length < sizeof(struct rtw89_rxdesc_short)) {
   rtw89_err(rtwdev, "failed to get urb length: %d\n",
      urb->actual_length);
   skb_queue_tail(&rtwusb->rx_free_queue, skb);
  } else {
   skb_put(skb, urb->actual_length);
   skb_queue_tail(&rtwusb->rx_queue, skb);
   queue_work(rtwusb->rxwq, &rtwusb->rx_work);
  }

  rtw89_usb_rx_resubmit(rtwusb, rxcb, GFP_ATOMIC);
 } else {
  skb_queue_tail(&rtwusb->rx_free_queue, skb);

  if (atomic_inc_return(&rtwusb->continual_io_error) > 4)
   set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags);

  switch (urb->status) {
  case -EINVAL:
  case -EPIPE:
  case -ENODEV:
  case -ESHUTDOWN:
   set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags);
   break;
  case -EPROTO:
  case -EILSEQ:
  case -ETIME:
  case -ECOMM:
  case -EOVERFLOW:
  case -ENOENT:
   break;
  case -EINPROGRESS:
   rtw89_info(rtwdev, "URB is in progress\n");
   break;
  default:
   rtw89_err(rtwdev, "%s status %d\n",
      __func__, urb->status);
   break;
  }
 }
}

static void rtw89_usb_cancel_rx_bufs(struct rtw89_usb *rtwusb)
{
 struct rtw89_usb_rx_ctrl_block *rxcb;
 int i;

 for (i = 0; i < RTW89_USB_RXCB_NUM; i++) {
  rxcb = &rtwusb->rx_cb[i];
  usb_kill_urb(rxcb->rx_urb);
 }
}

static void rtw89_usb_free_rx_bufs(struct rtw89_usb *rtwusb)
{
 struct rtw89_usb_rx_ctrl_block *rxcb;
 int i;

 for (i = 0; i < RTW89_USB_RXCB_NUM; i++) {
  rxcb = &rtwusb->rx_cb[i];
  usb_free_urb(rxcb->rx_urb);
 }
}

static int rtw89_usb_alloc_rx_bufs(struct rtw89_usb *rtwusb)
{
 struct rtw89_usb_rx_ctrl_block *rxcb;
 int i;

 for (i = 0; i < RTW89_USB_RXCB_NUM; i++) {
  rxcb = &rtwusb->rx_cb[i];

  rxcb->rtwdev = rtwusb->rtwdev;
  rxcb->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
  if (!rxcb->rx_urb) {
   rtw89_usb_free_rx_bufs(rtwusb);
   return -ENOMEM;
  }
 }

 return 0;
}

static int rtw89_usb_init_rx(struct rtw89_dev *rtwdev)
{
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 struct sk_buff *rx_skb;
 int i;

 rtwusb->rxwq = alloc_workqueue("rtw89_usb: rx wq", WQ_BH, 0);
 if (!rtwusb->rxwq) {
  rtw89_err(rtwdev, "failed to create RX work queue\n");
  return -ENOMEM;
 }

 skb_queue_head_init(&rtwusb->rx_queue);
 skb_queue_head_init(&rtwusb->rx_free_queue);

 INIT_WORK(&rtwusb->rx_work, rtw89_usb_rx_handler);
 INIT_WORK(&rtwusb->rx_urb_work, rtw89_usb_rx_resubmit_work);

 for (i = 0; i < RTW89_USB_RX_SKB_NUM; i++) {
  rx_skb = alloc_skb(RTW89_USB_RECVBUF_SZ, GFP_KERNEL);
  if (rx_skb)
   skb_queue_tail(&rtwusb->rx_free_queue, rx_skb);
 }

 return 0;
}

static void rtw89_usb_deinit_rx(struct rtw89_dev *rtwdev)
{
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);

 skb_queue_purge(&rtwusb->rx_queue);

 destroy_workqueue(rtwusb->rxwq);

 skb_queue_purge(&rtwusb->rx_free_queue);
}

static void rtw89_usb_start_rx(struct rtw89_dev *rtwdev)
{
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 int i;

 for (i = 0; i < RTW89_USB_RXCB_NUM; i++)
  rtw89_usb_rx_resubmit(rtwusb, &rtwusb->rx_cb[i], GFP_KERNEL);
}

static void rtw89_usb_init_tx(struct rtw89_dev *rtwdev)
{
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 int i;

 for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++)
  skb_queue_head_init(&rtwusb->tx_queue[i]);
}

static void rtw89_usb_deinit_tx(struct rtw89_dev *rtwdev)
{
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 int i;

 for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) {
  if (i == RTW89_TXCH_CH12)
   skb_queue_purge(&rtwusb->tx_queue[i]);
  else
   ieee80211_purge_tx_queue(rtwdev->hw, &rtwusb->tx_queue[i]);
 }
}

static void rtw89_usb_ops_reset(struct rtw89_dev *rtwdev)
{
 /* TODO: anything to do here? */
}

static int rtw89_usb_ops_start(struct rtw89_dev *rtwdev)
{
 return 0; /* Nothing to do. */
}

static void rtw89_usb_ops_stop(struct rtw89_dev *rtwdev)
{
 /* Nothing to do. */
}

static void rtw89_usb_ops_pause(struct rtw89_dev *rtwdev, bool pause)
{
 /* Nothing to do? */
}

static void rtw89_usb_ops_switch_mode(struct rtw89_dev *rtwdev, bool low_power)
{
 /* Nothing to do. */
}

static int rtw89_usb_ops_deinit(struct rtw89_dev *rtwdev)
{
 return 0; /* Nothing to do. */
}

static int rtw89_usb_ops_mac_pre_init(struct rtw89_dev *rtwdev)
{
 u32 val32;

 rtw89_write32_set(rtwdev, R_AX_USB_HOST_REQUEST_2, B_AX_R_USBIO_MODE);

 /* fix USB IO hang suggest by chihhanli@realtek.com */
 rtw89_write32_clr(rtwdev, R_AX_USB_WLAN0_1,
     B_AX_USBRX_RST | B_AX_USBTX_RST);

 val32 = rtw89_read32(rtwdev, R_AX_HCI_FUNC_EN);
 val32 &= ~(B_AX_HCI_RXDMA_EN | B_AX_HCI_TXDMA_EN);
 rtw89_write32(rtwdev, R_AX_HCI_FUNC_EN, val32);

 val32 |= B_AX_HCI_RXDMA_EN | B_AX_HCI_TXDMA_EN;
 rtw89_write32(rtwdev, R_AX_HCI_FUNC_EN, val32);
 /* fix USB TRX hang suggest by chihhanli@realtek.com */

 return 0;
}

static int rtw89_usb_ops_mac_pre_deinit(struct rtw89_dev *rtwdev)
{
 return 0; /* Nothing to do. */
}

static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev)
{
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 enum usb_device_speed speed;
 u32 ep;

 rtw89_write32_clr(rtwdev, R_AX_USB3_MAC_NPI_CONFIG_INTF_0,
     B_AX_SSPHY_LFPS_FILTER);

 speed = rtwusb->udev->speed;

 if (speed == USB_SPEED_SUPER)
  rtw89_write8(rtwdev, R_AX_RXDMA_SETTING, USB3_BULKSIZE);
 else if (speed == USB_SPEED_HIGH)
  rtw89_write8(rtwdev, R_AX_RXDMA_SETTING, USB2_BULKSIZE);
 else
  rtw89_write8(rtwdev, R_AX_RXDMA_SETTING, USB11_BULKSIZE);

 for (ep = 5; ep <= 12; ep++) {
  if (ep == 8)
   continue;

  rtw89_write8_mask(rtwdev, R_AX_USB_ENDPOINT_0,
      B_AX_EP_IDX, ep);
  rtw89_write8(rtwdev, R_AX_USB_ENDPOINT_2 + 1, NUMP);
 }

 return 0;
}

static void rtw89_usb_ops_recalc_int_mit(struct rtw89_dev *rtwdev)
{
 /* Nothing to do. */
}

static int rtw89_usb_ops_mac_lv1_rcvy(struct rtw89_dev *rtwdev,
          enum rtw89_lv1_rcvy_step step)
{
 u32 reg, mask;

 switch (rtwdev->chip->chip_id) {
 case RTL8851B:
 case RTL8852A:
 case RTL8852B:
  reg = R_AX_USB_WLAN0_1;
  mask = B_AX_USBRX_RST | B_AX_USBTX_RST;
  break;
 case RTL8852C:
  reg = R_AX_USB_WLAN0_1_V1;
  mask = B_AX_USBRX_RST_V1 | B_AX_USBTX_RST_V1;
  break;
 default:
  rtw89_err(rtwdev, "%s: fix me\n", __func__);
  return -EOPNOTSUPP;
 }

 switch (step) {
 case RTW89_LV1_RCVY_STEP_1:
  rtw89_write32_set(rtwdev, reg, mask);

  msleep(30);
  break;
 case RTW89_LV1_RCVY_STEP_2:
  rtw89_write32_clr(rtwdev, reg, mask);
  break;
 default:
  return -EINVAL;
 }

 return 0;
}

static void rtw89_usb_ops_dump_err_status(struct rtw89_dev *rtwdev)
{
 rtw89_warn(rtwdev, "%s TODO\n", __func__);
}

static const struct rtw89_hci_ops rtw89_usb_ops = {
 .tx_write = rtw89_usb_ops_tx_write,
 .tx_kick_off = rtw89_usb_ops_tx_kick_off,
 .flush_queues = NULL, /* Not needed? */
 .reset  = rtw89_usb_ops_reset,
 .start  = rtw89_usb_ops_start,
 .stop  = rtw89_usb_ops_stop,
 .pause  = rtw89_usb_ops_pause,
 .switch_mode = rtw89_usb_ops_switch_mode,
 .recalc_int_mit = rtw89_usb_ops_recalc_int_mit,

 .read8  = rtw89_usb_ops_read8,
 .read16  = rtw89_usb_ops_read16,
 .read32  = rtw89_usb_ops_read32,
 .write8  = rtw89_usb_ops_write8,
 .write16 = rtw89_usb_ops_write16,
 .write32 = rtw89_usb_ops_write32,

 .mac_pre_init = rtw89_usb_ops_mac_pre_init,
 .mac_pre_deinit = rtw89_usb_ops_mac_pre_deinit,
 .mac_post_init = rtw89_usb_ops_mac_post_init,
 .deinit  = rtw89_usb_ops_deinit,

 .check_and_reclaim_tx_resource = rtw89_usb_ops_check_and_reclaim_tx_resource,
 .mac_lv1_rcvy = rtw89_usb_ops_mac_lv1_rcvy,
 .dump_err_status = rtw89_usb_ops_dump_err_status,
 .napi_poll = NULL,

 .recovery_start = NULL,
 .recovery_complete = NULL,

 .ctrl_txdma_ch = NULL,
 .ctrl_txdma_fw_ch = NULL,
 .ctrl_trxhci = NULL,
 .poll_txdma_ch_idle = NULL,

 .clr_idx_all = NULL,
 .clear  = NULL,
 .disable_intr = NULL,
 .enable_intr = NULL,
 .rst_bdram = NULL,
};

static int rtw89_usb_parse(struct rtw89_dev *rtwdev,
      struct usb_interface *intf)
{
 struct usb_host_interface *host_interface = &intf->altsetting[0];
 struct usb_interface_descriptor *intf_desc = &host_interface->desc;
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 struct usb_endpoint_descriptor *endpoint;
 int num_out_pipes = 0;
 u8 num;
 int i;

 if (intf_desc->bNumEndpoints > RTW89_MAX_ENDPOINT_NUM) {
  rtw89_err(rtwdev, "found %d endpoints, expected %d max\n",
     intf_desc->bNumEndpoints, RTW89_MAX_ENDPOINT_NUM);
  return -EINVAL;
 }

 for (i = 0; i < intf_desc->bNumEndpoints; i++) {
  endpoint = &host_interface->endpoint[i].desc;
  num = usb_endpoint_num(endpoint);

  if (usb_endpoint_dir_in(endpoint) &&
      usb_endpoint_xfer_bulk(endpoint)) {
   if (rtwusb->in_pipe) {
    rtw89_err(rtwdev,
       "found more than 1 bulk in endpoint\n");
    return -EINVAL;
   }

   rtwusb->in_pipe = num;
  }

  if (usb_endpoint_dir_out(endpoint) &&
      usb_endpoint_xfer_bulk(endpoint)) {
   if (num_out_pipes >= RTW89_MAX_BULKOUT_NUM) {
    rtw89_err(rtwdev,
       "found more than %d bulk out endpoints\n",
       RTW89_MAX_BULKOUT_NUM);
    return -EINVAL;
   }

   rtwusb->out_pipe[num_out_pipes++] = num;
  }
 }

 if (num_out_pipes < 1) {
  rtw89_err(rtwdev, "no bulk out endpoints found\n");
  return -EINVAL;
 }

 return 0;
}

static int rtw89_usb_intf_init(struct rtw89_dev *rtwdev,
          struct usb_interface *intf)
{
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 int ret;

 ret = rtw89_usb_parse(rtwdev, intf);
 if (ret)
  return ret;

 rtwusb->vendor_req_buf = kmalloc(sizeof(*rtwusb->vendor_req_buf),
      GFP_KERNEL);
 if (!rtwusb->vendor_req_buf)
  return -ENOMEM;

 rtwusb->udev = usb_get_dev(interface_to_usbdev(intf));

 usb_set_intfdata(intf, rtwdev->hw);

 SET_IEEE80211_DEV(rtwdev->hw, &intf->dev);

 return 0;
}

static void rtw89_usb_intf_deinit(struct rtw89_dev *rtwdev,
      struct usb_interface *intf)
{
 struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);

 usb_put_dev(rtwusb->udev);
 kfree(rtwusb->vendor_req_buf);
 usb_set_intfdata(intf, NULL);
}

int rtw89_usb_probe(struct usb_interface *intf,
      const struct usb_device_id *id)
{
 const struct rtw89_driver_info *info;
 struct rtw89_dev *rtwdev;
 struct rtw89_usb *rtwusb;
 int ret;

 info = (const struct rtw89_driver_info *)id->driver_info;

 rtwdev = rtw89_alloc_ieee80211_hw(&intf->dev,
       sizeof(struct rtw89_usb),
       info->chip, info->variant);
 if (!rtwdev) {
  dev_err(&intf->dev, "failed to allocate hw\n");
  return -ENOMEM;
 }

 rtwusb = rtw89_usb_priv(rtwdev);
 rtwusb->rtwdev = rtwdev;

 rtwdev->hci.ops = &rtw89_usb_ops;
 rtwdev->hci.type = RTW89_HCI_TYPE_USB;

 ret = rtw89_usb_intf_init(rtwdev, intf);
 if (ret) {
  rtw89_err(rtwdev, "failed to initialise intf: %d\n", ret);
  goto err_free_hw;
 }

 if (rtwusb->udev->speed == USB_SPEED_SUPER)
  rtwdev->hci.dle_type = RTW89_HCI_DLE_TYPE_USB3;
 else
  rtwdev->hci.dle_type = RTW89_HCI_DLE_TYPE_USB2;

 rtw89_usb_init_tx(rtwdev);

 ret = rtw89_usb_alloc_rx_bufs(rtwusb);
 if (ret)
  goto err_intf_deinit;

 ret = rtw89_usb_init_rx(rtwdev);
 if (ret)
  goto err_free_rx_bufs;

 ret = rtw89_core_init(rtwdev);
 if (ret) {
  rtw89_err(rtwdev, "failed to initialise core: %d\n", ret);
  goto err_deinit_rx;
 }

 ret = rtw89_chip_info_setup(rtwdev);
 if (ret) {
  rtw89_err(rtwdev, "failed to setup chip information\n");
  goto err_core_deinit;
 }

 ret = rtw89_core_register(rtwdev);
 if (ret) {
  rtw89_err(rtwdev, "failed to register core\n");
  goto err_core_deinit;
 }

 rtw89_usb_start_rx(rtwdev);

 set_bit(RTW89_FLAG_PROBE_DONE, rtwdev->flags);

 return 0;

err_core_deinit:
 rtw89_core_deinit(rtwdev);
err_deinit_rx:
 rtw89_usb_deinit_rx(rtwdev);
err_free_rx_bufs:
 rtw89_usb_free_rx_bufs(rtwusb);
err_intf_deinit:
 rtw89_usb_intf_deinit(rtwdev, intf);
err_free_hw:
 rtw89_free_ieee80211_hw(rtwdev);

 return ret;
}
EXPORT_SYMBOL(rtw89_usb_probe);

void rtw89_usb_disconnect(struct usb_interface *intf)
{
 struct ieee80211_hw *hw = usb_get_intfdata(intf);
 struct rtw89_dev *rtwdev;
 struct rtw89_usb *rtwusb;

 if (!hw)
  return;

 rtwdev = hw->priv;
 rtwusb = rtw89_usb_priv(rtwdev);

 rtw89_usb_cancel_rx_bufs(rtwusb);

 rtw89_core_unregister(rtwdev);
 rtw89_core_deinit(rtwdev);
 rtw89_usb_deinit_rx(rtwdev);
 rtw89_usb_free_rx_bufs(rtwusb);
 rtw89_usb_deinit_tx(rtwdev);
 rtw89_usb_intf_deinit(rtwdev, intf);
 rtw89_free_ieee80211_hw(rtwdev);
}
EXPORT_SYMBOL(rtw89_usb_disconnect);

MODULE_AUTHOR("Bitterblue Smith ");
MODULE_DESCRIPTION("Realtek USB 802.11ax wireless driver");
MODULE_LICENSE("Dual BSD/GPL");

Messung V0.5
C=95 H=94 G=94

¤ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge