Message ID | 1dfa20d1-5fee-4e75-a2db-a59d723babe2@gmail.com (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Ping-Ke Shih |
Headers | show |
Series | wifi: rtw88: Add support for LED blinking | expand |
Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote: > Register a struct led_classdev with the kernel's LED subsystem and > create a throughput-based trigger for it. Then mac80211 makes the LED > blink. > > Tested with Tenda U12 (RTL8812AU), Tenda U9 (RTL8811CU), TP-Link Archer > T2U Nano (RTL8811AU), TP-Link Archer T3U Plus (RTL8812BU), Edimax > EW-7611UCB (RTL8821AU), LM842 (RTL8822CU). > > Also tested with devices which don't have LEDs: the laptop's internal > RTL8822CE and a no-name RTL8723DU. > > Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com> > --- > drivers/net/wireless/realtek/rtw88/main.c | 91 ++++++++++++++++++- > drivers/net/wireless/realtek/rtw88/main.h | 9 ++ > drivers/net/wireless/realtek/rtw88/reg.h | 12 +++ > drivers/net/wireless/realtek/rtw88/rtw8812a.c | 23 +++++ > drivers/net/wireless/realtek/rtw88/rtw8821a.c | 32 +++++++ > drivers/net/wireless/realtek/rtw88/rtw8821c.c | 25 +++++ > drivers/net/wireless/realtek/rtw88/rtw8822b.c | 25 +++++ > drivers/net/wireless/realtek/rtw88/rtw8822c.c | 25 +++++ > 8 files changed, 240 insertions(+), 2 deletions(-) > > diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c > index 6993f93c8f06..387940839f8b 100644 > --- a/drivers/net/wireless/realtek/rtw88/main.c > +++ b/drivers/net/wireless/realtek/rtw88/main.c > @@ -2221,6 +2221,86 @@ void rtw_core_deinit(struct rtw_dev *rtwdev) > } > EXPORT_SYMBOL(rtw_core_deinit); > > +#ifdef CONFIG_LEDS_CLASS Not prefer to have #ifdef in code. Please add a led.c and add an entry obj-$(CONFIG_LEDS_CLASS) += led.c to Makefile. Since you enclose whole functions, it looks not a big problem for this case. But I still want to avoid using of #ifdef to prevent people imitate this wrongly. > + > +static int rtw_led_set_blocking(struct led_classdev *led, > + enum led_brightness brightness) > +{ > + struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); > + > + rtwdev->chip->ops->led_set(led, brightness); > + > + return 0; > +} > + > +static void rtw_led_init(struct rtw_dev *rtwdev) > +{ > + static const struct ieee80211_tpt_blink rtw_tpt_blink[] = { > + { .throughput = 0 * 1024, .blink_time = 334 }, > + { .throughput = 1 * 1024, .blink_time = 260 }, > + { .throughput = 5 * 1024, .blink_time = 220 }, > + { .throughput = 10 * 1024, .blink_time = 190 }, > + { .throughput = 20 * 1024, .blink_time = 170 }, > + { .throughput = 50 * 1024, .blink_time = 150 }, > + { .throughput = 70 * 1024, .blink_time = 130 }, > + { .throughput = 100 * 1024, .blink_time = 110 }, > + { .throughput = 200 * 1024, .blink_time = 80 }, > + { .throughput = 300 * 1024, .blink_time = 50 }, > + }; > + struct led_classdev *led = &rtwdev->led_cdev; > + int err; > + > + if (!rtwdev->chip->ops->led_set) > + return; > + > + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) > + led->brightness_set_blocking = rtw_led_set_blocking; > + else > + led->brightness_set = rtwdev->chip->ops->led_set; > + > + snprintf(rtwdev->led_name, sizeof(rtwdev->led_name), > + "rtw88-%s", dev_name(rtwdev->dev)); > + > + led->name = rtwdev->led_name; > + led->max_brightness = LED_ON; > + led->default_trigger = > + ieee80211_create_tpt_led_trigger(rtwdev->hw, > + IEEE80211_TPT_LEDTRIG_FL_RADIO, > + rtw_tpt_blink, > + ARRAY_SIZE(rtw_tpt_blink)); > + > + err = led_classdev_register(rtwdev->dev, led); > + if (err) { > + rtw_warn(rtwdev, "Failed to register the LED, error %d\n", err); > + return; > + } > + > + rtwdev->led_registered = true; > +} > + > +static void rtw_led_deinit(struct rtw_dev *rtwdev) > +{ > + struct led_classdev *led = &rtwdev->led_cdev; > + > + if (!rtwdev->led_registered) > + return; > + > + rtwdev->chip->ops->led_set(led, LED_OFF); > + led_classdev_unregister(led); > +} > + > +#else > + > +static void rtw_led_init(struct rtw_dev *rtwdev) > +{ > +} > + > +static void rtw_led_deinit(struct rtw_dev *rtwdev) > +{ > +} > + > +#endif > + [...] > diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c > b/drivers/net/wireless/realtek/rtw88/rtw8812a.c > index 21795286a1a0..e16ba8d8a792 100644 > --- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c > +++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c > @@ -868,6 +868,26 @@ static void rtw8812a_pwr_track(struct rtw_dev *rtwdev) > dm_info->pwr_trk_triggered = false; > } > > +#ifdef CONFIG_LEDS_CLASS > + > +static void rtw8812a_led_set(struct led_classdev *led, > + enum led_brightness brightness) > +{ > + struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); > + u8 ledcfg; > + > + ledcfg = rtw_read8(rtwdev, REG_LED_CFG); > + ledcfg &= BIT(6) | BIT(4); > + ledcfg |= BIT(5); > + > + if (brightness == LED_OFF) > + ledcfg |= BIT(3); > + > + rtw_write8(rtwdev, REG_LED_CFG, ledcfg); > +} > + > +#endif > + > static void rtw8812a_fill_txdesc_checksum(struct rtw_dev *rtwdev, > struct rtw_tx_pkt_info *pkt_info, > u8 *txdesc) > @@ -916,6 +936,9 @@ static const struct rtw_chip_ops rtw8812a_ops = { > .config_bfee = NULL, > .set_gid_table = NULL, > .cfg_csi_rate = NULL, > +#ifdef CONFIG_LEDS_CLASS > + .led_set = rtw8812a_led_set, > +#endif Just build the code without checking CONFIG_LEDS_CLASS. It will waste some space, but acceptable. before after delta rtw8812a.o 15619 15771 152 rtw8821a.o 12922 13186 264 rtw8821c.o 18890 19034 144 rtw8822b.o 24860 25004 144 rtw8822c.o 65963 66155 192 Also I'm thinking if we can move rtw8812a_led_set to led.c as well. However, it looks very different from chip to chip.
On 02/01/2025 03:48, Ping-Ke Shih wrote: > Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote: >> Register a struct led_classdev with the kernel's LED subsystem and >> create a throughput-based trigger for it. Then mac80211 makes the LED >> blink. >> >> Tested with Tenda U12 (RTL8812AU), Tenda U9 (RTL8811CU), TP-Link Archer >> T2U Nano (RTL8811AU), TP-Link Archer T3U Plus (RTL8812BU), Edimax >> EW-7611UCB (RTL8821AU), LM842 (RTL8822CU). >> >> Also tested with devices which don't have LEDs: the laptop's internal >> RTL8822CE and a no-name RTL8723DU. >> >> Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com> >> --- >> drivers/net/wireless/realtek/rtw88/main.c | 91 ++++++++++++++++++- >> drivers/net/wireless/realtek/rtw88/main.h | 9 ++ >> drivers/net/wireless/realtek/rtw88/reg.h | 12 +++ >> drivers/net/wireless/realtek/rtw88/rtw8812a.c | 23 +++++ >> drivers/net/wireless/realtek/rtw88/rtw8821a.c | 32 +++++++ >> drivers/net/wireless/realtek/rtw88/rtw8821c.c | 25 +++++ >> drivers/net/wireless/realtek/rtw88/rtw8822b.c | 25 +++++ >> drivers/net/wireless/realtek/rtw88/rtw8822c.c | 25 +++++ >> 8 files changed, 240 insertions(+), 2 deletions(-) >> >> diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c >> index 6993f93c8f06..387940839f8b 100644 >> --- a/drivers/net/wireless/realtek/rtw88/main.c >> +++ b/drivers/net/wireless/realtek/rtw88/main.c >> @@ -2221,6 +2221,86 @@ void rtw_core_deinit(struct rtw_dev *rtwdev) >> } >> EXPORT_SYMBOL(rtw_core_deinit); >> >> +#ifdef CONFIG_LEDS_CLASS > > Not prefer to have #ifdef in code. Please add a led.c and add an entry > obj-$(CONFIG_LEDS_CLASS) += led.c > to Makefile. > > Since you enclose whole functions, it looks not a big problem for this case. > But I still want to avoid using of #ifdef to prevent people imitate this wrongly. > >> + >> +static int rtw_led_set_blocking(struct led_classdev *led, >> + enum led_brightness brightness) >> +{ >> + struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); >> + >> + rtwdev->chip->ops->led_set(led, brightness); >> + >> + return 0; >> +} >> + >> +static void rtw_led_init(struct rtw_dev *rtwdev) >> +{ >> + static const struct ieee80211_tpt_blink rtw_tpt_blink[] = { >> + { .throughput = 0 * 1024, .blink_time = 334 }, >> + { .throughput = 1 * 1024, .blink_time = 260 }, >> + { .throughput = 5 * 1024, .blink_time = 220 }, >> + { .throughput = 10 * 1024, .blink_time = 190 }, >> + { .throughput = 20 * 1024, .blink_time = 170 }, >> + { .throughput = 50 * 1024, .blink_time = 150 }, >> + { .throughput = 70 * 1024, .blink_time = 130 }, >> + { .throughput = 100 * 1024, .blink_time = 110 }, >> + { .throughput = 200 * 1024, .blink_time = 80 }, >> + { .throughput = 300 * 1024, .blink_time = 50 }, >> + }; >> + struct led_classdev *led = &rtwdev->led_cdev; >> + int err; >> + >> + if (!rtwdev->chip->ops->led_set) >> + return; >> + >> + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) >> + led->brightness_set_blocking = rtw_led_set_blocking; >> + else >> + led->brightness_set = rtwdev->chip->ops->led_set; >> + >> + snprintf(rtwdev->led_name, sizeof(rtwdev->led_name), >> + "rtw88-%s", dev_name(rtwdev->dev)); >> + >> + led->name = rtwdev->led_name; >> + led->max_brightness = LED_ON; >> + led->default_trigger = >> + ieee80211_create_tpt_led_trigger(rtwdev->hw, >> + IEEE80211_TPT_LEDTRIG_FL_RADIO, >> + rtw_tpt_blink, >> + ARRAY_SIZE(rtw_tpt_blink)); >> + >> + err = led_classdev_register(rtwdev->dev, led); >> + if (err) { >> + rtw_warn(rtwdev, "Failed to register the LED, error %d\n", err); >> + return; >> + } >> + >> + rtwdev->led_registered = true; >> +} >> + >> +static void rtw_led_deinit(struct rtw_dev *rtwdev) >> +{ >> + struct led_classdev *led = &rtwdev->led_cdev; >> + >> + if (!rtwdev->led_registered) >> + return; >> + >> + rtwdev->chip->ops->led_set(led, LED_OFF); >> + led_classdev_unregister(led); >> +} >> + >> +#else >> + >> +static void rtw_led_init(struct rtw_dev *rtwdev) >> +{ >> +} >> + >> +static void rtw_led_deinit(struct rtw_dev *rtwdev) >> +{ >> +} >> + >> +#endif >> + > > [...] > >> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c >> b/drivers/net/wireless/realtek/rtw88/rtw8812a.c >> index 21795286a1a0..e16ba8d8a792 100644 >> --- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c >> +++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c >> @@ -868,6 +868,26 @@ static void rtw8812a_pwr_track(struct rtw_dev *rtwdev) >> dm_info->pwr_trk_triggered = false; >> } >> >> +#ifdef CONFIG_LEDS_CLASS >> + >> +static void rtw8812a_led_set(struct led_classdev *led, >> + enum led_brightness brightness) >> +{ >> + struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); >> + u8 ledcfg; >> + >> + ledcfg = rtw_read8(rtwdev, REG_LED_CFG); >> + ledcfg &= BIT(6) | BIT(4); >> + ledcfg |= BIT(5); >> + >> + if (brightness == LED_OFF) >> + ledcfg |= BIT(3); >> + >> + rtw_write8(rtwdev, REG_LED_CFG, ledcfg); >> +} >> + >> +#endif >> + >> static void rtw8812a_fill_txdesc_checksum(struct rtw_dev *rtwdev, >> struct rtw_tx_pkt_info *pkt_info, >> u8 *txdesc) >> @@ -916,6 +936,9 @@ static const struct rtw_chip_ops rtw8812a_ops = { >> .config_bfee = NULL, >> .set_gid_table = NULL, >> .cfg_csi_rate = NULL, >> +#ifdef CONFIG_LEDS_CLASS >> + .led_set = rtw8812a_led_set, >> +#endif > > Just build the code without checking CONFIG_LEDS_CLASS. > It will waste some space, but acceptable. > > before after delta > rtw8812a.o 15619 15771 152 > rtw8821a.o 12922 13186 264 > rtw8821c.o 18890 19034 144 > rtw8822b.o 24860 25004 144 > rtw8822c.o 65963 66155 192 > > Also I'm thinking if we can move rtw8812a_led_set to led.c as well. However, > it looks very different from chip to chip. > > Yes, and RTL8814A will add one more unique led_set function.
diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index 6993f93c8f06..387940839f8b 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -2221,6 +2221,86 @@ void rtw_core_deinit(struct rtw_dev *rtwdev) } EXPORT_SYMBOL(rtw_core_deinit); +#ifdef CONFIG_LEDS_CLASS + +static int rtw_led_set_blocking(struct led_classdev *led, + enum led_brightness brightness) +{ + struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); + + rtwdev->chip->ops->led_set(led, brightness); + + return 0; +} + +static void rtw_led_init(struct rtw_dev *rtwdev) +{ + static const struct ieee80211_tpt_blink rtw_tpt_blink[] = { + { .throughput = 0 * 1024, .blink_time = 334 }, + { .throughput = 1 * 1024, .blink_time = 260 }, + { .throughput = 5 * 1024, .blink_time = 220 }, + { .throughput = 10 * 1024, .blink_time = 190 }, + { .throughput = 20 * 1024, .blink_time = 170 }, + { .throughput = 50 * 1024, .blink_time = 150 }, + { .throughput = 70 * 1024, .blink_time = 130 }, + { .throughput = 100 * 1024, .blink_time = 110 }, + { .throughput = 200 * 1024, .blink_time = 80 }, + { .throughput = 300 * 1024, .blink_time = 50 }, + }; + struct led_classdev *led = &rtwdev->led_cdev; + int err; + + if (!rtwdev->chip->ops->led_set) + return; + + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) + led->brightness_set_blocking = rtw_led_set_blocking; + else + led->brightness_set = rtwdev->chip->ops->led_set; + + snprintf(rtwdev->led_name, sizeof(rtwdev->led_name), + "rtw88-%s", dev_name(rtwdev->dev)); + + led->name = rtwdev->led_name; + led->max_brightness = LED_ON; + led->default_trigger = + ieee80211_create_tpt_led_trigger(rtwdev->hw, + IEEE80211_TPT_LEDTRIG_FL_RADIO, + rtw_tpt_blink, + ARRAY_SIZE(rtw_tpt_blink)); + + err = led_classdev_register(rtwdev->dev, led); + if (err) { + rtw_warn(rtwdev, "Failed to register the LED, error %d\n", err); + return; + } + + rtwdev->led_registered = true; +} + +static void rtw_led_deinit(struct rtw_dev *rtwdev) +{ + struct led_classdev *led = &rtwdev->led_cdev; + + if (!rtwdev->led_registered) + return; + + rtwdev->chip->ops->led_set(led, LED_OFF); + led_classdev_unregister(led); +} + +#else + +static void rtw_led_init(struct rtw_dev *rtwdev) +{ +} + +static void rtw_led_deinit(struct rtw_dev *rtwdev) +{ +} + +#endif + int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw) { bool sta_mode_only = rtwdev->hci.type == RTW_HCI_TYPE_SDIO; @@ -2292,16 +2372,18 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw) return ret; } + rtw_led_init(rtwdev); + ret = ieee80211_register_hw(hw); if (ret) { rtw_err(rtwdev, "failed to register hw\n"); - return ret; + goto led_deinit; } ret = rtw_regd_hint(rtwdev); if (ret) { rtw_err(rtwdev, "failed to hint regd\n"); - return ret; + goto led_deinit; } rtw_debugfs_init(rtwdev); @@ -2310,6 +2392,10 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw) rtwdev->bf_info.bfer_su_cnt = 0; return 0; + +led_deinit: + rtw_led_deinit(rtwdev); + return ret; } EXPORT_SYMBOL(rtw_register_hw); @@ -2320,6 +2406,7 @@ void rtw_unregister_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw) ieee80211_unregister_hw(hw); rtw_unset_supported_band(hw, chip); rtw_debugfs_deinit(rtwdev); + rtw_led_deinit(rtwdev); } EXPORT_SYMBOL(rtw_unregister_hw); diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 6ba9e0dcf9fd..39ab3f8b4a47 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -887,6 +887,9 @@ struct rtw_chip_ops { bool is_tx2_path); void (*config_txrx_mode)(struct rtw_dev *rtwdev, u8 tx_path, u8 rx_path, bool is_tx2_path); +#ifdef CONFIG_LEDS_CLASS + void (*led_set)(struct led_classdev *led, enum led_brightness brightness); +#endif /* for USB/SDIO only */ void (*fill_txdesc_checksum)(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, @@ -2097,6 +2100,12 @@ struct rtw_dev { struct completion fw_scan_density; bool ap_active; +#ifdef CONFIG_LEDS_CLASS + bool led_registered; + char led_name[32]; + struct led_classdev led_cdev; +#endif + /* hci related data, must be last */ u8 priv[] __aligned(sizeof(void *)); }; diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h index 95a39ae74cd3..e438405fba56 100644 --- a/drivers/net/wireless/realtek/rtw88/reg.h +++ b/drivers/net/wireless/realtek/rtw88/reg.h @@ -78,7 +78,19 @@ #define BIT_PAPE_SEL_EN BIT(25) #define BIT_DPDT_WL_SEL BIT(24) #define BIT_DPDT_SEL_EN BIT(23) +#define BIT_GPIO13_14_WL_CTRL_EN BIT(22) +#define BIT_LED2_SV BIT(19) +#define BIT_LED2_CM GENMASK(18, 16) +#define BIT_LED1_SV BIT(11) +#define BIT_LED1_CM GENMASK(10, 8) +#define BIT_LED0_SV BIT(3) +#define BIT_LED0_CM GENMASK(2, 0) +#define BIT_LED_MODE_SW_CTRL 0 +#define BIT_LED_MODE_RX 6 +#define BIT_LED_MODE_TX 4 +#define BIT_LED_MODE_TRX 2 #define REG_LEDCFG2 0x004E +#define REG_GPIO_PIN_CTRL_2 0x0060 #define REG_PAD_CTRL1 0x0064 #define BIT_BT_BTG_SEL BIT(31) #define BIT_PAPE_WLBT_SEL BIT(29) diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c index 21795286a1a0..e16ba8d8a792 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c @@ -868,6 +868,26 @@ static void rtw8812a_pwr_track(struct rtw_dev *rtwdev) dm_info->pwr_trk_triggered = false; } +#ifdef CONFIG_LEDS_CLASS + +static void rtw8812a_led_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); + u8 ledcfg; + + ledcfg = rtw_read8(rtwdev, REG_LED_CFG); + ledcfg &= BIT(6) | BIT(4); + ledcfg |= BIT(5); + + if (brightness == LED_OFF) + ledcfg |= BIT(3); + + rtw_write8(rtwdev, REG_LED_CFG, ledcfg); +} + +#endif + static void rtw8812a_fill_txdesc_checksum(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, u8 *txdesc) @@ -916,6 +936,9 @@ static const struct rtw_chip_ops rtw8812a_ops = { .config_bfee = NULL, .set_gid_table = NULL, .cfg_csi_rate = NULL, +#ifdef CONFIG_LEDS_CLASS + .led_set = rtw8812a_led_set, +#endif .fill_txdesc_checksum = rtw8812a_fill_txdesc_checksum, .coex_set_init = rtw8812a_coex_cfg_init, .coex_set_ant_switch = NULL, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c index dafab2af33bc..62070843f2b0 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c @@ -706,6 +706,35 @@ static void rtw8821a_pwr_track(struct rtw_dev *rtwdev) dm_info->pwr_trk_triggered = false; } +#ifdef CONFIG_LEDS_CLASS + +static void rtw8821a_led_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); + u32 gpio8_cfg; + u8 ledcfg; + + if (brightness == LED_OFF) { + gpio8_cfg = rtw_read32(rtwdev, REG_GPIO_PIN_CTRL_2); + gpio8_cfg &= ~BIT(24); + gpio8_cfg |= BIT(16) | BIT(8); + rtw_write32(rtwdev, REG_GPIO_PIN_CTRL_2, gpio8_cfg); + } else { + ledcfg = rtw_read8(rtwdev, REG_LED_CFG + 2); + gpio8_cfg = rtw_read32(rtwdev, REG_GPIO_PIN_CTRL_2); + + ledcfg &= BIT(7) | BIT(6); + rtw_write8(rtwdev, REG_LED_CFG + 2, ledcfg); + + gpio8_cfg &= ~(BIT(24) | BIT(8)); + gpio8_cfg |= BIT(16); + rtw_write32(rtwdev, REG_GPIO_PIN_CTRL_2, gpio8_cfg); + } +} + +#endif + static void rtw8821a_fill_txdesc_checksum(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, u8 *txdesc) @@ -853,6 +882,9 @@ static const struct rtw_chip_ops rtw8821a_ops = { .config_bfee = NULL, .set_gid_table = NULL, .cfg_csi_rate = NULL, +#ifdef CONFIG_LEDS_CLASS + .led_set = rtw8821a_led_set, +#endif .fill_txdesc_checksum = rtw8821a_fill_txdesc_checksum, .coex_set_init = rtw8821a_coex_cfg_init, .coex_set_ant_switch = rtw8821a_coex_cfg_ant_switch, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c index 0270225b9c20..8c09cde96506 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c @@ -1206,6 +1206,28 @@ static void rtw8821c_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl) dm_info->cck_pd_default + new_lvl * 2); } +#ifdef CONFIG_LEDS_CLASS + +static void rtw8821c_led_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); + u32 ledcfg; + + ledcfg = rtw_read32(rtwdev, REG_LED_CFG); + u32p_replace_bits(&ledcfg, BIT_LED_MODE_SW_CTRL, BIT_LED2_CM); + ledcfg &= ~BIT_GPIO13_14_WL_CTRL_EN; + + if (brightness == LED_OFF) + ledcfg |= BIT_LED2_SV; + else + ledcfg &= ~BIT_LED2_SV; + + rtw_write32(rtwdev, REG_LED_CFG, ledcfg); +} + +#endif + static void rtw8821c_fill_txdesc_checksum(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, u8 *txdesc) @@ -1655,6 +1677,9 @@ static const struct rtw_chip_ops rtw8821c_ops = { .config_bfee = rtw8821c_bf_config_bfee, .set_gid_table = rtw_bf_set_gid_table, .cfg_csi_rate = rtw_bf_cfg_csi_rate, +#ifdef CONFIG_LEDS_CLASS + .led_set = rtw8821c_led_set, +#endif .fill_txdesc_checksum = rtw8821c_fill_txdesc_checksum, .coex_set_init = rtw8821c_coex_cfg_init, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c index 739809f4cab5..580e418a3fdd 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c @@ -1566,6 +1566,28 @@ static void rtw8822b_adaptivity(struct rtw_dev *rtwdev) rtw_phy_set_edcca_th(rtwdev, l2h, h2l); } +#ifdef CONFIG_LEDS_CLASS + +static void rtw8822b_led_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); + u32 ledcfg; + + ledcfg = rtw_read32(rtwdev, REG_LED_CFG); + u32p_replace_bits(&ledcfg, BIT_LED_MODE_SW_CTRL, BIT_LED2_CM); + ledcfg &= ~BIT_GPIO13_14_WL_CTRL_EN; + + if (brightness == LED_OFF) + ledcfg |= BIT_LED2_SV; + else + ledcfg &= ~BIT_LED2_SV; + + rtw_write32(rtwdev, REG_LED_CFG, ledcfg); +} + +#endif + static void rtw8822b_fill_txdesc_checksum(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, u8 *txdesc) @@ -2146,6 +2168,9 @@ static const struct rtw_chip_ops rtw8822b_ops = { .cfg_csi_rate = rtw_bf_cfg_csi_rate, .adaptivity_init = rtw8822b_adaptivity_init, .adaptivity = rtw8822b_adaptivity, +#ifdef CONFIG_LEDS_CLASS + .led_set = rtw8822b_led_set, +#endif .fill_txdesc_checksum = rtw8822b_fill_txdesc_checksum, .coex_set_init = rtw8822b_coex_cfg_init, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index af6b76937f1d..8feede61cd11 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -4537,6 +4537,28 @@ static void rtw8822c_adaptivity(struct rtw_dev *rtwdev) rtw_phy_set_edcca_th(rtwdev, l2h, h2l); } +#ifdef CONFIG_LEDS_CLASS + +static void rtw8822c_led_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); + u32 ledcfg; + + ledcfg = rtw_read32(rtwdev, REG_LED_CFG); + u32p_replace_bits(&ledcfg, BIT_LED_MODE_SW_CTRL, BIT_LED2_CM); + ledcfg &= ~BIT_GPIO13_14_WL_CTRL_EN; + + if (brightness == LED_OFF) + ledcfg |= BIT_LED2_SV; + else + ledcfg &= ~BIT_LED2_SV; + + rtw_write32(rtwdev, REG_LED_CFG, ledcfg); +} + +#endif + static void rtw8822c_fill_txdesc_checksum(struct rtw_dev *rtwdev, struct rtw_tx_pkt_info *pkt_info, u8 *txdesc) @@ -4964,6 +4986,9 @@ static const struct rtw_chip_ops rtw8822c_ops = { .cfo_track = rtw8822c_cfo_track, .config_tx_path = rtw8822c_config_tx_path, .config_txrx_mode = rtw8822c_config_trx_mode, +#ifdef CONFIG_LEDS_CLASS + .led_set = rtw8822c_led_set, +#endif .fill_txdesc_checksum = rtw8822c_fill_txdesc_checksum, .coex_set_init = rtw8822c_coex_cfg_init,
Register a struct led_classdev with the kernel's LED subsystem and create a throughput-based trigger for it. Then mac80211 makes the LED blink. Tested with Tenda U12 (RTL8812AU), Tenda U9 (RTL8811CU), TP-Link Archer T2U Nano (RTL8811AU), TP-Link Archer T3U Plus (RTL8812BU), Edimax EW-7611UCB (RTL8821AU), LM842 (RTL8822CU). Also tested with devices which don't have LEDs: the laptop's internal RTL8822CE and a no-name RTL8723DU. Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com> --- drivers/net/wireless/realtek/rtw88/main.c | 91 ++++++++++++++++++- drivers/net/wireless/realtek/rtw88/main.h | 9 ++ drivers/net/wireless/realtek/rtw88/reg.h | 12 +++ drivers/net/wireless/realtek/rtw88/rtw8812a.c | 23 +++++ drivers/net/wireless/realtek/rtw88/rtw8821a.c | 32 +++++++ drivers/net/wireless/realtek/rtw88/rtw8821c.c | 25 +++++ drivers/net/wireless/realtek/rtw88/rtw8822b.c | 25 +++++ drivers/net/wireless/realtek/rtw88/rtw8822c.c | 25 +++++ 8 files changed, 240 insertions(+), 2 deletions(-) base-commit: 104372ff359486b26b5a2db33b8e1dc6bfb39812 prerequisite-patch-id: ffa5686d26c03c3bd942dd0e0c4e85d6cc1141c0 prerequisite-patch-id: 5fe117f3707ee7525549baaeea323157019be897 prerequisite-patch-id: f733af9c741a3e02f8dd374a402da4b07c51f97a prerequisite-patch-id: 26c5a5c5739a581686f97ff17916b9c641a0d189 prerequisite-patch-id: 73580f2822eceab5e3a1d7291beebd362b9af45f prerequisite-patch-id: ebd38e2e7e1f7731046d8641dbaaa2bcc9a385fe prerequisite-patch-id: ebc85d1f7996a01b46f27e78dc6ebfb5c5a8c013 prerequisite-patch-id: 4cb1a033c2beec35dfbee0cddb8c8d1df80f2994 prerequisite-patch-id: 3887710d4f2069a49bdeb35d6e17144fe9da9c84 prerequisite-patch-id: 6b23ff2c45082cbce357c8547e5582455b160649