Enabling charging/notification LED

Johnsondr80

Inactive Recognized Developer
Dec 13, 2010
3,468
5,142
0
Tuscaloosa
That's not what I asked for.
But so you will better understand, my mother fell and broke her hip on July 3rd and has been in the hospital ever since that day. She has been doing better just before doing worse again. We had to move her to a higher quality hospital, where she began to do better and then again got worse. I have been by her bed side the entire time, living in ICU with her watching her fight for her life. Much of the time she was on a ventilator and during the couple of times she was well enough to be removed from it her throat was so sore her voice could nearly be heard, and was only able to say a few words.
And as horrible as it was to see her like this, I stayed with her.
Then on July 21 her body had all the stress it could take. She passed at 11:50 pm with out a struggle.

Being a recognized developer of XDA I am fully aware of factory images and how to access/modify/add/remove anything I want. And believe it or not I know where to get them and identify the correct one I need.
How ever as a recognized dev of XDA I expected that the response to my request would be greeted with a reply of respect, where someone might just provide me what I was asking for.

It is your example @doitright that your response has illustrated the perfect reason why so many devs leave the forms, and rarely return. Your smartellic reply is an insult to those who regularly spend hours on end solving and creating software for your simple enjoyment, free of charge. But you offer nothing in return but a middle finger.

Most just ignore the request. Don't care. But you just have to make a show. Pretend to be helpful but in reality your a jerk.

Thanks for nothing.

I pray you never need help with anything on here. Because I fear you may be treated how you have treated me.

Peace

Sent from my Nexus 6 using Tapatalk
 

AlxMAX

Senior Member
Sep 30, 2007
541
132
43
Bucharest
@Johnsondr80: I am very sorry for your loss and I would like to express my deepest condolences. However, you shouldn't let your grief invade you. doitright could not know about your problems and he/she meant implicitly he/she did not have the file and suggested the source you can use to extract your file instead. Personally, I don't see any lack of respect. If you know the file is not in the system image or you can't extract it whatsoever, just tell us about it and one of us might help you with the file. There's no reason to use words like "jerk".
 

doitright

Senior Member
Oct 31, 2014
1,512
861
0
So back into this thread after some very long time away (at least from the technical end of things).

So here is what I know now (and have been playing with for a few hours now)...

1) Regular old GPIO LEDs *can* be controlled via PWM. See below;
Code:
&spmi_bus {
	qcom,[email protected] {
		qcom,[email protected] {
			compatible = "qcom,leds-qpnp";
			reg = <0xc600 0x100>;
			status = "okay";
			qcom,led_gpio_7 {
				label = "rgb";
				linux,name = "red";
				qcom,mode = "pwm";
				qcom,pwm-channel = <2>;
				qcom,start-idx = <1>;
				qcom,idx-len = <10>;
				qcom,duty-pcts = [00 19 32 4B 64
						 64 4B 32 19 00];
				qcom,lut-flags = <3>;
				qcom,pause-lo = <0>;
				qcom,pause-hi = <0>;
				qcom,ramp-step-ms = <255>;
				qcom,max-current = <12>;
				qcom,default-state = "on";
				qcom,turn-off-delay-ms = <500>;
				qcom,id = <5>;
				linux,default-trigger = "none";
				qcom,pwm-us = <1000>;
				qcom,use-blink;			};
		};

		qcom,[email protected] {
			compatible = "qcom,leds-qpnp";
			reg = <0xc700 0x100>;
			status = "okay";
			qcom,led_gpio_8 {
				label = "rgb";
				linux,name = "green";
				qcom,mode = "pwm";
				qcom,pwm-channel = <3>;
				qcom,start-idx = <1>;
				qcom,idx-len = <10>;
				qcom,duty-pcts = [00 19 32 4B 64
						 64 4B 32 19 00];
				qcom,lut-flags = <3>;
				qcom,pause-lo = <0>;
				qcom,pause-hi = <0>;
				qcom,ramp-step-ms = <255>;
				qcom,max-current = <12>;
				qcom,default-state = "on";
				qcom,turn-off-delay-ms = <500>;
				qcom,id = <5>;
				linux,default-trigger = "none";
				qcom,pwm-us = <1000>;
				qcom,use-blink;			};
		};

		qcom,[email protected] {
			compatible = "qcom,leds-qpnp";
			reg = <0xc800 0x100>;
			status = "okay";
			qcom,led_gpio_9 {
				label = "rgb";
				linux,name = "blue";
				qcom,mode = "pwm";
				qcom,pwm-channel = <4>;
				qcom,start-idx = <1>;
				qcom,idx-len = <10>;
				qcom,duty-pcts = [00 19 32 4B 64
						 64 4B 32 19 00];
				qcom,lut-flags = <3>;
				qcom,pause-lo = <0>;
				qcom,pause-hi = <0>;
				qcom,ramp-step-ms = <255>;
				qcom,max-current = <12>;
				qcom,default-state = "on";
				qcom,turn-off-delay-ms = <500>;
				qcom,id = <5>;
				linux,default-trigger = "none";
				qcom,pwm-us = <1000>;
				qcom,use-blink;
			};
		};
	};
};
Yes, this makes them *brightness controllable* with brightness values from 0-255.

2) qcom BLINKING is performed by LPG mode.

3) It switches between PWM and LPG mode by writing "1" into "blink".

4) I'm not sure what I'm missing, but when I do this with the above code, it just turns the light off. No blinking. :(

5) The PWM channels are HARD WIRED to the different GPIOs.

6) I believe that the LPGs are also hardwired to specific GPIOs.
[email protected] (gpio7) --> LPG3
[email protected] (gpio8) --> LPG4
[email protected] (gpio9) --> LPG5

Still working on it.
 
Last edited:
  • Like
Reactions: Johnsondr80

doitright

Senior Member
Oct 31, 2014
1,512
861
0
2) qcom BLINKING is performed by LPG mode.

3) It switches between PWM and LPG mode by writing "1" into "blink".

4) I'm not sure what I'm missing, but when I do this with the above code, it just turns the light off. No blinking. :(

5) The PWM channels are HARD WIRED to the different GPIOs.

6) I believe that the LPGs are also hardwired to specific GPIOs.
[email protected] (gpio7) --> LPG3
[email protected] (gpio8) --> LPG4
[email protected] (gpio9) --> LPG5

Still working on it.
I tried out the timer trigger (which uses kernel timers to decide when to turn the LED on and off), and what I have found is that it works fine when the CPU is kept online, but doesn't seem to have the ability to wake the CPU up to do the switch. That means that once the thing starts sleeping, the blinking doesn't really work -- just when the CPU comes alive.

As a stopgap method of getting a blinker, I'm going to look into modifying the timer trigger code to include an alarm. That should yield a *fairly low* added power consumption vs going entirely into hardware blinking. Once that is in place, I'll see about trying to figure out the LPG functionality. This is really difficult without the PMIC documentation, basically just guessing based on the implementation of other devices, and there really aren't many devices around with this PMIC. There's a couple of samsucks, which are completely useless due to their coding methods (basically obfuscated), a couple of motorolas, which are the same as nexus 6 (i.e., not implemented), an LG which has a very *strange* implementation, and the Razer Forge (pearlyn), which has its LED on a different GPIO. One good thing about the pearlyn is that I happen to own one, and it is unlocked, so I could use it to trace what is actually going on.


This is the lights.c implementation I'm using, based on https://github.com/cyanogenmod/android_device_moto_shamu/liblight/lights.c :
Code:
/*
 * Copyright (C) 2008 The Android Open Source Project
 * Copyright (C) 2014 The  Linux Foundation. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


// #define LOG_NDEBUG 0

#include <cutils/log.h>

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>

#include <sys/ioctl.h>
#include <sys/types.h>

#include <hardware/lights.h>

/******************************************************************************/

static pthread_once_t g_init = PTHREAD_ONCE_INIT;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;

