Skip to content

Commit

Permalink
Merge tag 'v0.12'
Browse files Browse the repository at this point in the history
  • Loading branch information
Unrud committed Mar 22, 2017
2 parents f599cda + 6d2c6e0 commit 9893eb6
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 11 deletions.
14 changes: 14 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)

if HAVE_SYSTEMD
systemdsystemunit_DATA = \
init-headphone.service
endif

sbin_SCRIPTS = src/init-headphone

CLEANFILES = init-headphone.service

init-headphone.service: init-headphone.service.in Makefile
sed -e 's,$${sbindir},${sbindir},g' init-headphone.service.in > init-headphone.service
58 changes: 58 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Changes in 0.12

* add Autotools
* improve logging

# Changes in 0.11

* remove list of supported models
* show error message if library not found
* improve logging
* show active default effect in help
* hide --force argument
* try to load missing kernel modules
* add configuration examples

# Changes in 0.10

* recognize more i2c bus names
* add license

# Changes in 0.9

* remove model check

# Changes in 0.8

* add ability to skip model check
* only print exception in verbose mode

# Changes in 0.7

* use baseboard product name instead of system product name to identify model
* model communication with device after newer versions of the Windows driver
* add lots of supposedly supported models
* improved help
* improved logging

# Changes in 0.6

* add Eurocom M4 to supported systems

# Changes in 0.5

* add W230SD to supported systems

# Changes in 0.4

* add support for Python 3 (still works with Python 2)
* python-smbus is no longer required
* better error handling

# Changes in 0.3

* add "Mythlogic Chaos 1313-A" and "HUMA H3" to supported products
* add mute/unmute and some effects

# Changes in 0.2
Initial release
117 changes: 112 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,36 @@
# init-headphone-ubuntu
# init-headphone
Manage the headphone amplifier found in some Clevo laptops.
Can initialize the device if headphones are not working after suspend.

**Ubuntu package for [init-headphone](https://github.com/Unrud/init-headphone)**
**There are packages for
[Arch Linux](https://aur.archlinux.org/packages/init-headphone/),
[Fedora](https://github.com/letitz/init-headphone/releases) and
[Ubuntu](https://github.com/Unrud/init-headphone-ubuntu/releases)**

## Installation

To install just run:

./autogen.sh
./configure
make
make install

If **systemd** is available, a unit file that starts the program automatically
gets installed. To enable it run:

systemctl enable init-headphone

If you are not using **systemd**, take a look at the *etc/* folder. It
includes example configuration for **pm-utils** and **upstart** to start the
program automatically.

On older Linux versions you might have to add the kernel parameter
``acpi_enforce_resources=lax`` to make the i2c driver work.

## Usage
```
$ init-headphone --help
init-headphone --help
usage: init-headphone [-h] [--version] [-v] [command]
Manage the headphone amplifier found in some Clevo laptops
Expand Down Expand Up @@ -34,8 +58,91 @@ available commands:
recovery
```

## Troubleshoot

* Run **init-headphone** with the ``--verbose`` argument.
* Use ``dmesg | grep i801`` to check for problems with the SMBus driver.
* List all i2c busses with ``i2cdetect -l`` to find the full name of the bus
**SMBus I801 adapter...**. Use that name as a argument for **i2cdetect**
like ``i2cdetect "SMBus I801 adapter at f040"``. This should show the
headphone amplifier at address 0x73.

## Information about the Windows driver

The Windows driver consists of a kernel space component **SvThANSP.sys** and a
user space component **hp.dll**.
The kernel driver is simple and only supports a few commands that allow access
to I/O ports.
**hp.dll** uses [DeviceIoControl](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216%28v=vs.85%29.aspx)
to communicates via the device file ``\\.\SvANSPDo`` with the driver.
It talks directly to the SMBus controller.

**hp.dll** exports the functions ``InitHeadphone()``, ``Set_Mute(bool)`` and
``Set_effect(int)`` to control the headphone amplifier which is connected to
the SMBus.

If you are interested in analyzing or running the Windows driver on Linux
(with [Wine](https://winehq.org)) take a look at: https://github.com/Unrud/init-headphone-tools

### Supported ``dwIoControlCode``s for [DeviceIoControl](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216%28v=vs.85%29.aspx)

#### 0x9C402494: Enumerate PCI device

``lpInBuffer`` amd ``lpOutBuffer`` look something like:

```c
struct {
int bus,
int device,
int func,
int pcireg,
int result,
int unused
}
```

The driver reads a word from the register ``pcireg`` of the with
``bus``, ``device`` and ``func`` specified PCI device.
The result is returned in ``result``. If the register doesn't exist, the
returned result is 0xffff. It's used to find the SMBus controller by
**hp.dll**.

#### 0x9C4024D0: Read byte

``lpInBuffer`` amd ``lpOutBuffer`` look something like:

```c
struct {
int address,
int data_read,
int data_write
}
```

The driver reads one byte from ``address``. The result is returned in
``data_read``.

#### 0x9C4024C4: Write byte

``lpInBuffer`` amd ``lpOutBuffer`` are the same as above.

The driver writes one byte ``data_write`` to ``address``.

### SMBus controller

A detailed description of the controller is available in the
[chipset datasheet](https://www-ssl.intel.com/content/dam/www/public/us/en/documents/datasheets/8-series-chipset-pch-datasheet.pdf).

The important registers are:

* **Transmit Slave Address Register**: Bit 0 indicates direction, the other
7 Bits are the device address (Offset: 0x4)
* **Host Command Register**: Command (Offset: 0x3)
* **Host Data 0 Register**: Data (Offset: 0x5)

## Supported models
This list is subject to change. If the headphone jack is not working after suspend, the model is probably supported.
This list is subject to change. If the headphone jack is not working after
suspend, the model is probably supported.

```x is used as wildcard```
* N151SD/N155SD/N170SD/N150SC/N151SC/N155SC
Expand Down Expand Up @@ -77,4 +184,4 @@ This list is subject to change. If the headphone jack is not working after suspe
* W970TUQ
* WA50SBQ
* WA50SFQ Series
* WA50SJQ Series
* WA50SJQ Series
8 changes: 8 additions & 0 deletions autogen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/sh
AUTORECONF="$(which autoreconf)"
if test -z "$AUTORECONF"
then
echo "*** Error: autoreconf not found"
exit 1
fi
"$AUTORECONF" --force --install --verbose || exit $?
23 changes: 23 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
AC_INIT([init-headphone], [0.12])

PKG_PROG_PKG_CONFIG
AC_ARG_WITH([systemdsystemunitdir],
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
[with_systemdsystemunitdir=auto])
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
with_systemdsystemunitdir=no],
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])

AM_INIT_AUTOMAKE([foreign])

AC_CONFIG_FILES([Makefile])

AC_OUTPUT
6 changes: 6 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
init-headphone (0.12.0) stable;

* improve logging

-- Unrud <unrud@openaliasbox.org> Wed, 22 Mar 2017 18:38:45 +0100

init-headphone (0.11.0) stable;

* show error message if library not found
Expand Down
2 changes: 1 addition & 1 deletion debian/init-headphone.service
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[Unit]
Description=Initialize headphone amplifier found in some Clevo laptops
Description=Initialize headphone amplifier
After=suspend.target
After=hibernate.target
After=hybrid-sleep.target
Expand Down
2 changes: 1 addition & 1 deletion debian/init-headphone.upstart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
description "Initialize headphone amplifier found in some Clevo laptops"
description "Initialize headphone amplifier"

start on runlevel [2345]

Expand Down
2 changes: 1 addition & 1 deletion etc/linux-systemd/init-headphone.service
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[Unit]
Description=Initialize headphone amplifier found in some Clevo laptops
Description=Initialize headphone amplifier
After=suspend.target
After=hibernate.target
After=hybrid-sleep.target
Expand Down
15 changes: 15 additions & 0 deletions init-headphone.service.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[Unit]
Description=Initialize headphone amplifier
After=suspend.target
After=hibernate.target
After=hybrid-sleep.target

[Service]
Type=oneshot
ExecStart=${sbindir}/init-headphone

[Install]
WantedBy=suspend.target
WantedBy=hibernate.target
WantedBy=hybrid-sleep.target
WantedBy=multi-user.target
35 changes: 32 additions & 3 deletions src/init-headphone
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ import ctypes
import logging
import os
import subprocess
import sys
import traceback

__all__ = ["init", "set_mute", "set_effect", "recovery"]

VERSION = "0.11"
VERSION = "0.12"
SUPPORTED_I2C_BUS_NAMES = ["SMBus I801 adapter"]
I2C_CLASS_PATH = "/sys/class/i2c-dev"
DEV_PATH = "/dev"
Expand Down Expand Up @@ -116,7 +117,7 @@ class SMBus(object):
err = self.__libc.ioctl(self.__fd, I2C_SLAVE, address)
if err != 0:
self.__logger.error("Can't set I2C slave address")
raise RuntimeError("Can't set I2C slave address")
raise OSError(err, os.strerror(err))
self.__address = address

def __access(self, read_write, device_cmd, size, data):
Expand All @@ -131,7 +132,7 @@ class SMBus(object):
err = self.__libc.ioctl(self.__fd, I2C_SMBUS, ctypes.byref(args))
if err != 0:
self.__logger.error("Can't transfer data on I2C bus")
raise RuntimeError("Can't transfer data on I2C bus")
raise OSError(err, os.strerror(err))

def write_byte_data(self, device_cmd, value):
self.__logger.info("Writing byte data on I2C bus: "
Expand Down Expand Up @@ -317,7 +318,35 @@ def main():
recovery()


class ColorStreamHandler(logging.StreamHandler):
def emit(self, record):
try:
FORMAT_SEQ = "\033[%dm"
fmt = 0 # reset
if record.levelno >= logging.ERROR:
fmt = 31 # red
elif record.levelno >= logging.WARNING:
fmt = 33 # yellow
msg = self.format(record)
if hasattr(self.stream, 'isatty') and self.stream.isatty():
fs = (FORMAT_SEQ + "%s" + FORMAT_SEQ + "\n") % (fmt, msg, 0)
else:
fs = "%s\n" % msg
self.stream.write(fs)
self.flush()
except Exception:
self.handleError(record)


def setup_logging():
ch = ColorStreamHandler(sys.stderr)
fmt = logging.Formatter('%(levelname)s:%(message)s')
ch.setFormatter(fmt)
logging.getLogger().addHandler(ch)


if __name__ == "__main__":
setup_logging()
try:
main()
except Exception:
Expand Down

0 comments on commit 9893eb6

Please sign in to comment.