Merge tag 'leds-next-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds
Pull LED updates from Lee Jones:
"New Drivers:
- Add support for Allwinner A100 RGB LED controller
- Add support for Maxim 5970 Dual Hot-swap controller
New Device Support:
- Add support for AW20108 to Awinic LED driver
New Functionality:
- Extend support for Net speeds to include; 2.5G, 5G and 10G
- Allow tx/rx and cts/dsr/dcd/rng TTY LEDS to be turned on and off
via sysfs if required
- Add support for hardware control in AW200xx
Fix-ups:
- Use safer methods for string handling
- Improve error handling; return proper error values, simplify,
avoid duplicates, etc
- Replace Mutex use with the Completion mechanism
- Fix include lists; alphabetise, remove unused, explicitly add used
- Use generic platform device properties
- Use/convert to new/better APIs/helpers/MACROs instead of
hand-rolling implementations
- Device Tree binding adaptions/conversions/creation
- Continue work to remove superfluous platform .remove() call-backs
- Remove superfluous/defunct code
- Trivial; whitespace, unused variables, spelling, clean-ups, etc
- Avoid unnecessary duplicate locks
Bug Fixes:
- Repair Kconfig based dependency lists
- Ensure unused dynamically allocated data is freed after use
- Fix support for brightness control
- Add missing sufficient delays during reset to ensure correct
operation
- Avoid division-by-zero issues"
* tag 'leds-next-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/leds: (45 commits)
leds: trigger: netdev: Add core support for hw not supporting fallback to LED sw control
leds: trigger: panic: Don't register panic notifier if creating the trigger failed
leds: sun50i-a100: Convert to be agnostic to property provider
leds: max5970: Add missing headers
leds: max5970: Make use of dev_err_probe()
leds: max5970: Make use of device properties
leds: max5970: Remove unused variable
leds: rgb: Drop obsolete dependency on COMPILE_TEST
leds: sun50i-a100: Avoid division-by-zero warning
leds: trigger: Remove unused function led_trigger_rename_static()
leds: qcom-lpg: Introduce a wrapper for getting driver data from a pwm chip
leds: gpio: Add kernel log if devm_fwnode_gpiod_get() fails
dt-bindings: leds: qcom,spmi-flash-led: Fix example node name
dt-bindings: leds: aw200xx: Fix led pattern and add reg constraints
dt-bindings: leds: awinic,aw200xx: Add AW20108 device
leds: aw200xx: Add support for aw20108 device
leds: aw200xx: Improve autodim calculation method
leds: aw200xx: Enable disable_locking flag in regmap config
leds: aw200xx: Add delay after software reset
dt-bindings: leds: aw200xx: Remove property "awinic,display-rows"
...
This commit is contained in:
@@ -41,33 +41,30 @@ static irqreturn_t gpio_trig_irq(int irq, void *_led)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static ssize_t gpio_trig_brightness_show(struct device *dev,
|
||||
static ssize_t desired_brightness_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", gpio_data->desired_brightness);
|
||||
return sysfs_emit(buf, "%u\n", gpio_data->desired_brightness);
|
||||
}
|
||||
|
||||
static ssize_t gpio_trig_brightness_store(struct device *dev,
|
||||
static ssize_t desired_brightness_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t n)
|
||||
{
|
||||
struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
|
||||
unsigned desired_brightness;
|
||||
u8 desired_brightness;
|
||||
int ret;
|
||||
|
||||
ret = sscanf(buf, "%u", &desired_brightness);
|
||||
if (ret < 1 || desired_brightness > 255) {
|
||||
dev_err(dev, "invalid value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = kstrtou8(buf, 10, &desired_brightness);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gpio_data->desired_brightness = desired_brightness;
|
||||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show,
|
||||
gpio_trig_brightness_store);
|
||||
static DEVICE_ATTR_RW(desired_brightness);
|
||||
|
||||
static struct attribute *gpio_trig_attrs[] = {
|
||||
&dev_attr_desired_brightness.attr,
|
||||
@@ -89,10 +86,7 @@ static int gpio_trig_activate(struct led_classdev *led)
|
||||
* The generic property "trigger-sources" is followed,
|
||||
* and we hope that this is a GPIO.
|
||||
*/
|
||||
gpio_data->gpiod = fwnode_gpiod_get_index(dev->fwnode,
|
||||
"trigger-sources",
|
||||
0, GPIOD_IN,
|
||||
"led-trigger");
|
||||
gpio_data->gpiod = gpiod_get_optional(dev, "trigger-sources", GPIOD_IN);
|
||||
if (IS_ERR(gpio_data->gpiod)) {
|
||||
ret = PTR_ERR(gpio_data->gpiod);
|
||||
kfree(gpio_data);
|
||||
@@ -104,6 +98,8 @@ static int gpio_trig_activate(struct led_classdev *led)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpiod_set_consumer_name(gpio_data->gpiod, "led-trigger");
|
||||
|
||||
gpio_data->led = led;
|
||||
led_set_trigger_data(led, gpio_data);
|
||||
|
||||
|
||||
@@ -38,6 +38,16 @@
|
||||
* tx - LED blinks on transmitted data
|
||||
* rx - LED blinks on receive data
|
||||
*
|
||||
* Note: If the user selects a mode that is not supported by hw, default
|
||||
* behavior is to fall back to software control of the LED. However not every
|
||||
* hw supports software control. LED callbacks brightness_set() and
|
||||
* brightness_set_blocking() are NULL in this case. hw_control_is_supported()
|
||||
* should use available means supported by hw to inform the user that selected
|
||||
* mode isn't supported by hw. This could be switching off the LED or any
|
||||
* hw blink mode. If software control fallback isn't possible, we return
|
||||
* -EOPNOTSUPP to the user, but still store the selected mode. This is needed
|
||||
* in case an intermediate unsupported mode is necessary to switch from one
|
||||
* supported mode to another.
|
||||
*/
|
||||
|
||||
struct led_netdev_data {
|
||||
@@ -99,6 +109,18 @@ static void set_baseline_state(struct led_netdev_data *trigger_data)
|
||||
trigger_data->link_speed == SPEED_1000)
|
||||
blink_on = true;
|
||||
|
||||
if (test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) &&
|
||||
trigger_data->link_speed == SPEED_2500)
|
||||
blink_on = true;
|
||||
|
||||
if (test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) &&
|
||||
trigger_data->link_speed == SPEED_5000)
|
||||
blink_on = true;
|
||||
|
||||
if (test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) &&
|
||||
trigger_data->link_speed == SPEED_10000)
|
||||
blink_on = true;
|
||||
|
||||
if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) &&
|
||||
trigger_data->duplex == DUPLEX_HALF)
|
||||
blink_on = true;
|
||||
@@ -289,6 +311,9 @@ static ssize_t netdev_led_attr_show(struct device *dev, char *buf,
|
||||
case TRIGGER_NETDEV_LINK_10:
|
||||
case TRIGGER_NETDEV_LINK_100:
|
||||
case TRIGGER_NETDEV_LINK_1000:
|
||||
case TRIGGER_NETDEV_LINK_2500:
|
||||
case TRIGGER_NETDEV_LINK_5000:
|
||||
case TRIGGER_NETDEV_LINK_10000:
|
||||
case TRIGGER_NETDEV_HALF_DUPLEX:
|
||||
case TRIGGER_NETDEV_FULL_DUPLEX:
|
||||
case TRIGGER_NETDEV_TX:
|
||||
@@ -306,6 +331,7 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
|
||||
size_t size, enum led_trigger_netdev_modes attr)
|
||||
{
|
||||
struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
|
||||
struct led_classdev *led_cdev = trigger_data->led_cdev;
|
||||
unsigned long state, mode = trigger_data->mode;
|
||||
int ret;
|
||||
int bit;
|
||||
@@ -319,6 +345,9 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
|
||||
case TRIGGER_NETDEV_LINK_10:
|
||||
case TRIGGER_NETDEV_LINK_100:
|
||||
case TRIGGER_NETDEV_LINK_1000:
|
||||
case TRIGGER_NETDEV_LINK_2500:
|
||||
case TRIGGER_NETDEV_LINK_5000:
|
||||
case TRIGGER_NETDEV_LINK_10000:
|
||||
case TRIGGER_NETDEV_HALF_DUPLEX:
|
||||
case TRIGGER_NETDEV_FULL_DUPLEX:
|
||||
case TRIGGER_NETDEV_TX:
|
||||
@@ -337,7 +366,10 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
|
||||
if (test_bit(TRIGGER_NETDEV_LINK, &mode) &&
|
||||
(test_bit(TRIGGER_NETDEV_LINK_10, &mode) ||
|
||||
test_bit(TRIGGER_NETDEV_LINK_100, &mode) ||
|
||||
test_bit(TRIGGER_NETDEV_LINK_1000, &mode)))
|
||||
test_bit(TRIGGER_NETDEV_LINK_1000, &mode) ||
|
||||
test_bit(TRIGGER_NETDEV_LINK_2500, &mode) ||
|
||||
test_bit(TRIGGER_NETDEV_LINK_5000, &mode) ||
|
||||
test_bit(TRIGGER_NETDEV_LINK_10000, &mode)))
|
||||
return -EINVAL;
|
||||
|
||||
cancel_delayed_work_sync(&trigger_data->work);
|
||||
@@ -345,6 +377,10 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
|
||||
trigger_data->mode = mode;
|
||||
trigger_data->hw_control = can_hw_control(trigger_data);
|
||||
|
||||
if (!led_cdev->brightness_set && !led_cdev->brightness_set_blocking &&
|
||||
!trigger_data->hw_control)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
set_baseline_state(trigger_data);
|
||||
|
||||
return size;
|
||||
@@ -367,6 +403,9 @@ DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK);
|
||||
DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10);
|
||||
DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100);
|
||||
DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000);
|
||||
DEFINE_NETDEV_TRIGGER(link_2500, TRIGGER_NETDEV_LINK_2500);
|
||||
DEFINE_NETDEV_TRIGGER(link_5000, TRIGGER_NETDEV_LINK_5000);
|
||||
DEFINE_NETDEV_TRIGGER(link_10000, TRIGGER_NETDEV_LINK_10000);
|
||||
DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX);
|
||||
DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX);
|
||||
DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX);
|
||||
@@ -425,6 +464,9 @@ static struct attribute *netdev_trig_attrs[] = {
|
||||
&dev_attr_link_10.attr,
|
||||
&dev_attr_link_100.attr,
|
||||
&dev_attr_link_1000.attr,
|
||||
&dev_attr_link_2500.attr,
|
||||
&dev_attr_link_5000.attr,
|
||||
&dev_attr_link_10000.attr,
|
||||
&dev_attr_full_duplex.attr,
|
||||
&dev_attr_half_duplex.attr,
|
||||
&dev_attr_rx.attr,
|
||||
@@ -522,6 +564,9 @@ static void netdev_trig_work(struct work_struct *work)
|
||||
test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) ||
|
||||
test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) ||
|
||||
test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) ||
|
||||
test_bit(TRIGGER_NETDEV_LINK_2500, &trigger_data->mode) ||
|
||||
test_bit(TRIGGER_NETDEV_LINK_5000, &trigger_data->mode) ||
|
||||
test_bit(TRIGGER_NETDEV_LINK_10000, &trigger_data->mode) ||
|
||||
test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) ||
|
||||
test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode);
|
||||
interval = jiffies_to_msecs(
|
||||
|
||||
@@ -64,10 +64,13 @@ static long led_panic_blink(int state)
|
||||
|
||||
static int __init ledtrig_panic_init(void)
|
||||
{
|
||||
led_trigger_register_simple("panic", &trigger);
|
||||
if (!trigger)
|
||||
return -ENOMEM;
|
||||
|
||||
atomic_notifier_chain_register(&panic_notifier_list,
|
||||
&led_trigger_panic_nb);
|
||||
|
||||
led_trigger_register_simple("panic", &trigger);
|
||||
panic_blink = led_panic_blink;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
@@ -12,15 +13,45 @@
|
||||
struct ledtrig_tty_data {
|
||||
struct led_classdev *led_cdev;
|
||||
struct delayed_work dwork;
|
||||
struct mutex mutex;
|
||||
struct completion sysfs;
|
||||
const char *ttyname;
|
||||
struct tty_struct *tty;
|
||||
int rx, tx;
|
||||
bool mode_rx;
|
||||
bool mode_tx;
|
||||
bool mode_cts;
|
||||
bool mode_dsr;
|
||||
bool mode_dcd;
|
||||
bool mode_rng;
|
||||
};
|
||||
|
||||
static void ledtrig_tty_restart(struct ledtrig_tty_data *trigger_data)
|
||||
/* Indicates which state the LED should now display */
|
||||
enum led_trigger_tty_state {
|
||||
TTY_LED_BLINK,
|
||||
TTY_LED_ENABLE,
|
||||
TTY_LED_DISABLE,
|
||||
};
|
||||
|
||||
enum led_trigger_tty_modes {
|
||||
TRIGGER_TTY_RX = 0,
|
||||
TRIGGER_TTY_TX,
|
||||
TRIGGER_TTY_CTS,
|
||||
TRIGGER_TTY_DSR,
|
||||
TRIGGER_TTY_DCD,
|
||||
TRIGGER_TTY_RNG,
|
||||
};
|
||||
|
||||
static int ledtrig_tty_wait_for_completion(struct device *dev)
|
||||
{
|
||||
schedule_delayed_work(&trigger_data->dwork, 0);
|
||||
struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = wait_for_completion_timeout(&trigger_data->sysfs,
|
||||
msecs_to_jiffies(LEDTRIG_TTY_INTERVAL * 20));
|
||||
if (ret == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ttyname_show(struct device *dev,
|
||||
@@ -28,14 +59,16 @@ static ssize_t ttyname_show(struct device *dev,
|
||||
{
|
||||
struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev);
|
||||
ssize_t len = 0;
|
||||
int completion;
|
||||
|
||||
mutex_lock(&trigger_data->mutex);
|
||||
reinit_completion(&trigger_data->sysfs);
|
||||
completion = ledtrig_tty_wait_for_completion(dev);
|
||||
if (completion < 0)
|
||||
return completion;
|
||||
|
||||
if (trigger_data->ttyname)
|
||||
len = sprintf(buf, "%s\n", trigger_data->ttyname);
|
||||
|
||||
mutex_unlock(&trigger_data->mutex);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -46,7 +79,7 @@ static ssize_t ttyname_store(struct device *dev,
|
||||
struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev);
|
||||
char *ttyname;
|
||||
ssize_t ret = size;
|
||||
bool running;
|
||||
int completion;
|
||||
|
||||
if (size > 0 && buf[size - 1] == '\n')
|
||||
size -= 1;
|
||||
@@ -59,9 +92,10 @@ static ssize_t ttyname_store(struct device *dev,
|
||||
ttyname = NULL;
|
||||
}
|
||||
|
||||
mutex_lock(&trigger_data->mutex);
|
||||
|
||||
running = trigger_data->ttyname != NULL;
|
||||
reinit_completion(&trigger_data->sysfs);
|
||||
completion = ledtrig_tty_wait_for_completion(dev);
|
||||
if (completion < 0)
|
||||
return completion;
|
||||
|
||||
kfree(trigger_data->ttyname);
|
||||
tty_kref_put(trigger_data->tty);
|
||||
@@ -69,29 +103,107 @@ static ssize_t ttyname_store(struct device *dev,
|
||||
|
||||
trigger_data->ttyname = ttyname;
|
||||
|
||||
mutex_unlock(&trigger_data->mutex);
|
||||
|
||||
if (ttyname && !running)
|
||||
ledtrig_tty_restart(trigger_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR_RW(ttyname);
|
||||
|
||||
static ssize_t ledtrig_tty_attr_show(struct device *dev, char *buf,
|
||||
enum led_trigger_tty_modes attr)
|
||||
{
|
||||
struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev);
|
||||
bool state;
|
||||
|
||||
switch (attr) {
|
||||
case TRIGGER_TTY_RX:
|
||||
state = trigger_data->mode_rx;
|
||||
break;
|
||||
case TRIGGER_TTY_TX:
|
||||
state = trigger_data->mode_tx;
|
||||
break;
|
||||
case TRIGGER_TTY_CTS:
|
||||
state = trigger_data->mode_cts;
|
||||
break;
|
||||
case TRIGGER_TTY_DSR:
|
||||
state = trigger_data->mode_dsr;
|
||||
break;
|
||||
case TRIGGER_TTY_DCD:
|
||||
state = trigger_data->mode_dcd;
|
||||
break;
|
||||
case TRIGGER_TTY_RNG:
|
||||
state = trigger_data->mode_rng;
|
||||
break;
|
||||
}
|
||||
|
||||
return sysfs_emit(buf, "%u\n", state);
|
||||
}
|
||||
|
||||
static ssize_t ledtrig_tty_attr_store(struct device *dev, const char *buf,
|
||||
size_t size, enum led_trigger_tty_modes attr)
|
||||
{
|
||||
struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev);
|
||||
bool state;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (attr) {
|
||||
case TRIGGER_TTY_RX:
|
||||
trigger_data->mode_rx = state;
|
||||
break;
|
||||
case TRIGGER_TTY_TX:
|
||||
trigger_data->mode_tx = state;
|
||||
break;
|
||||
case TRIGGER_TTY_CTS:
|
||||
trigger_data->mode_cts = state;
|
||||
break;
|
||||
case TRIGGER_TTY_DSR:
|
||||
trigger_data->mode_dsr = state;
|
||||
break;
|
||||
case TRIGGER_TTY_DCD:
|
||||
trigger_data->mode_dcd = state;
|
||||
break;
|
||||
case TRIGGER_TTY_RNG:
|
||||
trigger_data->mode_rng = state;
|
||||
break;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
#define DEFINE_TTY_TRIGGER(trigger_name, trigger) \
|
||||
static ssize_t trigger_name##_show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return ledtrig_tty_attr_show(dev, buf, trigger); \
|
||||
} \
|
||||
static ssize_t trigger_name##_store(struct device *dev, \
|
||||
struct device_attribute *attr, const char *buf, size_t size) \
|
||||
{ \
|
||||
return ledtrig_tty_attr_store(dev, buf, size, trigger); \
|
||||
} \
|
||||
static DEVICE_ATTR_RW(trigger_name)
|
||||
|
||||
DEFINE_TTY_TRIGGER(rx, TRIGGER_TTY_RX);
|
||||
DEFINE_TTY_TRIGGER(tx, TRIGGER_TTY_TX);
|
||||
DEFINE_TTY_TRIGGER(cts, TRIGGER_TTY_CTS);
|
||||
DEFINE_TTY_TRIGGER(dsr, TRIGGER_TTY_DSR);
|
||||
DEFINE_TTY_TRIGGER(dcd, TRIGGER_TTY_DCD);
|
||||
DEFINE_TTY_TRIGGER(rng, TRIGGER_TTY_RNG);
|
||||
|
||||
static void ledtrig_tty_work(struct work_struct *work)
|
||||
{
|
||||
struct ledtrig_tty_data *trigger_data =
|
||||
container_of(work, struct ledtrig_tty_data, dwork.work);
|
||||
struct serial_icounter_struct icount;
|
||||
enum led_trigger_tty_state state = TTY_LED_DISABLE;
|
||||
unsigned long interval = LEDTRIG_TTY_INTERVAL;
|
||||
bool invert = false;
|
||||
int status;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&trigger_data->mutex);
|
||||
|
||||
if (!trigger_data->ttyname) {
|
||||
/* exit without rescheduling */
|
||||
mutex_unlock(&trigger_data->mutex);
|
||||
return;
|
||||
}
|
||||
if (!trigger_data->ttyname)
|
||||
goto out;
|
||||
|
||||
/* try to get the tty corresponding to $ttyname */
|
||||
if (!trigger_data->tty) {
|
||||
@@ -115,32 +227,83 @@ static void ledtrig_tty_work(struct work_struct *work)
|
||||
trigger_data->tty = tty;
|
||||
}
|
||||
|
||||
ret = tty_get_icount(trigger_data->tty, &icount);
|
||||
if (ret) {
|
||||
dev_info(trigger_data->tty->dev, "Failed to get icount, stopped polling\n");
|
||||
mutex_unlock(&trigger_data->mutex);
|
||||
return;
|
||||
status = tty_get_tiocm(trigger_data->tty);
|
||||
if (status > 0) {
|
||||
if (trigger_data->mode_cts) {
|
||||
if (status & TIOCM_CTS)
|
||||
state = TTY_LED_ENABLE;
|
||||
}
|
||||
|
||||
if (trigger_data->mode_dsr) {
|
||||
if (status & TIOCM_DSR)
|
||||
state = TTY_LED_ENABLE;
|
||||
}
|
||||
|
||||
if (trigger_data->mode_dcd) {
|
||||
if (status & TIOCM_CAR)
|
||||
state = TTY_LED_ENABLE;
|
||||
}
|
||||
|
||||
if (trigger_data->mode_rng) {
|
||||
if (status & TIOCM_RNG)
|
||||
state = TTY_LED_ENABLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (icount.rx != trigger_data->rx ||
|
||||
icount.tx != trigger_data->tx) {
|
||||
unsigned long interval = LEDTRIG_TTY_INTERVAL;
|
||||
/*
|
||||
* The evaluation of rx/tx must be done after the evaluation
|
||||
* of TIOCM_*, because rx/tx has priority.
|
||||
*/
|
||||
if (trigger_data->mode_rx || trigger_data->mode_tx) {
|
||||
struct serial_icounter_struct icount;
|
||||
|
||||
led_blink_set_oneshot(trigger_data->led_cdev, &interval,
|
||||
&interval, 0);
|
||||
ret = tty_get_icount(trigger_data->tty, &icount);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
trigger_data->rx = icount.rx;
|
||||
trigger_data->tx = icount.tx;
|
||||
if (trigger_data->mode_tx && (icount.tx != trigger_data->tx)) {
|
||||
trigger_data->tx = icount.tx;
|
||||
invert = state == TTY_LED_ENABLE;
|
||||
state = TTY_LED_BLINK;
|
||||
}
|
||||
|
||||
if (trigger_data->mode_rx && (icount.rx != trigger_data->rx)) {
|
||||
trigger_data->rx = icount.rx;
|
||||
invert = state == TTY_LED_ENABLE;
|
||||
state = TTY_LED_BLINK;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&trigger_data->mutex);
|
||||
switch (state) {
|
||||
case TTY_LED_BLINK:
|
||||
led_blink_set_oneshot(trigger_data->led_cdev, &interval,
|
||||
&interval, invert);
|
||||
break;
|
||||
case TTY_LED_ENABLE:
|
||||
led_set_brightness(trigger_data->led_cdev,
|
||||
trigger_data->led_cdev->blink_brightness);
|
||||
break;
|
||||
case TTY_LED_DISABLE:
|
||||
fallthrough;
|
||||
default:
|
||||
led_set_brightness(trigger_data->led_cdev, LED_OFF);
|
||||
break;
|
||||
}
|
||||
|
||||
complete_all(&trigger_data->sysfs);
|
||||
schedule_delayed_work(&trigger_data->dwork,
|
||||
msecs_to_jiffies(LEDTRIG_TTY_INTERVAL * 2));
|
||||
}
|
||||
|
||||
static struct attribute *ledtrig_tty_attrs[] = {
|
||||
&dev_attr_ttyname.attr,
|
||||
&dev_attr_rx.attr,
|
||||
&dev_attr_tx.attr,
|
||||
&dev_attr_cts.attr,
|
||||
&dev_attr_dsr.attr,
|
||||
&dev_attr_dcd.attr,
|
||||
&dev_attr_rng.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ledtrig_tty);
|
||||
@@ -153,11 +316,17 @@ static int ledtrig_tty_activate(struct led_classdev *led_cdev)
|
||||
if (!trigger_data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Enable default rx/tx mode */
|
||||
trigger_data->mode_rx = true;
|
||||
trigger_data->mode_tx = true;
|
||||
|
||||
led_set_trigger_data(led_cdev, trigger_data);
|
||||
|
||||
INIT_DELAYED_WORK(&trigger_data->dwork, ledtrig_tty_work);
|
||||
trigger_data->led_cdev = led_cdev;
|
||||
mutex_init(&trigger_data->mutex);
|
||||
init_completion(&trigger_data->sysfs);
|
||||
|
||||
schedule_delayed_work(&trigger_data->dwork, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -168,6 +337,10 @@ static void ledtrig_tty_deactivate(struct led_classdev *led_cdev)
|
||||
|
||||
cancel_delayed_work_sync(&trigger_data->dwork);
|
||||
|
||||
kfree(trigger_data->ttyname);
|
||||
tty_kref_put(trigger_data->tty);
|
||||
trigger_data->tty = NULL;
|
||||
|
||||
kfree(trigger_data);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user