static struct light_state_t g_notification;
static struct light_state_t g_battery;
static int rgb_brightness_ratio = 32;

char const*const RED_LED_FILE
        = "/sys/class/leds/red/brightness";
char const*const GREEN_LED_FILE
        = "/sys/class/leds/green/brightness";
char const*const BLUE_LED_FILE
        = "/sys/class/leds/blue/brightness";
char const*const LCD_FILE
        = "/sys/class/leds/lcd-backlight/brightness";
char const*const RED_ON_MS_FILE
        = "/sys/class/leds/red/delay_on";
char const*const GREEN_ON_MS_FILE
        = "/sys/class/leds/green/delay_on";
char const*const BLUE_ON_MS_FILE
        = "/sys/class/leds/blue/delay_on";
char const*const RED_OFF_MS_FILE
        = "/sys/class/leds/red/delay_off";
char const*const GREEN_OFF_MS_FILE
        = "/sys/class/leds/green/delay_off";
char const*const BLUE_OFF_MS_FILE
        = "/sys/class/leds/blue/delay_off";
char const*const RED_TRIGGER_FILE
        = "/sys/class/leds/red/trigger";
char const*const GREEN_TRIGGER_FILE
        = "/sys/class/leds/green/trigger";
char const*const BLUE_TRIGGER_FILE
        = "/sys/class/leds/blue/trigger";

/**
 * device methods
 */

void init_globals(void)
{
    // init the mutex
    pthread_mutex_init(&g_lock, NULL);

    memset(&g_battery, 0, sizeof(g_battery));
    memset(&g_notification, 0, sizeof(g_notification));
}

static int
write_int(char const* path, int value)
{
    int fd;
    static int already_warned = 0;

    fd = open(path, O_RDWR);
    if (fd >= 0) {
        char buffer[20];
        int bytes = sprintf(buffer, "%d\n", value);
        ssize_t amt = write(fd, buffer, (size_t)bytes);
        close(fd);
        return amt == -1 ? -errno : 0;
    } else {
        if (already_warned == 0) {
            ALOGE("write_int failed to open %s\n", path);
            already_warned = 1;
        }
        return -errno;
    }
}

static int
is_lit(struct light_state_t const* state)
{
    return state->color & 0x00ffffff;
}

