From fb1e31fd1968433ebbf5c34be0c78b4d33891af1 Mon Sep 17 00:00:00 2001 From: Alexander Kiryukhin Date: Sun, 29 Jan 2023 07:21:38 +0300 Subject: [PATCH] initial --- Makefile | 18 ++++ README.md | 10 ++ huawei-codec-fix.service | 12 +++ huawei_matebook14s_codec_fix.cpp | 164 +++++++++++++++++++++++++++++++ restart-huawei-codec-fix.service | 10 ++ 5 files changed, 214 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 huawei-codec-fix.service create mode 100644 huawei_matebook14s_codec_fix.cpp create mode 100644 restart-huawei-codec-fix.service 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +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