1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Common code for mac80211 Prism54 drivers |
4 | * |
5 | * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> |
6 | * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> |
7 | * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> |
8 | * |
9 | * Based on: |
10 | * - the islsm (softmac prism54) driver, which is: |
11 | * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. |
12 | * - stlc45xx driver |
13 | * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). |
14 | */ |
15 | |
16 | #include <linux/firmware.h> |
17 | #include <linux/etherdevice.h> |
18 | |
19 | #include <net/mac80211.h> |
20 | #ifdef CONFIG_P54_LEDS |
21 | #include <linux/leds.h> |
22 | #endif /* CONFIG_P54_LEDS */ |
23 | |
24 | #include "p54.h" |
25 | #include "lmac.h" |
26 | |
27 | static void p54_update_leds(struct work_struct *work) |
28 | { |
29 | struct p54_common *priv = container_of(work, struct p54_common, |
30 | led_work.work); |
31 | int err, i, tmp, blink_delay = 400; |
32 | bool rerun = false; |
33 | |
34 | /* Don't toggle the LED, when the device is down. */ |
35 | if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) |
36 | return ; |
37 | |
38 | for (i = 0; i < ARRAY_SIZE(priv->leds); i++) |
39 | if (priv->leds[i].toggled) { |
40 | priv->softled_state |= BIT(i); |
41 | |
42 | tmp = 70 + 200 / (priv->leds[i].toggled); |
43 | if (tmp < blink_delay) |
44 | blink_delay = tmp; |
45 | |
46 | if (priv->leds[i].led_dev.brightness == LED_OFF) |
47 | rerun = true; |
48 | |
49 | priv->leds[i].toggled = |
50 | !!priv->leds[i].led_dev.brightness; |
51 | } else |
52 | priv->softled_state &= ~BIT(i); |
53 | |
54 | err = p54_set_leds(priv); |
55 | if (err && net_ratelimit()) |
56 | wiphy_err(priv->hw->wiphy, |
57 | "failed to update LEDs (%d).\n" , err); |
58 | |
59 | if (rerun) |
60 | ieee80211_queue_delayed_work(hw: priv->hw, dwork: &priv->led_work, |
61 | delay: msecs_to_jiffies(m: blink_delay)); |
62 | } |
63 | |
64 | static void p54_led_brightness_set(struct led_classdev *led_dev, |
65 | enum led_brightness brightness) |
66 | { |
67 | struct p54_led_dev *led = container_of(led_dev, struct p54_led_dev, |
68 | led_dev); |
69 | struct ieee80211_hw *dev = led->hw_dev; |
70 | struct p54_common *priv = dev->priv; |
71 | |
72 | if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) |
73 | return ; |
74 | |
75 | if ((brightness) && (led->registered)) { |
76 | led->toggled++; |
77 | ieee80211_queue_delayed_work(hw: priv->hw, dwork: &priv->led_work, HZ/10); |
78 | } |
79 | } |
80 | |
81 | static int p54_register_led(struct p54_common *priv, |
82 | unsigned int led_index, |
83 | char *name, const char *trigger) |
84 | { |
85 | struct p54_led_dev *led = &priv->leds[led_index]; |
86 | int err; |
87 | |
88 | if (led->registered) |
89 | return -EEXIST; |
90 | |
91 | snprintf(buf: led->name, size: sizeof(led->name), fmt: "p54-%s::%s" , |
92 | wiphy_name(wiphy: priv->hw->wiphy), name); |
93 | led->hw_dev = priv->hw; |
94 | led->index = led_index; |
95 | led->led_dev.name = led->name; |
96 | led->led_dev.default_trigger = trigger; |
97 | led->led_dev.brightness_set = p54_led_brightness_set; |
98 | |
99 | err = led_classdev_register(parent: wiphy_dev(wiphy: priv->hw->wiphy), led_cdev: &led->led_dev); |
100 | if (err) |
101 | wiphy_err(priv->hw->wiphy, |
102 | "Failed to register %s LED.\n" , name); |
103 | else |
104 | led->registered = 1; |
105 | |
106 | return err; |
107 | } |
108 | |
109 | int p54_init_leds(struct p54_common *priv) |
110 | { |
111 | int err; |
112 | |
113 | /* |
114 | * TODO: |
115 | * Figure out if the EEPROM contains some hints about the number |
116 | * of available/programmable LEDs of the device. |
117 | */ |
118 | |
119 | INIT_DELAYED_WORK(&priv->led_work, p54_update_leds); |
120 | |
121 | err = p54_register_led(priv, led_index: 0, name: "assoc" , |
122 | trigger: ieee80211_get_assoc_led_name(hw: priv->hw)); |
123 | if (err) |
124 | return err; |
125 | |
126 | err = p54_register_led(priv, led_index: 1, name: "tx" , |
127 | trigger: ieee80211_get_tx_led_name(hw: priv->hw)); |
128 | if (err) |
129 | return err; |
130 | |
131 | err = p54_register_led(priv, led_index: 2, name: "rx" , |
132 | trigger: ieee80211_get_rx_led_name(hw: priv->hw)); |
133 | if (err) |
134 | return err; |
135 | |
136 | err = p54_register_led(priv, led_index: 3, name: "radio" , |
137 | trigger: ieee80211_get_radio_led_name(hw: priv->hw)); |
138 | if (err) |
139 | return err; |
140 | |
141 | err = p54_set_leds(priv); |
142 | return err; |
143 | } |
144 | |
145 | void p54_unregister_leds(struct p54_common *priv) |
146 | { |
147 | int i; |
148 | |
149 | for (i = 0; i < ARRAY_SIZE(priv->leds); i++) { |
150 | if (priv->leds[i].registered) { |
151 | priv->leds[i].registered = false; |
152 | priv->leds[i].toggled = 0; |
153 | led_classdev_unregister(led_cdev: &priv->leds[i].led_dev); |
154 | } |
155 | } |
156 | |
157 | cancel_delayed_work_sync(dwork: &priv->led_work); |
158 | } |
159 | |