static int
rgb_to_brightness(struct light_state_t const* state)
{
    int color = state->color & 0x00ffffff;
    return ((77*((color>>16)&0x00ff))
            + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
}

static int
set_light_backlight(struct light_device_t* dev,
        struct light_state_t const* state)
{
    int err = 0;
    int brightness = rgb_to_brightness(state);
    if(!dev) {
        return -1;
    }
    pthread_mutex_lock(&g_lock);
    err = write_int(LCD_FILE, brightness);
    pthread_mutex_unlock(&g_lock);
    return err;
}

/**
 * There is a sperate gpio per led. Each with a maximum brightness of 20.
 * The normal rgb values are computed on a scale up to 255 so we divide them
 * by 12.75 so that varied brightness per led is spread out evenly per color.
 * The led is only optimal on absolute red, green or blue, and produces artifacts
 * of two different colors appearing at once when more than one is active, but
 * this is very limiting so ultimately we allow the end user full control.
ABOVE DESCRIPTION IS TYPICAL CYANOGENMOD NONSENSE. They have the LED
configured in DIGITAL mode without PWM, which means it is either ON or OFF.
Varying the brightness of a digital LED is completely meaningless.
... this implementation has been altered from the CM BOGUS to match up with
a kernel that ties PWM to the digital LED GPIO pins, yielding a brightness range
of [0,255]
 **/
static int
set_speaker_light_locked(struct light_device_t* dev,
        struct light_state_t const* state)
{
    unsigned int colorRGB = state->color;
    int red = 0;
    int green = 0;
    int blue = 0;

    red = ((colorRGB >> 16) & 0xFF) * rgb_brightness_ratio / 255;
    green = ((colorRGB >> 8) & 0xFF) * rgb_brightness_ratio / 255;
    blue = (colorRGB & 0xFF) * rgb_brightness_ratio / 255;
    if (red < 10) red = 0;
    if (blue < 10) blue = 0;
    if (green < 10) green = 0;

    write_int(RED_LED_FILE, red);
    write_int(GREEN_LED_FILE, green);
    write_int(BLUE_LED_FILE, blue);

#if 0
    ALOGD("set_speaker_light_locked colorRGB=%08X, red=%d, green=%d, blue=%d\n",
            colorRGB, red, green, blue);
#endif

    return 0;
}

static void
handle_speaker_battery_locked(struct light_device_t* dev)
{
    if (is_lit(&g_notification)) {
        set_speaker_light_locked(dev, &g_notification);
    } else {
        set_speaker_light_locked(dev, &g_battery);
    }
}

static int
set_light_notifications(struct light_device_t* dev,
        struct light_state_t const* state)
{
    pthread_mutex_lock(&g_lock);
    g_notification = *state;
    handle_speaker_battery_locked(dev);
    pthread_mutex_unlock(&g_lock);
    return 0;
}

static int
set_light_battery(struct light_device_t* dev,
        struct light_state_t const* state)
{
    pthread_mutex_lock(&g_lock);
    g_battery = *state;
    handle_speaker_battery_locked(dev);
    pthread_mutex_unlock(&g_lock);
    return 0;
}

/** Close the lights device */
static int
close_lights(struct light_device_t *dev)
{
    if (dev) {
        free(dev);
    }
    return 0;
}


/******************************************************************************/

/**
 * module methods
 */

/** Open a new instance of a lights device using name */
static int open_lights(const struct hw_module_t* module, char const* name,
        struct hw_device_t** device)
{
    int (*set_light)(struct light_device_t* dev,
            struct light_state_t const* state);

    if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))
        set_light = set_light_backlight;
    else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
        set_light = set_light_notifications;
    else if (0 == strcmp(LIGHT_ID_BATTERY, name))
        set_light = set_light_battery;
    else
        return -EINVAL;

    pthread_once(&g_init, init_globals);

    struct light_device_t *dev = malloc(sizeof(struct light_device_t));
    memset(dev, 0, sizeof(*dev));

    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = 0;
    dev->common.module = (struct hw_module_t*)module;
    dev->common.close = (int (*)(struct hw_device_t*))close_lights;
    dev->set_light = set_light;

    *device = (struct hw_device_t*)dev;
    return 0;
}

static struct hw_module_methods_t lights_module_methods = {
    .open =  open_lights,
};

/*
 * The lights Module
 */
struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .version_major = 1,
    .version_minor = 0,
    .id = LIGHTS_HARDWARE_MODULE_ID,
    .name = "shamu lights module",
    .author = "Google, Inc.",
    .methods = &lights_module_methods,
};
This implementation does away with their 0-20 intensity adjustment (which doesn't make any sense at all, since any kernel implementation that exposes the 0-20 range has the gpio configured in BINARY -- on or off) in favor of a brightness ratio shift of 32/255 so that it outputs a sensible brightness level instead of DEATH RAY mode.
 
Last edited:
  • Like
Reactions: nazfalas

doitright

Senior Member
Oct 31, 2014
1,512
861
0
So very interesting thing I'm finding out about this... it turns out that a new day is a new perspective.

So what I've learned today... is that the PWM and LPG are, in fact, the same device. The distinction is all in the *mode of operation*.
Blinking LED and all the fancy patterns like fading in and out, are the LPG *mode*.
And since I have the brightness controls working, means that the PWM/LPG are activated. I'm just having trouble getting it to work correctly in LPG mode.

qcom,use-blink is just a mode switcher.
 
Last edited:
  • Like
Reactions: nazfalas

MrBrady

Senior Member
Oct 26, 2010
3,313
899
253
Silicon Valley
Does anybody know of a way to have the LED enabled for notifications but disabled for charging? It's annoying to have the phone plugged in next to the bedside with a bright green LED lighting up the room.
 

doitright

Senior Member
Oct 31, 2014
1,512
861
0
Does anybody know of a way to have the LED enabled for notifications but disabled for charging? It's annoying to have the phone plugged in next to the bedside with a bright green LED lighting up the room.
In lights.c function open_lights, comment out the 'else if' that checks for name=="LIGHT_ID_BATTERY", then compile your lights.shamu.

Better option though is to apply my adjustments to the kernel to enable pwm and just turn down the brightness. The charging led is very nice to have, as long as it is subtle and isn't a death ray.

---------- Post added at 02:15 AM ---------- Previous post was at 02:11 AM ----------

I think that the driver may be fubared.
I wonder if the blinker code was maybe not compiled into the pmic firmware? It is labeled as "special function 1", which suggests that it could be programmed differently. I've also seen references that suggest that it may be possible to program some really strange functionality into that hardware block.
 
  • Like
Reactions: MrBrady

MrBrady

Senior Member
Oct 26, 2010
3,313
899
253
Silicon Valley
In lights.c function open_lights, comment out the 'else if' that checks for name=="LIGHT_ID_BATTERY", then compile your lights.shamu.

Better option though is to apply my adjustments to the kernel to enable pwm and just turn down the brightness. The charging led is very nice to have, as long as it is subtle and isn't a death ray.

---------- Post added at 02:15 AM ---------- Previous post was at 02:11 AM ----------



I wonder if the blinker code was maybe not compiled into the pmic firmware? It is labeled as "special function 1", which suggests that it could be programmed differently. I've also seen references that suggest that it may be possible to program some really strange functionality into that hardware block.
Thanks for the info. My ideal situation is to have the LED "display black" (aka off) when fully charged. Some custom ROMs let you set this feature. I'm thinking about going back to stock for stability, but the LED is the one big conflict for me.
 

doitright

Senior Member
Oct 31, 2014
1,512
861
0
Thanks for the info. My ideal situation is to have the LED "display black" (aka off) when fully charged. Some custom ROMs let you set this feature. I'm thinking about going back to stock for stability, but the LED is the one big conflict for me.
From the sysimage, the *only* change you need is to lights.shamu.so (lights.c source).
Kernel change is also quite minimal to improve light functionality.
You could actually modify the battery lights function and if it is sending pure green, modify it to off.

personally, I quite *HATE* it when the light goes off when charged. The light being solid on lets you know that its actually still plugged in.
 
  • Like
Reactions: MrBrady

saa1618

Member
Aug 7, 2016
30
15
0
Bengaluru
Hi Devs,

Nice thread you have here, the LED is definitely an elephant in the room when I use the Nexus 6 rarely or let it charge overnight bedside. I have rooted it and flashed the Elite kernel. I'm running the Official Google Nougat 7.0 now. The charging LED works but not notifications. Can you suggest what lines of code to edit and on what files so the Notification LED works with any application?

Thank you and happy new year!

Love,
Syed
 

Yank555

Senior Member
Sep 18, 2009
8,716
19,947
113
Hi Devs,

Nice thread you have here, the LED is definitely an elephant in the room when I use the Nexus 6 rarely or let it charge overnight bedside. I have rooted it and flashed the Elite kernel. I'm running the Official Google Nougat 7.0 now. The charging LED works but not notifications. Can you suggest what lines of code to edit and on what files so the Notification LED works with any application?

Thank you and happy new year!

Love,
Syed
That will need more than a change in a file. While charging led can be controlled by the kernel directly (easy), app notifications require changes in the device tree, you'll only find this in custom ROMs, or maybe using apps like lightflow (not 100% sure about the name amd not sure it'll work at all, or if so, reliably and it will likely require root)...

And even then, led's on shamu are crippled, no blinking, no real dimming.

JP.
 
  • Like
Reactions: saa1618

MartinN6

Senior Member
Jul 6, 2015
626
357
93
Kassel
Led charging color

Is there any chance to edit the lights.shamu.so file to change the colors of the enabled charging leds?
Is the lights.shamu.so file responsible for the colors?