aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Kiryukhin <i@neonxp.dev>2023-01-29 07:21:38 +0300
committerAlexander Kiryukhin <i@neonxp.dev>2023-01-29 07:21:38 +0300
commitfb1e31fd1968433ebbf5c34be0c78b4d33891af1 (patch)
tree9288a5d3cf7390299136df80aaa909bc0484bbe7
initialHEADmaster
-rw-r--r--Makefile18
-rw-r--r--README.md10
-rw-r--r--huawei-codec-fix.service12
-rw-r--r--huawei_matebook14s_codec_fix.cpp164
-rw-r--r--restart-huawei-codec-fix.service10
5 files changed, 214 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..30d7bbb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+huawei-sound-fix:
+ @gcc huawei_matebook14s_codec_fix.cpp -o huawei-sound-fix
+ @echo "Now run sudo make install"
+
+.PHONY:
+install: huawei-sound-fix
+ @cp huawei-sound-fix /usr/local/bin/
+ @chmod +x /usr/local/bin/huawei-sound-fix
+ @cp *.service /etc/systemd/system/
+ @systemctl daemon-reload
+ @systemctl enable huawei-codec-fix.service
+ @systemctl enable restart-huawei-codec-fix.service
+ @systemctl start huawei-codec-fix.service
+ @echo "Now reboot required"
+
+.PHONY:
+clear:
+ @rm huawei-sound-fix \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5b004a8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,10 @@
+# Fix sound on Huawei laptops on modern linux
+
+Recipe from https://github.com/thesofproject/linux/issues/3350#issuecomment-1399427143
+
+```
+git clone https://github.com/neonxp/fix-huawei-sound.git
+make
+sudo make install
+reboot
+``` \ No newline at end of file
diff --git a/huawei-codec-fix.service b/huawei-codec-fix.service
new file mode 100644
index 0000000..1a04f58
--- /dev/null
+++ b/huawei-codec-fix.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=huawei-codec-fix
+Before=sleep.target
+StopWhenUnneeded=yes
+
+[Service]
+Type=idle
+ExecStart=/usr/local/bin/huawei-sound-fix
+RemainAfterExit=yes
+
+[Install]
+WantedBy=multi-user.target suspend.target hibernate.target hybrid-sleep.target sleep.target \ No newline at end of file
diff --git a/huawei_matebook14s_codec_fix.cpp b/huawei_matebook14s_codec_fix.cpp
new file mode 100644
index 0000000..9d8c57e
--- /dev/null
+++ b/huawei_matebook14s_codec_fix.cpp
@@ -0,0 +1,164 @@
+/*
+* File: huawei_matebook14s_codec_fix.cpp
+* Author: ursul_polar
+*
+* Created on Jan 22, 2023, 5:34 AM
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/io.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <syslog.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/stat.h>
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+#define HDA_VERB(nid,verb,param) ((nid)<<24 | (verb)<<8 | (param))
+#define HDA_IOCTL_VERB_WRITE _IOWR('H', 0x11, struct hda_verb_ioctl)
+#define GET_VARIABLE_NAME(Variable) (void(Variable),#Variable)
+
+// Struct to contain HDA_VERBS properties
+struct hda_verb_ioctl {
+ u32 verb; /* HDA_VERB() */
+ u32 res; /* response */
+}state_check;
+
+// Derivate struct to handle conn and EAPD separately
+struct verbs_enable{
+ struct hda_verb_ioctl conn;
+ struct hda_verb_ioctl eapd;
+}speaker_enable, headphone_enable;
+
+bool done = false;
+
+// If TERM Signal is sent will cause the loop to exit
+void trpsig(int)
+{
+ done = true;
+}
+
+int get_conn_state(int fd){
+ // Set standard verb to get status for HP connection
+ state_check.verb = HDA_VERB(0x16, 0x0f09, 0x0);
+
+ // Check connection status
+ if (ioctl(fd, HDA_IOCTL_VERB_WRITE, &state_check) < 0)
+ syslog(LOG_ERR, "Failed to read connection state verb.");
+ return state_check.res >> 28;
+}
+
+int get_snd_device(){
+ int fd;
+
+ // Check that we can RW with regards to the sound card
+ fd = open("/dev/snd/hwC0D0", O_RDWR|O_NOCTTY);
+
+ if (fd < 0) {
+ syslog(LOG_ERR, "Failed to open snd device.");
+ exit(EXIT_FAILURE);
+ }
+
+ return fd;
+}
+
+void clear_pin(int fd, const char* type){
+ struct hda_verb_ioctl clear_pins;
+
+ int array[]={0x715, 0x716, 0x717};
+
+ for(int i=0; i<3; i++){
+
+ if(type == "headphone" && array[i] != 0x715)
+ clear_pins.verb = HDA_VERB(0x1, array[i], 0x2);
+ else if( type =="headphone" && array[i] == 0x715)
+ clear_pins.verb = HDA_VERB(0x1, array[i], 0x0);
+
+ if(type == "spkr" && array[i] == 0x715)
+ clear_pins.verb = HDA_VERB(0x1, array[i], 0x2);
+ else if( type =="spkr" && array[i] != 0x715)
+ continue;
+
+ if (clear_pins.verb > 0){
+ if (ioctl(fd, HDA_IOCTL_VERB_WRITE, &clear_pins) < 0)
+ syslog(LOG_ERR, "Failed to write clear pin hda verb.");
+ else
+ syslog(LOG_INFO, "Written data to hda verb %.8x", clear_pins.verb);
+ }
+ }
+
+}
+
+void enable_verb(int fd, struct hda_verb_ioctl conn, struct hda_verb_ioctl eapd, const char* type){
+
+ // Enable speakers with hda verbs
+ if (ioctl(fd, HDA_IOCTL_VERB_WRITE, &conn) < 0)
+ syslog(LOG_ERR, "Failed to write connection slector hda verb.");
+ if (ioctl(fd, HDA_IOCTL_VERB_WRITE, &eapd) < 0)
+ syslog(LOG_ERR, "Failed to write EAPD hda verb.");
+
+ clear_pin(fd, type);
+}
+
+int main(int argc, char** argv) {
+ // TERM Signal check
+ signal(SIGTERM, &trpsig);
+
+ // Sound card file descriptor and connection state vars
+ int fd, state;
+
+ // Check connection status
+ state = 0;
+
+ // Set standard verbs to enable connections and EAPD for speaker
+ speaker_enable.conn.verb = HDA_VERB(0x16, 0x701, 0x0001);
+ speaker_enable.eapd.verb = HDA_VERB(0x17, 0x70c, 0x0002);
+
+ // Set standard verbs to enable connections and EAPD for headphones
+ headphone_enable.conn.verb = HDA_VERB(0x16, 0x701, 0x0000);
+ headphone_enable.eapd.verb = HDA_VERB(0x17, 0x70c, 0x0000);
+
+ // Write to syslog
+ syslog(LOG_INFO, "Daemon started.");
+
+ // Check that we can RW with regards to the sound card
+ fd = get_snd_device();
+
+ // Enable Speaker on startup so that there's no need to have a audio jack inserted
+ // to trigger the switch
+ enable_verb(fd, speaker_enable.conn, speaker_enable.eapd, "spkr");
+ syslog(LOG_INFO, "Enabled speaker output");
+
+ // Enter loop to detect connection changes
+ while (!done)
+ {
+ sleep(1);
+
+ if (state != get_conn_state(fd) && state == 8){
+ //Enable speaker
+ enable_verb(fd, speaker_enable.conn, speaker_enable.eapd, "spkr");
+ syslog(LOG_INFO, "Enabled speaker output");
+ }
+
+ if(state != get_conn_state(fd) && state == 0){
+ //Enable headphones
+ enable_verb(fd, headphone_enable.conn, headphone_enable.eapd, "headphone");
+ syslog(LOG_INFO, "Enabled headphone output");
+ }
+
+ state = get_conn_state(fd);
+ }
+ // Close and exit
+ close(fd);
+ syslog(LOG_INFO, "Daemon stopped.");
+ exit(EXIT_SUCCESS);
+}
diff --git a/restart-huawei-codec-fix.service b/restart-huawei-codec-fix.service
new file mode 100644
index 0000000..c46c319
--- /dev/null
+++ b/restart-huawei-codec-fix.service
@@ -0,0 +1,10 @@
+[Unit]
+Description=Restart huawei codec fix after resume
+After=suspend.target
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/systemctl --no-block restart huawei-codec-fix.service
+
+[Install]
+WantedBy=suspend.target \ No newline at end of file