developer | b11a539 | 2022-03-31 00:34:47 +0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: ISC |
| 2 | /* |
| 3 | * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> |
| 4 | */ |
| 5 | |
| 6 | #include <linux/kernel.h> |
| 7 | #include <linux/module.h> |
| 8 | |
| 9 | #include "../mt76x02_usb.h" |
| 10 | #include "mt76x2u.h" |
| 11 | |
| 12 | static const struct usb_device_id mt76x2u_device_table[] = { |
| 13 | { USB_DEVICE(0x0b05, 0x1833) }, /* Asus USB-AC54 */ |
| 14 | { USB_DEVICE(0x0b05, 0x17eb) }, /* Asus USB-AC55 */ |
| 15 | { USB_DEVICE(0x0b05, 0x180b) }, /* Asus USB-N53 B1 */ |
| 16 | { USB_DEVICE(0x0e8d, 0x7612) }, /* Aukey USBAC1200 - Alfa AWUS036ACM */ |
| 17 | { USB_DEVICE(0x057c, 0x8503) }, /* Avm FRITZ!WLAN AC860 */ |
| 18 | { USB_DEVICE(0x7392, 0xb711) }, /* Edimax EW 7722 UAC */ |
| 19 | { USB_DEVICE(0x0e8d, 0x7632) }, /* HC-M7662BU1 */ |
| 20 | { USB_DEVICE(0x2c4e, 0x0103) }, /* Mercury UD13 */ |
| 21 | { USB_DEVICE(0x0846, 0x9053) }, /* Netgear A6210 */ |
| 22 | { USB_DEVICE(0x045e, 0x02e6) }, /* XBox One Wireless Adapter */ |
| 23 | { USB_DEVICE(0x045e, 0x02fe) }, /* XBox One Wireless Adapter */ |
| 24 | { }, |
| 25 | }; |
| 26 | |
| 27 | static int mt76x2u_probe(struct usb_interface *intf, |
| 28 | const struct usb_device_id *id) |
| 29 | { |
| 30 | static const struct mt76_driver_ops drv_ops = { |
| 31 | .drv_flags = MT_DRV_SW_RX_AIRTIME, |
| 32 | .survey_flags = SURVEY_INFO_TIME_TX, |
| 33 | .update_survey = mt76x02_update_channel, |
| 34 | .tx_prepare_skb = mt76x02u_tx_prepare_skb, |
| 35 | .tx_complete_skb = mt76x02u_tx_complete_skb, |
| 36 | .tx_status_data = mt76x02_tx_status_data, |
| 37 | .rx_skb = mt76x02_queue_rx_skb, |
| 38 | .sta_ps = mt76x02_sta_ps, |
| 39 | .sta_add = mt76x02_sta_add, |
| 40 | .sta_remove = mt76x02_sta_remove, |
| 41 | }; |
| 42 | struct usb_device *udev = interface_to_usbdev(intf); |
| 43 | struct mt76x02_dev *dev; |
| 44 | struct mt76_dev *mdev; |
| 45 | int err; |
| 46 | |
| 47 | mdev = mt76_alloc_device(&intf->dev, sizeof(*dev), &mt76x2u_ops, |
| 48 | &drv_ops); |
| 49 | if (!mdev) |
| 50 | return -ENOMEM; |
| 51 | |
| 52 | dev = container_of(mdev, struct mt76x02_dev, mt76); |
| 53 | |
| 54 | udev = usb_get_dev(udev); |
| 55 | usb_reset_device(udev); |
| 56 | |
| 57 | usb_set_intfdata(intf, dev); |
| 58 | |
| 59 | mt76x02u_init_mcu(mdev); |
| 60 | err = mt76u_init(mdev, intf); |
| 61 | if (err < 0) |
| 62 | goto err; |
| 63 | |
| 64 | mdev->rev = mt76_rr(dev, MT_ASIC_VERSION); |
| 65 | dev_info(mdev->dev, "ASIC revision: %08x\n", mdev->rev); |
| 66 | if (!is_mt76x2(dev)) { |
| 67 | err = -ENODEV; |
| 68 | goto err; |
| 69 | } |
| 70 | |
| 71 | err = mt76x2u_register_device(dev); |
| 72 | if (err < 0) |
| 73 | goto err; |
| 74 | |
| 75 | return 0; |
| 76 | |
| 77 | err: |
| 78 | mt76u_queues_deinit(&dev->mt76); |
| 79 | mt76_free_device(&dev->mt76); |
| 80 | usb_set_intfdata(intf, NULL); |
| 81 | usb_put_dev(udev); |
| 82 | |
| 83 | return err; |
| 84 | } |
| 85 | |
| 86 | static void mt76x2u_disconnect(struct usb_interface *intf) |
| 87 | { |
| 88 | struct usb_device *udev = interface_to_usbdev(intf); |
| 89 | struct mt76x02_dev *dev = usb_get_intfdata(intf); |
| 90 | struct ieee80211_hw *hw = mt76_hw(dev); |
| 91 | |
| 92 | set_bit(MT76_REMOVED, &dev->mphy.state); |
| 93 | ieee80211_unregister_hw(hw); |
| 94 | mt76x2u_cleanup(dev); |
| 95 | mt76_free_device(&dev->mt76); |
| 96 | usb_set_intfdata(intf, NULL); |
| 97 | usb_put_dev(udev); |
| 98 | } |
| 99 | |
| 100 | static int __maybe_unused mt76x2u_suspend(struct usb_interface *intf, |
| 101 | pm_message_t state) |
| 102 | { |
| 103 | struct mt76x02_dev *dev = usb_get_intfdata(intf); |
| 104 | |
| 105 | mt76u_stop_rx(&dev->mt76); |
| 106 | |
| 107 | return 0; |
| 108 | } |
| 109 | |
| 110 | static int __maybe_unused mt76x2u_resume(struct usb_interface *intf) |
| 111 | { |
| 112 | struct mt76x02_dev *dev = usb_get_intfdata(intf); |
| 113 | int err; |
| 114 | |
| 115 | err = mt76u_resume_rx(&dev->mt76); |
| 116 | if (err < 0) |
| 117 | goto err; |
| 118 | |
| 119 | err = mt76x2u_init_hardware(dev); |
| 120 | if (err < 0) |
| 121 | goto err; |
| 122 | |
| 123 | return 0; |
| 124 | |
| 125 | err: |
| 126 | mt76x2u_cleanup(dev); |
| 127 | return err; |
| 128 | } |
| 129 | |
| 130 | MODULE_DEVICE_TABLE(usb, mt76x2u_device_table); |
| 131 | MODULE_FIRMWARE(MT7662_FIRMWARE); |
| 132 | MODULE_FIRMWARE(MT7662_ROM_PATCH); |
| 133 | |
| 134 | static struct usb_driver mt76x2u_driver = { |
| 135 | .name = KBUILD_MODNAME, |
| 136 | .id_table = mt76x2u_device_table, |
| 137 | .probe = mt76x2u_probe, |
| 138 | .disconnect = mt76x2u_disconnect, |
| 139 | #ifdef CONFIG_PM |
| 140 | .suspend = mt76x2u_suspend, |
| 141 | .resume = mt76x2u_resume, |
| 142 | .reset_resume = mt76x2u_resume, |
| 143 | #endif /* CONFIG_PM */ |
| 144 | .soft_unbind = 1, |
| 145 | .disable_hub_initiated_lpm = 1, |
| 146 | }; |
| 147 | module_usb_driver(mt76x2u_driver); |
| 148 | |
| 149 | MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>"); |
| 150 | MODULE_LICENSE("Dual BSD/GPL"); |