Skip to content

Instantly share code, notes, and snippets.

@Orochimarufan
Last active November 2, 2021 19:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Orochimarufan/d8e42cfaa32c2cdd13640dad6a10b847 to your computer and use it in GitHub Desktop.
Save Orochimarufan/d8e42cfaa32c2cdd13640dad6a10b847 to your computer and use it in GitHub Desktop.
Hi10 pro speaker enabler
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <fcntl.h>
int gpio_offset() {
struct utsname ver;
int major, minor;
uname(&ver);
sscanf(ver.release, "%d.%d.", &major, &minor);
if (major == 4) {
// until 4.15
if (minor < 16)
return 21;
}
// 4.16 +
return 27;
}
void die_usage(const char *prog) {
printf( "spken (c) 2018 Taeyeon Mori\n"
"CHUWI Hi10 Pro speaker enabler\n"
"\n"
"Usage: %s [enable:0/1]\n"
"\n"
"If omitted, speakers are enabled\n",
prog);
exit(1);
}
void die(const char *msg) {
perror(msg);
exit(errno);
}
int main(int argc, const char**argv) {
char pathbuf[256];
int enable = 1;
int fd, gpio;
DIR *dir;
struct dirent *ent;
// Check argv
if (argc > 2)
die_usage(argv[0]);
else if (argc == 2) {
if (argv[1][0] == '1')
;
else if (argv[1][0] == '0')
enable = 0;
else
die_usage(argv[0]);
if (argv[1][1] != 0)
die_usage(argv[0]);
}
// find chip
dir = opendir("/sys/devices/platform/INT33FF:01/gpio");
if (!dir)
die("Could not open INT33FF:01/gpio in sysfs");
while (1) {
ent = readdir(dir);
if (!ent) {
if (errno)
die("Could not list INT33FF:01/gpio in sysfs");
else {
puts("No gpiochip in INT33FF:01!");
exit(2);
}
}
if (!strncmp(ent->d_name, "gpiochip", 8))
break;
}
//printf("Found gpiochip: %s\n", ent->d_name);
closedir(dir);
// find number
gpio = atoi(ent->d_name + 8) + gpio_offset();
printf("Using gpio %d\n", gpio);
if (snprintf(pathbuf, 255, "/sys/class/gpio/gpio%d/value", gpio) < 0)
die("Could not build path name");
// Export
if (access(pathbuf, F_OK)) {
fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd < 0)
die("Could not open /sys/class/gpio/export");
if(dprintf(fd, "%d", gpio) < 0)
die("Could not export GPIO");
close(fd);
}
// Open
fd = open(pathbuf, O_WRONLY);
if (fd < 0)
die("Could not open gpio pin");
if (dprintf(fd, "%d", enable) < 0)
die("Could not write gpio pin");
close(fd);
return 0;
}
@boschkundendienst
Copy link

boschkundendienst commented Apr 20, 2018

Hi,

first of all, thanks for your efforts to make sound working. I had your solution in place runing on my Arch Linux system and it worked well. Yesterday kernel 4.16.2 arrived in Arch Linux and now enabling the speaker via GPIO pin no longer works. It is now telling Could not write gpio pin: Operation not permitted.

sudo /usr/local/sbin/spken 1
Using gpio 362
Could not write gpio pin: Operation not permitted

As far as I could find out /sys/class/gpio/<exported pin>/direction has value in which means we can not write because write is only allowed when direction is out as stated here:

	"value" ... reads as either 0 (low) or 1 (high). If the GPIO
		is configured as an output, this value may be written;
		any nonzero value is treated as high.

I don't know if they made something wrong with the kernel or a small rewrite of your code is needed. Anyway it would be nice if you could spend a minute of your time to take a look again what is going on. - Thanks in advance!

Here is my output of ls -l /sys/class/gpio/ after boot.

--w------- 1 root root 4096 20. Apr 11:01 export
lrwxrwxrwx 1 root root    0 20. Apr 11:02 gpiochip225 -> ../../devices/platform/INT0002:00/gpio/gpiochip225
lrwxrwxrwx 1 root root    0 20. Apr 11:01 gpiochip228 -> ../../devices/platform/INT33FF:03/gpio/gpiochip228
lrwxrwxrwx 1 root root    0 20. Apr 11:01 gpiochip314 -> ../../devices/platform/INT33FF:02/gpio/gpiochip314
lrwxrwxrwx 1 root root    0 20. Apr 11:01 gpiochip341 -> ../../devices/platform/INT33FF:01/gpio/gpiochip341
lrwxrwxrwx 1 root root    0 20. Apr 11:01 gpiochip414 -> ../../devices/platform/INT33FF:00/gpio/gpiochip414
--w------- 1 root root 4096 20. Apr 11:01 unexport

So gpiochip341 -> ../../devices/platform/INT33FF:01/gpio/gpiochip341 is the relevant part (on this boot).

Tried (as root) to switch direction from in to out and could then write the value to 1 but that does not enable sound again.

[root@arch]# echo 341 > /sys/class/gpio/export

[root@arch]# ls -l /sys/class/gpio/
insgesamt 0
--w------- 1 root root 4096 20. Apr 11:05 export
lrwxrwxrwx 1 root root    0 20. Apr 11:05 gpio341 -> ../../devices/platform/INT33FF:01/gpiochip1/gpio/gpio341
lrwxrwxrwx 1 root root    0 20. Apr 11:02 gpiochip225 -> ../../devices/platform/INT0002:00/gpio/gpiochip225
lrwxrwxrwx 1 root root    0 20. Apr 11:01 gpiochip228 -> ../../devices/platform/INT33FF:03/gpio/gpiochip228
lrwxrwxrwx 1 root root    0 20. Apr 11:01 gpiochip314 -> ../../devices/platform/INT33FF:02/gpio/gpiochip314
lrwxrwxrwx 1 root root    0 20. Apr 11:01 gpiochip341 -> ../../devices/platform/INT33FF:01/gpio/gpiochip341
lrwxrwxrwx 1 root root    0 20. Apr 11:01 gpiochip414 -> ../../devices/platform/INT33FF:00/gpio/gpiochip414
--w------- 1 root root 4096 20. Apr 11:02 unexport

gpio341 was exported successfully

[root@arch]# cat /sys/class/gpio/gpio341/direction
in
[root@arch]# echo "out" > /sys/class/gpio/gpio341/direction
[root@arch]# echo "out" > /sys/class/gpio/gpio341/direction
[root@arch]# cat /sys/class/gpio/gpio341/value
0
[root@arch]# echo 1 > /sys/class/gpio/gpio341/value
[root@arch]# cat /sys/class/gpio/gpio341/value
1

still no sound

I just wante to note that I blacklisted snd_hdmi_lpe_audio to enable sound at all but I think this is not relevant to the problem.

# /etc/modprobe.d/blacklist.conf
# see https://wiki.archlinux.org/index.php/Kernel_module#Blacklisting
# do not load "snd_hdmi_lpe_audio" to make Chuwi sound
# at least work on headphone jack
install snd_hdmi_lpe_audio /bin/false

Regards

Peter

@Orochimarufan
Copy link
Author

For some reason the pin offset changed. Code is updated to work with 4.16

Hardcoding numbers is a hack in the first place, so it's not all too surprising it broke. Unfortunately I'm not aware of a userspace solution to properly extract the pin number from the DSDT AND map it to the linux subsystem numbers...

@boschkundendienst
Copy link

boschkundendienst commented Apr 23, 2018

Thank you so much for the update. I am not very deep in this pin number thing but how do you currently find it out maybe I could then try to find a way to get it from userspace. Maybe something like this could work (if you can find the correct PIN number in the output)?

Extract ACPI tables (as root)

# cat /sys/firmware/acpi/tables/DSDT > dsdt.dat

Decompile

# iasl -d dsdt.dat

Maybe the needed info is in the dsdt.dsl??

Info taken from Arch Wiki (DSDT).

@Orochimarufan
Copy link
Author

Well yes, but you'll still have to somehow map it to the numbering system linux uses. I originally found out by writing a patch to the kernel driver, since driver code can get the info pretty easily; see the earlier discussion in danielotero/linux-on-hi10#8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment